diff options
| author | Enrico Tassi | 2015-03-09 11:07:53 +0100 |
|---|---|---|
| committer | Enrico Tassi | 2015-03-09 11:24:38 +0100 |
| commit | fc84c27eac260dffd8f2fb1cb56d599f1e3486d9 (patch) | |
| tree | c16205f1637c80833a4c4598993c29fa0fd8c373 /mathcomp/algebra/mxalgebra.v | |
Initial commit
Diffstat (limited to 'mathcomp/algebra/mxalgebra.v')
| -rw-r--r-- | mathcomp/algebra/mxalgebra.v | 2764 |
1 files changed, 2764 insertions, 0 deletions
diff --git a/mathcomp/algebra/mxalgebra.v b/mathcomp/algebra/mxalgebra.v new file mode 100644 index 0000000..54d5588 --- /dev/null +++ b/mathcomp/algebra/mxalgebra.v @@ -0,0 +1,2764 @@ +(* (c) Copyright Microsoft Corporation and Inria. All rights reserved. *) +Require Import ssreflect ssrbool ssrfun eqtype ssrnat seq div choice fintype. +Require Import finfun bigop prime binomial ssralg finset fingroup finalg. +Require Import perm zmodp matrix. + +(*****************************************************************************) +(* In this file we develop the rank and row space theory of matrices, based *) +(* on an extended Gaussian elimination procedure similar to LUP *) +(* decomposition. This provides us with a concrete but generic model of *) +(* finite dimensional vector spaces and F-algebras, in which vectors, linear *) +(* functions, families, bases, subspaces, ideals and subrings are all *) +(* represented using matrices. This model can be used as a foundation for *) +(* the usual theory of abstract linear algebra, but it can also be used to *) +(* develop directly substantial theories, such as the theory of finite group *) +(* linear representation. *) +(* Here we define the following concepts and notations: *) +(* Gaussian_elimination A == a permuted triangular decomposition (L, U, r) *) +(* of A, with L a column permutation of a lower triangular *) +(* invertible matrix, U a row permutation of an upper *) +(* triangular invertible matrix, and r the rank of A, all *) +(* satisfying the identity L *m pid_mx r *m U = A. *) +(* \rank A == the rank of A. *) +(* row_free A <=> the rows of A are linearly free (i.e., the rank and *) +(* height of A are equal). *) +(* row_full A <=> the row-space of A spans all row-vectors (i.e., the *) +(* rank and width of A are equal). *) +(* col_ebase A == the extended column basis of A (the first matrix L *) +(* returned by Gaussian_elimination A). *) +(* row_ebase A == the extended row base of A (the second matrix U *) +(* returned by Gaussian_elimination A). *) +(* col_base A == a basis for the columns of A: a row-full matrix *) +(* consisting of the first \rank A columns of col_ebase A. *) +(* row_base A == a basis for the rows of A: a row-free matrix consisting *) +(* of the first \rank A rows of row_ebase A. *) +(* pinvmx A == a partial inverse for A in its row space (or on its *) +(* column space, equivalently). In particular, if u is a *) +(* row vector in the row_space of A, then u *m pinvmx A is *) +(* the row vector of the coefficients of a decomposition *) +(* of u as a sub of rows of A. *) +(* kermx A == the row kernel of A : a square matrix whose row space *) +(* consists of all u such that u *m A = 0 (it consists of *) +(* the inverse of col_ebase A, with the top \rank A rows *) +(* zeroed out). Also, kermx A is a partial right inverse *) +(* to col_ebase A, in the row space anihilated by A. *) +(* cokermx A == the cokernel of A : a square matrix whose column space *) +(* consists of all v such that A *m v = 0 (it consists of *) +(* the inverse of row_ebase A, with the leftmost \rank A *) +(* columns zeroed out). *) +(* eigenvalue g a <=> a is an eigenvalue of the square matrix g. *) +(* eigenspace g a == a square matrix whose row space is the eigenspace of *) +(* the eigenvalue a of g (or 0 if a is not an eigenvalue). *) +(* We use a different scope %MS for matrix row-space set-like operations; to *) +(* avoid confusion, this scope should not be opened globally. Note that the *) +(* the arguments of \rank _ and the operations below have default scope %MS. *) +(* (A <= B)%MS <=> the row-space of A is included in the row-space of B. *) +(* We test for this by testing if cokermx B anihilates A. *) +(* (A < B)%MS <=> the row-space of A is properly included in the *) +(* row-space of B. *) +(* (A <= B <= C)%MS == (A <= B)%MS && (B <= C)%MS, and similarly for *) +(* (A < B <= C)%MS, (A < B <= C)%MS and (A < B < C)%MS. *) +(* (A == B)%MS == (A <= B <= A)%MS (A and B have the same row-space). *) +(* (A :=: B)%MS == A and B behave identically wrt. \rank and <=. This *) +(* triple rewrite rule is the Prop version of (A == B)%MS. *) +(* Note that :=: cannot be treated as a setoid-style *) +(* Equivalence because its arguments can have different *) +(* types: A and B need not have the same number of rows, *) +(* and often don't (e.g., in row_base A :=: A). *) +(* <<A>>%MS == a square matrix with the same row-space as A; <<A>>%MS *) +(* is a canonical representation of the subspace generated *) +(* by A, viewed as a list of row-vectors: if (A == B)%MS, *) +(* then <<A>>%MS = <<B>>%MS. *) +(* (A + B)%MS == a square matrix whose row-space is the sum of the *) +(* row-spaces of A and B; thus (A + B == col_mx A B)%MS. *) +(* (\sum_i <expr i>)%MS == the "big" version of (_ + _)%MS; as the latter *) +(* has a canonical abelian monoid structure, most generic *) +(* bigop lemmas apply (the other bigop indexing notations *) +(* are also defined). *) +(* (A :&: B)%MS == a square matrix whose row-space is the intersection of *) +(* the row-spaces of A and B. *) +(* (\bigcap_i <expr i>)%MS == the "big" version of (_ :&: _)%MS, which also *) +(* has a canonical abelian monoid structure. *) +(* A^C%MS == a square matrix whose row-space is a complement to the *) +(* the row-space of A (it consists of row_ebase A with the *) +(* top \rank A rows zeroed out). *) +(* (A :\: B)%MS == a square matrix whose row-space is a complement of the *) +(* the row-space of (A :&: B)%MS in the row-space of A. *) +(* We have (A :\: B := A :&: (capmx_gen A B)^C)%MS, where *) +(* capmx_gen A B is a rectangular matrix equivalent to *) +(* (A :&: B)%MS, i.e., (capmx_gen A B == A :&: B)%MS. *) +(* proj_mx A B == a square matrix that projects (A + B)%MS onto A *) +(* parellel to B, when (A :&: B)%MS = 0 (A and B must also *) +(* be square). *) +(* mxdirect S == the sum expression S is a direct sum. This is a NON *) +(* EXTENSIONAL notation: the exact boolean expression is *) +(* inferred from the syntactic form of S (expanding *) +(* definitions, however); both (\sum_(i | _) _)%MS and *) +(* (_ + _)%MS sums are recognized. This construct uses a *) +(* variant of the reflexive ("quote") canonical structure, *) +(* mxsum_expr. The structure also recognizes sums of *) +(* matrix ranks, so that lemmas concerning the rank of *) +(* direct sums can be used bidirectionally. *) +(* The next set of definitions let us represent F-algebras using matrices: *) +(* 'A[F]_(m, n) == the type of matrices encoding (sub)algebras of square *) +(* n x n matrices, via mxvec; as in the matrix type *) +(* notation, m and F can be omitted (m defaults to n ^ 2). *) +(* := 'M[F]_(m, n ^ 2). *) +(* (A \in R)%MS <=> the square matrix A belongs to the linear set of *) +(* matrices (most often, a sub-algebra) encoded by the *) +(* row space of R. This is simply notation, so all the *) +(* lemmas and rewrite rules for (_ <= _)%MS can apply. *) +(* := (mxvec A <= R)%MS. *) +(* (R * S)%MS == a square n^2 x n^2 matrix whose row-space encodes the *) +(* linear set of n x n matrices generated by the pointwise *) +(* product of the sets of matrices encoded by R and S. *) +(* 'C(R)%MS == a square matric encoding the centraliser of the set of *) +(* square matrices encoded by R. *) +(* 'C_S(R)%MS := (S :&: 'C(R))%MS (the centraliser of R in S). *) +(* 'Z(R)%MS == the center of R (i.e., 'C_R(R)%MS). *) +(* left_mx_ideal R S <=> S is a left ideal for R (R * S <= S)%MS. *) +(* right_mx_ideal R S <=> S is a right ideal for R (S * R <= S)%MS. *) +(* mx_ideal R S <=> S is a bilateral ideal for R. *) +(* mxring_id R e <-> e is an identity element for R (Prop predicate). *) +(* has_mxring_id R <=> R has a nonzero identity element (bool predicate). *) +(* mxring R <=> R encodes a nontrivial subring. *) +(*****************************************************************************) + +Set Implicit Arguments. +Unset Strict Implicit. +Unset Printing Implicit Defensive. + +Import GroupScope. +Import GRing.Theory. +Open Local Scope ring_scope. + +Reserved Notation "\rank A" (at level 10, A at level 8, format "\rank A"). +Reserved Notation "A ^C" (at level 8, format "A ^C"). + +Notation "''A_' ( m , n )" := 'M_(m, n ^ 2) + (at level 8, format "''A_' ( m , n )") : type_scope. + +Notation "''A_' ( n )" := 'A_(n ^ 2, n) + (at level 8, only parsing) : type_scope. + +Notation "''A_' n" := 'A_(n) + (at level 8, n at next level, format "''A_' n") : type_scope. + +Notation "''A' [ F ]_ ( m , n )" := 'M[F]_(m, n ^ 2) + (at level 8, only parsing) : type_scope. + +Notation "''A' [ F ]_ ( n )" := 'A[F]_(n ^ 2, n) + (at level 8, only parsing) : type_scope. + +Notation "''A' [ F ]_ n" := 'A[F]_(n) + (at level 8, n at level 2, only parsing) : type_scope. + +Delimit Scope matrix_set_scope with MS. + +Notation Local simp := (Monoid.Theory.simpm, oppr0). + +(*****************************************************************************) +(******************** Rank and row-space theory ******************************) +(*****************************************************************************) + +Section RowSpaceTheory. + +Variable F : fieldType. +Implicit Types m n p r : nat. + +Local Notation "''M_' ( m , n )" := 'M[F]_(m, n) : type_scope. +Local Notation "''M_' n" := 'M[F]_(n, n) : type_scope. + +(* Decomposition with double pivoting; computes the rank, row and column *) +(* images, kernels, and complements of a matrix. *) + +Fixpoint Gaussian_elimination {m n} : 'M_(m, n) -> 'M_m * 'M_n * nat := + match m, n with + | _.+1, _.+1 => fun A : 'M_(1 + _, 1 + _) => + if [pick ij | A ij.1 ij.2 != 0] is Some (i, j) then + let a := A i j in let A1 := xrow i 0 (xcol j 0 A) in + let u := ursubmx A1 in let v := a^-1 *: dlsubmx A1 in + let: (L, U, r) := Gaussian_elimination (drsubmx A1 - v *m u) in + (xrow i 0 (block_mx 1 0 v L), xcol j 0 (block_mx a%:M u 0 U), r.+1) + else (1%:M, 1%:M, 0%N) + | _, _ => fun _ => (1%:M, 1%:M, 0%N) + end. + +Section Defs. + +Variables (m n : nat) (A : 'M_(m, n)). + +Fact Gaussian_elimination_key : unit. Proof. by []. Qed. + +Let LUr := locked_with Gaussian_elimination_key (@Gaussian_elimination) m n A. + +Definition col_ebase := LUr.1.1. +Definition row_ebase := LUr.1.2. +Definition mxrank := if [|| m == 0 | n == 0]%N then 0%N else LUr.2. + +Definition row_free := mxrank == m. +Definition row_full := mxrank == n. + +Definition row_base : 'M_(mxrank, n) := pid_mx mxrank *m row_ebase. +Definition col_base : 'M_(m, mxrank) := col_ebase *m pid_mx mxrank. + +Definition complmx : 'M_n := copid_mx mxrank *m row_ebase. +Definition kermx : 'M_m := copid_mx mxrank *m invmx col_ebase. +Definition cokermx : 'M_n := invmx row_ebase *m copid_mx mxrank. + +Definition pinvmx : 'M_(n, m) := + invmx row_ebase *m pid_mx mxrank *m invmx col_ebase. + +End Defs. + +Arguments Scope mxrank [nat_scope nat_scope matrix_set_scope]. +Local Notation "\rank A" := (mxrank A) : nat_scope. +Arguments Scope complmx [nat_scope nat_scope matrix_set_scope]. +Local Notation "A ^C" := (complmx A) : matrix_set_scope. + +Definition submx_def := idfun (fun m1 m2 n (A : 'M_(m1, n)) (B : 'M_(m2, n)) => + A *m cokermx B == 0). +Fact submx_key : unit. Proof. by []. Qed. +Definition submx := locked_with submx_key submx_def. +Canonical submx_unlockable := [unlockable fun submx]. + +Arguments Scope submx + [nat_scope nat_scope nat_scope matrix_set_scope matrix_set_scope]. +Prenex Implicits submx. +Local Notation "A <= B" := (submx A B) : matrix_set_scope. +Local Notation "A <= B <= C" := ((A <= B) && (B <= C))%MS : matrix_set_scope. +Local Notation "A == B" := (A <= B <= A)%MS : matrix_set_scope. + +Definition ltmx m1 m2 n (A : 'M_(m1, n)) (B : 'M_(m2, n)) := + (A <= B)%MS && ~~ (B <= A)%MS. +Arguments Scope ltmx + [nat_scope nat_scope nat_scope matrix_set_scope matrix_set_scope]. +Prenex Implicits ltmx. +Local Notation "A < B" := (ltmx A B) : matrix_set_scope. + +Definition eqmx m1 m2 n (A : 'M_(m1, n)) (B : 'M_(m2, n)) := + prod (\rank A = \rank B) + (forall m3 (C : 'M_(m3, n)), + ((A <= C) = (B <= C)) * ((C <= A) = (C <= B)))%MS. +Arguments Scope eqmx + [nat_scope nat_scope nat_scope matrix_set_scope matrix_set_scope]. +Local Notation "A :=: B" := (eqmx A B) : matrix_set_scope. + +Section LtmxIdentities. + +Variables (m1 m2 n : nat) (A : 'M_(m1, n)) (B : 'M_(m2, n)). + +Lemma ltmxE : (A < B)%MS = ((A <= B)%MS && ~~ (B <= A)%MS). Proof. by []. Qed. + +Lemma ltmxW : (A < B)%MS -> (A <= B)%MS. Proof. by case/andP. Qed. + +Lemma ltmxEneq : (A < B)%MS = (A <= B)%MS && ~~ (A == B)%MS. +Proof. by apply: andb_id2l => ->. Qed. + +Lemma submxElt : (A <= B)%MS = (A == B)%MS || (A < B)%MS. +Proof. by rewrite -andb_orr orbN andbT. Qed. + +End LtmxIdentities. + +(* The definition of the row-space operator is rigged to return the identity *) +(* matrix for full matrices. To allow for further tweaks that will make the *) +(* row-space intersection operator strictly commutative and monoidal, we *) +(* slightly generalize some auxiliary definitions: we parametrize the *) +(* "equivalent subspace and identity" choice predicate equivmx by a boolean *) +(* determining whether the matrix should be the identity (so for genmx A its *) +(* value is row_full A), and introduce a "quasi-identity" predicate qidmx *) +(* that selects non-square full matrices along with the identity matrix 1%:M *) +(* (this does not affect genmx, which chooses a square matrix). *) +(* The choice witness for genmx A is either 1%:M for a row-full A, or else *) +(* row_base A padded with null rows. *) +Let qidmx m n (A : 'M_(m, n)) := + if m == n then A == pid_mx n else row_full A. +Let equivmx m n (A : 'M_(m, n)) idA (B : 'M_n) := + (B == A)%MS && (qidmx B == idA). +Let equivmx_spec m n (A : 'M_(m, n)) idA (B : 'M_n) := + prod (B :=: A)%MS (qidmx B = idA). +Definition genmx_witness m n (A : 'M_(m, n)) : 'M_n := + if row_full A then 1%:M else pid_mx (\rank A) *m row_ebase A. +Definition genmx_def := idfun (fun m n (A : 'M_(m, n)) => + choose (equivmx A (row_full A)) (genmx_witness A) : 'M_n). +Fact genmx_key : unit. Proof. by []. Qed. +Definition genmx := locked_with genmx_key genmx_def. +Canonical genmx_unlockable := [unlockable fun genmx]. +Local Notation "<< A >>" := (genmx A) : matrix_set_scope. + +(* The setwise sum is tweaked so that 0 is a strict identity element for *) +(* square matrices, because this lets us use the bigop component. As a result *) +(* setwise sum is not quite strictly extensional. *) +Let addsmx_nop m n (A : 'M_(m, n)) := conform_mx <<A>>%MS A. +Definition addsmx_def := idfun (fun m1 m2 n (A : 'M_(m1, n)) (B : 'M_(m2, n)) => + if A == 0 then addsmx_nop B else if B == 0 then addsmx_nop A else + <<col_mx A B>>%MS : 'M_n). +Fact addsmx_key : unit. Proof. by []. Qed. +Definition addsmx := locked_with addsmx_key addsmx_def. +Canonical addsmx_unlockable := [unlockable fun addsmx]. +Arguments Scope addsmx + [nat_scope nat_scope nat_scope matrix_set_scope matrix_set_scope]. +Prenex Implicits addsmx. +Local Notation "A + B" := (addsmx A B) : matrix_set_scope. +Local Notation "\sum_ ( i | P ) B" := (\big[addsmx/0]_(i | P) B%MS) + : matrix_set_scope. +Local Notation "\sum_ ( i <- r | P ) B" := (\big[addsmx/0]_(i <- r | P) B%MS) + : matrix_set_scope. + +(* The set intersection is similarly biased so that the identity matrix is a *) +(* strict identity. This is somewhat more delicate than for the sum, because *) +(* the test for the identity is non-extensional. This forces us to actually *) +(* bias the choice operator so that it does not accidentally map an *) +(* intersection of non-identity matrices to 1%:M; this would spoil *) +(* associativity: if B :&: C = 1%:M but B and C are not identity, then for a *) +(* square matrix A we have A :&: (B :&: C) = A != (A :&: B) :&: C in general. *) +(* To complicate matters there may not be a square non-singular matrix *) +(* different than 1%:M, since we could be dealing with 'M['F_2]_1. We *) +(* sidestep the issue by making all non-square row-full matrices identities, *) +(* and choosing a normal representative that preserves the qidmx property. *) +(* Thus A :&: B = 1%:M iff A and B are both identities, and this suffices for *) +(* showing that associativity is strict. *) +Let capmx_witness m n (A : 'M_(m, n)) := + if row_full A then conform_mx 1%:M A else <<A>>%MS. +Let capmx_norm m n (A : 'M_(m, n)) := + choose (equivmx A (qidmx A)) (capmx_witness A). +Let capmx_nop m n (A : 'M_(m, n)) := conform_mx (capmx_norm A) A. +Definition capmx_gen m1 m2 n (A : 'M_(m1, n)) (B : 'M_(m2, n)) := + lsubmx (kermx (col_mx A B)) *m A. +Definition capmx_def := idfun (fun m1 m2 n (A : 'M_(m1, n)) (B : 'M_(m2, n)) => + if qidmx A then capmx_nop B else + if qidmx B then capmx_nop A else + if row_full B then capmx_norm A else capmx_norm (capmx_gen A B) : 'M_n). +Fact capmx_key : unit. Proof. by []. Qed. +Definition capmx := locked_with capmx_key capmx_def. +Canonical capmx_unlockable := [unlockable fun capmx]. +Arguments Scope capmx + [nat_scope nat_scope nat_scope matrix_set_scope matrix_set_scope]. +Prenex Implicits capmx. +Local Notation "A :&: B" := (capmx A B) : matrix_set_scope. +Local Notation "\bigcap_ ( i | P ) B" := (\big[capmx/1%:M]_(i | P) B) + : matrix_set_scope. + +Definition diffmx_def := idfun (fun m1 m2 n (A : 'M_(m1, n)) (B : 'M_(m2, n)) => + <<capmx_gen A (capmx_gen A B)^C>>%MS : 'M_n). +Fact diffmx_key : unit. Proof. by []. Qed. +Definition diffmx := locked_with diffmx_key diffmx_def. +Canonical diffmx_unlockable := [unlockable fun diffmx]. +Arguments Scope diffmx + [nat_scope nat_scope nat_scope matrix_set_scope matrix_set_scope]. +Prenex Implicits diffmx. +Local Notation "A :\: B" := (diffmx A B) : matrix_set_scope. + +Definition proj_mx n (U V : 'M_n) : 'M_n := pinvmx (col_mx U V) *m col_mx U 0. + +Local Notation GaussE := Gaussian_elimination. + +Fact mxrankE m n (A : 'M_(m, n)) : \rank A = (GaussE A).2. +Proof. by rewrite /mxrank unlock /=; case: m n A => [|m] [|n]. Qed. + +Lemma rank_leq_row m n (A : 'M_(m, n)) : \rank A <= m. +Proof. +rewrite mxrankE. +elim: m n A => [|m IHm] [|n] //= A; case: pickP => [[i j] _|] //=. +by move: (_ - _) => B; case: GaussE (IHm _ B) => [[L U] r] /=. +Qed. + +Lemma row_leq_rank m n (A : 'M_(m, n)) : (m <= \rank A) = row_free A. +Proof. by rewrite /row_free eqn_leq rank_leq_row. Qed. + +Lemma rank_leq_col m n (A : 'M_(m, n)) : \rank A <= n. +Proof. +rewrite mxrankE. +elim: m n A => [|m IHm] [|n] //= A; case: pickP => [[i j] _|] //=. +by move: (_ - _) => B; case: GaussE (IHm _ B) => [[L U] r] /=. +Qed. + +Lemma col_leq_rank m n (A : 'M_(m, n)) : (n <= \rank A) = row_full A. +Proof. by rewrite /row_full eqn_leq rank_leq_col. Qed. + +Let unitmx1F := @unitmx1 F. +Lemma row_ebase_unit m n (A : 'M_(m, n)) : row_ebase A \in unitmx. +Proof. +rewrite /row_ebase unlock; elim: m n A => [|m IHm] [|n] //= A. +case: pickP => [[i j] /= nzAij | //=]; move: (_ - _) => B. +case: GaussE (IHm _ B) => [[L U] r] /= uU. +rewrite unitmxE xcolE det_mulmx (@det_ublock _ 1) det_scalar1 !unitrM. +by rewrite unitfE nzAij -!unitmxE uU unitmx_perm. +Qed. + +Lemma col_ebase_unit m n (A : 'M_(m, n)) : col_ebase A \in unitmx. +Proof. +rewrite /col_ebase unlock; elim: m n A => [|m IHm] [|n] //= A. +case: pickP => [[i j] _|] //=; move: (_ - _) => B. +case: GaussE (IHm _ B) => [[L U] r] /= uL. +rewrite unitmxE xrowE det_mulmx (@det_lblock _ 1) det1 mul1r unitrM. +by rewrite -unitmxE unitmx_perm. +Qed. +Hint Resolve rank_leq_row rank_leq_col row_ebase_unit col_ebase_unit. + +Lemma mulmx_ebase m n (A : 'M_(m, n)) : + col_ebase A *m pid_mx (\rank A) *m row_ebase A = A. +Proof. +rewrite mxrankE /col_ebase /row_ebase unlock. +elim: m n A => [n A | m IHm]; first by rewrite [A]flatmx0 [_ *m _]flatmx0. +case=> [A | n]; first by rewrite [_ *m _]thinmx0 [A]thinmx0. +rewrite -(add1n m) -?(add1n n) => A /=. +case: pickP => [[i0 j0] | A0] /=; last first. + apply/matrixP=> i j; rewrite pid_mx_0 mulmx0 mul0mx mxE. + by move/eqP: (A0 (i, j)). +set a := A i0 j0 => nz_a; set A1 := xrow _ _ _. +set u := ursubmx _; set v := _ *: _; set B : 'M_(m, n) := _ - _. +move: (rank_leq_col B) (rank_leq_row B) {IHm}(IHm n B); rewrite mxrankE. +case: (GaussE B) => [[L U] r] /= r_m r_n defB. +have ->: pid_mx (1 + r) = block_mx 1 0 0 (pid_mx r) :> 'M[F]_(1 + m, 1 + n). + rewrite -(subnKC r_m) -(subnKC r_n) pid_mx_block -col_mx0 -row_mx0. + by rewrite block_mxA castmx_id col_mx0 row_mx0 -scalar_mx_block -pid_mx_block. +rewrite xcolE xrowE mulmxA -xcolE -!mulmxA. +rewrite !(addr0, add0r, mulmx0, mul0mx, mulmx_block, mul1mx) mulmxA defB. +rewrite addrC subrK mul_mx_scalar scalerA divff // scale1r. +have ->: a%:M = ulsubmx A1 by rewrite [_ A1]mx11_scalar !mxE !lshift0 !tpermR. +rewrite submxK /A1 xrowE !xcolE -!mulmxA mulmxA -!perm_mxM !tperm2 !perm_mx1. +by rewrite mulmx1 mul1mx. +Qed. + +Lemma mulmx_base m n (A : 'M_(m, n)) : col_base A *m row_base A = A. +Proof. by rewrite mulmxA -[col_base A *m _]mulmxA pid_mx_id ?mulmx_ebase. Qed. + +Lemma mulmx1_min_rank r m n (A : 'M_(m, n)) M N : + M *m A *m N = 1%:M :> 'M_r -> r <= \rank A. +Proof. by rewrite -{1}(mulmx_base A) mulmxA -mulmxA; move/mulmx1_min. Qed. +Implicit Arguments mulmx1_min_rank [r m n A]. + +Lemma mulmx_max_rank r m n (M : 'M_(m, r)) (N : 'M_(r, n)) : + \rank (M *m N) <= r. +Proof. +set MN := M *m N; set rMN := \rank _. +pose L : 'M_(rMN, m) := pid_mx rMN *m invmx (col_ebase MN). +pose U : 'M_(n, rMN) := invmx (row_ebase MN) *m pid_mx rMN. +suffices: L *m M *m (N *m U) = 1%:M by exact: mulmx1_min. +rewrite mulmxA -(mulmxA L) -[M *m N]mulmx_ebase -/MN. +by rewrite !mulmxA mulmxKV // mulmxK // !pid_mx_id /rMN ?pid_mx_1. +Qed. +Implicit Arguments mulmx_max_rank [r m n]. + +Lemma mxrank_tr m n (A : 'M_(m, n)) : \rank A^T = \rank A. +Proof. +apply/eqP; rewrite eqn_leq -{3}[A]trmxK -{1}(mulmx_base A) -{1}(mulmx_base A^T). +by rewrite !trmx_mul !mulmx_max_rank. +Qed. + +Lemma mxrank_add m n (A B : 'M_(m, n)) : \rank (A + B)%R <= \rank A + \rank B. +Proof. +by rewrite -{1}(mulmx_base A) -{1}(mulmx_base B) -mul_row_col mulmx_max_rank. +Qed. + +Lemma mxrankM_maxl m n p (A : 'M_(m, n)) (B : 'M_(n, p)) : + \rank (A *m B) <= \rank A. +Proof. by rewrite -{1}(mulmx_base A) -mulmxA mulmx_max_rank. Qed. + +Lemma mxrankM_maxr m n p (A : 'M_(m, n)) (B : 'M_(n, p)) : + \rank (A *m B) <= \rank B. +Proof. by rewrite -mxrank_tr -(mxrank_tr B) trmx_mul mxrankM_maxl. Qed. + +Lemma mxrank_scale m n a (A : 'M_(m, n)) : \rank (a *: A) <= \rank A. +Proof. by rewrite -mul_scalar_mx mxrankM_maxr. Qed. + +Lemma mxrank_scale_nz m n a (A : 'M_(m, n)) : + a != 0 -> \rank (a *: A) = \rank A. +Proof. +move=> nza; apply/eqP; rewrite eqn_leq -{3}[A]scale1r -(mulVf nza). +by rewrite -scalerA !mxrank_scale. +Qed. + +Lemma mxrank_opp m n (A : 'M_(m, n)) : \rank (- A) = \rank A. +Proof. by rewrite -scaleN1r mxrank_scale_nz // oppr_eq0 oner_eq0. Qed. + +Lemma mxrank0 m n : \rank (0 : 'M_(m, n)) = 0%N. +Proof. by apply/eqP; rewrite -leqn0 -(@mulmx0 _ m 0 n 0) mulmx_max_rank. Qed. + +Lemma mxrank_eq0 m n (A : 'M_(m, n)) : (\rank A == 0%N) = (A == 0). +Proof. +apply/eqP/eqP=> [rA0 | ->{A}]; last exact: mxrank0. +move: (col_base A) (row_base A) (mulmx_base A); rewrite rA0 => Ac Ar <-. +by rewrite [Ac]thinmx0 mul0mx. +Qed. + +Lemma mulmx_coker m n (A : 'M_(m, n)) : A *m cokermx A = 0. +Proof. +by rewrite -{1}[A]mulmx_ebase -!mulmxA mulKVmx // mul_pid_mx_copid ?mulmx0. +Qed. + +Lemma submxE m1 m2 n (A : 'M_(m1, n)) (B : 'M_(m2, n)) : + (A <= B)%MS = (A *m cokermx B == 0). +Proof. by rewrite unlock. Qed. + +Lemma mulmxKpV m1 m2 n (A : 'M_(m1, n)) (B : 'M_(m2, n)) : + (A <= B)%MS -> A *m pinvmx B *m B = A. +Proof. +rewrite submxE !mulmxA mulmxBr mulmx1 subr_eq0 => /eqP defA. +rewrite -{4}[B]mulmx_ebase -!mulmxA mulKmx //. +by rewrite (mulmxA (pid_mx _)) pid_mx_id // !mulmxA -{}defA mulmxKV. +Qed. + +Lemma submxP m1 m2 n (A : 'M_(m1, n)) (B : 'M_(m2, n)) : + reflect (exists D, A = D *m B) (A <= B)%MS. +Proof. +apply: (iffP idP) => [/mulmxKpV | [D ->]]; first by exists (A *m pinvmx B). +by rewrite submxE -mulmxA mulmx_coker mulmx0. +Qed. +Implicit Arguments submxP [m1 m2 n A B]. + +Lemma submx_refl m n (A : 'M_(m, n)) : (A <= A)%MS. +Proof. by rewrite submxE mulmx_coker. Qed. +Hint Resolve submx_refl. + +Lemma submxMl m n p (D : 'M_(m, n)) (A : 'M_(n, p)) : (D *m A <= A)%MS. +Proof. by rewrite submxE -mulmxA mulmx_coker mulmx0. Qed. + +Lemma submxMr m1 m2 n p (A : 'M_(m1, n)) (B : 'M_(m2, n)) (C : 'M_(n, p)) : + (A <= B)%MS -> (A *m C <= B *m C)%MS. +Proof. by case/submxP=> D ->; rewrite -mulmxA submxMl. Qed. + +Lemma mulmx_sub m n1 n2 p (C : 'M_(m, n1)) A (B : 'M_(n2, p)) : + (A <= B -> C *m A <= B)%MS. +Proof. by case/submxP=> D ->; rewrite mulmxA submxMl. Qed. + +Lemma submx_trans m1 m2 m3 n + (A : 'M_(m1, n)) (B : 'M_(m2, n)) (C : 'M_(m3, n)) : + (A <= B -> B <= C -> A <= C)%MS. +Proof. by case/submxP=> D ->{A}; exact: mulmx_sub. Qed. + +Lemma ltmx_sub_trans m1 m2 m3 n + (A : 'M_(m1, n)) (B : 'M_(m2, n)) (C : 'M_(m3, n)) : + (A < B)%MS -> (B <= C)%MS -> (A < C)%MS. +Proof. +case/andP=> sAB ltAB sBC; rewrite ltmxE (submx_trans sAB) //. +by apply: contra ltAB; exact: submx_trans. +Qed. + +Lemma sub_ltmx_trans m1 m2 m3 n + (A : 'M_(m1, n)) (B : 'M_(m2, n)) (C : 'M_(m3, n)) : + (A <= B)%MS -> (B < C)%MS -> (A < C)%MS. +Proof. +move=> sAB /andP[sBC ltBC]; rewrite ltmxE (submx_trans sAB) //. +by apply: contra ltBC => sCA; exact: submx_trans sAB. +Qed. + +Lemma ltmx_trans m n : transitive (@ltmx m m n). +Proof. by move=> A B C; move/ltmxW; exact: sub_ltmx_trans. Qed. + +Lemma ltmx_irrefl m n : irreflexive (@ltmx m m n). +Proof. by move=> A; rewrite /ltmx submx_refl andbF. Qed. + +Lemma sub0mx m1 m2 n (A : 'M_(m2, n)) : ((0 : 'M_(m1, n)) <= A)%MS. +Proof. by rewrite submxE mul0mx. Qed. + +Lemma submx0null m1 m2 n (A : 'M[F]_(m1, n)) : + (A <= (0 : 'M_(m2, n)))%MS -> A = 0. +Proof. by case/submxP=> D; rewrite mulmx0. Qed. + +Lemma submx0 m n (A : 'M_(m, n)) : (A <= (0 : 'M_n))%MS = (A == 0). +Proof. by apply/idP/eqP=> [|->]; [exact: submx0null | exact: sub0mx]. Qed. + +Lemma lt0mx m n (A : 'M_(m, n)) : ((0 : 'M_n) < A)%MS = (A != 0). +Proof. by rewrite /ltmx sub0mx submx0. Qed. + +Lemma ltmx0 m n (A : 'M[F]_(m, n)) : (A < (0 : 'M_n))%MS = false. +Proof. by rewrite /ltmx sub0mx andbF. Qed. + +Lemma eqmx0P m n (A : 'M_(m, n)) : reflect (A = 0) (A == (0 : 'M_n))%MS. +Proof. by rewrite submx0 sub0mx andbT; exact: eqP. Qed. + +Lemma eqmx_eq0 m1 m2 n (A : 'M_(m1, n)) (B : 'M_(m2, n)) : + (A :=: B)%MS -> (A == 0) = (B == 0). +Proof. by move=> eqAB; rewrite -!submx0 eqAB. Qed. + +Lemma addmx_sub m1 m2 n (A : 'M_(m1, n)) (B : 'M_(m1, n)) (C : 'M_(m2, n)) : + (A <= C)%MS -> (B <= C)%MS -> ((A + B)%R <= C)%MS. +Proof. +by case/submxP=> A' ->; case/submxP=> B' ->; rewrite -mulmxDl submxMl. +Qed. + +Lemma summx_sub m1 m2 n (B : 'M_(m2, n)) + I (r : seq I) (P : pred I) (A_ : I -> 'M_(m1, n)) : + (forall i, P i -> A_ i <= B)%MS -> ((\sum_(i <- r | P i) A_ i)%R <= B)%MS. +Proof. +move=> leAB; elim/big_ind: _ => // [|A1 A2]; [exact: sub0mx | exact: addmx_sub]. +Qed. + +Lemma scalemx_sub m1 m2 n a (A : 'M_(m1, n)) (B : 'M_(m2, n)) : + (A <= B)%MS -> (a *: A <= B)%MS. +Proof. by case/submxP=> A' ->; rewrite scalemxAl submxMl. Qed. + +Lemma row_sub m n i (A : 'M_(m, n)) : (row i A <= A)%MS. +Proof. by rewrite rowE submxMl. Qed. + +Lemma eq_row_sub m n v (A : 'M_(m, n)) i : row i A = v -> (v <= A)%MS. +Proof. by move <-; rewrite row_sub. Qed. + +Lemma nz_row_sub m n (A : 'M_(m, n)) : (nz_row A <= A)%MS. +Proof. by rewrite /nz_row; case: pickP => [i|] _; rewrite ?row_sub ?sub0mx. Qed. + +Lemma row_subP m1 m2 n (A : 'M_(m1, n)) (B : 'M_(m2, n)) : + reflect (forall i, row i A <= B)%MS (A <= B)%MS. +Proof. +apply: (iffP idP) => [sAB i|sAB]. + by apply: submx_trans sAB; exact: row_sub. +rewrite submxE; apply/eqP/row_matrixP=> i; apply/eqP. +by rewrite row_mul row0 -submxE. +Qed. +Implicit Arguments row_subP [m1 m2 n A B]. + +Lemma rV_subP m1 m2 n (A : 'M_(m1, n)) (B : 'M_(m2, n)) : + reflect (forall v : 'rV_n, v <= A -> v <= B)%MS (A <= B)%MS. +Proof. +apply: (iffP idP) => [sAB v Av | sAB]; first exact: submx_trans sAB. +by apply/row_subP=> i; rewrite sAB ?row_sub. +Qed. +Implicit Arguments rV_subP [m1 m2 n A B]. + +Lemma row_subPn m1 m2 n (A : 'M_(m1, n)) (B : 'M_(m2, n)) : + reflect (exists i, ~~ (row i A <= B)%MS) (~~ (A <= B)%MS). +Proof. by rewrite (sameP row_subP forallP) negb_forall; exact: existsP. Qed. + +Lemma sub_rVP n (u v : 'rV_n) : reflect (exists a, u = a *: v) (u <= v)%MS. +Proof. +apply: (iffP submxP) => [[w ->] | [a ->]]. + by exists (w 0 0); rewrite -mul_scalar_mx -mx11_scalar. +by exists a%:M; rewrite mul_scalar_mx. +Qed. + +Lemma rank_rV n (v : 'rV_n) : \rank v = (v != 0). +Proof. +case: eqP => [-> | nz_v]; first by rewrite mxrank0. +by apply/eqP; rewrite eqn_leq rank_leq_row lt0n mxrank_eq0; exact/eqP. +Qed. + +Lemma rowV0Pn m n (A : 'M_(m, n)) : + reflect (exists2 v : 'rV_n, v <= A & v != 0)%MS (A != 0). +Proof. +rewrite -submx0; apply: (iffP idP) => [| [v svA]]; last first. + by rewrite -submx0; exact: contra (submx_trans _). +by case/row_subPn=> i; rewrite submx0; exists (row i A); rewrite ?row_sub. +Qed. + +Lemma rowV0P m n (A : 'M_(m, n)) : + reflect (forall v : 'rV_n, v <= A -> v = 0)%MS (A == 0). +Proof. +rewrite -[A == 0]negbK; case: rowV0Pn => IH. + by right; case: IH => v svA nzv IH; case/eqP: nzv; exact: IH. +by left=> v svA; apply/eqP; apply/idPn=> nzv; case: IH; exists v. +Qed. + +Lemma submx_full m1 m2 n (A : 'M_(m1, n)) (B : 'M_(m2, n)) : + row_full B -> (A <= B)%MS. +Proof. +by rewrite submxE /cokermx =>/eqnP->; rewrite /copid_mx pid_mx_1 subrr !mulmx0. +Qed. + +Lemma row_fullP m n (A : 'M_(m, n)) : + reflect (exists B, B *m A = 1%:M) (row_full A). +Proof. +apply: (iffP idP) => [Afull | [B kA]]. + by exists (1%:M *m pinvmx A); apply: mulmxKpV (submx_full _ Afull). +by rewrite [_ A]eqn_leq rank_leq_col (mulmx1_min_rank B 1%:M) ?mulmx1. +Qed. +Implicit Arguments row_fullP [m n A]. + +Lemma row_full_inj m n p A : row_full A -> injective (@mulmx _ m n p A). +Proof. +case/row_fullP=> A' A'K; apply: can_inj (mulmx A') _ => B. +by rewrite mulmxA A'K mul1mx. +Qed. + +Lemma row_freeP m n (A : 'M_(m, n)) : + reflect (exists B, A *m B = 1%:M) (row_free A). +Proof. +rewrite /row_free -mxrank_tr. +apply: (iffP row_fullP) => [] [B kA]; + by exists B^T; rewrite -trmx1 -kA trmx_mul ?trmxK. +Qed. + +Lemma row_free_inj m n p A : row_free A -> injective ((@mulmx _ m n p)^~ A). +Proof. +case/row_freeP=> A' AK; apply: can_inj (mulmx^~ A') _ => B. +by rewrite -mulmxA AK mulmx1. +Qed. + +Lemma row_free_unit n (A : 'M_n) : row_free A = (A \in unitmx). +Proof. +apply/row_fullP/idP=> [[A'] | uA]; first by case/mulmx1_unit. +by exists (invmx A); rewrite mulVmx. +Qed. + +Lemma row_full_unit n (A : 'M_n) : row_full A = (A \in unitmx). +Proof. exact: row_free_unit. Qed. + +Lemma mxrank_unit n (A : 'M_n) : A \in unitmx -> \rank A = n. +Proof. by rewrite -row_full_unit =>/eqnP. Qed. + +Lemma mxrank1 n : \rank (1%:M : 'M_n) = n. +Proof. by apply: mxrank_unit; exact: unitmx1. Qed. + +Lemma mxrank_delta m n i j : \rank (delta_mx i j : 'M_(m, n)) = 1%N. +Proof. +apply/eqP; rewrite eqn_leq lt0n mxrank_eq0. +rewrite -{1}(mul_delta_mx (0 : 'I_1)) mulmx_max_rank. +by apply/eqP; move/matrixP; move/(_ i j); move/eqP; rewrite !mxE !eqxx oner_eq0. +Qed. + +Lemma mxrankS m1 m2 n (A : 'M_(m1, n)) (B : 'M_(m2, n)) : + (A <= B)%MS -> \rank A <= \rank B. +Proof. by case/submxP=> D ->; rewrite mxrankM_maxr. Qed. + +Lemma submx1 m n (A : 'M_(m, n)) : (A <= 1%:M)%MS. +Proof. by rewrite submx_full // row_full_unit unitmx1. Qed. + +Lemma sub1mx m n (A : 'M_(m, n)) : (1%:M <= A)%MS = row_full A. +Proof. +apply/idP/idP; last exact: submx_full. +by move/mxrankS; rewrite mxrank1 col_leq_rank. +Qed. + +Lemma ltmx1 m n (A : 'M_(m, n)) : (A < 1%:M)%MS = ~~ row_full A. +Proof. by rewrite /ltmx sub1mx submx1. Qed. + +Lemma lt1mx m n (A : 'M_(m, n)) : (1%:M < A)%MS = false. +Proof. by rewrite /ltmx submx1 andbF. Qed. + +Lemma eqmxP m1 m2 n (A : 'M_(m1, n)) (B : 'M_(m2, n)) : + reflect (A :=: B)%MS (A == B)%MS. +Proof. +apply: (iffP andP) => [[sAB sBA] | eqAB]; last by rewrite !eqAB. +split=> [|m3 C]; first by apply/eqP; rewrite eqn_leq !mxrankS. +split; first by apply/idP/idP; exact: submx_trans. +by apply/idP/idP=> sC; exact: submx_trans sC _. +Qed. +Implicit Arguments eqmxP [m1 m2 n A B]. + +Lemma rV_eqP m1 m2 n (A : 'M_(m1, n)) (B : 'M_(m2, n)) : + reflect (forall u : 'rV_n, (u <= A) = (u <= B))%MS (A == B)%MS. +Proof. +apply: (iffP idP) => [eqAB u | eqAB]; first by rewrite (eqmxP eqAB). +by apply/andP; split; apply/rV_subP=> u; rewrite eqAB. +Qed. + +Lemma eqmx_refl m1 n (A : 'M_(m1, n)) : (A :=: A)%MS. +Proof. by []. Qed. + +Lemma eqmx_sym m1 m2 n (A : 'M_(m1, n)) (B : 'M_(m2, n)) : + (A :=: B)%MS -> (B :=: A)%MS. +Proof. by move=> eqAB; split=> [|m3 C]; rewrite !eqAB. Qed. + +Lemma eqmx_trans m1 m2 m3 n (A : 'M_(m1, n)) (B : 'M_(m2, n)) (C : 'M_(m3, n)) : + (A :=: B)%MS -> (B :=: C)%MS -> (A :=: C)%MS. +Proof. by move=> eqAB eqBC; split=> [|m4 D]; rewrite !eqAB !eqBC. Qed. + +Lemma eqmx_rank m1 m2 n (A : 'M_(m1, n)) (B : 'M_(m2, n)) : + (A == B)%MS -> \rank A = \rank B. +Proof. by move/eqmxP->. Qed. + +Lemma lt_eqmx m1 m2 m3 n (A : 'M_(m1, n)) (B : 'M_(m2, n)) : + (A :=: B)%MS -> + forall C : 'M_(m3, n), (((A < C) = (B < C))%MS * ((C < A) = (C < B))%MS)%type. +Proof. by move=> eqAB C; rewrite /ltmx !eqAB. Qed. + +Lemma eqmxMr m1 m2 n p (A : 'M_(m1, n)) (B : 'M_(m2, n)) (C : 'M_(n, p)) : + (A :=: B)%MS -> (A *m C :=: B *m C)%MS. +Proof. by move=> eqAB; apply/eqmxP; rewrite !submxMr ?eqAB. Qed. + +Lemma eqmxMfull m n p (A : 'M_(m, n)) (B : 'M_(n, p)) : + row_full A -> (A *m B :=: B)%MS. +Proof. +case/row_fullP=> A' A'A; apply/eqmxP; rewrite submxMl /=. +by apply/submxP; exists A'; rewrite mulmxA A'A mul1mx. +Qed. + +Lemma eqmx0 m n : ((0 : 'M[F]_(m, n)) :=: (0 : 'M_n))%MS. +Proof. by apply/eqmxP; rewrite !sub0mx. Qed. + +Lemma eqmx_scale m n a (A : 'M_(m, n)) : a != 0 -> (a *: A :=: A)%MS. +Proof. +move=> nz_a; apply/eqmxP; rewrite scalemx_sub //. +by rewrite -{1}[A]scale1r -(mulVf nz_a) -scalerA scalemx_sub. +Qed. + +Lemma eqmx_opp m n (A : 'M_(m, n)) : (- A :=: A)%MS. +Proof. +by rewrite -scaleN1r; apply: eqmx_scale => //; rewrite oppr_eq0 oner_eq0. +Qed. + +Lemma submxMfree m1 m2 n p (A : 'M_(m1, n)) (B : 'M_(m2, n)) (C : 'M_(n, p)) : + row_free C -> (A *m C <= B *m C)%MS = (A <= B)%MS. +Proof. +case/row_freeP=> C' C_C'_1; apply/idP/idP=> sAB; last exact: submxMr. +by rewrite -[A]mulmx1 -[B]mulmx1 -C_C'_1 !mulmxA submxMr. +Qed. + +Lemma eqmxMfree m1 m2 n p (A : 'M_(m1, n)) (B : 'M_(m2, n)) (C : 'M_(n, p)) : + row_free C -> (A *m C :=: B *m C)%MS -> (A :=: B)%MS. +Proof. +by move=> Cfree eqAB; apply/eqmxP; move/eqmxP: eqAB; rewrite !submxMfree. +Qed. + +Lemma mxrankMfree m n p (A : 'M_(m, n)) (B : 'M_(n, p)) : + row_free B -> \rank (A *m B) = \rank A. +Proof. +by move=> Bfree; rewrite -mxrank_tr trmx_mul eqmxMfull /row_full mxrank_tr. +Qed. + +Lemma eq_row_base m n (A : 'M_(m, n)) : (row_base A :=: A)%MS. +Proof. +apply/eqmxP; apply/andP; split; apply/submxP. + exists (pid_mx (\rank A) *m invmx (col_ebase A)). + by rewrite -{8}[A]mulmx_ebase !mulmxA mulmxKV // pid_mx_id. +exists (col_ebase A *m pid_mx (\rank A)). +by rewrite mulmxA -(mulmxA _ _ (pid_mx _)) pid_mx_id // mulmx_ebase. +Qed. + +Let qidmx_eq1 n (A : 'M_n) : qidmx A = (A == 1%:M). +Proof. by rewrite /qidmx eqxx pid_mx_1. Qed. + +Let genmx_witnessP m n (A : 'M_(m, n)) : + equivmx A (row_full A) (genmx_witness A). +Proof. +rewrite /equivmx qidmx_eq1 /genmx_witness. +case fullA: (row_full A); first by rewrite eqxx sub1mx submx1 fullA. +set B := _ *m _; have defB : (B == A)%MS. + apply/andP; split; apply/submxP. + exists (pid_mx (\rank A) *m invmx (col_ebase A)). + by rewrite -{3}[A]mulmx_ebase !mulmxA mulmxKV // pid_mx_id. + exists (col_ebase A *m pid_mx (\rank A)). + by rewrite mulmxA -(mulmxA _ _ (pid_mx _)) pid_mx_id // mulmx_ebase. +rewrite defB -negb_add addbF; case: eqP defB => // ->. +by rewrite sub1mx fullA. +Qed. + +Lemma genmxE m n (A : 'M_(m, n)) : (<<A>> :=: A)%MS. +Proof. +by rewrite unlock; apply/eqmxP; case/andP: (chooseP (genmx_witnessP A)). +Qed. + +Lemma eq_genmx m1 m2 n (A : 'M_(m1, n)) (B : 'M_(m2, n)) : + (A :=: B -> <<A>> = <<B>>)%MS. +Proof. +move=> eqAB; rewrite unlock. +have{eqAB} eqAB: equivmx A (row_full A) =1 equivmx B (row_full B). + by move=> C; rewrite /row_full /equivmx !eqAB. +rewrite (eq_choose eqAB) (choose_id _ (genmx_witnessP B)) //. +by rewrite -eqAB genmx_witnessP. +Qed. + +Lemma genmxP m1 m2 n (A : 'M_(m1, n)) (B : 'M_(m2, n)) : + reflect (<<A>> = <<B>>)%MS (A == B)%MS. +Proof. +apply: (iffP idP) => eqAB; first exact: eq_genmx (eqmxP _). +by rewrite -!(genmxE A) eqAB !genmxE andbb. +Qed. +Implicit Arguments genmxP [m1 m2 n A B]. + +Lemma genmx0 m n : <<0 : 'M_(m, n)>>%MS = 0. +Proof. by apply/eqP; rewrite -submx0 genmxE sub0mx. Qed. + +Lemma genmx1 n : <<1%:M : 'M_n>>%MS = 1%:M. +Proof. +rewrite unlock; case/andP: (chooseP (@genmx_witnessP n n 1%:M)) => _ /eqP. +by rewrite qidmx_eq1 row_full_unit unitmx1 => /eqP. +Qed. + +Lemma genmx_id m n (A : 'M_(m, n)) : (<<<<A>>>> = <<A>>)%MS. +Proof. by apply: eq_genmx; exact: genmxE. Qed. + +Lemma row_base_free m n (A : 'M_(m, n)) : row_free (row_base A). +Proof. by apply/eqnP; rewrite eq_row_base. Qed. + +Lemma mxrank_gen m n (A : 'M_(m, n)) : \rank <<A>> = \rank A. +Proof. by rewrite genmxE. Qed. + +Lemma col_base_full m n (A : 'M_(m, n)) : row_full (col_base A). +Proof. +apply/row_fullP; exists (pid_mx (\rank A) *m invmx (col_ebase A)). +by rewrite !mulmxA mulmxKV // pid_mx_id // pid_mx_1. +Qed. +Hint Resolve row_base_free col_base_full. + +Lemma mxrank_leqif_sup m1 m2 n (A : 'M_(m1, n)) (B : 'M_(m2, n)) : + (A <= B)%MS -> \rank A <= \rank B ?= iff (B <= A)%MS. +Proof. +move=> sAB; split; first by rewrite mxrankS. +apply/idP/idP=> [| sBA]; last by rewrite eqn_leq !mxrankS. +case/submxP: sAB => D ->; rewrite -{-2}(mulmx_base B) mulmxA. +rewrite mxrankMfree // => /row_fullP[E kE]. +by rewrite -{1}[row_base B]mul1mx -kE -(mulmxA E) (mulmxA _ E) submxMl. +Qed. + +Lemma mxrank_leqif_eq m1 m2 n (A : 'M_(m1, n)) (B : 'M_(m2, n)) : + (A <= B)%MS -> \rank A <= \rank B ?= iff (A == B)%MS. +Proof. by move=> sAB; rewrite sAB; exact: mxrank_leqif_sup. Qed. + +Lemma ltmxErank m1 m2 n (A : 'M_(m1, n)) (B : 'M_(m2, n)) : + (A < B)%MS = (A <= B)%MS && (\rank A < \rank B). +Proof. +by apply: andb_id2l => sAB; rewrite (ltn_leqif (mxrank_leqif_sup sAB)). +Qed. + +Lemma rank_ltmx m1 m2 n (A : 'M_(m1, n)) (B : 'M_(m2, n)) : + (A < B)%MS -> \rank A < \rank B. +Proof. by rewrite ltmxErank => /andP[]. Qed. + +Lemma eqmx_cast m1 m2 n (A : 'M_(m1, n)) e : + ((castmx e A : 'M_(m2, n)) :=: A)%MS. +Proof. by case: e A; case: m2 / => A e; rewrite castmx_id. Qed. + +Lemma eqmx_conform m1 m2 n (A : 'M_(m1, n)) (B : 'M_(m2, n)) : + (conform_mx A B :=: A \/ conform_mx A B :=: B)%MS. +Proof. +case: (eqVneq m2 m1) => [-> | neqm12] in B *. + by right; rewrite conform_mx_id. +by left; rewrite nonconform_mx ?neqm12. +Qed. + +Let eqmx_sum_nop m n (A : 'M_(m, n)) : (addsmx_nop A :=: A)%MS. +Proof. +case: (eqmx_conform <<A>>%MS A) => // eq_id_gen. +exact: eqmx_trans (genmxE A). +Qed. + +Section AddsmxSub. + +Variable (m1 m2 n : nat) (A : 'M[F]_(m1, n)) (B : 'M[F]_(m2, n)). + +Lemma col_mx_sub m3 (C : 'M_(m3, n)) : + (col_mx A B <= C)%MS = (A <= C)%MS && (B <= C)%MS. +Proof. +rewrite !submxE mul_col_mx -col_mx0. +by apply/eqP/andP; [case/eq_col_mx=> -> -> | case; do 2!move/eqP->]. +Qed. + +Lemma addsmxE : (A + B :=: col_mx A B)%MS. +Proof. +have:= submx_refl (col_mx A B); rewrite col_mx_sub; case/andP=> sAS sBS. +rewrite unlock; do 2?case: eqP => [AB0 | _]; last exact: genmxE. + by apply/eqmxP; rewrite !eqmx_sum_nop sBS col_mx_sub AB0 sub0mx /=. +by apply/eqmxP; rewrite !eqmx_sum_nop sAS col_mx_sub AB0 sub0mx andbT /=. +Qed. + +Lemma addsmx_sub m3 (C : 'M_(m3, n)) : + (A + B <= C)%MS = (A <= C)%MS && (B <= C)%MS. +Proof. by rewrite addsmxE col_mx_sub. Qed. + +Lemma addsmxSl : (A <= A + B)%MS. +Proof. by have:= submx_refl (A + B)%MS; rewrite addsmx_sub; case/andP. Qed. + +Lemma addsmxSr : (B <= A + B)%MS. +Proof. by have:= submx_refl (A + B)%MS; rewrite addsmx_sub; case/andP. Qed. + +Lemma addsmx_idPr : reflect (A + B :=: B)%MS (A <= B)%MS. +Proof. +have:= @eqmxP _ _ _ (A + B)%MS B. +by rewrite addsmxSr addsmx_sub submx_refl !andbT. +Qed. + +Lemma addsmx_idPl : reflect (A + B :=: A)%MS (B <= A)%MS. +Proof. +have:= @eqmxP _ _ _ (A + B)%MS A. +by rewrite addsmxSl addsmx_sub submx_refl !andbT. +Qed. + +End AddsmxSub. + +Lemma adds0mx m1 m2 n (B : 'M_(m2, n)) : ((0 : 'M_(m1, n)) + B :=: B)%MS. +Proof. by apply/eqmxP; rewrite addsmx_sub sub0mx addsmxSr /= andbT. Qed. + +Lemma addsmx0 m1 m2 n (A : 'M_(m1, n)) : (A + (0 : 'M_(m2, n)) :=: A)%MS. +Proof. by apply/eqmxP; rewrite addsmx_sub sub0mx addsmxSl /= !andbT. Qed. + +Let addsmx_nop_eq0 m n (A : 'M_(m, n)) : (addsmx_nop A == 0) = (A == 0). +Proof. by rewrite -!submx0 eqmx_sum_nop. Qed. + +Let addsmx_nop0 m n : addsmx_nop (0 : 'M_(m, n)) = 0. +Proof. by apply/eqP; rewrite addsmx_nop_eq0. Qed. + +Let addsmx_nop_id n (A : 'M_n) : addsmx_nop A = A. +Proof. exact: conform_mx_id. Qed. + +Lemma addsmxC m1 m2 n (A : 'M_(m1, n)) (B : 'M_(m2, n)) : (A + B = B + A)%MS. +Proof. +have: (A + B == B + A)%MS. + by apply/andP; rewrite !addsmx_sub andbC -addsmx_sub andbC -addsmx_sub. +move/genmxP; rewrite [@addsmx]unlock -!submx0 !submx0. +by do 2!case: eqP => [// -> | _]; rewrite ?genmx_id ?addsmx_nop0. +Qed. + +Lemma adds0mx_id m1 n (B : 'M_n) : ((0 : 'M_(m1, n)) + B)%MS = B. +Proof. by rewrite unlock eqxx addsmx_nop_id. Qed. + +Lemma addsmx0_id m2 n (A : 'M_n) : (A + (0 : 'M_(m2, n)))%MS = A. +Proof. by rewrite addsmxC adds0mx_id. Qed. + +Lemma addsmxA m1 m2 m3 n (A : 'M_(m1, n)) (B : 'M_(m2, n)) (C : 'M_(m3, n)) : + (A + (B + C) = A + B + C)%MS. +Proof. +have: (A + (B + C) :=: A + B + C)%MS. + by apply/eqmxP/andP; rewrite !addsmx_sub -andbA andbA -!addsmx_sub. +rewrite {1 3}[in @addsmx m1]unlock [in @addsmx n]unlock !addsmx_nop_id -!submx0. +rewrite !addsmx_sub ![@addsmx]unlock -!submx0; move/eq_genmx. +by do 3!case: (_ <= 0)%MS; rewrite //= !genmx_id. +Qed. + +Canonical addsmx_monoid n := + Monoid.Law (@addsmxA n n n n) (@adds0mx_id n n) (@addsmx0_id n n). +Canonical addsmx_comoid n := Monoid.ComLaw (@addsmxC n n n). + +Lemma addsmxMr m1 m2 n p (A : 'M_(m1, n)) (B : 'M_(m2, n)) (C : 'M_(n, p)) : + ((A + B)%MS *m C :=: A *m C + B *m C)%MS. +Proof. by apply/eqmxP; rewrite !addsmxE -!mul_col_mx !submxMr ?addsmxE. Qed. + +Lemma addsmxS m1 m2 m3 m4 n (A : 'M_(m1, n)) (B : 'M_(m2, n)) + (C : 'M_(m3, n)) (D : 'M_(m4, n)) : + (A <= C -> B <= D -> A + B <= C + D)%MS. +Proof. +move=> sAC sBD. +by rewrite addsmx_sub {1}addsmxC !(submx_trans _ (addsmxSr _ _)). +Qed. + +Lemma addmx_sub_adds m m1 m2 n (A : 'M_(m, n)) (B : 'M_(m, n)) + (C : 'M_(m1, n)) (D : 'M_(m2, n)) : + (A <= C -> B <= D -> (A + B)%R <= C + D)%MS. +Proof. +move=> sAC; move/(addsmxS sAC); apply: submx_trans. +by rewrite addmx_sub ?addsmxSl ?addsmxSr. +Qed. + +Lemma addsmx_addKl n m1 m2 (A : 'M_(m1, n)) (B C : 'M_(m2, n)) : + (B <= A)%MS -> (A + (B + C)%R :=: A + C)%MS. +Proof. +move=> sBA; apply/eqmxP; rewrite !addsmx_sub !addsmxSl. +by rewrite -{3}[C](addKr B) !addmx_sub_adds ?eqmx_opp. +Qed. + +Lemma addsmx_addKr n m1 m2 (A B : 'M_(m1, n)) (C : 'M_(m2, n)) : + (B <= C)%MS -> ((A + B)%R + C :=: A + C)%MS. +Proof. by rewrite -!(addsmxC C) addrC; exact: addsmx_addKl. Qed. + +Lemma adds_eqmx m1 m2 m3 m4 n (A : 'M_(m1, n)) (B : 'M_(m2, n)) + (C : 'M_(m3, n)) (D : 'M_(m4, n)) : + (A :=: C -> B :=: D -> A + B :=: C + D)%MS. +Proof. by move=> eqAC eqBD; apply/eqmxP; rewrite !addsmxS ?eqAC ?eqBD. Qed. + +Lemma genmx_adds m1 m2 n (A : 'M_(m1, n)) (B : 'M_(m2, n)) : + (<<(A + B)%MS>> = <<A>> + <<B>>)%MS. +Proof. +rewrite -(eq_genmx (adds_eqmx (genmxE A) (genmxE B))). +by rewrite [@addsmx]unlock !addsmx_nop_id !(fun_if (@genmx _ _)) !genmx_id. +Qed. + +Lemma sub_addsmxP m1 m2 m3 n + (A : 'M_(m1, n)) (B : 'M_(m2, n)) (C : 'M_(m3, n)) : + reflect (exists u, A = u.1 *m B + u.2 *m C) (A <= B + C)%MS. +Proof. +apply: (iffP idP) => [|[u ->]]; last by rewrite addmx_sub_adds ?submxMl. +rewrite addsmxE; case/submxP=> u ->; exists (lsubmx u, rsubmx u). +by rewrite -mul_row_col hsubmxK. +Qed. +Implicit Arguments sub_addsmxP [m1 m2 m3 n A B C]. + +Variable I : finType. +Implicit Type P : pred I. + +Lemma genmx_sums P n (B_ : I -> 'M_n) : + <<(\sum_(i | P i) B_ i)%MS>>%MS = (\sum_(i | P i) <<B_ i>>)%MS. +Proof. exact: (big_morph _ (@genmx_adds n n n) (@genmx0 n n)). Qed. + +Lemma sumsmx_sup i0 P m n (A : 'M_(m, n)) (B_ : I -> 'M_n) : + P i0 -> (A <= B_ i0)%MS -> (A <= \sum_(i | P i) B_ i)%MS. +Proof. +by move=> Pi0 sAB; apply: submx_trans sAB _; rewrite (bigD1 i0) // addsmxSl. +Qed. +Implicit Arguments sumsmx_sup [P m n A B_]. + +Lemma sumsmx_subP P m n (A_ : I -> 'M_n) (B : 'M_(m, n)) : + reflect (forall i, P i -> A_ i <= B)%MS (\sum_(i | P i) A_ i <= B)%MS. +Proof. +apply: (iffP idP) => [sAB i Pi | sAB]. + by apply: submx_trans sAB; apply: sumsmx_sup Pi _. +by elim/big_rec: _ => [|i Ai Pi sAiB]; rewrite ?sub0mx // addsmx_sub sAB. +Qed. + +Lemma summx_sub_sums P m n (A : I -> 'M[F]_(m, n)) B : + (forall i, P i -> A i <= B i)%MS -> + ((\sum_(i | P i) A i)%R <= \sum_(i | P i) B i)%MS. +Proof. +by move=> sAB; apply: summx_sub => i Pi; rewrite (sumsmx_sup i) ?sAB. +Qed. + +Lemma sumsmxS P n (A B : I -> 'M[F]_n) : + (forall i, P i -> A i <= B i)%MS -> + (\sum_(i | P i) A i <= \sum_(i | P i) B i)%MS. +Proof. +by move=> sAB; apply/sumsmx_subP=> i Pi; rewrite (sumsmx_sup i) ?sAB. +Qed. + +Lemma eqmx_sums P n (A B : I -> 'M[F]_n) : + (forall i, P i -> A i :=: B i)%MS -> + (\sum_(i | P i) A i :=: \sum_(i | P i) B i)%MS. +Proof. by move=> eqAB; apply/eqmxP; rewrite !sumsmxS // => i; move/eqAB->. Qed. + +Lemma sub_sumsmxP P m n (A : 'M_(m, n)) (B_ : I -> 'M_n) : + reflect (exists u_, A = \sum_(i | P i) u_ i *m B_ i) + (A <= \sum_(i | P i) B_ i)%MS. +Proof. +apply: (iffP idP) => [| [u_ ->]]; last first. + by apply: summx_sub_sums => i _; exact: submxMl. +elim: {P}_.+1 {-2}P A (ltnSn #|P|) => // b IHb P A. +case: (pickP P) => [i Pi | P0 _]; last first. + rewrite big_pred0 //; move/submx0null->. + by exists (fun _ => 0); rewrite big_pred0. +rewrite (cardD1x Pi) (bigD1 i) //= => /IHb{b IHb} /= IHi /sub_addsmxP[u ->]. +have [u_ ->] := IHi _ (submxMl u.2 _). +exists [eta u_ with i |-> u.1]; rewrite (bigD1 i Pi) /= eqxx; congr (_ + _). +by apply: eq_bigr => j /andP[_ /negPf->]. +Qed. + +Lemma sumsmxMr_gen P m n A (B : 'M[F]_(m, n)) : + ((\sum_(i | P i) A i)%MS *m B :=: \sum_(i | P i) <<A i *m B>>)%MS. +Proof. +apply/eqmxP/andP; split; last first. + by apply/sumsmx_subP=> i Pi; rewrite genmxE submxMr ?(sumsmx_sup i). +have [u ->] := sub_sumsmxP _ _ _ (submx_refl (\sum_(i | P i) A i)%MS). +by rewrite mulmx_suml summx_sub_sums // => i _; rewrite genmxE -mulmxA submxMl. +Qed. + +Lemma sumsmxMr P n (A_ : I -> 'M[F]_n) (B : 'M_n) : + ((\sum_(i | P i) A_ i)%MS *m B :=: \sum_(i | P i) (A_ i *m B))%MS. +Proof. +by apply: eqmx_trans (sumsmxMr_gen _ _ _) (eqmx_sums _) => i _; exact: genmxE. +Qed. + +Lemma rank_pid_mx m n r : r <= m -> r <= n -> \rank (pid_mx r : 'M_(m, n)) = r. +Proof. +do 2!move/subnKC <-; rewrite pid_mx_block block_mxEv row_mx0 -addsmxE addsmx0. +by rewrite -mxrank_tr tr_row_mx trmx0 trmx1 -addsmxE addsmx0 mxrank1. +Qed. + +Lemma rank_copid_mx n r : r <= n -> \rank (copid_mx r : 'M_n) = (n - r)%N. +Proof. +move/subnKC <-; rewrite /copid_mx pid_mx_block scalar_mx_block. +rewrite opp_block_mx !oppr0 add_block_mx !addr0 subrr block_mxEv row_mx0. +rewrite -addsmxE adds0mx -mxrank_tr tr_row_mx trmx0 trmx1. +by rewrite -addsmxE adds0mx mxrank1 addKn. +Qed. + +Lemma mxrank_compl m n (A : 'M_(m, n)) : \rank A^C = (n - \rank A)%N. +Proof. by rewrite mxrankMfree ?row_free_unit ?rank_copid_mx. Qed. + +Lemma mxrank_ker m n (A : 'M_(m, n)) : \rank (kermx A) = (m - \rank A)%N. +Proof. by rewrite mxrankMfree ?row_free_unit ?unitmx_inv ?rank_copid_mx. Qed. + +Lemma kermx_eq0 n m (A : 'M_(m, n)) : (kermx A == 0) = row_free A. +Proof. by rewrite -mxrank_eq0 mxrank_ker subn_eq0 row_leq_rank. Qed. + +Lemma mxrank_coker m n (A : 'M_(m, n)) : \rank (cokermx A) = (n - \rank A)%N. +Proof. by rewrite eqmxMfull ?row_full_unit ?unitmx_inv ?rank_copid_mx. Qed. + +Lemma cokermx_eq0 n m (A : 'M_(m, n)) : (cokermx A == 0) = row_full A. +Proof. by rewrite -mxrank_eq0 mxrank_coker subn_eq0 col_leq_rank. Qed. + +Lemma mulmx_ker m n (A : 'M_(m, n)) : kermx A *m A = 0. +Proof. +by rewrite -{2}[A]mulmx_ebase !mulmxA mulmxKV // mul_copid_mx_pid ?mul0mx. +Qed. + +Lemma mulmxKV_ker m n p (A : 'M_(n, p)) (B : 'M_(m, n)) : + B *m A = 0 -> B *m col_ebase A *m kermx A = B. +Proof. +rewrite mulmxA mulmxBr mulmx1 mulmxBl mulmxK //. +rewrite -{1}[A]mulmx_ebase !mulmxA => /(canRL (mulmxK (row_ebase_unit A))). +rewrite mul0mx // => BA0; apply: (canLR (addrK _)). +by rewrite -(pid_mx_id _ _ n (rank_leq_col A)) mulmxA BA0 !mul0mx addr0. +Qed. + +Lemma sub_kermxP p m n (A : 'M_(m, n)) (B : 'M_(p, m)) : + reflect (B *m A = 0) (B <= kermx A)%MS. +Proof. +apply: (iffP submxP) => [[D ->]|]; first by rewrite -mulmxA mulmx_ker mulmx0. +by move/mulmxKV_ker; exists (B *m col_ebase A). +Qed. + +Lemma mulmx0_rank_max m n p (A : 'M_(m, n)) (B : 'M_(n, p)) : + A *m B = 0 -> \rank A + \rank B <= n. +Proof. +move=> AB0; rewrite -{3}(subnK (rank_leq_row B)) leq_add2r. +rewrite -mxrank_ker mxrankS //; exact/sub_kermxP. +Qed. + +Lemma mxrank_Frobenius m n p q (A : 'M_(m, n)) B (C : 'M_(p, q)) : + \rank (A *m B) + \rank (B *m C) <= \rank B + \rank (A *m B *m C). +Proof. +rewrite -{2}(mulmx_base (A *m B)) -mulmxA (eqmxMfull _ (col_base_full _)). +set C2 := row_base _ *m C. +rewrite -{1}(subnK (rank_leq_row C2)) -(mxrank_ker C2) addnAC leq_add2r. +rewrite addnC -{1}(mulmx_base B) -mulmxA eqmxMfull //. +set C1 := _ *m C; rewrite -{2}(subnKC (rank_leq_row C1)) leq_add2l -mxrank_ker. +rewrite -(mxrankMfree _ (row_base_free (A *m B))). +have: (row_base (A *m B) <= row_base B)%MS by rewrite !eq_row_base submxMl. +case/submxP=> D defD; rewrite defD mulmxA mxrankMfree ?mxrankS //. +by apply/sub_kermxP; rewrite -mulmxA (mulmxA D) -defD -/C2 mulmx_ker. +Qed. + +Lemma mxrank_mul_min m n p (A : 'M_(m, n)) (B : 'M_(n, p)) : + \rank A + \rank B - n <= \rank (A *m B). +Proof. +by have:= mxrank_Frobenius A 1%:M B; rewrite mulmx1 mul1mx mxrank1 leq_subLR. +Qed. + +Lemma addsmx_compl_full m n (A : 'M_(m, n)) : row_full (A + A^C)%MS. +Proof. +rewrite /row_full addsmxE; apply/row_fullP. +exists (row_mx (pinvmx A) (cokermx A)); rewrite mul_row_col. +rewrite -{2}[A]mulmx_ebase -!mulmxA mulKmx // -mulmxDr !mulmxA. +by rewrite pid_mx_id ?copid_mx_id // -mulmxDl addrC subrK mul1mx mulVmx. +Qed. + +Lemma sub_capmx_gen m1 m2 m3 n (A : 'M_(m1, n)) (B : 'M_(m2, n)) (C : 'M_(m3, n)) : + (A <= capmx_gen B C)%MS = (A <= B)%MS && (A <= C)%MS. +Proof. +apply/idP/andP=> [sAI | [/submxP[B' ->{A}] /submxP[C' eqBC']]]. + rewrite !(submx_trans sAI) ?submxMl // /capmx_gen. + have:= mulmx_ker (col_mx B C); set K := kermx _. + rewrite -{1}[K]hsubmxK mul_row_col; move/(canRL (addrK _))->. + by rewrite add0r -mulNmx submxMl. +have: (row_mx B' (- C') <= kermx (col_mx B C))%MS. + by apply/sub_kermxP; rewrite mul_row_col eqBC' mulNmx subrr. +case/submxP=> D; rewrite -[kermx _]hsubmxK mul_mx_row. +by case/eq_row_mx=> -> _; rewrite -mulmxA submxMl. +Qed. + +Let capmx_witnessP m n (A : 'M_(m, n)) : equivmx A (qidmx A) (capmx_witness A). +Proof. +rewrite /equivmx qidmx_eq1 /qidmx /capmx_witness. +rewrite -sub1mx; case s1A: (1%:M <= A)%MS => /=; last first. + rewrite !genmxE submx_refl /= -negb_add; apply: contra {s1A}(negbT s1A). + case: eqP => [<- _| _]; first by rewrite genmxE. + by case: eqP A => //= -> A; move/eqP->; rewrite pid_mx_1. +case: (m =P n) => [-> | ne_mn] in A s1A *. + by rewrite conform_mx_id submx_refl pid_mx_1 eqxx. +by rewrite nonconform_mx ?submx1 ?s1A ?eqxx //; case: eqP. +Qed. + +Let capmx_normP m n (A : 'M_(m, n)) : equivmx_spec A (qidmx A) (capmx_norm A). +Proof. by case/andP: (chooseP (capmx_witnessP A)) => /eqmxP defN /eqP. Qed. + +Let capmx_norm_eq m1 m2 n (A : 'M_(m1, n)) (B : 'M_(m2, n)) : + qidmx A = qidmx B -> (A == B)%MS -> capmx_norm A = capmx_norm B. +Proof. +move=> eqABid /eqmxP eqAB. +have{eqABid eqAB} eqAB: equivmx A (qidmx A) =1 equivmx B (qidmx B). + by move=> C; rewrite /equivmx eqABid !eqAB. +rewrite {1}/capmx_norm (eq_choose eqAB). +by apply: choose_id; first rewrite -eqAB; exact: capmx_witnessP. +Qed. + +Let capmx_nopP m n (A : 'M_(m, n)) : equivmx_spec A (qidmx A) (capmx_nop A). +Proof. +rewrite /capmx_nop; case: (eqVneq m n) => [-> | ne_mn] in A *. + by rewrite conform_mx_id. +rewrite nonconform_mx ?ne_mn //; exact: capmx_normP. +Qed. + +Let sub_qidmx m1 m2 n (A : 'M_(m1, n)) (B : 'M_(m2, n)) : + qidmx B -> (A <= B)%MS. +Proof. +rewrite /qidmx => idB; apply: {A}submx_trans (submx1 A) _. +by case: eqP B idB => [-> _ /eqP-> | _ B]; rewrite (=^~ sub1mx, pid_mx_1). +Qed. + +Let qidmx_cap m1 m2 n (A : 'M_(m1, n)) (B : 'M_(m2, n)) : + qidmx (A :&: B)%MS = qidmx A && qidmx B. +Proof. +rewrite unlock -sub1mx. +case idA: (qidmx A); case idB: (qidmx B); try by rewrite capmx_nopP. +case s1B: (_ <= B)%MS; first by rewrite capmx_normP. +apply/idP=> /(sub_qidmx 1%:M). +by rewrite capmx_normP sub_capmx_gen s1B andbF. +Qed. + +Let capmx_eq_norm m1 m2 n (A : 'M_(m1, n)) (B : 'M_(m2, n)) : + qidmx A = qidmx B -> (A :&: B)%MS = capmx_norm (A :&: B)%MS. +Proof. +move=> eqABid; rewrite unlock -sub1mx {}eqABid. +have norm_id m (C : 'M_(m, n)) (N := capmx_norm C) : capmx_norm N = N. + by apply: capmx_norm_eq; rewrite ?capmx_normP ?andbb. +case idB: (qidmx B); last by case: ifP; rewrite norm_id. +rewrite /capmx_nop; case: (eqVneq m2 n) => [-> | neqm2n] in B idB *. + have idN := idB; rewrite -{1}capmx_normP !qidmx_eq1 in idN idB. + by rewrite conform_mx_id (eqP idN) (eqP idB). +by rewrite nonconform_mx ?neqm2n ?norm_id. +Qed. + +Lemma capmxE m1 m2 n (A : 'M_(m1, n)) (B : 'M_(m2, n)) : + (A :&: B :=: capmx_gen A B)%MS. +Proof. +rewrite unlock -sub1mx; apply/eqmxP. +have:= submx_refl (capmx_gen A B); rewrite !sub_capmx_gen => /andP[sIA sIB]. +case idA: (qidmx A); first by rewrite !capmx_nopP submx_refl sub_qidmx. +case idB: (qidmx B); first by rewrite !capmx_nopP submx_refl sub_qidmx. +case s1B: (1%:M <= B)%MS; rewrite !capmx_normP ?sub_capmx_gen sIA ?sIB //=. +by rewrite submx_refl (submx_trans (submx1 _)). +Qed. + +Lemma capmxSl m1 m2 n (A : 'M_(m1, n)) (B : 'M_(m2, n)) : (A :&: B <= A)%MS. +Proof. by rewrite capmxE submxMl. Qed. + +Lemma sub_capmx m m1 m2 n (A : 'M_(m, n)) (B : 'M_(m1, n)) (C : 'M_(m2, n)) : + (A <= B :&: C)%MS = (A <= B)%MS && (A <= C)%MS. +Proof. by rewrite capmxE sub_capmx_gen. Qed. + +Lemma capmxC m1 m2 n (A : 'M_(m1, n)) (B : 'M_(m2, n)) : (A :&: B = B :&: A)%MS. +Proof. +have [eqAB|] := eqVneq (qidmx A) (qidmx B). + rewrite (capmx_eq_norm eqAB) (capmx_eq_norm (esym eqAB)). + apply: capmx_norm_eq; first by rewrite !qidmx_cap andbC. + by apply/andP; split; rewrite !sub_capmx andbC -sub_capmx. +by rewrite negb_eqb !unlock => /addbP <-; case: (qidmx A). +Qed. + +Lemma capmxSr m1 m2 n (A : 'M_(m1, n)) (B : 'M_(m2, n)) : (A :&: B <= B)%MS. +Proof. by rewrite capmxC capmxSl. Qed. + +Lemma capmx_idPr n m1 m2 (A : 'M_(m1, n)) (B : 'M_(m2, n)) : + reflect (A :&: B :=: B)%MS (B <= A)%MS. +Proof. +have:= @eqmxP _ _ _ (A :&: B)%MS B. +by rewrite capmxSr sub_capmx submx_refl !andbT. +Qed. + +Lemma capmx_idPl n m1 m2 (A : 'M_(m1, n)) (B : 'M_(m2, n)) : + reflect (A :&: B :=: A)%MS (A <= B)%MS. +Proof. by rewrite capmxC; exact: capmx_idPr. Qed. + +Lemma capmxS m1 m2 m3 m4 n (A : 'M_(m1, n)) (B : 'M_(m2, n)) + (C : 'M_(m3, n)) (D : 'M_(m4, n)) : + (A <= C -> B <= D -> A :&: B <= C :&: D)%MS. +Proof. +by move=> sAC sBD; rewrite sub_capmx {1}capmxC !(submx_trans (capmxSr _ _)). +Qed. + +Lemma cap_eqmx m1 m2 m3 m4 n (A : 'M_(m1, n)) (B : 'M_(m2, n)) + (C : 'M_(m3, n)) (D : 'M_(m4, n)) : + (A :=: C -> B :=: D -> A :&: B :=: C :&: D)%MS. +Proof. by move=> eqAC eqBD; apply/eqmxP; rewrite !capmxS ?eqAC ?eqBD. Qed. + +Lemma capmxMr m1 m2 n p (A : 'M_(m1, n)) (B : 'M_(m2, n)) (C : 'M_(n, p)) : + ((A :&: B) *m C <= A *m C :&: B *m C)%MS. +Proof. by rewrite sub_capmx !submxMr ?capmxSl ?capmxSr. Qed. + +Lemma cap0mx m1 m2 n (A : 'M_(m2, n)) : ((0 : 'M_(m1, n)) :&: A)%MS = 0. +Proof. exact: submx0null (capmxSl _ _). Qed. + +Lemma capmx0 m1 m2 n (A : 'M_(m1, n)) : (A :&: (0 : 'M_(m2, n)))%MS = 0. +Proof. exact: submx0null (capmxSr _ _). Qed. + +Lemma capmxT m1 m2 n (A : 'M_(m1, n)) (B : 'M_(m2, n)) : + row_full B -> (A :&: B :=: A)%MS. +Proof. +rewrite -sub1mx => s1B; apply/eqmxP. +by rewrite capmxSl sub_capmx submx_refl (submx_trans (submx1 A)). +Qed. + +Lemma capTmx m1 m2 n (A : 'M_(m1, n)) (B : 'M_(m2, n)) : + row_full A -> (A :&: B :=: B)%MS. +Proof. by move=> Afull; apply/eqmxP; rewrite capmxC !capmxT ?andbb. Qed. + +Let capmx_nop_id n (A : 'M_n) : capmx_nop A = A. +Proof. by rewrite /capmx_nop conform_mx_id. Qed. + +Lemma cap1mx n (A : 'M_n) : (1%:M :&: A = A)%MS. +Proof. by rewrite unlock qidmx_eq1 eqxx capmx_nop_id. Qed. + +Lemma capmx1 n (A : 'M_n) : (A :&: 1%:M = A)%MS. +Proof. by rewrite capmxC cap1mx. Qed. + +Lemma genmx_cap m1 m2 n (A : 'M_(m1, n)) (B : 'M_(m2, n)) : + <<A :&: B>>%MS = (<<A>> :&: <<B>>)%MS. +Proof. +rewrite -(eq_genmx (cap_eqmx (genmxE A) (genmxE B))). +case idAB: (qidmx <<A>> || qidmx <<B>>)%MS. + rewrite [@capmx]unlock !capmx_nop_id !(fun_if (@genmx _ _)) !genmx_id. + by case: (qidmx _) idAB => //= ->. +case idA: (qidmx _) idAB => //= idB; rewrite {2}capmx_eq_norm ?idA //. +set C := (_ :&: _)%MS; have eq_idC: row_full C = qidmx C. + rewrite qidmx_cap idA -sub1mx sub_capmx genmxE; apply/andP=> [[s1A]]. + by case/idP: idA; rewrite qidmx_eq1 -genmx1 (sameP eqP genmxP) submx1. +rewrite unlock /capmx_norm eq_idC. +by apply: choose_id (capmx_witnessP _); rewrite -eq_idC genmx_witnessP. +Qed. + +Lemma capmxA m1 m2 m3 n (A : 'M_(m1, n)) (B : 'M_(m2, n)) (C : 'M_(m3, n)) : + (A :&: (B :&: C) = A :&: B :&: C)%MS. +Proof. +rewrite (capmxC A B) capmxC; wlog idA: m1 m3 A C / qidmx A. + move=> IH; case idA: (qidmx A); first exact: IH. + case idC: (qidmx C); first by rewrite -IH. + rewrite (@capmx_eq_norm n m3) ?qidmx_cap ?idA ?idC ?andbF //. + rewrite capmx_eq_norm ?qidmx_cap ?idA ?idC ?andbF //. + apply: capmx_norm_eq; first by rewrite !qidmx_cap andbAC. + by apply/andP; split; rewrite !sub_capmx andbAC -!sub_capmx. +rewrite -!(capmxC A) [in @capmx m1]unlock idA capmx_nop_id. +have [eqBC |] :=eqVneq (qidmx B) (qidmx C). + rewrite (@capmx_eq_norm n) ?capmx_nopP // capmx_eq_norm //. + by apply: capmx_norm_eq; rewrite ?qidmx_cap ?capmxS ?capmx_nopP. +by rewrite !unlock capmx_nopP capmx_nop_id; do 2?case: (qidmx _) => //. +Qed. + +Canonical capmx_monoid n := + Monoid.Law (@capmxA n n n n) (@cap1mx n) (@capmx1 n). +Canonical capmx_comoid n := Monoid.ComLaw (@capmxC n n n). + +Lemma bigcapmx_inf i0 P m n (A_ : I -> 'M_n) (B : 'M_(m, n)) : + P i0 -> (A_ i0 <= B -> \bigcap_(i | P i) A_ i <= B)%MS. +Proof. by move=> Pi0; apply: submx_trans; rewrite (bigD1 i0) // capmxSl. Qed. + +Lemma sub_bigcapmxP P m n (A : 'M_(m, n)) (B_ : I -> 'M_n) : + reflect (forall i, P i -> A <= B_ i)%MS (A <= \bigcap_(i | P i) B_ i)%MS. +Proof. +apply: (iffP idP) => [sAB i Pi | sAB]. + by apply: (submx_trans sAB); rewrite (bigcapmx_inf Pi). +by elim/big_rec: _ => [|i Pi C sAC]; rewrite ?submx1 // sub_capmx sAB. +Qed. + +Lemma genmx_bigcap P n (A_ : I -> 'M_n) : + (<<\bigcap_(i | P i) A_ i>> = \bigcap_(i | P i) <<A_ i>>)%MS. +Proof. exact: (big_morph _ (@genmx_cap n n n) (@genmx1 n)). Qed. + +Lemma matrix_modl m1 m2 m3 n (A : 'M_(m1, n)) (B : 'M_(m2, n)) (C : 'M_(m3, n)) : + (A <= C -> A + (B :&: C) :=: (A + B) :&: C)%MS. +Proof. +move=> sAC; set D := ((A + B) :&: C)%MS; apply/eqmxP. +rewrite sub_capmx addsmxS ?capmxSl // addsmx_sub sAC capmxSr /=. +have: (D <= B + A)%MS by rewrite addsmxC capmxSl. +case/sub_addsmxP=> u defD; rewrite defD addrC addmx_sub_adds ?submxMl //. +rewrite sub_capmx submxMl -[_ *m B](addrK (u.2 *m A)) -defD. +by rewrite addmx_sub ?capmxSr // eqmx_opp mulmx_sub. +Qed. + +Lemma matrix_modr m1 m2 m3 n (A : 'M_(m1, n)) (B : 'M_(m2, n)) (C : 'M_(m3, n)) : + (C <= A -> (A :&: B) + C :=: A :&: (B + C))%MS. +Proof. by rewrite !(capmxC A) -!(addsmxC C); exact: matrix_modl. Qed. + +Lemma capmx_compl m n (A : 'M_(m, n)) : (A :&: A^C)%MS = 0. +Proof. +set D := (A :&: A^C)%MS; have: (D <= D)%MS by []. +rewrite sub_capmx andbC => /andP[/submxP[B defB]]. +rewrite submxE => /eqP; rewrite defB -!mulmxA mulKVmx ?copid_mx_id //. +by rewrite mulmxA => ->; rewrite mul0mx. +Qed. + +Lemma mxrank_mul_ker m n p (A : 'M_(m, n)) (B : 'M_(n, p)) : + (\rank (A *m B) + \rank (A :&: kermx B))%N = \rank A. +Proof. +apply/eqP; set K := kermx B; set C := (A :&: K)%MS. +rewrite -(eqmxMr B (eq_row_base A)); set K' := _ *m B. +rewrite -{2}(subnKC (rank_leq_row K')) -mxrank_ker eqn_add2l. +rewrite -(mxrankMfree _ (row_base_free A)) mxrank_leqif_sup. + rewrite sub_capmx -(eq_row_base A) submxMl. + by apply/sub_kermxP; rewrite -mulmxA mulmx_ker. +have /submxP[C' defC]: (C <= row_base A)%MS by rewrite eq_row_base capmxSl. +rewrite defC submxMr //; apply/sub_kermxP. +by rewrite mulmxA -defC; apply/sub_kermxP; rewrite capmxSr. +Qed. + +Lemma mxrank_injP m n p (A : 'M_(m, n)) (f : 'M_(n, p)) : + reflect (\rank (A *m f) = \rank A) ((A :&: kermx f)%MS == 0). +Proof. +rewrite -mxrank_eq0 -(eqn_add2l (\rank (A *m f))). +by rewrite mxrank_mul_ker addn0 eq_sym; exact: eqP. +Qed. + +Lemma mxrank_disjoint_sum m1 m2 n (A : 'M_(m1, n)) (B : 'M_(m2, n)) : + (A :&: B)%MS = 0 -> \rank (A + B)%MS = (\rank A + \rank B)%N. +Proof. +move=> AB0; pose Ar := row_base A; pose Br := row_base B. +have [Afree Bfree]: row_free Ar /\ row_free Br by rewrite !row_base_free. +have: (Ar :&: Br <= A :&: B)%MS by rewrite capmxS ?eq_row_base. +rewrite {}AB0 submx0 -mxrank_eq0 capmxE mxrankMfree //. +set Cr := col_mx Ar Br; set Crl := lsubmx _; rewrite mxrank_eq0 => /eqP Crl0. +rewrite -(adds_eqmx (eq_row_base _) (eq_row_base _)) addsmxE -/Cr. +suffices K0: kermx Cr = 0. + by apply/eqP; rewrite eqn_leq rank_leq_row -subn_eq0 -mxrank_ker K0 mxrank0. +move/eqP: (mulmx_ker Cr); rewrite -[kermx Cr]hsubmxK mul_row_col -/Crl Crl0. +rewrite mul0mx add0r -mxrank_eq0 mxrankMfree // mxrank_eq0 => /eqP->. +exact: row_mx0. +Qed. + +Lemma diffmxE m1 m2 n (A : 'M_(m1, n)) (B : 'M_(m2, n)) : + (A :\: B :=: A :&: (capmx_gen A B)^C)%MS. +Proof. by rewrite unlock; apply/eqmxP; rewrite !genmxE !capmxE andbb. Qed. + +Lemma genmx_diff m1 m2 n (A : 'M_(m1, n)) (B : 'M_(m2, n)) : + (<<A :\: B>> = A :\: B)%MS. +Proof. by rewrite [@diffmx]unlock genmx_id. Qed. + +Lemma diffmxSl m1 m2 n (A : 'M_(m1, n)) (B : 'M_(m2, n)) : (A :\: B <= A)%MS. +Proof. by rewrite diffmxE capmxSl. Qed. + +Lemma capmx_diff m1 m2 n (A : 'M_(m1, n)) (B : 'M_(m2, n)) : + ((A :\: B) :&: B)%MS = 0. +Proof. +apply/eqP; pose C := capmx_gen A B; rewrite -submx0 -(capmx_compl C). +by rewrite sub_capmx -capmxE sub_capmx andbAC -sub_capmx -diffmxE -sub_capmx. +Qed. + +Lemma addsmx_diff_cap_eq m1 m2 n (A : 'M_(m1, n)) (B : 'M_(m2, n)) : + (A :\: B + A :&: B :=: A)%MS. +Proof. +apply/eqmxP; rewrite addsmx_sub capmxSl diffmxSl /=. +set C := (A :\: B)%MS; set D := capmx_gen A B. +suffices sACD: (A <= C + D)%MS. + by rewrite (submx_trans sACD) ?addsmxS ?capmxE. +have:= addsmx_compl_full D; rewrite /row_full addsmxE. +case/row_fullP=> U /(congr1 (mulmx A)); rewrite mulmx1. +rewrite -[U]hsubmxK mul_row_col mulmxDr addrC 2!mulmxA. +set V := _ *m _ => defA; rewrite -defA; move/(canRL (addrK _)): defA => defV. +suffices /submxP[W ->]: (V <= C)%MS by rewrite -mul_row_col addsmxE submxMl. +rewrite diffmxE sub_capmx {1}defV -mulNmx addmx_sub 1?mulmx_sub //. +by rewrite -capmxE capmxSl. +Qed. + +Lemma mxrank_cap_compl m1 m2 n (A : 'M_(m1, n)) (B : 'M_(m2, n)) : + (\rank (A :&: B) + \rank (A :\: B))%N = \rank A. +Proof. +rewrite addnC -mxrank_disjoint_sum ?addsmx_diff_cap_eq //. +by rewrite (capmxC A) capmxA capmx_diff cap0mx. +Qed. + +Lemma mxrank_sum_cap m1 m2 n (A : 'M_(m1, n)) (B : 'M_(m2, n)) : + (\rank (A + B) + \rank (A :&: B) = \rank A + \rank B)%N. +Proof. +set C := (A :&: B)%MS; set D := (A :\: B)%MS. +have rDB: \rank (A + B)%MS = \rank (D + B)%MS. + apply/eqP; rewrite mxrank_leqif_sup; first by rewrite addsmxS ?diffmxSl. + by rewrite addsmx_sub addsmxSr -(addsmx_diff_cap_eq A B) addsmxS ?capmxSr. +rewrite {1}rDB mxrank_disjoint_sum ?capmx_diff //. +by rewrite addnC addnA mxrank_cap_compl. +Qed. + +Lemma mxrank_adds_leqif m1 m2 n (A : 'M_(m1, n)) (B : 'M_(m2, n)) : + \rank (A + B) <= \rank A + \rank B ?= iff (A :&: B <= (0 : 'M_n))%MS. +Proof. +rewrite -mxrank_sum_cap; split; first exact: leq_addr. +by rewrite addnC (@eqn_add2r _ 0) eq_sym mxrank_eq0 -submx0. +Qed. + +(* Subspace projection matrix *) + +Lemma proj_mx_sub m n U V (W : 'M_(m, n)) : (W *m proj_mx U V <= U)%MS. +Proof. by rewrite !mulmx_sub // -addsmxE addsmx0. Qed. + +Lemma proj_mx_compl_sub m n U V (W : 'M_(m, n)) : + (W <= U + V -> W - W *m proj_mx U V <= V)%MS. +Proof. +rewrite addsmxE => sWUV; rewrite mulmxA -{1}(mulmxKpV sWUV) -mulmxBr. +by rewrite mulmx_sub // opp_col_mx add_col_mx subrr subr0 -addsmxE adds0mx. +Qed. + +Lemma proj_mx_id m n U V (W : 'M_(m, n)) : + (U :&: V = 0)%MS -> (W <= U)%MS -> W *m proj_mx U V = W. +Proof. +move=> dxUV sWU; apply/eqP; rewrite -subr_eq0 -submx0 -dxUV. +rewrite sub_capmx addmx_sub ?eqmx_opp ?proj_mx_sub //= -eqmx_opp opprB. +by rewrite proj_mx_compl_sub // (submx_trans sWU) ?addsmxSl. +Qed. + +Lemma proj_mx_0 m n U V (W : 'M_(m, n)) : + (U :&: V = 0)%MS -> (W <= V)%MS -> W *m proj_mx U V = 0. +Proof. +move=> dxUV sWV; apply/eqP; rewrite -submx0 -dxUV. +rewrite sub_capmx proj_mx_sub /= -[_ *m _](subrK W) addmx_sub // -eqmx_opp. +by rewrite opprB proj_mx_compl_sub // (submx_trans sWV) ?addsmxSr. +Qed. + +Lemma add_proj_mx m n U V (W : 'M_(m, n)) : + (U :&: V = 0)%MS -> (W <= U + V)%MS -> + W *m proj_mx U V + W *m proj_mx V U = W. +Proof. +move=> dxUV sWUV; apply/eqP; rewrite -subr_eq0 -submx0 -dxUV. +rewrite -addrA sub_capmx {2}addrCA -!(opprB W). +by rewrite !{1}addmx_sub ?proj_mx_sub ?eqmx_opp ?proj_mx_compl_sub // addsmxC. +Qed. + +Lemma proj_mx_proj n (U V : 'M_n) : + let P := proj_mx U V in (U :&: V = 0)%MS -> P *m P = P. +Proof. by move=> P dxUV; rewrite -{-2}[P]mul1mx proj_mx_id ?proj_mx_sub. Qed. + +(* Completing a partially injective matrix to get a unit matrix. *) + +Lemma complete_unitmx m n (U : 'M_(m, n)) (f : 'M_n) : + \rank (U *m f) = \rank U -> {g : 'M_n | g \in unitmx & U *m f = U *m g}. +Proof. +move=> injfU; pose V := <<U>>%MS; pose W := V *m f. +pose g := proj_mx V (V^C)%MS *m f + cokermx V *m row_ebase W. +have defW: V *m g = W. + rewrite mulmxDr mulmxA proj_mx_id ?genmxE ?capmx_compl //. + by rewrite mulmxA mulmx_coker mul0mx addr0. +exists g; last first. + have /submxP[u ->]: (U <= V)%MS by rewrite genmxE. + by rewrite -!mulmxA defW. +rewrite -row_full_unit -sub1mx; apply/submxP. +have: (invmx (col_ebase W) *m W <= V *m g)%MS by rewrite defW submxMl. +case/submxP=> v def_v; exists (invmx (row_ebase W) *m (v *m V + (V^C)%MS)). +rewrite -mulmxA mulmxDl -mulmxA -def_v -{3}[W]mulmx_ebase -mulmxA. +rewrite mulKmx ?col_ebase_unit // [_ *m g]mulmxDr mulmxA. +rewrite (proj_mx_0 (capmx_compl _)) // mul0mx add0r 2!mulmxA. +rewrite mulmxK ?row_ebase_unit // copid_mx_id ?rank_leq_row //. +rewrite (eqmxMr _ (genmxE U)) injfU genmxE addrC -mulmxDl subrK. +by rewrite mul1mx mulVmx ?row_ebase_unit. +Qed. + +(* Mapping between two subspaces with the same dimension. *) + +Lemma eq_rank_unitmx m1 m2 n (U : 'M_(m1, n)) (V : 'M_(m2, n)) : + \rank U = \rank V -> {f : 'M_n | f \in unitmx & V :=: U *m f}%MS. +Proof. +move=> eqrUV; pose f := invmx (row_ebase <<U>>%MS) *m row_ebase <<V>>%MS. +have defUf: (<<U>> *m f :=: <<V>>)%MS. + rewrite -[<<U>>%MS]mulmx_ebase mulmxA mulmxK ?row_ebase_unit // -mulmxA. + rewrite genmxE eqrUV -genmxE -{3}[<<V>>%MS]mulmx_ebase -mulmxA. + move: (pid_mx _ *m _) => W; apply/eqmxP. + by rewrite !eqmxMfull ?andbb // row_full_unit col_ebase_unit. +have{defUf} defV: (V :=: U *m f)%MS. + by apply/eqmxP; rewrite -!(eqmxMr f (genmxE U)) !defUf !genmxE andbb. +have injfU: \rank (U *m f) = \rank U by rewrite -defV eqrUV. +by have [g injg defUg] := complete_unitmx injfU; exists g; rewrite -?defUg. +Qed. + +Section SumExpr. + +(* This is the infrastructure to support the mxdirect predicate. We use a *) +(* bespoke canonical structure to decompose a matrix expression into binary *) +(* and n-ary products, using some of the "quote" technology. This lets us *) +(* characterize direct sums as set sums whose rank is equal to the sum of the *) +(* ranks of the individual terms. The mxsum_expr/proper_mxsum_expr structures *) +(* below supply both the decomposition and the calculation of the rank sum. *) +(* The mxsum_spec dependent predicate family expresses the consistency of *) +(* these two decompositions. *) +(* The main technical difficulty we need to overcome is the fact that *) +(* the "catch-all" case of canonical structures has a priority lower than *) +(* constant expansion. However, it is undesireable that local abbreviations *) +(* be opaque for the direct-sum predicate, e.g., not be able to handle *) +(* let S := (\sum_(i | P i) LargeExpression i)%MS in mxdirect S -> ...). *) +(* As in "quote", we use the interleaving of constant expansion and *) +(* canonical projection matching to achieve our goal: we use a "wrapper" type *) +(* (indeed, the wrapped T type defined in ssrfun.v) with a self-inserting *) +(* non-primitive constructor to gain finer control over the type and *) +(* structure inference process. The innermost, primitive, constructor flags *) +(* trivial sums; it is initially hidden by an eta-expansion, which has been *) +(* made into a (default) canonical structure -- this lets type inference *) +(* automatically insert this outer tag. *) +(* In detail, we define three types *) +(* mxsum_spec S r <-> There exists a finite list of matrices A1, ..., Ak *) +(* such that S is the set sum of the Ai, and r is the sum *) +(* of the ranks of the Ai, i.e., S = (A1 + ... + Ak)%MS *) +(* and r = \rank A1 + ... + \rank Ak. Note that *) +(* mxsum_spec is a recursive dependent predicate family *) +(* whose elimination rewrites simultaneaously S, r and *) +(* the height of S. *) +(* proper_mxsum_expr n == The interface for proper sum expressions; this is *) +(* a double-entry interface, keyed on both the matrix sum *) +(* value and the rank sum. The matrix value is restricted *) +(* to square matrices, as the "+"%MS operator always *) +(* returns a square matrix. This interface has two *) +(* canonical insances, for binary and n-ary sums. *) +(* mxsum_expr m n == The interface for general sum expressions, comprising *) +(* both proper sums and trivial sums consisting of a *) +(* single matrix. The key values are WRAPPED as this lets *) +(* us give priority to the "proper sum" interpretation *) +(* (see below). To allow for trivial sums, the matrix key *) +(* can have any dimension. The mxsum_expr interface has *) +(* two canonical instances, for trivial and proper sums, *) +(* keyed to the Wrap and wrap constructors, respectively. *) +(* The projections for the two interfaces above are *) +(* proper_mxsum_val, mxsum_val : these are respectively coercions to 'M_n *) +(* and wrapped 'M_(m, n); thus, the matrix sum for an *) +(* S : mxsum_expr m n can be written unwrap S. *) +(* proper_mxsum_rank, mxsum_rank : projections to the nat and wrapped nat, *) +(* respectively; the rank sum for S : mxsum_expr m n is *) +(* thus written unwrap (mxsum_rank S). *) +(* The mxdirect A predicate actually gets A in a phantom argument, which is *) +(* used to infer an (implicit) S : mxsum_expr such that unwrap S = A; the *) +(* actual definition is \rank (unwrap S) == unwrap (mxsum_rank S). *) +(* Note that the inference of S is inherently ambiguous: ANY matrix can be *) +(* viewed as a trivial sum, including one whose description is manifestly a *) +(* proper sum. We use the wrapped type and the interaction between delta *) +(* reduction and canonical structure inference to resolve this ambiguity in *) +(* favor of proper sums, as follows: *) +(* - The phantom type sets up a unification problem of the form *) +(* unwrap (mxsum_val ?S) = A *) +(* with unknown evar ?S : mxsum_expr m n. *) +(* - As the constructor wrap is also a default Canonical instance for the *) +(* wrapped type, so A is immediately replaced with unwrap (wrap A) and *) +(* we get the residual unification problem *) +(* mxsum_val ?S = wrap A *) +(* - Now Coq tries to apply the proper sum Canonical instance, which has *) +(* key projection wrap (proper_mxsum_val ?PS) where ?PS is a fresh evar *) +(* (of type proper_mxsum_expr n). This can only succeed if m = n, and if *) +(* a solution can be found to the recursive unification problem *) +(* proper_mxsum_val ?PS = A *) +(* This causes Coq to look for one of the two canonical constants for *) +(* proper_mxsum_val (addsmx or bigop) at the head of A, delta-expanding *) +(* A as needed, and then inferring recursively mxsum_expr structures for *) +(* the last argument(s) of that constant. *) +(* - If the above step fails then the wrap constant is expanded, revealing *) +(* the primitive Wrap constructor; the unification problem now becomes *) +(* mxsum_val ?S = Wrap A *) +(* which fits perfectly the trivial sum canonical structure, whose key *) +(* projection is Wrap ?B where ?B is a fresh evar. Thus the inference *) +(* succeeds, and returns the trivial sum. *) +(* Note that the rank projections also register canonical values, so that the *) +(* same process can be used to infer a sum structure from the rank sum. In *) +(* that case, however, there is no ambiguity and the inference can fail, *) +(* because the rank sum for a trivial sum is not an arbitrary integer -- it *) +(* must be of the form \rank ?B. It is nevertheless necessary to use the *) +(* wrapped nat type for the rank sums, because in the non-trivial case the *) +(* head constant of the nat expression is determined by the proper_mxsum_expr *) +(* canonical structure, so the mxsum_expr structure must use a generic *) +(* constant, namely wrap. *) + +Inductive mxsum_spec n : forall m, 'M[F]_(m, n) -> nat -> Prop := + | TrivialMxsum m A + : @mxsum_spec n m A (\rank A) + | ProperMxsum m1 m2 T1 T2 r1 r2 of + @mxsum_spec n m1 T1 r1 & @mxsum_spec n m2 T2 r2 + : mxsum_spec (T1 + T2)%MS (r1 + r2)%N. +Arguments Scope mxsum_spec [nat_scope nat_scope matrix_set_scope nat_scope]. + +Structure mxsum_expr m n := Mxsum { + mxsum_val :> wrapped 'M_(m, n); + mxsum_rank : wrapped nat; + _ : mxsum_spec (unwrap mxsum_val) (unwrap mxsum_rank) +}. + +Canonical trivial_mxsum m n A := + @Mxsum m n (Wrap A) (Wrap (\rank A)) (TrivialMxsum A). + +Structure proper_mxsum_expr n := ProperMxsumExpr { + proper_mxsum_val :> 'M_n; + proper_mxsum_rank : nat; + _ : mxsum_spec proper_mxsum_val proper_mxsum_rank +}. + +Definition proper_mxsumP n (S : proper_mxsum_expr n) := + let: ProperMxsumExpr _ _ termS := S return mxsum_spec S (proper_mxsum_rank S) + in termS. + +Canonical sum_mxsum n (S : proper_mxsum_expr n) := + @Mxsum n n (wrap (S : 'M_n)) (wrap (proper_mxsum_rank S)) (proper_mxsumP S). + +Section Binary. +Variable (m1 m2 n : nat) (S1 : mxsum_expr m1 n) (S2 : mxsum_expr m2 n). +Fact binary_mxsum_proof : + mxsum_spec (unwrap S1 + unwrap S2) + (unwrap (mxsum_rank S1) + unwrap (mxsum_rank S2)). +Proof. by case: S1 S2 => [A1 r1 A1P] [A2 r2 A2P]; right. Qed. +Canonical binary_mxsum_expr := ProperMxsumExpr binary_mxsum_proof. +End Binary. + +Section Nary. +Context J (r : seq J) (P : pred J) n (S_ : J -> mxsum_expr n n). +Fact nary_mxsum_proof : + mxsum_spec (\sum_(j <- r | P j) unwrap (S_ j)) + (\sum_(j <- r | P j) unwrap (mxsum_rank (S_ j))). +Proof. +elim/big_rec2: _ => [|j]; first by rewrite -(mxrank0 n n); left. +by case: (S_ j); right. +Qed. +Canonical nary_mxsum_expr := ProperMxsumExpr nary_mxsum_proof. +End Nary. + +Definition mxdirect_def m n T of phantom 'M_(m, n) (unwrap (mxsum_val T)) := + \rank (unwrap T) == unwrap (mxsum_rank T). + +End SumExpr. + +Notation mxdirect A := (mxdirect_def (Phantom 'M_(_,_) A%MS)). + +Lemma mxdirectP n (S : proper_mxsum_expr n) : + reflect (\rank S = proper_mxsum_rank S) (mxdirect S). +Proof. exact: eqnP. Qed. +Implicit Arguments mxdirectP [n S]. + +Lemma mxdirect_trivial m n A : mxdirect (unwrap (@trivial_mxsum m n A)). +Proof. exact: eqxx. Qed. + +Lemma mxrank_sum_leqif m n (S : mxsum_expr m n) : + \rank (unwrap S) <= unwrap (mxsum_rank S) ?= iff mxdirect (unwrap S). +Proof. +rewrite /mxdirect_def; case: S => [[A] [r] /= defAr]; split=> //=. +elim: m A r / defAr => // m1 m2 A1 A2 r1 r2 _ leAr1 _ leAr2. +by apply: leq_trans (leq_add leAr1 leAr2); rewrite mxrank_adds_leqif. +Qed. + +Lemma mxdirectE m n (S : mxsum_expr m n) : + mxdirect (unwrap S) = (\rank (unwrap S) == unwrap (mxsum_rank S)). +Proof. by []. Qed. + +Lemma mxdirectEgeq m n (S : mxsum_expr m n) : + mxdirect (unwrap S) = (\rank (unwrap S) >= unwrap (mxsum_rank S)). +Proof. by rewrite (geq_leqif (mxrank_sum_leqif S)). Qed. + +Section BinaryDirect. + +Variables m1 m2 n : nat. + +Lemma mxdirect_addsE (S1 : mxsum_expr m1 n) (S2 : mxsum_expr m2 n) : + mxdirect (unwrap S1 + unwrap S2) + = [&& mxdirect (unwrap S1), mxdirect (unwrap S2) + & unwrap S1 :&: unwrap S2 == 0]%MS. +Proof. +rewrite (@mxdirectE n) /=. +have:= leqif_add (mxrank_sum_leqif S1) (mxrank_sum_leqif S2). +move/(leqif_trans (mxrank_adds_leqif (unwrap S1) (unwrap S2)))=> ->. +by rewrite andbC -andbA submx0. +Qed. + +Lemma mxdirect_addsP (A : 'M_(m1, n)) (B : 'M_(m2, n)) : + reflect (A :&: B = 0)%MS (mxdirect (A + B)). +Proof. by rewrite mxdirect_addsE !mxdirect_trivial; exact: eqP. Qed. + +End BinaryDirect. + +Section NaryDirect. + +Variables (P : pred I) (n : nat). + +Let TIsum A_ i := (A_ i :&: (\sum_(j | P j && (j != i)) A_ j) = 0 :> 'M_n)%MS. + +Let mxdirect_sums_recP (S_ : I -> mxsum_expr n n) : + reflect (forall i, P i -> mxdirect (unwrap (S_ i)) /\ TIsum (unwrap \o S_) i) + (mxdirect (\sum_(i | P i) (unwrap (S_ i)))). +Proof. +rewrite /TIsum; apply: (iffP eqnP) => /= [dxS i Pi | dxS]. + set Si' := (\sum_(j | _) unwrap (S_ j))%MS. + have: mxdirect (unwrap (S_ i) + Si') by apply/eqnP; rewrite /= -!(bigD1 i). + by rewrite mxdirect_addsE => /and3P[-> _ /eqP]. +elim: _.+1 {-2 4}P (subxx P) (ltnSn #|P|) => // m IHm Q; move/subsetP=> sQP. +case: (pickP Q) => [i Qi | Q0]; last by rewrite !big_pred0 ?mxrank0. +rewrite (cardD1x Qi) !((bigD1 i) Q) //=. +move/IHm=> <- {IHm}/=; last by apply/subsetP=> j /andP[/sQP]. +case: (dxS i (sQP i Qi)) => /eqnP=> <- TiQ_0; rewrite mxrank_disjoint_sum //. +apply/eqP; rewrite -submx0 -{2}TiQ_0 capmxS //=. +by apply/sumsmx_subP=> j /= /andP[Qj i'j]; rewrite (sumsmx_sup j) ?[P j]sQP. +Qed. + +Lemma mxdirect_sumsP (A_ : I -> 'M_n) : + reflect (forall i, P i -> A_ i :&: (\sum_(j | P j && (j != i)) A_ j) = 0)%MS + (mxdirect (\sum_(i | P i) A_ i)). +Proof. +apply: (iffP (mxdirect_sums_recP _)) => dxA i /dxA; first by case. +by rewrite mxdirect_trivial. +Qed. + +Lemma mxdirect_sumsE (S_ : I -> mxsum_expr n n) (xunwrap := unwrap) : + reflect (and (forall i, P i -> mxdirect (unwrap (S_ i))) + (mxdirect (\sum_(i | P i) (xunwrap (S_ i))))) + (mxdirect (\sum_(i | P i) (unwrap (S_ i)))). +Proof. +apply: (iffP (mxdirect_sums_recP _)) => [dxS | [dxS_ dxS] i Pi]. + by do [split; last apply/mxdirect_sumsP] => i; case/dxS. +by split; [exact: dxS_ | exact: mxdirect_sumsP Pi]. +Qed. + +End NaryDirect. + +Section SubDaddsmx. + +Variables m m1 m2 n : nat. +Variables (A : 'M[F]_(m, n)) (B1 : 'M[F]_(m1, n)) (B2 : 'M[F]_(m2, n)). + +CoInductive sub_daddsmx_spec : Prop := + SubDaddsmxSpec A1 A2 of (A1 <= B1)%MS & (A2 <= B2)%MS & A = A1 + A2 + & forall C1 C2, (C1 <= B1)%MS -> (C2 <= B2)%MS -> + A = C1 + C2 -> C1 = A1 /\ C2 = A2. + +Lemma sub_daddsmx : (B1 :&: B2 = 0)%MS -> (A <= B1 + B2)%MS -> sub_daddsmx_spec. +Proof. +move=> dxB /sub_addsmxP[u defA]. +exists (u.1 *m B1) (u.2 *m B2); rewrite ?submxMl // => C1 C2 sCB1 sCB2. +move/(canLR (addrK _)) => defC1. +suffices: (C2 - u.2 *m B2 <= B1 :&: B2)%MS. + by rewrite dxB submx0 subr_eq0 -defC1 defA; move/eqP->; rewrite addrK. +rewrite sub_capmx -opprB -{1}(canLR (addKr _) defA) -addrA defC1. +by rewrite !(eqmx_opp, addmx_sub) ?submxMl. +Qed. + +End SubDaddsmx. + +Section SubDsumsmx. + +Variables (P : pred I) (m n : nat) (A : 'M[F]_(m, n)) (B : I -> 'M[F]_n). + +CoInductive sub_dsumsmx_spec : Prop := + SubDsumsmxSpec A_ of forall i, P i -> (A_ i <= B i)%MS + & A = \sum_(i | P i) A_ i + & forall C, (forall i, P i -> C i <= B i)%MS -> + A = \sum_(i | P i) C i -> {in SimplPred P, C =1 A_}. + +Lemma sub_dsumsmx : + mxdirect (\sum_(i | P i) B i) -> (A <= \sum_(i | P i) B i)%MS -> + sub_dsumsmx_spec. +Proof. +move/mxdirect_sumsP=> dxB /sub_sumsmxP[u defA]. +pose A_ i := u i *m B i. +exists A_ => //= [i _ | C sCB defAC i Pi]; first exact: submxMl. +apply/eqP; rewrite -subr_eq0 -submx0 -{dxB}(dxB i Pi) /=. +rewrite sub_capmx addmx_sub ?eqmx_opp ?submxMl ?sCB //=. +rewrite -(subrK A (C i)) -addrA -opprB addmx_sub ?eqmx_opp //. + rewrite addrC defAC (bigD1 i) // addKr /= summx_sub // => j Pi'j. + by rewrite (sumsmx_sup j) ?sCB //; case/andP: Pi'j. +rewrite addrC defA (bigD1 i) // addKr /= summx_sub // => j Pi'j. +by rewrite (sumsmx_sup j) ?submxMl. +Qed. + +End SubDsumsmx. + +Section Eigenspace. + +Variables (n : nat) (g : 'M_n). + +Definition eigenspace a := kermx (g - a%:M). +Definition eigenvalue : pred F := fun a => eigenspace a != 0. + +Lemma eigenspaceP a m (W : 'M_(m, n)) : + reflect (W *m g = a *: W) (W <= eigenspace a)%MS. +Proof. +rewrite (sameP (sub_kermxP _ _) eqP). +by rewrite mulmxBr subr_eq0 mul_mx_scalar; exact: eqP. +Qed. + +Lemma eigenvalueP a : + reflect (exists2 v : 'rV_n, v *m g = a *: v & v != 0) (eigenvalue a). +Proof. by apply: (iffP (rowV0Pn _)) => [] [v]; move/eigenspaceP; exists v. Qed. + +Lemma mxdirect_sum_eigenspace (P : pred I) a_ : + {in P &, injective a_} -> mxdirect (\sum_(i | P i) eigenspace (a_ i)). +Proof. +elim: {P}_.+1 {-2}P (ltnSn #|P|) => // m IHm P lePm inj_a. +apply/mxdirect_sumsP=> i Pi; apply/eqP/rowV0P => v. +rewrite sub_capmx => /andP[/eigenspaceP def_vg]. +set Vi' := (\sum_(i | _) _)%MS => Vi'v. +have dxVi': mxdirect Vi'. + rewrite (cardD1x Pi) in lePm; apply: IHm => //. + by apply: sub_in2 inj_a => j /andP[]. +case/sub_dsumsmx: Vi'v => // u Vi'u def_v _. +rewrite def_v big1 // => j Pi'j; apply/eqP. +have nz_aij: a_ i - a_ j != 0. + by case/andP: Pi'j => Pj ne_ji; rewrite subr_eq0 eq_sym (inj_in_eq inj_a). +case: (sub_dsumsmx dxVi' (sub0mx 1 _)) => C _ _ uniqC. +rewrite -(eqmx_eq0 (eqmx_scale _ nz_aij)). +rewrite (uniqC (fun k => (a_ i - a_ k) *: u k)) => // [|k Pi'k|]. +- by rewrite -(uniqC (fun _ => 0)) ?big1 // => k Pi'k; exact: sub0mx. +- by rewrite scalemx_sub ?Vi'u. +rewrite -{1}(subrr (v *m g)) {1}def_vg def_v scaler_sumr mulmx_suml -sumrB. +by apply: eq_bigr => k /Vi'u/eigenspaceP->; rewrite scalerBl. +Qed. + +End Eigenspace. + +End RowSpaceTheory. + +Hint Resolve submx_refl. +Implicit Arguments submxP [F m1 m2 n A B]. +Implicit Arguments eq_row_sub [F m n v A]. +Implicit Arguments row_subP [F m1 m2 n A B]. +Implicit Arguments rV_subP [F m1 m2 n A B]. +Implicit Arguments row_subPn [F m1 m2 n A B]. +Implicit Arguments sub_rVP [F n u v]. +Implicit Arguments rV_eqP [F m1 m2 n A B]. +Implicit Arguments rowV0Pn [F m n A]. +Implicit Arguments rowV0P [F m n A]. +Implicit Arguments eqmx0P [F m n A]. +Implicit Arguments row_fullP [F m n A]. +Implicit Arguments row_freeP [F m n A]. +Implicit Arguments eqmxP [F m1 m2 n A B]. +Implicit Arguments genmxP [F m1 m2 n A B]. +Implicit Arguments addsmx_idPr [F m1 m2 n A B]. +Implicit Arguments addsmx_idPl [F m1 m2 n A B]. +Implicit Arguments sub_addsmxP [F m1 m2 m3 n A B C]. +Implicit Arguments sumsmx_sup [F I P m n A B_]. +Implicit Arguments sumsmx_subP [F I P m n A_ B]. +Implicit Arguments sub_sumsmxP [F I P m n A B_]. +Implicit Arguments sub_kermxP [F p m n A B]. +Implicit Arguments capmx_idPr [F m1 m2 n A B]. +Implicit Arguments capmx_idPl [F m1 m2 n A B]. +Implicit Arguments bigcapmx_inf [F I P m n A_ B]. +Implicit Arguments sub_bigcapmxP [F I P m n A B_]. +Implicit Arguments mxrank_injP [F m n A f]. +Implicit Arguments mxdirectP [F n S]. +Implicit Arguments mxdirect_addsP [F m1 m2 n A B]. +Implicit Arguments mxdirect_sumsP [F I P n A_]. +Implicit Arguments mxdirect_sumsE [F I P n S_]. +Implicit Arguments eigenspaceP [F n g a m W]. +Implicit Arguments eigenvalueP [F n g a]. + +Arguments Scope mxrank [_ nat_scope nat_scope matrix_set_scope]. +Arguments Scope complmx [_ nat_scope nat_scope matrix_set_scope]. +Arguments Scope row_full [_ nat_scope nat_scope matrix_set_scope]. +Arguments Scope submx + [_ nat_scope nat_scope nat_scope matrix_set_scope matrix_set_scope]. +Arguments Scope ltmx + [_ nat_scope nat_scope nat_scope matrix_set_scope matrix_set_scope]. +Arguments Scope eqmx + [_ nat_scope nat_scope nat_scope matrix_set_scope matrix_set_scope]. +Arguments Scope addsmx + [_ nat_scope nat_scope nat_scope matrix_set_scope matrix_set_scope]. +Arguments Scope capmx + [_ nat_scope nat_scope nat_scope matrix_set_scope matrix_set_scope]. +Arguments Scope diffmx + [_ nat_scope nat_scope nat_scope matrix_set_scope matrix_set_scope]. +Prenex Implicits mxrank genmx complmx submx ltmx addsmx capmx. +Notation "\rank A" := (mxrank A) : nat_scope. +Notation "<< A >>" := (genmx A) : matrix_set_scope. +Notation "A ^C" := (complmx A) : matrix_set_scope. +Notation "A <= B" := (submx A B) : matrix_set_scope. +Notation "A < B" := (ltmx A B) : matrix_set_scope. +Notation "A <= B <= C" := ((submx A B) && (submx B C)) : matrix_set_scope. +Notation "A < B <= C" := (ltmx A B && submx B C) : matrix_set_scope. +Notation "A <= B < C" := (submx A B && ltmx B C) : matrix_set_scope. +Notation "A < B < C" := (ltmx A B && ltmx B C) : matrix_set_scope. +Notation "A == B" := ((submx A B) && (submx B A)) : matrix_set_scope. +Notation "A :=: B" := (eqmx A B) : matrix_set_scope. +Notation "A + B" := (addsmx A B) : matrix_set_scope. +Notation "A :&: B" := (capmx A B) : matrix_set_scope. +Notation "A :\: B" := (diffmx A B) : matrix_set_scope. +Notation mxdirect S := (mxdirect_def (Phantom 'M_(_,_) S%MS)). + +Notation "\sum_ ( i <- r | P ) B" := + (\big[addsmx/0%R]_(i <- r | P%B) B%MS) : matrix_set_scope. +Notation "\sum_ ( i <- r ) B" := + (\big[addsmx/0%R]_(i <- r) B%MS) : matrix_set_scope. +Notation "\sum_ ( m <= i < n | P ) B" := + (\big[addsmx/0%R]_(m <= i < n | P%B) B%MS) : matrix_set_scope. +Notation "\sum_ ( m <= i < n ) B" := + (\big[addsmx/0%R]_(m <= i < n) B%MS) : matrix_set_scope. +Notation "\sum_ ( i | P ) B" := + (\big[addsmx/0%R]_(i | P%B) B%MS) : matrix_set_scope. +Notation "\sum_ i B" := + (\big[addsmx/0%R]_i B%MS) : matrix_set_scope. +Notation "\sum_ ( i : t | P ) B" := + (\big[addsmx/0%R]_(i : t | P%B) B%MS) (only parsing) : matrix_set_scope. +Notation "\sum_ ( i : t ) B" := + (\big[addsmx/0%R]_(i : t) B%MS) (only parsing) : matrix_set_scope. +Notation "\sum_ ( i < n | P ) B" := + (\big[addsmx/0%R]_(i < n | P%B) B%MS) : matrix_set_scope. +Notation "\sum_ ( i < n ) B" := + (\big[addsmx/0%R]_(i < n) B%MS) : matrix_set_scope. +Notation "\sum_ ( i 'in' A | P ) B" := + (\big[addsmx/0%R]_(i in A | P%B) B%MS) : matrix_set_scope. +Notation "\sum_ ( i 'in' A ) B" := + (\big[addsmx/0%R]_(i in A) B%MS) : matrix_set_scope. + +Notation "\bigcap_ ( i <- r | P ) B" := + (\big[capmx/1%:M]_(i <- r | P%B) B%MS) : matrix_set_scope. +Notation "\bigcap_ ( i <- r ) B" := + (\big[capmx/1%:M]_(i <- r) B%MS) : matrix_set_scope. +Notation "\bigcap_ ( m <= i < n | P ) B" := + (\big[capmx/1%:M]_(m <= i < n | P%B) B%MS) : matrix_set_scope. +Notation "\bigcap_ ( m <= i < n ) B" := + (\big[capmx/1%:M]_(m <= i < n) B%MS) : matrix_set_scope. +Notation "\bigcap_ ( i | P ) B" := + (\big[capmx/1%:M]_(i | P%B) B%MS) : matrix_set_scope. +Notation "\bigcap_ i B" := + (\big[capmx/1%:M]_i B%MS) : matrix_set_scope. +Notation "\bigcap_ ( i : t | P ) B" := + (\big[capmx/1%:M]_(i : t | P%B) B%MS) (only parsing) : matrix_set_scope. +Notation "\bigcap_ ( i : t ) B" := + (\big[capmx/1%:M]_(i : t) B%MS) (only parsing) : matrix_set_scope. +Notation "\bigcap_ ( i < n | P ) B" := + (\big[capmx/1%:M]_(i < n | P%B) B%MS) : matrix_set_scope. +Notation "\bigcap_ ( i < n ) B" := + (\big[capmx/1%:M]_(i < n) B%MS) : matrix_set_scope. +Notation "\bigcap_ ( i 'in' A | P ) B" := + (\big[capmx/1%:M]_(i in A | P%B) B%MS) : matrix_set_scope. +Notation "\bigcap_ ( i 'in' A ) B" := + (\big[capmx/1%:M]_(i in A) B%MS) : matrix_set_scope. + +Section CardGL. + +Variable F : finFieldType. + +Lemma card_GL n : n > 0 -> + #|'GL_n[F]| = (#|F| ^ 'C(n, 2) * \prod_(1 <= i < n.+1) (#|F| ^ i - 1))%N. +Proof. +case: n => // n' _; set n := n'.+1; set p := #|F|. +rewrite big_nat_rev big_add1 -triangular_sum expn_sum -big_split /=. +pose fr m := [pred A : 'M[F]_(m, n) | \rank A == m]. +set m := {-7}n; transitivity #|fr m|. + by rewrite cardsT /= card_sub; apply: eq_card => A; rewrite -row_free_unit. +elim: m (leqnn m : m <= n) => [_|m IHm]; last move/ltnW=> le_mn. + rewrite (@eq_card1 _ (0 : 'M_(0, n))) ?big_geq //= => A. + by rewrite flatmx0 !inE !eqxx. +rewrite big_nat_recr // -{}IHm //= !subSS mulnBr muln1 -expnD subnKC //. +rewrite -sum_nat_const /= -sum1_card -add1n. +rewrite (partition_big dsubmx (fr m)) /= => [|A]; last first. + rewrite !inE -{1}(vsubmxK A); move: {A}(_ A) (_ A) => Ad Au Afull. + rewrite eqn_leq rank_leq_row -(leq_add2l (\rank Au)) -mxrank_sum_cap. + rewrite {1 3}[@mxrank]lock addsmxE (eqnP Afull) -lock -addnA. + by rewrite leq_add ?rank_leq_row ?leq_addr. +apply: eq_bigr => A rAm; rewrite (reindex (col_mx^~ A)) /=; last first. + exists usubmx => [v _ | vA]; first by rewrite col_mxKu. + by case/andP=> _ /eqP <-; rewrite vsubmxK. +transitivity #|~: [set v *m A | v in 'rV_m]|; last first. + rewrite cardsCs setCK card_imset ?card_matrix ?card_ord ?mul1n //. + have [B AB1] := row_freeP rAm; apply: can_inj (mulmx^~ B) _ => v. + by rewrite -mulmxA AB1 mulmx1. +rewrite -sum1_card; apply: eq_bigl => v; rewrite !inE col_mxKd eqxx. +rewrite andbT eqn_leq rank_leq_row /= -(leq_add2r (\rank (v :&: A)%MS)). +rewrite -addsmxE mxrank_sum_cap (eqnP rAm) addnAC leq_add2r. +rewrite (ltn_leqif (mxrank_leqif_sup _)) ?capmxSl // sub_capmx submx_refl. +by congr (~~ _); apply/submxP/imsetP=> [] [u]; exists u. +Qed. + +(* An alternate, somewhat more elementary proof, that does not rely on the *) +(* row-space theory, but directly performs the LUP decomposition. *) +Lemma LUP_card_GL n : n > 0 -> + #|'GL_n[F]| = (#|F| ^ 'C(n, 2) * \prod_(1 <= i < n.+1) (#|F| ^ i - 1))%N. +Proof. +case: n => // n' _; set n := n'.+1; set p := #|F|. +rewrite cardsT /= card_sub /GRing.unit /= big_add1 /= -triangular_sum -/n. +elim: {n'}n => [|n IHn]. + rewrite !big_geq // mul1n (@eq_card _ _ predT) ?card_matrix //= => M. + by rewrite {1}[M]flatmx0 -(flatmx0 1%:M) unitmx1. +rewrite !big_nat_recr //= expnD mulnAC mulnA -{}IHn -mulnA mulnC. +set LHS := #|_|; rewrite -[n.+1]muln1 -{2}[n]mul1n {}/LHS. +rewrite -!card_matrix subn1 -(cardC1 0) -mulnA; set nzC := predC1 _. +rewrite -sum1_card (partition_big lsubmx nzC) => [|A]; last first. + rewrite unitmxE unitfE; apply: contra; move/eqP=> v0. + rewrite -[A]hsubmxK v0 -[n.+1]/(1 + n)%N -col_mx0. + rewrite -[rsubmx _]vsubmxK -det_tr tr_row_mx !tr_col_mx !trmx0. + by rewrite det_lblock [0]mx11_scalar det_scalar1 mxE mul0r. +rewrite -sum_nat_const; apply: eq_bigr; rewrite /= -[n.+1]/(1 + n)%N => v nzv. +case: (pickP (fun i => v i 0 != 0)) => [k nza | v0]; last first. + by case/eqP: nzv; apply/colP=> i; move/eqP: (v0 i); rewrite mxE. +have xrkK: involutive (@xrow F _ _ 0 k). + by move=> m A /=; rewrite /xrow -row_permM tperm2 row_perm1. +rewrite (reindex_inj (inv_inj (xrkK (1 + n)%N))) /= -[n.+1]/(1 + n)%N. +rewrite (partition_big ursubmx xpredT) //= -sum_nat_const. +apply: eq_bigr => u _; set a : F := v _ _ in nza. +set v1 : 'cV_(1 + n) := xrow 0 k v. +have def_a: usubmx v1 = a%:M. + by rewrite [_ v1]mx11_scalar mxE lshift0 mxE tpermL. +pose Schur := dsubmx v1 *m (a^-1 *: u). +pose L : 'M_(1 + n) := block_mx a%:M 0 (dsubmx v1) 1%:M. +pose U B : 'M_(1 + n) := block_mx 1 (a^-1 *: u) 0 B. +rewrite (reindex (fun B => L *m U B)); last first. + exists (fun A1 => drsubmx A1 - Schur) => [B _ | A1]. + by rewrite mulmx_block block_mxKdr mul1mx addrC addKr. + rewrite !inE mulmx_block !mulmx0 mul0mx !mulmx1 !addr0 mul1mx addrC subrK. + rewrite mul_scalar_mx scalerA divff // scale1r andbC; case/and3P => /eqP <- _. + rewrite -{1}(hsubmxK A1) xrowE mul_mx_row row_mxKl -xrowE => /eqP def_v. + rewrite -def_a block_mxEh vsubmxK /v1 -def_v xrkK. + apply: trmx_inj; rewrite tr_row_mx tr_col_mx trmx_ursub trmx_drsub trmx_lsub. + by rewrite hsubmxK vsubmxK. +rewrite -sum1_card; apply: eq_bigl => B; rewrite xrowE unitmxE. +rewrite !det_mulmx unitrM -unitmxE unitmx_perm det_lblock det_ublock. +rewrite !det_scalar1 det1 mulr1 mul1r unitrM unitfE nza -unitmxE. +rewrite mulmx_block !mulmx0 mul0mx !addr0 !mulmx1 mul1mx block_mxKur. +rewrite mul_scalar_mx scalerA divff // scale1r eqxx andbT. +by rewrite block_mxEh mul_mx_row row_mxKl -def_a vsubmxK -xrowE xrkK eqxx andbT. +Qed. + +Lemma card_GL_1 : #|'GL_1[F]| = #|F|.-1. +Proof. by rewrite card_GL // mul1n big_nat1 expn1 subn1. Qed. + +Lemma card_GL_2 : #|'GL_2[F]| = (#|F| * #|F|.-1 ^ 2 * #|F|.+1)%N. +Proof. +rewrite card_GL // big_ltn // big_nat1 expn1 -(addn1 #|F|) -subn1 -!mulnA. +by rewrite -subn_sqr. +Qed. + +End CardGL. + +Lemma logn_card_GL_p n p : prime p -> logn p #|'GL_n(p)| = 'C(n, 2). +Proof. +move=> p_pr; have p_gt1 := prime_gt1 p_pr. +have p_i_gt0: p ^ _ > 0 by move=> i; rewrite expn_gt0 ltnW. +rewrite (card_GL _ (ltn0Sn n.-1)) card_ord Fp_cast // big_add1 /=. +pose p'gt0 m := m > 0 /\ logn p m = 0%N. +suffices [Pgt0 p'P]: p'gt0 (\prod_(0 <= i < n.-1.+1) (p ^ i.+1 - 1))%N. + by rewrite lognM // p'P pfactorK //; case n. +apply big_ind => [|m1 m2 [m10 p'm1] [m20]|i _]; rewrite {}/p'gt0 ?logn1 //. + by rewrite muln_gt0 m10 lognM ?p'm1. +rewrite lognE -if_neg subn_gt0 p_pr /= -{1 2}(exp1n i.+1) ltn_exp2r // p_gt1. +by rewrite dvdn_subr ?dvdn_exp // gtnNdvd. +Qed. + +Section MatrixAlgebra. + +Variables F : fieldType. + +Local Notation "A \in R" := (@submx F _ _ _ (mxvec A) R). + +Lemma mem0mx m n (R : 'A_(m, n)) : 0 \in R. +Proof. by rewrite linear0 sub0mx. Qed. + +Lemma memmx0 n A : (A \in (0 : 'A_n)) -> A = 0. +Proof. by rewrite submx0 mxvec_eq0; move/eqP. Qed. + +Lemma memmx1 n (A : 'M_n) : (A \in mxvec 1%:M) = is_scalar_mx A. +Proof. +apply/sub_rVP/is_scalar_mxP=> [[a] | [a ->]]. + by rewrite -linearZ scale_scalar_mx mulr1 => /(can_inj mxvecK); exists a. +by exists a; rewrite -linearZ scale_scalar_mx mulr1. +Qed. + +Lemma memmx_subP m1 m2 n (R1 : 'A_(m1, n)) (R2 : 'A_(m2, n)) : + reflect (forall A, A \in R1 -> A \in R2) (R1 <= R2)%MS. +Proof. +apply: (iffP idP) => [sR12 A R1_A | sR12]; first exact: submx_trans sR12. +by apply/rV_subP=> vA; rewrite -(vec_mxK vA); exact: sR12. +Qed. +Implicit Arguments memmx_subP [m1 m2 n R1 R2]. + +Lemma memmx_eqP m1 m2 n (R1 : 'A_(m1, n)) (R2 : 'A_(m2, n)) : + reflect (forall A, (A \in R1) = (A \in R2)) (R1 == R2)%MS. +Proof. +apply: (iffP eqmxP) => [eqR12 A | eqR12]; first by rewrite eqR12. +by apply/eqmxP; apply/rV_eqP=> vA; rewrite -(vec_mxK vA) eqR12. +Qed. +Implicit Arguments memmx_eqP [m1 m2 n R1 R2]. + +Lemma memmx_addsP m1 m2 n A (R1 : 'A_(m1, n)) (R2 : 'A_(m2, n)) : + reflect (exists D, [/\ D.1 \in R1, D.2 \in R2 & A = D.1 + D.2]) + (A \in R1 + R2)%MS. +Proof. +apply: (iffP sub_addsmxP) => [[u /(canRL mxvecK)->] | [D []]]. + exists (vec_mx (u.1 *m R1), vec_mx (u.2 *m R2)). + by rewrite /= linearD !vec_mxK !submxMl. +case/submxP=> u1 defD1 /submxP[u2 defD2] ->. +by exists (u1, u2); rewrite linearD /= defD1 defD2. +Qed. +Implicit Arguments memmx_addsP [m1 m2 n A R1 R2]. + +Lemma memmx_sumsP (I : finType) (P : pred I) n (A : 'M_n) R_ : + reflect (exists2 A_, A = \sum_(i | P i) A_ i & forall i, A_ i \in R_ i) + (A \in \sum_(i | P i) R_ i)%MS. +Proof. +apply: (iffP sub_sumsmxP) => [[C defA] | [A_ -> R_A] {A}]. + exists (fun i => vec_mx (C i *m R_ i)) => [|i]. + by rewrite -linear_sum -defA /= mxvecK. + by rewrite vec_mxK submxMl. +exists (fun i => mxvec (A_ i) *m pinvmx (R_ i)). +by rewrite linear_sum; apply: eq_bigr => i _; rewrite mulmxKpV. +Qed. +Implicit Arguments memmx_sumsP [I P n A R_]. + +Lemma has_non_scalar_mxP m n (R : 'A_(m, n)) : + (1%:M \in R)%MS -> + reflect (exists2 A, A \in R & ~~ is_scalar_mx A)%MS (1 < \rank R). +Proof. +case: (posnP n) => [-> | n_gt0] in R *; set S := mxvec _ => sSR. + by rewrite [R]thinmx0 mxrank0; right; case; rewrite /is_scalar_mx ?insubF. +have rankS: \rank S = 1%N. + apply/eqP; rewrite eqn_leq rank_leq_row lt0n mxrank_eq0 mxvec_eq0. + by rewrite -mxrank_eq0 mxrank1 -lt0n. +rewrite -{2}rankS (ltn_leqif (mxrank_leqif_sup sSR)). +apply: (iffP idP) => [/row_subPn[i] | [A sAR]]. + rewrite -[row i R]vec_mxK memmx1; set A := vec_mx _ => nsA. + by exists A; rewrite // vec_mxK row_sub. +by rewrite -memmx1; apply: contra; exact: submx_trans. +Qed. + +Definition mulsmx m1 m2 n (R1 : 'A[F]_(m1, n)) (R2 : 'A_(m2, n)) := + (\sum_i <<R1 *m lin_mx (mulmxr (vec_mx (row i R2)))>>)%MS. + +Arguments Scope mulsmx + [nat_scope nat_scope nat_scope matrix_set_scope matrix_set_scope]. + +Local Notation "R1 * R2" := (mulsmx R1 R2) : matrix_set_scope. + +Lemma genmx_muls m1 m2 n (R1 : 'A_(m1, n)) (R2 : 'A_(m2, n)) : + <<(R1 * R2)%MS>>%MS = (R1 * R2)%MS. +Proof. by rewrite genmx_sums; apply: eq_bigr => i; rewrite genmx_id. Qed. + +Lemma mem_mulsmx m1 m2 n (R1 : 'A_(m1, n)) (R2 : 'A_(m2, n)) A1 A2 : + (A1 \in R1 -> A2 \in R2 -> A1 *m A2 \in R1 * R2)%MS. +Proof. +move=> R_A1 R_A2; rewrite -[A2]mxvecK; case/submxP: R_A2 => a ->{A2}. +rewrite mulmx_sum_row !linear_sum summx_sub // => i _. +rewrite !linearZ scalemx_sub {a}//= (sumsmx_sup i) // genmxE. +rewrite -[A1]mxvecK; case/submxP: R_A1 => a ->{A1}. +by apply/submxP; exists a; rewrite mulmxA mul_rV_lin. +Qed. + +Lemma mulsmx_subP m1 m2 m n + (R1 : 'A_(m1, n)) (R2 : 'A_(m2, n)) (R : 'A_(m, n)) : + reflect (forall A1 A2, A1 \in R1 -> A2 \in R2 -> A1 *m A2 \in R) + (R1 * R2 <= R)%MS. +Proof. +apply: (iffP memmx_subP) => [sR12R A1 A2 R_A1 R_A2 | sR12R A]. + by rewrite sR12R ?mem_mulsmx. +case/memmx_sumsP=> A_ -> R_A; rewrite linear_sum summx_sub //= => j _. +rewrite (submx_trans (R_A _)) // genmxE; apply/row_subP=> i. +by rewrite row_mul mul_rV_lin sR12R ?vec_mxK ?row_sub. +Qed. +Implicit Arguments mulsmx_subP [m1 m2 m n R1 R2 R]. + +Lemma mulsmxS m1 m2 m3 m4 n (R1 : 'A_(m1, n)) (R2 : 'A_(m2, n)) + (R3 : 'A_(m3, n)) (R4 : 'A_(m4, n)) : + (R1 <= R3 -> R2 <= R4 -> R1 * R2 <= R3 * R4)%MS. +Proof. +move=> sR13 sR24; apply/mulsmx_subP=> A1 A2 R_A1 R_A2. +by apply: mem_mulsmx; [exact: submx_trans sR13 | exact: submx_trans sR24]. +Qed. + +Lemma muls_eqmx m1 m2 m3 m4 n (R1 : 'A_(m1, n)) (R2 : 'A_(m2, n)) + (R3 : 'A_(m3, n)) (R4 : 'A_(m4, n)) : + (R1 :=: R3 -> R2 :=: R4 -> R1 * R2 = R3 * R4)%MS. +Proof. +move=> eqR13 eqR24; rewrite -(genmx_muls R1 R2) -(genmx_muls R3 R4). +by apply/genmxP; rewrite !mulsmxS ?eqR13 ?eqR24. +Qed. + +Lemma mulsmxP m1 m2 n A (R1 : 'A_(m1, n)) (R2 : 'A_(m2, n)) : + reflect (exists2 A1, forall i, A1 i \in R1 + & exists2 A2, forall i, A2 i \in R2 + & A = \sum_(i < n ^ 2) A1 i *m A2 i) + (A \in R1 * R2)%MS. +Proof. +apply: (iffP idP) => [R_A|[A1 R_A1 [A2 R_A2 ->{A}]]]; last first. + by rewrite linear_sum summx_sub // => i _; rewrite mem_mulsmx. +have{R_A}: (A \in R1 * <<R2>>)%MS. + by apply: memmx_subP R_A; rewrite mulsmxS ?genmxE. +case/memmx_sumsP=> A_ -> R_A; pose A2_ i := vec_mx (row i <<R2>>%MS). +pose A1_ i := mxvec (A_ i) *m pinvmx (R1 *m lin_mx (mulmxr (A2_ i))) *m R1. +exists (vec_mx \o A1_) => [i|]; first by rewrite vec_mxK submxMl. +exists A2_ => [i|]; first by rewrite vec_mxK -(genmxE R2) row_sub. +apply: eq_bigr => i _; rewrite -[_ *m _](mx_rV_lin (mulmxr_linear _ _)). +by rewrite -mulmxA mulmxKpV ?mxvecK // -(genmxE (_ *m _)) R_A. +Qed. +Implicit Arguments mulsmxP [m1 m2 n A R1 R2]. + +Lemma mulsmxA m1 m2 m3 n (R1 : 'A_(m1, n)) (R2 : 'A_(m2, n)) (R3 : 'A_(m3, n)) : + (R1 * (R2 * R3) = R1 * R2 * R3)%MS. +Proof. +rewrite -(genmx_muls (_ * _)%MS) -genmx_muls; apply/genmxP; apply/andP; split. + apply/mulsmx_subP=> A1 A23 R_A1; case/mulsmxP=> A2 R_A2 [A3 R_A3 ->{A23}]. + by rewrite !linear_sum summx_sub //= => i _; rewrite mulmxA !mem_mulsmx. +apply/mulsmx_subP=> _ A3 /mulsmxP[A1 R_A1 [A2 R_A2 ->]] R_A3. +rewrite mulmx_suml linear_sum summx_sub //= => i _. +by rewrite -mulmxA !mem_mulsmx. +Qed. + +Lemma mulsmx_addl m1 m2 m3 n + (R1 : 'A_(m1, n)) (R2 : 'A_(m2, n)) (R3 : 'A_(m3, n)) : + ((R1 + R2) * R3 = R1 * R3 + R2 * R3)%MS. +Proof. +rewrite -(genmx_muls R2 R3) -(genmx_muls R1 R3) -genmx_muls -genmx_adds. +apply/genmxP; rewrite andbC addsmx_sub !mulsmxS ?addsmxSl ?addsmxSr //=. +apply/mulsmx_subP=> _ A3 /memmx_addsP[A [R_A1 R_A2 ->]] R_A3. +by rewrite mulmxDl linearD addmx_sub_adds ?mem_mulsmx. +Qed. + +Lemma mulsmx_addr m1 m2 m3 n + (R1 : 'A_(m1, n)) (R2 : 'A_(m2, n)) (R3 : 'A_(m3, n)) : + (R1 * (R2 + R3) = R1 * R2 + R1 * R3)%MS. +Proof. +rewrite -(genmx_muls R1 R3) -(genmx_muls R1 R2) -genmx_muls -genmx_adds. +apply/genmxP; rewrite andbC addsmx_sub !mulsmxS ?addsmxSl ?addsmxSr //=. +apply/mulsmx_subP=> A1 _ R_A1 /memmx_addsP[A [R_A2 R_A3 ->]]. +by rewrite mulmxDr linearD addmx_sub_adds ?mem_mulsmx. +Qed. + +Lemma mulsmx0 m1 m2 n (R1 : 'A_(m1, n)) : (R1 * (0 : 'A_(m2, n)) = 0)%MS. +Proof. +apply/eqP; rewrite -submx0; apply/mulsmx_subP=> A1 A0 _. +by rewrite [A0 \in 0]eqmx0 => /memmx0->; rewrite mulmx0 mem0mx. +Qed. + +Lemma muls0mx m1 m2 n (R2 : 'A_(m2, n)) : ((0 : 'A_(m1, n)) * R2 = 0)%MS. +Proof. +apply/eqP; rewrite -submx0; apply/mulsmx_subP=> A0 A2. +by rewrite [A0 \in 0]eqmx0 => /memmx0->; rewrite mul0mx mem0mx. +Qed. + +Definition left_mx_ideal m1 m2 n (R1 : 'A_(m1, n)) (R2 : 'A_(m2, n)) := + (R1 * R2 <= R2)%MS. + +Definition right_mx_ideal m1 m2 n (R1 : 'A_(m1, n)) (R2 : 'A_(m2, n)) := + (R2 * R1 <= R2)%MS. + +Definition mx_ideal m1 m2 n (R1 : 'A_(m1, n)) (R2 : 'A_(m2, n)) := + left_mx_ideal R1 R2 && right_mx_ideal R1 R2. + +Definition mxring_id m n (R : 'A_(m, n)) e := + [/\ e != 0, + e \in R, + forall A, A \in R -> e *m A = A + & forall A, A \in R -> A *m e = A]%MS. + +Definition has_mxring_id m n (R : 'A[F]_(m , n)) := + (R != 0) && + (row_mx 0 (row_mx (mxvec R) (mxvec R)) + <= row_mx (cokermx R) (row_mx (lin_mx (mulmx R \o lin_mulmx)) + (lin_mx (mulmx R \o lin_mulmxr))))%MS. + +Definition mxring m n (R : 'A_(m, n)) := + left_mx_ideal R R && has_mxring_id R. + +Lemma mxring_idP m n (R : 'A_(m, n)) : + reflect (exists e, mxring_id R e) (has_mxring_id R). +Proof. +apply: (iffP andP) => [[nzR] | [e [nz_e Re ideR idRe]]]. + case/submxP=> v; rewrite -[v]vec_mxK; move/vec_mx: v => e. + rewrite !mul_mx_row; case/eq_row_mx => /eqP. + rewrite eq_sym -submxE => Re. + case/eq_row_mx; rewrite !{1}mul_rV_lin1 /= mxvecK. + set u := (_ *m _) => /(can_inj mxvecK) idRe /(can_inj mxvecK) ideR. + exists e; split=> // [ | A /submxP[a defA] | A /submxP[a defA]]. + - by apply: contra nzR; rewrite ideR => /eqP->; rewrite !linear0. + - by rewrite -{2}[A]mxvecK defA idRe mulmxA mx_rV_lin -defA /= mxvecK. + by rewrite -{2}[A]mxvecK defA ideR mulmxA mx_rV_lin -defA /= mxvecK. +split. + by apply: contraNneq nz_e => R0; rewrite R0 eqmx0 in Re; rewrite (memmx0 Re). +apply/submxP; exists (mxvec e); rewrite !mul_mx_row !{1}mul_rV_lin1. +rewrite submxE in Re; rewrite {Re}(eqP Re). +congr (row_mx 0 (row_mx (mxvec _) (mxvec _))); apply/row_matrixP=> i. + by rewrite !row_mul !mul_rV_lin1 /= mxvecK ideR vec_mxK ?row_sub. +by rewrite !row_mul !mul_rV_lin1 /= mxvecK idRe vec_mxK ?row_sub. +Qed. +Implicit Arguments mxring_idP [m n R]. + +Section CentMxDef. + +Variables (m n : nat) (R : 'A[F]_(m, n)). + +Definition cent_mx_fun (B : 'M[F]_n) := R *m lin_mx (mulmxr B \- mulmx B). + +Lemma cent_mx_fun_is_linear : linear cent_mx_fun. +Proof. +move=> a A B; apply/row_matrixP=> i; rewrite linearP row_mul mul_rV_lin. +rewrite /= {-3}[row]lock row_mul mul_rV_lin -lock row_mul mul_rV_lin. +by rewrite -linearP -(linearP [linear of mulmx _ \- mulmxr _]). +Qed. +Canonical cent_mx_fun_additive := Additive cent_mx_fun_is_linear. +Canonical cent_mx_fun_linear := Linear cent_mx_fun_is_linear. + +Definition cent_mx := kermx (lin_mx cent_mx_fun). + +Definition center_mx := (R :&: cent_mx)%MS. + +End CentMxDef. + +Local Notation "''C' ( R )" := (cent_mx R) : matrix_set_scope. +Local Notation "''Z' ( R )" := (center_mx R) : matrix_set_scope. + +Lemma cent_rowP m n B (R : 'A_(m, n)) : + reflect (forall i (A := vec_mx (row i R)), A *m B = B *m A) (B \in 'C(R))%MS. +Proof. +apply: (iffP sub_kermxP); rewrite mul_vec_lin => cBE. + move/(canRL mxvecK): cBE => cBE i A /=; move/(congr1 (row i)): cBE. + rewrite row_mul mul_rV_lin -/A; move/(canRL mxvecK). + by move/(canRL (subrK _)); rewrite !linear0 add0r. +apply: (canLR vec_mxK); apply/row_matrixP=> i. +by rewrite row_mul mul_rV_lin /= cBE subrr !linear0. +Qed. +Implicit Arguments cent_rowP [m n B R]. + +Lemma cent_mxP m n B (R : 'A_(m, n)) : + reflect (forall A, A \in R -> A *m B = B *m A) (B \in 'C(R))%MS. +Proof. +apply: (iffP cent_rowP) => cEB => [A sAE | i A]. + rewrite -[A]mxvecK -(mulmxKpV sAE); move: (mxvec A *m _) => u. + rewrite !mulmx_sum_row !linear_sum mulmx_suml; apply: eq_bigr => i _ /=. + by rewrite !linearZ -scalemxAl /= cEB. +by rewrite cEB // vec_mxK row_sub. +Qed. +Implicit Arguments cent_mxP [m n B R]. + +Lemma scalar_mx_cent m n a (R : 'A_(m, n)) : (a%:M \in 'C(R))%MS. +Proof. by apply/cent_mxP=> A _; exact: scalar_mxC. Qed. + +Lemma center_mx_sub m n (R : 'A_(m, n)) : ('Z(R) <= R)%MS. +Proof. exact: capmxSl. Qed. + +Lemma center_mxP m n A (R : 'A_(m, n)) : + reflect (A \in R /\ forall B, B \in R -> B *m A = A *m B) + (A \in 'Z(R))%MS. +Proof. +rewrite sub_capmx; case R_A: (A \in R); last by right; case. +by apply: (iffP cent_mxP) => [cAR | [_ cAR]]. +Qed. +Implicit Arguments center_mxP [m n A R]. + +Lemma mxring_id_uniq m n (R : 'A_(m, n)) e1 e2 : + mxring_id R e1 -> mxring_id R e2 -> e1 = e2. +Proof. +by case=> [_ Re1 idRe1 _] [_ Re2 _ ide2R]; rewrite -(idRe1 _ Re2) ide2R. +Qed. + +Lemma cent_mx_ideal m n (R : 'A_(m, n)) : left_mx_ideal 'C(R)%MS 'C(R)%MS. +Proof. +apply/mulsmx_subP=> A1 A2 C_A1 C_A2; apply/cent_mxP=> B R_B. +by rewrite mulmxA (cent_mxP C_A1) // -!mulmxA (cent_mxP C_A2). +Qed. + +Lemma cent_mx_ring m n (R : 'A_(m, n)) : n > 0 -> mxring 'C(R)%MS. +Proof. +move=> n_gt0; rewrite /mxring cent_mx_ideal; apply/mxring_idP. +exists 1%:M; split=> [||A _|A _]; rewrite ?mulmx1 ?mul1mx ?scalar_mx_cent //. +by rewrite -mxrank_eq0 mxrank1 -lt0n. +Qed. + +Lemma mxdirect_adds_center m1 m2 n (R1 : 'A_(m1, n)) (R2 : 'A_(m2, n)) : + mx_ideal (R1 + R2)%MS R1 -> mx_ideal (R1 + R2)%MS R2 -> + mxdirect (R1 + R2) -> + ('Z((R1 + R2)%MS) :=: 'Z(R1) + 'Z(R2))%MS. +Proof. +case/andP=> idlR1 idrR1 /andP[idlR2 idrR2] /mxdirect_addsP dxR12. +apply/eqmxP/andP; split. + apply/memmx_subP=> z0; rewrite sub_capmx => /andP[]. + case/memmx_addsP=> z [R1z1 R2z2 ->{z0}] Cz. + rewrite linearD addmx_sub_adds //= ?sub_capmx ?R1z1 ?R2z2 /=. + apply/cent_mxP=> A R1_A; have R_A := submx_trans R1_A (addsmxSl R1 R2). + have Rz2 := submx_trans R2z2 (addsmxSr R1 R2). + rewrite -{1}[z.1](addrK z.2) mulmxBr (cent_mxP Cz) // mulmxDl. + rewrite [A *m z.2]memmx0 1?[z.2 *m A]memmx0 ?addrK //. + by rewrite -dxR12 sub_capmx (mulsmx_subP idlR1) // (mulsmx_subP idrR2). + by rewrite -dxR12 sub_capmx (mulsmx_subP idrR1) // (mulsmx_subP idlR2). + apply/cent_mxP=> A R2_A; have R_A := submx_trans R2_A (addsmxSr R1 R2). + have Rz1 := submx_trans R1z1 (addsmxSl R1 R2). + rewrite -{1}[z.2](addKr z.1) mulmxDr (cent_mxP Cz) // mulmxDl. + rewrite mulmxN [A *m z.1]memmx0 1?[z.1 *m A]memmx0 ?addKr //. + by rewrite -dxR12 sub_capmx (mulsmx_subP idrR1) // (mulsmx_subP idlR2). + by rewrite -dxR12 sub_capmx (mulsmx_subP idlR1) // (mulsmx_subP idrR2). +rewrite addsmx_sub; apply/andP; split. + apply/memmx_subP=> z; rewrite sub_capmx => /andP[R1z cR1z]. + have Rz := submx_trans R1z (addsmxSl R1 R2). + rewrite sub_capmx Rz; apply/cent_mxP=> A0. + case/memmx_addsP=> A [R1_A1 R2_A2] ->{A0}. + have R_A2 := submx_trans R2_A2 (addsmxSr R1 R2). + rewrite mulmxDl mulmxDr (cent_mxP cR1z) //; congr (_ + _). + rewrite [A.2 *m z]memmx0 1?[z *m A.2]memmx0 //. + by rewrite -dxR12 sub_capmx (mulsmx_subP idrR1) // (mulsmx_subP idlR2). + by rewrite -dxR12 sub_capmx (mulsmx_subP idlR1) // (mulsmx_subP idrR2). +apply/memmx_subP=> z; rewrite !sub_capmx => /andP[R2z cR2z]. +have Rz := submx_trans R2z (addsmxSr R1 R2); rewrite Rz. +apply/cent_mxP=> _ /memmx_addsP[A [R1_A1 R2_A2 ->]]. +rewrite mulmxDl mulmxDr (cent_mxP cR2z _ R2_A2) //; congr (_ + _). +have R_A1 := submx_trans R1_A1 (addsmxSl R1 R2). +rewrite [A.1 *m z]memmx0 1?[z *m A.1]memmx0 //. + by rewrite -dxR12 sub_capmx (mulsmx_subP idlR1) // (mulsmx_subP idrR2). +by rewrite -dxR12 sub_capmx (mulsmx_subP idrR1) // (mulsmx_subP idlR2). +Qed. + +Lemma mxdirect_sums_center (I : finType) m n (R : 'A_(m, n)) R_ : + (\sum_i R_ i :=: R)%MS -> mxdirect (\sum_i R_ i) -> + (forall i : I, mx_ideal R (R_ i)) -> + ('Z(R) :=: \sum_i 'Z(R_ i))%MS. +Proof. +move=> defR dxR idealR. +have sR_R: (R_ _ <= R)%MS by move=> i; rewrite -defR (sumsmx_sup i). +have anhR i j A B : i != j -> A \in R_ i -> B \in R_ j -> A *m B = 0. + move=> ne_ij RiA RjB; apply: memmx0. + have [[_ idRiR] [idRRj _]] := (andP (idealR i), andP (idealR j)). + rewrite -(mxdirect_sumsP dxR j) // sub_capmx (sumsmx_sup i) //. + by rewrite (mulsmx_subP idRRj) // (memmx_subP (sR_R i)). + by rewrite (mulsmx_subP idRiR) // (memmx_subP (sR_R j)). +apply/eqmxP/andP; split. + apply/memmx_subP=> Z; rewrite sub_capmx => /andP[]. + rewrite -{1}defR => /memmx_sumsP[z ->{Z} Rz cRz]. + apply/memmx_sumsP; exists z => // i; rewrite sub_capmx Rz. + apply/cent_mxP=> A RiA; have:= cent_mxP cRz A (memmx_subP (sR_R i) A RiA). + rewrite (bigD1 i) //= mulmxDl mulmxDr mulmx_suml mulmx_sumr. + by rewrite !big1 ?addr0 // => j; last rewrite eq_sym; move/anhR->. +apply/sumsmx_subP => i _; apply/memmx_subP=> z; rewrite sub_capmx. +case/andP=> Riz cRiz; rewrite sub_capmx (memmx_subP (sR_R i)) //=. +apply/cent_mxP=> A; rewrite -{1}defR; case/memmx_sumsP=> a -> R_a. +rewrite (bigD1 i) // mulmxDl mulmxDr mulmx_suml mulmx_sumr. +rewrite !big1 => [|j|j]; first by rewrite !addr0 (cent_mxP cRiz). + by rewrite eq_sym => /anhR->. +by move/anhR->. +Qed. + +End MatrixAlgebra. + +Arguments Scope mulsmx + [_ nat_scope nat_scope nat_scope matrix_set_scope matrix_set_scope]. +Arguments Scope left_mx_ideal + [_ nat_scope nat_scope nat_scope matrix_set_scope matrix_set_scope]. +Arguments Scope right_mx_ideal + [_ nat_scope nat_scope nat_scope matrix_set_scope matrix_set_scope]. +Arguments Scope mx_ideal + [_ nat_scope nat_scope nat_scope matrix_set_scope matrix_set_scope]. +Arguments Scope mxring_id + [_ nat_scope nat_scope ring_scope matrix_set_scope]. +Arguments Scope has_mxring_id + [_ nat_scope nat_scope ring_scope matrix_set_scope]. +Arguments Scope mxring + [_ nat_scope nat_scope matrix_set_scope]. +Arguments Scope cent_mx + [_ nat_scope nat_scope matrix_set_scope]. +Arguments Scope center_mx + [_ nat_scope nat_scope matrix_set_scope]. + +Prenex Implicits mulsmx. + +Notation "A \in R" := (submx (mxvec A) R) : matrix_set_scope. +Notation "R * S" := (mulsmx R S) : matrix_set_scope. +Notation "''C' ( R )" := (cent_mx R) : matrix_set_scope. +Notation "''C_' R ( S )" := (R :&: 'C(S))%MS : matrix_set_scope. +Notation "''C_' ( R ) ( S )" := ('C_R(S))%MS (only parsing) : matrix_set_scope. +Notation "''Z' ( R )" := (center_mx R) : matrix_set_scope. + +Implicit Arguments memmx_subP [F m1 m2 n R1 R2]. +Implicit Arguments memmx_eqP [F m1 m2 n R1 R2]. +Implicit Arguments memmx_addsP [F m1 m2 n R1 R2]. +Implicit Arguments memmx_sumsP [F I P n A R_]. +Implicit Arguments mulsmx_subP [F m1 m2 m n R1 R2 R]. +Implicit Arguments mulsmxP [F m1 m2 n A R1 R2]. +Implicit Arguments mxring_idP [m n R]. +Implicit Arguments cent_rowP [F m n B R]. +Implicit Arguments cent_mxP [F m n B R]. +Implicit Arguments center_mxP [F m n A R]. + +(* Parametricity for the row-space/F-algebra theory. *) +Section MapMatrixSpaces. + +Variables (aF rF : fieldType) (f : {rmorphism aF -> rF}). +Local Notation "A ^f" := (map_mx f A) : ring_scope. + +Lemma Gaussian_elimination_map m n (A : 'M_(m, n)) : + Gaussian_elimination A^f = ((col_ebase A)^f, (row_ebase A)^f, \rank A). +Proof. +rewrite mxrankE /row_ebase /col_ebase unlock. +elim: m n A => [|m IHm] [|n] A /=; rewrite ?map_mx1 //. +set pAnz := [pred k | A k.1 k.2 != 0]. +rewrite (@eq_pick _ _ pAnz) => [|k]; last by rewrite /= mxE fmorph_eq0. +case: {+}(pick _) => [[i j]|]; last by rewrite !map_mx1. +rewrite mxE -fmorphV -map_xcol -map_xrow -map_dlsubmx -map_drsubmx. +rewrite -map_ursubmx -map_mxZ -map_mxM -map_mx_sub {}IHm /=. +case: {+}(Gaussian_elimination _) => [[L U] r] /=; rewrite map_xrow map_xcol. +by rewrite !(@map_block_mx _ _ f 1 _ 1) !map_mx0 ?map_mx1 ?map_scalar_mx. +Qed. + +Lemma mxrank_map m n (A : 'M_(m, n)) : \rank A^f = \rank A. +Proof. by rewrite mxrankE Gaussian_elimination_map. Qed. + +Lemma row_free_map m n (A : 'M_(m, n)) : row_free A^f = row_free A. +Proof. by rewrite /row_free mxrank_map. Qed. + +Lemma row_full_map m n (A : 'M_(m, n)) : row_full A^f = row_full A. +Proof. by rewrite /row_full mxrank_map. Qed. + +Lemma map_row_ebase m n (A : 'M_(m, n)) : (row_ebase A)^f = row_ebase A^f. +Proof. by rewrite {2}/row_ebase unlock Gaussian_elimination_map. Qed. + +Lemma map_col_ebase m n (A : 'M_(m, n)) : (col_ebase A)^f = col_ebase A^f. +Proof. by rewrite {2}/col_ebase unlock Gaussian_elimination_map. Qed. + +Lemma map_row_base m n (A : 'M_(m, n)) : + (row_base A)^f = castmx (mxrank_map A, erefl n) (row_base A^f). +Proof. +move: (mxrank_map A); rewrite {2}/row_base mxrank_map => eqrr. +by rewrite castmx_id map_mxM map_pid_mx map_row_ebase. +Qed. + +Lemma map_col_base m n (A : 'M_(m, n)) : + (col_base A)^f = castmx (erefl m, mxrank_map A) (col_base A^f). +Proof. +move: (mxrank_map A); rewrite {2}/col_base mxrank_map => eqrr. +by rewrite castmx_id map_mxM map_pid_mx map_col_ebase. +Qed. + +Lemma map_pinvmx m n (A : 'M_(m, n)) : (pinvmx A)^f = pinvmx A^f. +Proof. +rewrite !map_mxM !map_invmx map_row_ebase map_col_ebase. +by rewrite map_pid_mx -mxrank_map. +Qed. + +Lemma map_kermx m n (A : 'M_(m, n)) : (kermx A)^f = kermx A^f. +Proof. +by rewrite !map_mxM map_invmx map_col_ebase -mxrank_map map_copid_mx. +Qed. + +Lemma map_cokermx m n (A : 'M_(m, n)) : (cokermx A)^f = cokermx A^f. +Proof. +by rewrite !map_mxM map_invmx map_row_ebase -mxrank_map map_copid_mx. +Qed. + +Lemma map_submx m1 m2 n (A : 'M_(m1, n)) (B : 'M_(m2, n)) : + (A^f <= B^f)%MS = (A <= B)%MS. +Proof. by rewrite !submxE -map_cokermx -map_mxM map_mx_eq0. Qed. + +Lemma map_ltmx m1 m2 n (A : 'M_(m1, n)) (B : 'M_(m2, n)) : + (A^f < B^f)%MS = (A < B)%MS. +Proof. by rewrite /ltmx !map_submx. Qed. + +Lemma map_eqmx m1 m2 n (A : 'M_(m1, n)) (B : 'M_(m2, n)) : + (A^f :=: B^f)%MS <-> (A :=: B)%MS. +Proof. +split=> [/eqmxP|eqAB]; first by rewrite !map_submx => /eqmxP. +by apply/eqmxP; rewrite !map_submx !eqAB !submx_refl. +Qed. + +Lemma map_genmx m n (A : 'M_(m, n)) : (<<A>>^f :=: <<A^f>>)%MS. +Proof. by apply/eqmxP; rewrite !(genmxE, map_submx) andbb. Qed. + +Lemma map_addsmx m1 m2 n (A : 'M_(m1, n)) (B : 'M_(m2, n)) : + (((A + B)%MS)^f :=: A^f + B^f)%MS. +Proof. +by apply/eqmxP; rewrite !addsmxE -map_col_mx !map_submx !addsmxE andbb. +Qed. + +Lemma map_capmx_gen m1 m2 n (A : 'M_(m1, n)) (B : 'M_(m2, n)) : + (capmx_gen A B)^f = capmx_gen A^f B^f. +Proof. by rewrite map_mxM map_lsubmx map_kermx map_col_mx. Qed. + +Lemma map_capmx m1 m2 n (A : 'M_(m1, n)) (B : 'M_(m2, n)) : + ((A :&: B)^f :=: A^f :&: B^f)%MS. +Proof. +by apply/eqmxP; rewrite !capmxE -map_capmx_gen !map_submx -!capmxE andbb. +Qed. + +Lemma map_complmx m n (A : 'M_(m, n)) : (A^C^f = A^f^C)%MS. +Proof. by rewrite map_mxM map_row_ebase -mxrank_map map_copid_mx. Qed. + +Lemma map_diffmx m1 m2 n (A : 'M_(m1, n)) (B : 'M_(m2, n)) : + ((A :\: B)^f :=: A^f :\: B^f)%MS. +Proof. +apply/eqmxP; rewrite !diffmxE -map_capmx_gen -map_complmx. +by rewrite -!map_capmx !map_submx -!diffmxE andbb. +Qed. + +Lemma map_eigenspace n (g : 'M_n) a : (eigenspace g a)^f = eigenspace g^f (f a). +Proof. by rewrite map_kermx map_mx_sub ?map_scalar_mx. Qed. + +Lemma eigenvalue_map n (g : 'M_n) a : eigenvalue g^f (f a) = eigenvalue g a. +Proof. by rewrite /eigenvalue -map_eigenspace map_mx_eq0. Qed. + +Lemma memmx_map m n A (E : 'A_(m, n)) : (A^f \in E^f)%MS = (A \in E)%MS. +Proof. by rewrite -map_mxvec map_submx. Qed. + +Lemma map_mulsmx m1 m2 n (E1 : 'A_(m1, n)) (E2 : 'A_(m2, n)) : + ((E1 * E2)%MS^f :=: E1^f * E2^f)%MS. +Proof. +rewrite /mulsmx; elim/big_rec2: _ => [|i A Af _ eqA]; first by rewrite map_mx0. +apply: (eqmx_trans (map_addsmx _ _)); apply: adds_eqmx {A Af}eqA. +apply/eqmxP; rewrite !map_genmx !genmxE map_mxM. +apply/rV_eqP=> u; congr (u <= _ *m _)%MS. +by apply: map_lin_mx => //= A; rewrite map_mxM // map_vec_mx map_row. +Qed. + +Lemma map_cent_mx m n (E : 'A_(m, n)) : ('C(E)%MS)^f = 'C(E^f)%MS. +Proof. +rewrite map_kermx //; congr (kermx _); apply: map_lin_mx => // A. +rewrite map_mxM //; congr (_ *m _); apply: map_lin_mx => //= B. +by rewrite map_mx_sub ? map_mxM. +Qed. + +Lemma map_center_mx m n (E : 'A_(m, n)) : (('Z(E))^f :=: 'Z(E^f))%MS. +Proof. by rewrite /center_mx -map_cent_mx; exact: map_capmx. Qed. + +End MapMatrixSpaces. + + |
