Mathlib.LinearAlgebra
Linear-algebra helpers staged for Mathlib: monomial-matrix and generalized-permutation lemmas used by causal-discovery identification proofs.
Cholesky 3 core · 7 supporting This file proves that every real symmetric positive-definite matrix factors as the product of the transpose of an upper-triangular matrix with positive diagonal and that matrix itself, and that this factorization is uniq ★ cholesky_exists★ cholesky_unique
Real Cholesky factorization: existence and uniqueness
This file proves that every real symmetric positive-definite matrix factors as the product of the
transpose of an upper-triangular matrix with positive diagonal and that matrix itself, and that
this factorization is unique. Concretely, for a positive-definite matrix M there is a unique
upper-triangular matrix U with strictly positive diagonal entries such that M equals the
transpose of U times U.
The public API consists of the predicate IsUpperTri, the existence theorem
cholesky_exists, and the uniqueness theorem cholesky_unique; the LDL and orthogonal
triangular lemmas expose the proof ingredients needed by those results. Existence is
obtained from the LDL decomposition by absorbing the square roots of the diagonal factor;
uniqueness reduces to orthogonal_upperTri_pos_diag_eq_one, the fact that an orthogonal
upper-triangular matrix with positive diagonal is the identity.
An upper-triangular matrix in the sense j < i → U i j = 0, i.e. all entries strictly below the diagonal vanish.
Definition (Lean source)
For a real positive-definite d × d matrix M, there exists an upper-triangular matrix U (zero below the diagonal) with strictly positive diagonal entries such that M factors as Uᵀ · U.
Formal statement
Proof (Lean source)
For a linearly ordered finite index type ι and a linearly ordered field K, and two matrices U, V indexed by ι × ι and valued in K, if U is upper-triangular, i.e. zero below the diagonal, with strictly positive diagonal entries, V is likewise upper-triangular with strictly positive diagonal entries, and U and V have the same Gram matrix, Uᵀ · U = Vᵀ · V, then U equals V.
Formal statement
Proof (Lean source)
7 supporting declarations (lemmas, instances)
-
isUpperTri_iff_blockTriangulartheorem — The entrywise upper-triangular predicate is exactly Mathlib's block-triangular predicate for the identity order.Proof (Lean source)
theorem isUpperTri_iff_blockTriangular {ι K : Type*} [LT ι] [Zero K] {U : Matrix ι ι K} : IsUpperTri U ↔ U.BlockTriangular id := by rfl -
ldl_lowerInv_diag_onetheorem — The Gram-Schmidt lower-inverse matrix is unitriangular: its diagonal entries are 1.Proof (Lean source)
theorem ldl_lowerInv_diag_one (hM : M.PosDef) (i : Fin d) : lowerInv hM i i = 1 := by letI := (Mᵀ.toNormedAddCommGroup hM.transpose) letI := (Mᵀ.toInnerProductSpace hM.transpose.posSemidef) have key : ∀ (c : Fin d), c < i → InnerProductSpace.gramSchmidt ℝ (⇑(Pi.basisFun ℝ (Fin d))) c i = 0 := by intro c hc have h2 := InnerProductSpace.gramSchmidt_triangular (𝕜 := ℝ) hc (Pi.basisFun ℝ (Fin d)) simpa using h2 rw [lowerInv, InnerProductSpace.gramSchmidt_def] simp only [Pi.sub_apply, Finset.sum_apply, Pi.basisFun_apply, Pi.single_eq_same, sub_eq_self] refine Finset.sum_eq_zero (fun c hc => ?_) rw [Submodule.starProjection_singleton] change _ * InnerProductSpace.gramSchmidt ℝ (⇑(Pi.basisFun ℝ (Fin d))) c i = 0 rw [key c (Finset.mem_Iio.mp hc), mul_zero] -
ldl_diagEntries_postheorem — The diagonal entries of the LDL decomposition of a real positive-definite matrix are strictly positive.Proof (Lean source)
theorem ldl_diagEntries_pos (hM : M.PosDef) (i : Fin d) : 0 < diagEntries hM i := by have hne : lowerInv hM i ≠ 0 := by intro h have h1 := ldl_lowerInv_diag_one hM i rw [h] at h1 simp at h1 rw [diagEntries] simp only [EuclideanSpace.inner_toLp_toLp, star_trivial] rw [dotProduct_comm] exact hM.dotProduct_mulVec_pos hne -
ldl_lowerInv_blockTriangulartheorem — LDL.lowerInv is lower-triangular in the BlockTriangular toDual sense.Proof (Lean source)
theorem ldl_lowerInv_blockTriangular (hM : M.PosDef) : (lowerInv hM).BlockTriangular toDual := by intro i j hij exact LDL.lowerInv_triangular hM (by simpa using hij) -
ldl_lower_blockTriangulartheorem — LDL.lower (the inverse of LDL.lowerInv) is lower-triangular.Proof (Lean source)
theorem ldl_lower_blockTriangular (hM : M.PosDef) : (lower hM).BlockTriangular toDual := by rw [lower] exact blockTriangular_inv_of_blockTriangular (ldl_lowerInv_blockTriangular hM) -
ldl_lower_diag_onetheorem — LDL.lower is unitriangular: its diagonal entries are 1.Proof (Lean source)
theorem ldl_lower_diag_one (hM : M.PosDef) (i : Fin d) : lower hM i i = 1 := by have hlow := ldl_lower_blockTriangular hM have hupp := ldl_lowerInv_blockTriangular hM have hmul : lower hM * lowerInv hM = 1 := by rw [lower]; exact Matrix.inv_mul_of_invertible _ have hii : (lower hM * lowerInv hM) i i = 1 := by rw [hmul]; simp rw [Matrix.mul_apply] at hii rw [Finset.sum_eq_single i] at hii · rw [ldl_lowerInv_diag_one hM i, mul_one] at hii; exact hii · intro k _ hki rcases lt_or_gt_of_ne hki with hk | hk · rw [hupp (by simpa using hk), mul_zero] · rw [hlow (by simpa using hk), zero_mul] · intro h; exact absurd (Finset.mem_univ i) h -
orthogonal_upperTri_pos_diag_eq_onetheorem — An orthogonal (Wᵀ * W = 1) upper-triangular matrix with strictly positive diagonal is the identity matrix.hypothesesι K :hortho :Wᵀ * W = 1hupp :∀ i jifj < ithenW i j = 0hpos :∀ i, 0 < W i iconclusionW = 1Proof (Lean source)
theorem orthogonal_upperTri_pos_diag_eq_one {ι K : Type*} [Fintype ι] [LinearOrder ι] [Field K] [LinearOrder K] [IsStrictOrderedRing K] {W : Matrix ι ι K} (hortho : Wᵀ * W = 1) (hupp : ∀ i j, j < i → W i j = 0) (hpos : ∀ i, 0 < W i i) : W = 1 := by classical haveI : Invertible W := invertibleOfLeftInverse _ _ hortho -- `W⁻¹ = Wᵀ` have hinv : W⁻¹ = Wᵀ := Matrix.inv_eq_left_inv hortho have hWupp : W.BlockTriangular id := hupp -- `Wᵀ` is upper-triangular (as `W⁻¹`) and lower-triangular (as a transpose of upper). have hWTupp : (Wᵀ).BlockTriangular id := hinv ▸ blockTriangular_inv_of_blockTriangular hWupp have hWTlow : (Wᵀ).BlockTriangular toDual := hWupp.transpose -- Hence `Wᵀ` is diagonal: off-diagonal entries vanish. have hWTdiag : ∀ i j, i ≠ j → (Wᵀ) i j = 0 := by intro i j hij rcases lt_or_gt_of_ne hij with h | h · exact hWTlow (by simpa using h) · exact hWTupp (by simpa using h) -- The diagonal entries square to one and are positive, hence equal one. ext i j have hsq : ∀ k, W k k * W k k = 1 := by intro k have := congrFun (congrFun hortho k) k rw [Matrix.mul_apply] at this rw [Finset.sum_eq_single k] at this · simpa [Matrix.transpose_apply] using this · intro l _ hlk have hz : (Wᵀ) k l = 0 := hWTdiag k l (Ne.symm hlk) rw [hz, zero_mul] · intro h; exact absurd (Finset.mem_univ k) h have hdiagone : ∀ k, W k k = 1 := by intro k have h1 := hsq k nlinarith [hpos k] rcases eq_or_ne i j with rfl | hne · simp [hdiagone i] · rcases lt_or_gt_of_ne hne with h | h · -- `i < j`: read off `W i j` from the vanishing transpose entry `Wᵀ j i` have : (Wᵀ) j i = 0 := hWTdiag j i (Ne.symm hne) simpa [Matrix.transpose_apply, Matrix.one_apply_ne hne] using this · -- `j < i`: upper-triangularity kills it rw [hupp i j h, Matrix.one_apply_ne (Ne.symm (by simpa using h.ne))]
ConfluentVandermonde 9 core · 18 supporting This file proves nonsingularity of the Hermite evaluation matrix at distinct complex nodes, together with the pinned variant having one simple node and all remaining nodes doubled. ★ det_confluentVandermonde_ne_zero★ det_pinnedConfluentVandermonde_ne_zero
Confluent Vandermonde matrices
This file proves nonsingularity of the Hermite evaluation matrix at distinct complex nodes, together with the pinned variant having one simple node and all remaining nodes doubled.
The monomial exponent attached to the two copies of Fin n.
Definition (Lean source)
Monomial coefficients encoded by the doubled finite index.
Definition (Lean source)
The square Hermite-evaluation matrix with value and first-derivative columns at every node. Rows are monomials of degrees 0, ..., 2n-1.
For a positive number of nodes n (1 ≤ n), a field K, and pairwise distinct nodes s : Fin n → K, the confluent Vandermonde determinant at s is nonzero.
Formal statement
Proof (Lean source)
Monomial degrees for one simple node and n - 1 doubled nodes.
Definition (Lean source)
Embed the doubled-node index as a positive node index.
Definition (Lean source)
Encode pinned Hermite coefficients as a polynomial.
Definition (Lean source)
The pinned Hermite matrix records values at all nodes and derivatives at nodes 1, ..., n-1, with the derivative at node zero deleted.
For a positive number of nodes n (1 ≤ n), a field K, and pairwise distinct nodes s : Fin n → K, the pinned confluent Vandermonde determinant at s is nonzero: one node contributes only value evaluation and every other node contributes both value and first-derivative evaluation.
Formal statement
Proof (Lean source)
18 supporting declarations (lemmas, instances)
-
doubledExponent_injectivelemma — The doubled exponent encoding is injective.Proof (Lean source)
lemma doubledExponent_injective {n : ℕ} : Injective (doubledExponent : Fin n ⊕ Fin n → ℕ) := by intro i j hij rcases i with i | i <;> rcases j with j | j · simp only [doubledExponent] at hij exact congrArg inl (Fin.ext hij) · simp only [doubledExponent] at hij exfalso have := i.isLt omega · simp only [doubledExponent] at hij exfalso have := j.isLt omega · simp only [doubledExponent] at hij exact congrArg inr (Fin.ext (Nat.add_left_cancel hij)) -
doubledExponent_ltlemma — Every doubled exponent is strictly below 2n.Proof (Lean source)
lemma doubledExponent_lt {n : ℕ} (i : Fin n ⊕ Fin n) : doubledExponent i < 2 * n := by rcases i with i | i <;> simp only [doubledExponent] · omega · have := i.isLt omega -
coeff_doubledCoefficientPolynomiallemma — Reading a doubled coefficient polynomial at an encoded exponent recovers the corresponding coefficient.hypothesesconclusion(doubledCoefficientPolynomial v).coeff (doubledExponent i) = v iProof (Lean source)
lemma coeff_doubledCoefficientPolynomial {n : ℕ} {K : Type*} [Semiring K] (v : Fin n ⊕ Fin n → K) (i : Fin n ⊕ Fin n) : (doubledCoefficientPolynomial v).coeff (doubledExponent i) = v i := by classical change (Polynomial.lcoeff K (doubledExponent i)) (∑ j, Polynomial.monomial (doubledExponent j) (v j)) = v i rw [map_sum] rw [Finset.sum_eq_single i] · simp · intro j _ hji rw [Polynomial.lcoeff_apply, Polynomial.coeff_monomial] exact if_neg (doubledExponent_injective.ne hji) · simp -
doubledCoefficientPolynomial_eq_zero_ifflemma — A doubled coefficient polynomial vanishes exactly when its coefficient vector vanishes.hypothesesconclusiondoubledCoefficientPolynomial v = 0 ↔ v = 0Proof (Lean source)
lemma doubledCoefficientPolynomial_eq_zero_iff {n : ℕ} {K : Type*} [Semiring K] (v : Fin n ⊕ Fin n → K) : doubledCoefficientPolynomial v = 0 ↔ v = 0 := by constructor · intro hp funext i have := congrArg (fun p : Polynomial K => p.coeff (doubledExponent i)) hp simpa [coeff_doubledCoefficientPolynomial] using this · rintro rfl simp [doubledCoefficientPolynomial] -
natDegree_doubledCoefficientPolynomial_ltlemma — A doubled coefficient polynomial has degree below 2n when n is positive.hypothesesconclusion(doubledCoefficientPolynomial v).natDegree < 2 * nProof (Lean source)
lemma natDegree_doubledCoefficientPolynomial_lt {n : ℕ} {K : Type*} [Semiring K] (hn : 1 ≤ n) {v : Fin n ⊕ Fin n → K} : (doubledCoefficientPolynomial v).natDegree < 2 * n := by apply lt_of_le_of_lt (Polynomial.natDegree_sum_le_of_forall_le (univ) _ (fun i _ => (Polynomial.natDegree_monomial_le _).trans (Nat.le_pred_of_lt (doubledExponent_lt i)))) have hpos : 0 < 2 * n := by omega exact Nat.pred_lt hpos.ne' -
eval_doubledCoefficientPolynomiallemma — Evaluating a polynomial whose coefficients are indexed by two blocks of monomial powers gives the finite sum of those coefficients weighted by the corresponding powers of the evaluation point.hypothesesconclusion(doubledCoefficientPolynomial v).eval x = ∑ a, x ^ doubledExponent a * v aProof (Lean source)
lemma eval_doubledCoefficientPolynomial {n : ℕ} {K : Type*} [CommSemiring K] (v : Fin n ⊕ Fin n → K) (x : K) : (doubledCoefficientPolynomial v).eval x = ∑ a, x ^ doubledExponent a * v a := by simp only [doubledCoefficientPolynomial] rw [Polynomial.eval_finset_sum] simp only [Polynomial.eval_monomial] apply Finset.sum_congr rfl intro a _ ring -
eval_derivative_doubledCoefficientPolynomiallemma — Evaluating the derivative of a polynomial whose coefficients are indexed by two blocks of monomial powers gives the finite sum of coefficients weighted by their monomial exponents and the corresponding reduced powers.hypothesesconclusion(doubledCoefficientPolynomial v).derivative.eval x= ∑ a, ((doubledExponent a : K) * x ^ (doubledExponent a - 1)) * v aProof (Lean source)
lemma eval_derivative_doubledCoefficientPolynomial {n : ℕ} {K : Type*} [CommSemiring K] (v : Fin n ⊕ Fin n → K) (x : K) : (doubledCoefficientPolynomial v).derivative.eval x = ∑ a, ((doubledExponent a : K) * x ^ (doubledExponent a - 1)) * v a := by simp only [doubledCoefficientPolynomial, map_sum, Polynomial.derivative_monomial] rw [Polynomial.eval_finset_sum] simp only [Polynomial.eval_monomial] apply Finset.sum_congr rfl intro a _ ring -
doubledCoefficientPolynomial_eq_zero_of_eval_derivativelemma — A polynomial of degree below 2n whose value and derivative vanish at n distinct points is zero.hypothesesn :ℕK :Type*Field Khn :1 ≤ ns :Fin n → Khs :heval :∀ i, (doubledCoefficientPolynomial v).eval (s i) = 0hderiv :∀ i, (doubledCoefficientPolynomial v).derivative.eval (s i) = 0conclusionv = 0Proof (Lean source)
lemma doubledCoefficientPolynomial_eq_zero_of_eval_derivative {n : ℕ} {K : Type*} [Field K] (hn : 1 ≤ n) (s : Fin n → K) (hs : Injective s) (v : Fin n ⊕ Fin n → K) (heval : ∀ i, (doubledCoefficientPolynomial v).eval (s i) = 0) (hderiv : ∀ i, (doubledCoefficientPolynomial v).derivative.eval (s i) = 0) : v = 0 := by by_contra hv have hp : doubledCoefficientPolynomial v ≠ 0 := (doubledCoefficientPolynomial_eq_zero_iff v).not.mpr hv have hfactor (i : Fin n) : (Polynomial.X - Polynomial.C (s i)) ^ 2 ∣ doubledCoefficientPolynomial v := by rw [← Polynomial.le_rootMultiplicity_iff hp] apply (Polynomial.one_lt_rootMultiplicity_iff_isRoot hp).2 exact ⟨heval i, hderiv i⟩ have hcop : Pairwise (Function.onFun IsCoprime fun i : Fin n => (Polynomial.X - Polynomial.C (s i)) ^ 2) := by intro i j hij apply (Polynomial.isCoprime_X_sub_C_of_isUnit_sub (show IsUnit (s i - s j) from (sub_ne_zero.mpr (hs.ne hij)).isUnit)).pow have hprod : (∏ i : Fin n, (Polynomial.X - Polynomial.C (s i)) ^ 2) ∣ doubledCoefficientPolynomial v := Fintype.prod_dvd_of_coprime hcop hfactor have hdegree := Polynomial.natDegree_le_of_dvd hprod hp have hleft : (∏ i : Fin n, (Polynomial.X - Polynomial.C (s i)) ^ 2).natDegree = 2 * n := by calc _ = ∑ i : Fin n, ((Polynomial.X - Polynomial.C (s i)) ^ 2).natDegree := by apply Polynomial.natDegree_prod intro i _ exact pow_ne_zero _ (Polynomial.X_sub_C_ne_zero (s i)) _ = 2 * n := by simp [Polynomial.natDegree_pow, Nat.mul_comm] rw [hleft] at hdegree exact (not_lt_of_ge hdegree) (natDegree_doubledCoefficientPolynomial_lt hn) -
pinnedSucc_injectivelemma — The positive-node embedding is injective.Proof (Lean source)
lemma pinnedSucc_injective {n : ℕ} : Injective (pinnedSucc (n := n)) := by intro i j hij apply Fin.ext have := congrArg val hij simp only [pinnedSucc] at this omega -
pinnedSucc_ne_zerolemma — A positive-node index never equals the distinguished zero node.Proof (Lean source)
lemma pinnedSucc_ne_zero {n : ℕ} (hn : 1 ≤ n) (i : Fin (n - 1)) : pinnedSucc i ≠ ⟨0, hn⟩ := by intro h have := congrArg val h simp [pinnedSucc] at this -
pinnedExponent_injectivelemma — The pinned exponent encoding is injective.Proof (Lean source)
lemma pinnedExponent_injective {n : ℕ} : Injective (pinnedExponent : Fin n ⊕ Fin (n - 1) → ℕ) := by intro i j hij rcases i with i | i <;> rcases j with j | j · exact congrArg inl (Fin.ext hij) · exfalso simp only [pinnedExponent] at hij have := i.isLt omega · exfalso simp only [pinnedExponent] at hij have := j.isLt omega · simp only [pinnedExponent] at hij exact congrArg inr (Fin.ext (Nat.add_left_cancel hij)) -
pinnedExponent_ltlemma — Every pinned exponent is strictly below 2n - 1.Proof (Lean source)
lemma pinnedExponent_lt {n : ℕ} (i : Fin n ⊕ Fin (n - 1)) : pinnedExponent i < 2 * n - 1 := by rcases i with i | i <;> simp only [pinnedExponent] · have := i.isLt omega · have := i.isLt omega -
coeff_pinnedCoefficientPolynomiallemma — Reading a pinned coefficient polynomial at an encoded exponent recovers the corresponding coefficient.hypothesesconclusion(pinnedCoefficientPolynomial v).coeff (pinnedExponent i) = v iProof (Lean source)
lemma coeff_pinnedCoefficientPolynomial {n : ℕ} {K : Type*} [Semiring K] (v : Fin n ⊕ Fin (n - 1) → K) (i : Fin n ⊕ Fin (n - 1)) : (pinnedCoefficientPolynomial v).coeff (pinnedExponent i) = v i := by classical change (Polynomial.lcoeff K (pinnedExponent i)) (∑ j, Polynomial.monomial (pinnedExponent j) (v j)) = v i rw [map_sum, Finset.sum_eq_single i] · simp · intro j _ hji rw [Polynomial.lcoeff_apply, Polynomial.coeff_monomial] exact if_neg (pinnedExponent_injective.ne hji) · simp -
pinnedCoefficientPolynomial_eq_zero_ifflemma — A pinned coefficient polynomial vanishes exactly when its coefficient vector vanishes.hypothesesconclusionpinnedCoefficientPolynomial v = 0 ↔ v = 0Proof (Lean source)
lemma pinnedCoefficientPolynomial_eq_zero_iff {n : ℕ} {K : Type*} [Semiring K] (v : Fin n ⊕ Fin (n - 1) → K) : pinnedCoefficientPolynomial v = 0 ↔ v = 0 := by constructor · intro hp funext i have hi := congrArg (fun p : Polynomial K => p.coeff (pinnedExponent i)) hp simpa [coeff_pinnedCoefficientPolynomial] using hi · rintro rfl simp [pinnedCoefficientPolynomial] -
natDegree_pinnedCoefficientPolynomial_ltlemma — A pinned coefficient polynomial has degree below 2n - 1.hypothesesconclusion(pinnedCoefficientPolynomial v).natDegree < 2 * n - 1Proof (Lean source)
lemma natDegree_pinnedCoefficientPolynomial_lt {n : ℕ} {K : Type*} [Semiring K] (hn : 1 ≤ n) {v : Fin n ⊕ Fin (n - 1) → K} : (pinnedCoefficientPolynomial v).natDegree < 2 * n - 1 := by have hpos : 0 < 2 * n - 1 := by omega apply lt_of_le_of_lt (Polynomial.natDegree_sum_le_of_forall_le (univ) _ (fun i _ => (Polynomial.natDegree_monomial_le _).trans (Nat.le_pred_of_lt (pinnedExponent_lt i)))) exact Nat.pred_lt hpos.ne' -
eval_pinnedCoefficientPolynomiallemma — Evaluating a pinned coefficient polynomial at a point equals the sum of its coefficients, each weighted by that point raised to the coefficient's associated pinned exponent.hypothesesconclusion(pinnedCoefficientPolynomial v).eval x = ∑ a, x ^ pinnedExponent a * v aProof (Lean source)
lemma eval_pinnedCoefficientPolynomial {n : ℕ} {K : Type*} [Field K] (v : Fin n ⊕ Fin (n - 1) → K) (x : K) : (pinnedCoefficientPolynomial v).eval x = ∑ a, x ^ pinnedExponent a * v a := by simp only [pinnedCoefficientPolynomial] rw [Polynomial.eval_finset_sum] simp only [Polynomial.eval_monomial] apply Finset.sum_congr rfl intro a _ ring -
eval_derivative_pinnedCoefficientPolynomiallemma — Evaluating the derivative of a pinned coefficient polynomial at a point equals the sum of its coefficients weighted by the corresponding derivative monomial values at that point.hypothesesconclusion(pinnedCoefficientPolynomial v).derivative.eval x= ∑ a, ((pinnedExponent a : K) * x ^ (pinnedExponent a - 1)) * v aProof (Lean source)
lemma eval_derivative_pinnedCoefficientPolynomial {n : ℕ} {K : Type*} [Field K] (v : Fin n ⊕ Fin (n - 1) → K) (x : K) : (pinnedCoefficientPolynomial v).derivative.eval x = ∑ a, ((pinnedExponent a : K) * x ^ (pinnedExponent a - 1)) * v a := by simp only [pinnedCoefficientPolynomial, map_sum, Polynomial.derivative_monomial] rw [Polynomial.eval_finset_sum] simp only [Polynomial.eval_monomial] apply Finset.sum_congr rfl intro a _ ring -
pinnedCoefficientPolynomial_eq_zero_of_eval_derivativelemma — A polynomial in the pinned coefficient model is zero when it vanishes at all nodes and its derivative vanishes at every nondistinguished node.hypothesesn :ℕK :Type*Field Khn :1 ≤ ns :Fin n → Khs :heval :∀ i, (pinnedCoefficientPolynomial v).eval (s i) = 0hderiv :conclusionv = 0Proof (Lean source)
lemma pinnedCoefficientPolynomial_eq_zero_of_eval_derivative {n : ℕ} {K : Type*} [Field K] (hn : 1 ≤ n) (s : Fin n → K) (hs : Injective s) (v : Fin n ⊕ Fin (n - 1) → K) (heval : ∀ i, (pinnedCoefficientPolynomial v).eval (s i) = 0) (hderiv : ∀ i : Fin (n - 1), (pinnedCoefficientPolynomial v).derivative.eval (s (pinnedSucc i)) = 0) : v = 0 := by by_contra hv have hp : pinnedCoefficientPolynomial v ≠ 0 := (pinnedCoefficientPolynomial_eq_zero_iff v).not.mpr hv let first := Polynomial.X - Polynomial.C (s ⟨0, hn⟩) let rest : Fin (n - 1) → Polynomial K := fun i => (Polynomial.X - Polynomial.C (s (pinnedSucc i))) ^ 2 have hfirst : first ∣ pinnedCoefficientPolynomial v := by rw [Polynomial.dvd_iff_isRoot] exact heval ⟨0, hn⟩ have hrest (i : Fin (n - 1)) : rest i ∣ pinnedCoefficientPolynomial v := by dsimp [rest] rw [← Polynomial.le_rootMultiplicity_iff hp] apply (Polynomial.one_lt_rootMultiplicity_iff_isRoot hp).2 exact ⟨heval (pinnedSucc i), hderiv i⟩ have hrestcop : Pairwise (onFun IsCoprime rest) := by intro i j hij apply (Polynomial.isCoprime_X_sub_C_of_isUnit_sub (show IsUnit (s (pinnedSucc i) - s (pinnedSucc j)) from (sub_ne_zero.mpr (by intro h apply hij apply pinnedSucc_injective exact hs h)).isUnit)).pow have hrestprod : (∏ i, rest i) ∣ pinnedCoefficientPolynomial v := Fintype.prod_dvd_of_coprime hrestcop hrest have hfirstrest : IsCoprime first (∏ i, rest i) := by apply IsCoprime.prod_right intro i _ dsimp [first, rest] exact (Polynomial.isCoprime_X_sub_C_of_isUnit_sub (show IsUnit (s ⟨0, hn⟩ - s (pinnedSucc i)) from (sub_ne_zero.mpr (hs.ne (pinnedSucc_ne_zero hn i).symm)).isUnit)).pow_right have htotal : first * (∏ i, rest i) ∣ pinnedCoefficientPolynomial v := hfirstrest.mul_dvd hfirst hrestprod have hdegree := Polynomial.natDegree_le_of_dvd htotal hp have hleft : (first * ∏ i, rest i).natDegree = 2 * n - 1 := by rw [Polynomial.natDegree_mul (Polynomial.X_sub_C_ne_zero _) (Finset.prod_ne_zero_iff.mpr (fun i _ => pow_ne_zero _ (Polynomial.X_sub_C_ne_zero _)))] have hrestdeg : (∏ i, rest i).natDegree = 2 * (n - 1) := by calc _ = ∑ i : Fin (n - 1), (rest i).natDegree := by apply Polynomial.natDegree_prod intro i _ exact pow_ne_zero _ (Polynomial.X_sub_C_ne_zero _) _ = 2 * (n - 1) := by simp [rest, Polynomial.natDegree_pow, Nat.mul_comm] rw [hrestdeg] simp omega rw [hleft] at hdegree exact (not_lt_of_ge hdegree) (natDegree_pinnedCoefficientPolynomial_lt hn)
MonomialMatrix 3 core · 0 supporting This file collects the linear-algebra facts about generalized-permutation (monomial) matrices that underlie linear causal-discovery identification arguments. ★ genPerm_of_det_ne_zero_of_colSupport★ perm_uniqueness★ eq_of_genPerm_triangular_unitDiag
Generalized-permutation (monomial) matrices
This file collects the linear-algebra facts about generalized-permutation (monomial) matrices that underlie linear causal-discovery identification arguments. A generalized-permutation matrix has exactly one non-zero entry in each row and column — equivalently, it is a permutation composed with a non-zero diagonal rescaling. Such matrices are the ambiguity group of independent component analysis: a mixing matrix is recovered only up to relabelling and rescaling its columns. The three results here are: (1) an invertible matrix with at most one non-zero entry per column is automatically of this generalized-permutation form; (2) a simultaneous row/column permutation of a lower-triangular matrix with non-zero diagonal keeps a non-zero diagonal exactly when the two permutations agree; and (3) two unit-diagonal matrices related by a generalized permutation, one of them triangular in a causal order, must be equal. They are consumed by the LiNGAM and linear-causal-disentanglement developments to turn an ICA-level identification (sharp only up to generalized permutation) into a sharp structural identification.
For a finite index type ι and a commutative ring K, and a square matrix W over ι × ι valued in K with nonzero determinant, if every column of W has at most one non-zero entry (for any two distinct rows i ≠ k, at least one of W i j, W k j vanishes at column j), then W is a generalized permutation matrix: there are a permutation τ of ι and non-zero scalings d with W i j = if j = τ i then d i else 0.
Formal statement
Proof (Lean source)
Permutation uniqueness for lower-triangular matrices (LiNGAM Appendix A, Lemma 1). For a matrix M over Fin n × Fin n that is lower-triangular (M i j = 0 whenever i < j) with non-zero diagonal entries, and permutations σ, τ of Fin n, the row/column-permuted matrix (i ↦ M (σ i) (τ i)) has a non-zero diagonal at every i if and only if σ = τ.
Formal statement
Proof (Lean source)
Generalized-permutation reduction. For matrices C, C' over Fin n × Fin n valued in K, if C has unit diagonal and C' has unit diagonal, C is lower triangular in some causal order σ (C i j = 0 when σ i < σ j), and C' is obtained from C by a generalized permutation with permutation τ and scalings d, i.e. C' i j = d i · C (τ i) j for all i, j, then C = C': the unit diagonal plus triangularity force the underlying permutation to be the identity and every scaling to be one. (Formerly Discovery.LiNGAM.lingam_reduction.)
Formal statement
Proof (Lean source)
StackedVandermonde 4 core · 0 supporting This file gives a constructive injectivity certificate for a stacked family of weighted Vandermonde evaluation maps at distinct nodes in a commutative domain. ★ stacked_contraction_injective_of_generic_weights
Stacked Vandermonde systems
This file gives a constructive injectivity certificate for a stacked family of weighted Vandermonde evaluation maps at distinct nodes in a commutative domain.
The affine power vector whose r-th entry is z^r. Over a field, this is the coefficient vector of (X₀ + z X₁)^k after normalization by the corresponding nonzero binomial coefficient.
Definition (Lean source)
The coefficient-vector version of the stacked contraction map. Over a field in the affine chart ℓ j = X₀ + slopes j X₁, its k-th component agrees with the coefficient vector of ∑ j, weights j k * e j • (ℓ j)^k up to invertible binomial diagonal rescaling.
Definition (Lean source)
The two-block specialization from the block-Vandermonde argument: J₀ = {0} and J_{N-1} = {1, …, N}.
Definition (Lean source)
For a commutative integral domain K, at least three indices N + 1 (2 ≤ N), and pairwise distinct slope values slopes : Fin (N + 1) → K, there exist weights Fin (N + 1) → Fin N → K for which the stacked contraction is injective. The first block detects coordinate 0, while the last block is a square Vandermonde system on coordinates 1,…,N.
Formal statement
Proof (Lean source)
VandermondeSynthesis 2 core · 3 supporting This file computes the kernel dimension of a finite moment-synthesis map with distinct complex nodes and one endpoint coordinate. ★ endpointOrderSynthesis_ker_finrank
Vandermonde synthesis maps
This file computes the kernel dimension of a finite moment-synthesis map with distinct complex nodes and one endpoint coordinate.
Moment synthesis from n commutative-semiring-valued nodes and one terminal coordinate. The terminal coordinate contributes only to the highest requested moment.
Definition (Lean source)
For natural numbers n, r, a field K, and pairwise distinct nodes s : Fin n → K, the dimension of the kernel of the order-r endpoint synthesis map equals n - r: below the square threshold (r < n) the kernel has dimension n - r, and at or above it (r ≥ n) the kernel is zero, matching n - r = 0 under truncated subtraction.
Formal statement
Proof (Lean source)
3 supporting declarations (lemmas, instances)
-
endpointOrderSynthesis_singlelemma — Applying the moment-synthesis map to the standard basis vector for a node produces the vector of that node's powers across the requested moment orders.hypothesesconclusionendpointOrderSynthesis s r (single j.castSucc 1) = fun a => s j ^ a.valProof (Lean source)
lemma endpointOrderSynthesis_single {n r : ℕ} {K : Type*} [CommSemiring K] (s : Fin n → K) (j : Fin n) : endpointOrderSynthesis s r (single j.castSucc 1) = fun a => s j ^ a.val := by funext a simp only [endpointOrderSynthesis, LinearMap.coe_mk, AddHom.coe_mk] rw [Finset.sum_eq_single j] · simp · intro b _ hb have hcast : b.castSucc ≠ j.castSucc := fun h => hb (Fin.castSucc_injective n h) simp [Pi.single_eq_of_ne hcast] · simp -
endpointOrderSynthesis_surjectivelemma — With distinct node values, the finite moment-synthesis map can reproduce every target vector of moments up through an order smaller than the number of nodes.hypothesesconclusionProof (Lean source)
lemma endpointOrderSynthesis_surjective {n r : ℕ} {K : Type*} [Field K] (s : Fin n → K) (hs : Injective s) (hr : r < n) : Surjective (endpointOrderSynthesis s r) := by let e : Fin (r + 1) → Fin n := fun j => ⟨j, by omega⟩ have he : Injective e := by intro x y h apply Fin.ext simpa [e] using congrArg Fin.val h let A : Matrix (Fin (r + 1)) (Fin (r + 1)) K := (Matrix.vandermonde (s ∘ e)).transpose have hdet : A.det ≠ 0 := by rw [Matrix.det_transpose] exact Matrix.det_vandermonde_ne_zero_iff.mpr (hs.comp he) have hAinj : Injective A.mulVecLin := by intro x y hxy apply sub_eq_zero.mp apply Matrix.eq_zero_of_mulVec_eq_zero hdet change A.mulVec x = A.mulVec y at hxy rw [Matrix.mulVec_sub, hxy, sub_self] let E : (Fin (r + 1) → K) ≃ₗ[K] (Fin (r + 1) → K) := LinearEquiv.ofInjectiveEndo A.mulVecLin hAinj intro y let w : Fin (r + 1) → K := E.symm y let z : Fin (n + 1) → K := ∑ j : Fin (r + 1), w j • (single (e j).castSucc (1 : K) : Fin (n + 1) → K) refine ⟨z, ?_⟩ rw [show z = ∑ j : Fin (r + 1), w j • (single (e j).castSucc (1 : K) : Fin (n + 1) → K) by rfl, map_sum] simp_rw [map_smul, endpointOrderSynthesis_single] have hEy : A.mulVec w = y := E.apply_symm_apply y funext a simp only [Finset.sum_apply, Pi.smul_apply, smul_eq_mul] calc ∑ j : Fin (r + 1), w j * s (e j) ^ a.val = A.mulVec w a := by simp [A, mulVec, dotProduct, Matrix.vandermonde_apply, e, mul_comm] _ = y a := congrFun hEy a -
endpointOrderSynthesis_injectivelemma — With distinct node values, moments through an order at least as large as the number of nodes uniquely determine every coordinate supplied to the moment-synthesis map.hypothesesconclusionProof (Lean source)
lemma endpointOrderSynthesis_injective {n r : ℕ} {K : Type*} [CommRing K] [IsDomain K] (s : Fin n → K) (hs : Injective s) (hr : n ≤ r) : Injective (endpointOrderSynthesis s r) := by intro z z' hzz' apply sub_eq_zero.mp let v := z - z' have hv : endpointOrderSynthesis s r v = 0 := by rw [map_sub, hzz', sub_self] have hfinite : (fun j : Fin n => v j.castSucc) = 0 := by apply Matrix.eq_zero_of_forall_pow_sum_mul_pow_eq_zero hs intro a have ha : a.val < r := lt_of_lt_of_le a.isLt hr have hcoord := congrFun hv ⟨a, by omega⟩ simpa [endpointOrderSynthesis, ha.ne] using hcoord funext j refine Fin.lastCases ?_ (fun i => ?_) j · have hcoord := congrFun hv (last r) have hzero : ∀ i : Fin n, v i.castSucc = 0 := fun i => congrFun hfinite i simpa [v, endpointOrderSynthesis, hzero] using hcoord · exact congrFun hfinite i