(* Upadted for the latest version of HOL Light (JULY 2014) *)
(* ========================================================================= *)
(* A library for vectors of complex numbers. *)
(* Much inspired from HOL-Light real vector library <"vectors.ml">. *)
(* *)
(* (c) Copyright, Sanaz Khan Afshar & Vincent Aravantinos 2011-13 *)
(* Hardware Verification Group, *)
(* Concordia University *)
(* *)
(* Contact: <s_khanaf@encs.concordia.ca> *)
(* <vincent@encs.concordia.ca> *)
(* *)
(* Acknowledgements: *)
(* - Harsh Singhal: n-dimensional dot product, utility theorems *)
(* *)
(* Last update: July 2013 *)
(* *)
(* ========================================================================= *)
(* ========================================================================= *)
(* ADDITIONS TO THE BASE LIBRARY *)
(* ========================================================================= *)
(* ----------------------------------------------------------------------- *)
(* Additional tacticals *)
(* ----------------------------------------------------------------------- *)
let SINGLE f x = f [x];;
let distrib fs x = map (fun f -> f x) fs;;
let DISTRIB ttacs x = EVERY (distrib ttacs x);;
let REWRITE_TACS = MAP_EVERY (SINGLE REWRITE_TAC);;
let GCONJUNCTS thm = map GEN_ALL (CONJUNCTS (SPEC_ALL thm));;
(* ----------------------------------------------------------------------- *)
(* Additions to the vectors library *)
(* ----------------------------------------------------------------------- *)
(* ----------------------------------------------------------------------- *)
(* Additions to the library of complex numbers *)
(* ----------------------------------------------------------------------- *)
(* Lemmas *)
let [RE_NORM;IM_NORM;ABS_RE_NORM;ABS_IM_NORM] = GCONJUNCTS RE_IM_NORM;;
let NORM_RE = prove
(`!x. &0 <=
norm x + Re x /\ &0 <=
norm x - Re x`,
GEN_TAC THEN MP_TAC (SPEC_ALL ABS_RE_NORM) THEN REAL_ARITH_TAC);;
let [NORM_RE_ADD;NORM_RE_SUB] = GCONJUNCTS NORM_RE;;
let COMPLEX_EQ_RCANCEL_IMP = GEN_ALL (MATCH_MP (MESON []
`(p <=> r \/ q) ==> (p /\ ~r ==> q) `) (SPEC_ALL COMPLEX_EQ_MUL_RCANCEL));;
let COMPLEX_BALANCE_DIV_MUL = prove
(`!x y z t. ~(z=Cx(&0)) ==> (x = y/z * t <=> x*z = y * t)`,
REPEAT STRIP_TAC THEN POP_ASSUM (fun x ->
ASSUME_TAC (REWRITE_RULE[x] (SPEC_ALL
COMPLEX_EQ_MUL_RCANCEL))
THEN ASSUME_TAC (REWRITE_RULE[x] (SPECL [`x:complex`;`z:complex`]
COMPLEX_DIV_RMUL)))
THEN SUBGOAL_THEN `x=y/z*t <=> x*z=(y/z*t)*z:complex` (SINGLE REWRITE_TAC)
THENL [ASM_REWRITE_TAC[];
REWRITE_TAC[SIMPLE_COMPLEX_ARITH `(y/z*t)*z=(y/z*z)*t:complex`]
THEN ASM_REWRITE_TAC[]]);;
(* ----------------------------------------------------------------------- *)
(* Additions to the topology library *)
(* ----------------------------------------------------------------------- *)
prioritize_vector ();;
(* Lemmas *)
(* ----------------------------------------------------------------------- *)
(* Embedding of reals in complex numbers *)
(* ----------------------------------------------------------------------- *)
(* TODO
*let RE_EQ_NORM = prove(`!x. Re x = norm x <=> real x /\ &0 <= real_of_complex x`,
*)
(* ----------------------------------------------------------------------- *)
(* Additions to the vectors library *)
(* ----------------------------------------------------------------------- *)
let COMPONENT_TAC x =
REPEAT GEN_TAC THEN CHOOSE_TAC (SPEC_ALL FINITE_INDEX_INRANGE_2)
THEN ASM_SIMP_TAC[x;LAMBDA_BETA];;
let COMMON_TAC =
REWRITE_TAC[vector_const;vector_map;vector_map2;vector_map3]
THEN ONCE_REWRITE_TAC[CART_EQ] THEN SIMP_TAC[LAMBDA_BETA;o_DEF];;
let VECTOR_MAP_SIMP_TAC = REWRITE_TAC[
VECTOR_MAP_VECTOR_CONST;VECTOR_MAP2_LVECTOR_CONST;
VECTOR_MAP2_RVECTOR_CONST;VECTOR_MAP_VECTOR_MAP;VECTOR_MAP2_RVECTOR_MAP;
VECTOR_MAP2_LVECTOR_MAP;VECTOR_MAP2_RVECTOR_MAP2;VECTOR_MAP2_LVECTOR_MAP2;
VECTOR_MAP_VECTOR_MAP2];;
let VECTOR_MAP_PROPERTY_TAC fs prop =
REWRITE_TAC fs THEN VECTOR_MAP_SIMP_TAC THEN ONCE_REWRITE_TAC[CART_EQ]
THEN REWRITE_TAC[VECTOR_MAP_COMPONENT;VECTOR_MAP2_COMPONENT;
VECTOR_MAP3_COMPONENT;VECTOR_CONST_COMPONENT;o_DEF;prop];;
let VECTOR_MAP_PROPERTY thm f prop =
prove(thm,VECTOR_MAP_PROPERTY_TAC f prop);;
let COMMON_TAC x =
SIMP_TAC[CART_EQ;pastecart;x;LAMBDA_BETA] THEN REPEAT STRIP_TAC
THEN REPEAT COND_CASES_TAC THEN POP_ASSUM MP_TAC THEN REWRITE_TAC[]
THEN SUBGOAL_THEN `1<= i-dimindex(:N) /\ i-dimindex(:N) <= dimindex(:M)`
ASSUME_TAC
THEN ASM_SIMP_TAC[LAMBDA_BETA]
THEN REPEAT (POP_ASSUM (MP_TAC o REWRITE_RULE[DIMINDEX_FINITE_SUM]))
THEN ARITH_TAC;;
(* ========================================================================= *)
(* BASIC ARITHMETIC *)
(* ========================================================================= *)
make_overloadable "%" `:A-> B-> B`;;
let prioritize_cvector () =
overload_interface("--",`(cvector_neg):complex^N->complex^N`);
overload_interface("+",`(cvector_add):complex^N->complex^N->complex^N`);
overload_interface("-",`(cvector_sub):complex^N->complex^N->complex^N`);
overload_interface("%",`(cvector_mul):complex->complex^N->complex^N`);;
overload_interface("%",`(%):real->real^N->real^N`);;
prioritize_cvector ();;
(* Simple generic tactic adapted from VECTOR_ARITH_TAC *)
let CVECTOR_ARITH_TAC =
let RENAMED_LAMBDA_BETA th =
if fst(dest_fun_ty(type_of(funpow 3 rand (concl th)))) = aty
then INST_TYPE [aty,bty; bty,aty] LAMBDA_BETA else LAMBDA_BETA
in
POP_ASSUM_LIST(K ALL_TAC) THEN
REPEAT(GEN_TAC ORELSE CONJ_TAC ORELSE DISCH_TAC ORELSE EQ_TAC) THEN
REPEAT(POP_ASSUM MP_TAC) THEN REWRITE_TAC[IMP_IMP; GSYM CONJ_ASSOC] THEN
GEN_REWRITE_TAC ONCE_DEPTH_CONV [CART_EQ] THEN
REWRITE_TAC[AND_FORALL_THM] THEN TRY EQ_TAC THEN
TRY(MATCH_MP_TAC MONO_FORALL) THEN GEN_TAC THEN
REWRITE_TAC[TAUT `(a ==> b) /\ (a ==> c) <=> a ==> b /\ c`;
TAUT `(a ==> b) \/ (a ==> c) <=> a ==> b \/ c`] THEN
TRY(MATCH_MP_TAC(TAUT `(a ==> b ==> c) ==> (a ==> b) ==> (a ==> c)`)) THEN
REWRITE_TAC[cvector_zero;cvector_add; cvector_sub; cvector_neg; cvector_mul; vector_map;vector_map2;vector_const] THEN
DISCH_THEN(fun th -> REWRITE_TAC[MATCH_MP(RENAMED_LAMBDA_BETA th) th]) THEN
SIMPLE_COMPLEX_ARITH_TAC;;
let CVECTOR_ARITH tm = prove(tm,CVECTOR_ARITH_TAC);;
(* ========================================================================= *)
(* VECTOR SPACE AXIOMS AND ADDITIONAL BASIC RESULTS *)
(* ========================================================================= *)
let CVECTOR_MAP_PROPERTY thm =
VECTOR_MAP_PROPERTY thm [cvector_zero;cvector_add;cvector_sub;cvector_neg;
cvector_mul];;
let CVECTOR_ADD_SYM = CVECTOR_MAP_PROPERTY
`!x y:complex^N. x + y = y + x`
COMPLEX_ADD_SYM;;
let CVECTOR_ADD_ASSOC = CVECTOR_MAP_PROPERTY
`!x y z:complex^N. x + (y + z) = (x + y) + z`
COMPLEX_ADD_ASSOC;;
let CVECTOR_ADD_ID = CVECTOR_MAP_PROPERTY
`!x:complex^N. x + cvector_zero = x /\ cvector_zero + x = x`
(CONJ COMPLEX_ADD_RID COMPLEX_ADD_LID);;
let [CVECTOR_ADD_RID;CVECTOR_ADD_LID] = GCONJUNCTS CVECTOR_ADD_ID;;
let CVECTOR_ADD_INV = CVECTOR_MAP_PROPERTY
`!x:complex^N. x + -- x = cvector_zero /\ --x + x = cvector_zero`
(CONJ COMPLEX_ADD_RINV COMPLEX_ADD_LINV);;
let CVECTOR_MUL_ASSOC = CVECTOR_MAP_PROPERTY
`!a b x:complex^N. a % (b % x) = (a * b) % x`
COMPLEX_MUL_ASSOC;;
let CVECTOR_SUB_LDISTRIB = CVECTOR_MAP_PROPERTY
`!c x y:complex^N. c % (x - y) = c % x - c % y`
COMPLEX_SUB_LDISTRIB;;
let CVECTOR_SCALAR_RDIST = CVECTOR_MAP_PROPERTY
`!a b x:complex^N. (a + b) % x = a % x + b % x`
COMPLEX_ADD_RDISTRIB;;
let CVECTOR_MUL_ID = CVECTOR_MAP_PROPERTY
`!x:complex^N. Cx(&1) % x = x`
COMPLEX_MUL_LID;;
let CVECTOR_SUB_REFL = CVECTOR_MAP_PROPERTY
`!x:complex^N. x - x = cvector_zero`
COMPLEX_SUB_REFL;;
let CVECTOR_SUB_RADD = CVECTOR_MAP_PROPERTY
`!x y:complex^N. x - (x + y) = --y`
COMPLEX_ADD_SUB2;;
let CVECTOR_NEG_SUB = CVECTOR_MAP_PROPERTY
`!x y:complex^N. --(x - y) = y - x`
COMPLEX_NEG_SUB;;
let CVECTOR_SUB_EQ = CVECTOR_MAP_PROPERTY
`!x y:complex^N. (x - y = cvector_zero) <=> (x = y)`
COMPLEX_SUB_0;;
let CVECTOR_MUL_LZERO = CVECTOR_MAP_PROPERTY
`!x. Cx(&0) % x = cvector_zero`
COMPLEX_MUL_LZERO;;
let CVECTOR_SUB_ADD = CVECTOR_MAP_PROPERTY
`!x y:complex^N. (x - y) + y = x`
COMPLEX_SUB_ADD;;
let CVECTOR_SUB_ADD2 = CVECTOR_MAP_PROPERTY
`!x y:complex^N. y + (x - y) = x`
COMPLEX_SUB_ADD2;;
let CVECTOR_ADD_LDISTRIB = CVECTOR_MAP_PROPERTY
`!c x y:complex^N. c % (x + y) = c % x + c % y`
COMPLEX_ADD_LDISTRIB;;
let CVECTOR_ADD_RDISTRIB = CVECTOR_MAP_PROPERTY
`!a b x:complex^N. (a + b) % x = a % x + b % x`
COMPLEX_ADD_RDISTRIB;;
let CVECTOR_SUB_RDISTRIB = CVECTOR_MAP_PROPERTY
`!a b x:complex^N. (a - b) % x = a % x - b % x`
COMPLEX_SUB_RDISTRIB;;
let CVECTOR_ADD_SUB = CVECTOR_MAP_PROPERTY
`!x y:complex^N. (x + y:complex^N) - x = y`
COMPLEX_ADD_SUB;;
let CVECTOR_EQ_ADDR = CVECTOR_MAP_PROPERTY
`!x y:complex^N. (x + y = x) <=> (y = cvector_zero)`
COMPLEX_EQ_ADD_LCANCEL_0;;
let CVECTOR_SUB = CVECTOR_MAP_PROPERTY
`!x y:complex^N. x - y = x + --(y:complex^N)`
complex_sub;;
let CVECTOR_SUB_RZERO = CVECTOR_MAP_PROPERTY
`!x:complex^N. x - cvector_zero = x`
COMPLEX_SUB_RZERO;;
let CVECTOR_MUL_RZERO = CVECTOR_MAP_PROPERTY
`!c:complex. c % cvector_zero = cvector_zero`
COMPLEX_MUL_RZERO;;
let CVECTOR_MUL_LZERO = CVECTOR_MAP_PROPERTY
`!x:complex^N. Cx(&0) % x = cvector_zero`
COMPLEX_MUL_LZERO;;
let CVECTOR_NEG_MINUS1 = CVECTOR_MAP_PROPERTY
`!x:complex^N. --x = (--(Cx(&1))) % x`
(GSYM COMPLEX_NEG_MINUS1);;
let CVECTOR_SUB_LZERO = CVECTOR_MAP_PROPERTY
`!x:complex^N. cvector_zero - x = --x`
COMPLEX_SUB_LZERO;;
let CVECTOR_NEG_NEG = CVECTOR_MAP_PROPERTY
`!x:complex^N. --(--(x:complex^N)) = x`
COMPLEX_NEG_NEG;;
let CVECTOR_MUL_LNEG = CVECTOR_MAP_PROPERTY
`!c x:complex^N. --c % x = --(c % x)`
COMPLEX_MUL_LNEG;;
let CVECTOR_MUL_RNEG = CVECTOR_MAP_PROPERTY
`!c x:complex^N. c % --x = --(c % x)`
COMPLEX_MUL_RNEG;;
let CVECTOR_NEG_0 = CVECTOR_MAP_PROPERTY
`--cvector_zero = cvector_zero`
COMPLEX_NEG_0;;
let CVECTOR_NEG_EQ_0 = CVECTOR_MAP_PROPERTY
`!x:complex^N. --x = cvector_zero <=> x = cvector_zero`
COMPLEX_NEG_EQ_0;;
let CVECTOR_ADD_AC = prove
(`!x y z:complex^N.
(x + y = y + x) /\ ((x + y) + z = x + y + z) /\ (x + y + z = y + x + z)`,
MESON_TAC[CVECTOR_ADD_SYM;CVECTOR_ADD_ASSOC]);;
(* ========================================================================= *)
(* LINEARITY *)
(* ========================================================================= *)
let COMMON_TAC additional_thms =
SIMP_TAC[clinear] THEN REPEAT STRIP_TAC THEN ONCE_REWRITE_TAC[CART_EQ]
THEN SIMP_TAC(CVECTOR_ADD_COMPONENT::CVECTOR_MUL_COMPONENT::additional_thms)
THEN SIMPLE_COMPLEX_ARITH_TAC;;
let CLINEAR_CMUL = prove
(`!f:complex^M->complex^N c x. clinear f ==> (f (c % x) = c % f x)`,
SIMP_TAC[clinear]);;
let CLINEAR_NEG = prove
(`!f:complex^M->complex^N x. clinear f ==> (f (--x) = --(f x))`,
ONCE_REWRITE_TAC[CVECTOR_NEG_MINUS1] THEN SIMP_TAC[
CLINEAR_CMUL]);;
let CLINEAR_ADD = prove
(`!f:complex^M->complex^N x y. clinear f ==> (f (x + y) = f x + f y)`,
SIMP_TAC[clinear]);;
(* ========================================================================= *)
(* PASTING COMPLEX VECTORS *)
(* ========================================================================= *)
let FSTCART_CLINEAR = CONJUNCT1 CLINEAR_FSTCART_SNDCART;;
let SNDCART_CLINEAR = CONJUNCT2 CLINEAR_FSTCART_SNDCART;;
let FSTCART_CVECTOR_ZERO = CONJUNCT1 FSTCART_SNDCART_CVECTOR_ZERO;;
let SNDCART_CVECTOR_ZERO = CONJUNCT2 FSTCART_SNDCART_CVECTOR_ZERO;;
let PASTECART_TAC xs =
REWRITE_TAC(PASTECART_EQ::FSTCART_PASTECART::SNDCART_PASTECART::xs);;
(* ========================================================================= *)
(* REAL AND IMAGINARY VECTORS *)
(* ========================================================================= *)
(* ========================================================================= *)
(* FLATTENING COMPLEX VECTORS INTO REAL VECTORS *)
(* *)
(* Note: *)
(* Theoretically, the following could be defined more generally for matrices *)
(* instead of complex vectors, but this would require a "finite_prod" type *)
(* constructor, which is not available right now, and which, at first sight, *)
(* would probably require dependent types. *)
(* ========================================================================= *)
(* ========================================================================= *)
(* CONJUGATE VECTOR. *)
(* ========================================================================= *)
let CVECTOR_MAP_PROPERTY thm =
VECTOR_MAP_PROPERTY thm [cvector_zero;cvector_add;cvector_sub;cvector_neg;
cvector_mul;cvector_cnj;cvector_re;cvector_im];;
let CVECTOR_CNJ_ADD = CVECTOR_MAP_PROPERTY
`!x y:complex^N. cvector_cnj (x+y) = cvector_cnj x + cvector_cnj y`
CNJ_ADD;;
let CVECTOR_CNJ_SUB = CVECTOR_MAP_PROPERTY
`!x y:complex^N. cvector_cnj (x-y) = cvector_cnj x - cvector_cnj y`
CNJ_SUB;;
let CVECTOR_CNJ_NEG = CVECTOR_MAP_PROPERTY
`!x:complex^N. cvector_cnj (--x) = --(cvector_cnj x)`
CNJ_NEG;;
let CVECTOR_RE_CNJ = CVECTOR_MAP_PROPERTY
`!x:complex^N. cvector_re (cvector_cnj x) = cvector_re x`
RE_CNJ;;
let CVECTOR_CNJ_CNJ = CVECTOR_MAP_PROPERTY
`!x:complex^N. cvector_cnj (cvector_cnj x) = x`
CNJ_CNJ;;
(* ========================================================================= *)
(* CROSS PRODUCTS IN COMPLEX^3. *)
(* ========================================================================= *)
prioritize_vector();;
parse_as_infix("ccross",(20,"right"));;
let CCROSS_COMPONENT = prove
(`!x y:complex^3.
(x ccross y)$1 = x$2 * y$3 - x$3 * y$2
/\ (x ccross y)$2 = x$3 * y$1 - x$1 * y$3
/\ (x ccross y)$3 = x$1 * y$2 - x$2 * y$1`,
(* simple handy instantiation of CART_EQ for dimension 3*)
let CART_EQ3 = prove
(`!x y:complex^3. x = y <=> x$1 = y$1 /\ x$2 = y$2 /\ x$3 = y$3`,
GEN_REWRITE_TAC (PATH_CONV "rbrblr") [
CART_EQ]
THEN REWRITE_TAC[DIMINDEX_3;
FORALL_3]);;
let CCROSS_TAC lemmas =
REWRITE_TAC(CART_EQ3::CCROSS_COMPONENT::lemmas)
THEN SIMPLE_COMPLEX_ARITH_TAC;;
(* ========================================================================= *)
(* DOT PRODUCTS IN COMPLEX^N *)
(* *)
(* Only difference with the real case: *)
(* we take the conjugate of the 2nd argument *)
(* ========================================================================= *)
prioritize_complex();;
parse_as_infix("cdot",(20,"right"));;
(* The dot product is symmetric MODULO the conjugate *)
(* The following theorems are usual axioms of the hermitian dot product, they are proved later on.
* let CDOT_SELF_POS = prove(`!x:complex^N. &0 <= real_of_complex (x cdot x)`, ...
* let CDOT_EQ_0 = prove(`!x:complex^N. x cdot x = Cx(&0) <=> x = cvector_zero`
*)
(* Cauchy Schwarz inequality: proved later on
* let CDOT_CAUCHY_SCHWARZ = prove (`!x y:complex^N. norm (x cdot y) pow 2 <= cnorm2 x * cnorm2 y`,
* let CDOT_CAUCHY_SCHWARZ_EQUAL = prove(`!x y:complex^N. norm (x cdot y) pow 2 = cnorm2 x * cnorm2 y <=> collinear_cvectors x y`,
*)
let CDOT3 = prove
(`!x y:complex^3.
x cdot y = (x$1 *
cnj (y$1) + x$2 *
cnj (y$2) + x$3 *
cnj (y$3))`,
REWRITE_TAC[cdot] THEN SIMP_TAC [DIMINDEX_3] THEN REWRITE_TAC[
VSUM_3]);;
(* ========================================================================= *)
(* RELATION WITH REAL DOT AND CROSS PRODUCTS *)
(* ========================================================================= *)
(* ========================================================================= *)
(* NORM, UNIT VECTORS. *)
(* ========================================================================= *)
let CNORM2_SUB = prove
(`!x y:complex^N. cnorm2 (x-y) = cnorm2 (y-x)`,
REWRITE_TAC[cnorm2;
CDOT_LSUB;
CDOT_RSUB] THEN REPEAT GEN_TAC THEN AP_TERM_TAC
THEN SIMPLE_COMPLEX_ARITH_TAC);;
overload_interface ("norm",`cnorm:complex^N->real`);;
(* Triangle inequality. Proved later on using Cauchy Schwarz inequality.
* let CNORM_TRIANGLE = prove(`!x y:complex^N. norm (x+y) <= norm x + norm y`, ...
*)
(* ========================================================================= *)
(* COLLINEARITY *)
(* ========================================================================= *)
(* Definition of collinearity between complex vectors.
* Note: This is different from collinearity between points (which is the one defined in HOL-Light library)
*)
let CVECTOR_MUL_INV = prove
(`!a x y:complex^N. ~(a = Cx(&0)) /\ x = a % y ==> y = inv a % x`,
REPEAT STRIP_TAC THEN ASM_SIMP_TAC[CVECTOR_MUL_ASSOC;
MESON[] `(p\/q) <=> (~p ==> q)`;
COMPLEX_MUL_LINV;CVECTOR_MUL_ID]);;
(* ========================================================================= *)
(* ORTHOGONALITY *)
(* ========================================================================= *)
let [CORTHOGONAL_LZERO;CORTHOGONAL_RZERO] = GCONJUNCTS CORTHOGONAL_0;;
let CORTHOGONAL_MUL_CLAUSES = prove
(`!x y a.
(corthogonal x y ==> corthogonal x (a%y))
/\ (corthogonal x y \/ a = Cx(&0) <=> corthogonal x (a%y))
/\ (corthogonal x y ==> corthogonal (a%x) y)
/\ (corthogonal x y \/ a = Cx(&0) <=> corthogonal (a%x) y)`,
let [CORTHOGONAL_RMUL;CORTHOGONAL_RMUL_EQ;CORTHOGONAL_LMUL;
CORTHOGONAL_LMUL_EQ] = GCONJUNCTS CORTHOGONAL_MUL_CLAUSES;;
let CORTHOGONAL_LRMUL_CLAUSES = prove
(`!x y a b.
(corthogonal x y ==> corthogonal (a%x) (b%y))
/\ (corthogonal x y \/ a = Cx(&0) \/ b = Cx(&0)
<=> corthogonal (a%x) (b%y))`,
let [CORTHOGONAL_LRMUL;CORTHOGONAL_LRMUL_EQ] =
GCONJUNCTS CORTHOGONAL_LRMUL_CLAUSES;;
let [CORTHOGONAL_RREAL;CORTHOGONAL_LREAL] =
GCONJUNCTS CORTHOGONAL_REAL_CLAUSES;;
let CORTHOGONAL_UNIT = prove
(`!x y:complex^N.
(corthogonal x (cunit y) <=> corthogonal x y)
/\ (corthogonal (cunit x) y <=> corthogonal x y)`,
let [CORTHOGONAL_RUNIT;CORTHOGONAL_LUNIT] = GCONJUNCTS CORTHOGONAL_UNIT;;
(* ========================================================================= *)
(* VSUM *)
(* ========================================================================= *)
(* ========================================================================= *)
(* INFINITE SUM *)
(* ========================================================================= *)
let CINFSUM_LINEAR = prove
(`!f (h:complex^M->complex^N) s.
csummable s f /\ clinear h ==> cinfsum s (h o f) = h (cinfsum s f)`,