From 6ca93918fca5350ee0889524028d535b222910f0 Mon Sep 17 00:00:00 2001 From: Per Alexandersson Date: Sun, 2 Aug 2026 11:56:10 +0000 Subject: [PATCH 001/141] Add sign-regular matrix predicates --- RealRooted.lean | 1 + .../LinearAlgebra/Matrix/SignRegular.lean | 89 +++++++++++++++++++ 2 files changed, 90 insertions(+) create mode 100644 RealRooted/Mathlib/LinearAlgebra/Matrix/SignRegular.lean diff --git a/RealRooted.lean b/RealRooted.lean index 4bca6b20..6037251d 100644 --- a/RealRooted.lean +++ b/RealRooted.lean @@ -203,6 +203,7 @@ import RealRooted.Mathlib.Data.Nat.Cast.Basic import RealRooted.Mathlib.Data.Nat.Choose.Cast import RealRooted.Mathlib.LinearAlgebra.Matrix.Determinant.Basic import RealRooted.Mathlib.LinearAlgebra.Matrix.KernelSignVariation +import RealRooted.Mathlib.LinearAlgebra.Matrix.SignRegular import RealRooted.Mathlib.LinearAlgebra.Matrix.SignVariation import RealRooted.Mathlib.LinearAlgebra.Matrix.TotallyNonneg import RealRooted.Mathlib.LinearAlgebra.Matrix.VariationDiminishing diff --git a/RealRooted/Mathlib/LinearAlgebra/Matrix/SignRegular.lean b/RealRooted/Mathlib/LinearAlgebra/Matrix/SignRegular.lean new file mode 100644 index 00000000..1f2a7020 --- /dev/null +++ b/RealRooted/Mathlib/LinearAlgebra/Matrix/SignRegular.lean @@ -0,0 +1,89 @@ +import RealRooted.Mathlib.LinearAlgebra.Matrix.TotallyNonneg + +/-! +# Sign-consistent and sign-regular rectangular matrices + +This file introduces orientation-free predicates for Karlin's sign-consistent +and sign-regular matrices. Two minors have a common weak (respectively strict) +sign exactly when their product is nonnegative (respectively positive). +-/ + +public section + +namespace Matrix + +variable {R ι κ : Type*} [CommRing R] [PartialOrder R] +variable [Preorder ι] [Preorder κ] + +/-- The minors of order `q` of `M` have a common weak sign. -/ +def IsSignConsistentOrder (M : Matrix ι κ R) (q : ℕ) : Prop := + ∀ ⦃rows rows' : Fin q → ι⦄ ⦃cols cols' : Fin q → κ⦄, + StrictMono rows → StrictMono rows' → StrictMono cols → StrictMono cols' → + 0 ≤ (M.submatrix rows cols).det * (M.submatrix rows' cols').det + +/-- The minors of order `q` of `M` are nonzero and have a common strict sign. -/ +def IsStrictlySignConsistentOrder (M : Matrix ι κ R) (q : ℕ) : Prop := + ∀ ⦃rows rows' : Fin q → ι⦄ ⦃cols cols' : Fin q → κ⦄, + StrictMono rows → StrictMono rows' → StrictMono cols → StrictMono cols' → + 0 < (M.submatrix rows cols).det * (M.submatrix rows' cols').det + +/-- A matrix is sign regular if its minors have a common weak sign at every order. -/ +def IsSignRegular (M : Matrix ι κ R) : Prop := + ∀ q, M.IsSignConsistentOrder q + +/-- A matrix is strictly sign regular if its minors have a common strict sign +at every order. -/ +def IsStrictlySignRegular (M : Matrix ι κ R) : Prop := + ∀ q, M.IsStrictlySignConsistentOrder q + +protected lemma IsStrictlySignConsistentOrder.toSignConsistentOrder + {M : Matrix ι κ R} {q : ℕ} (hM : M.IsStrictlySignConsistentOrder q) : + M.IsSignConsistentOrder q := by + intro rows rows' cols cols' hrows hrows' hcols hcols' + exact (hM hrows hrows' hcols hcols').le + +protected lemma IsStrictlySignRegular.toSignRegular {M : Matrix ι κ R} + (hM : M.IsStrictlySignRegular) : M.IsSignRegular := + fun q ↦ (hM q).toSignConsistentOrder + +section OrderedRing + +variable [IsStrictOrderedRing R] + +lemma IsTotallyNonnegRect.isSignConsistentOrder {M : Matrix ι κ R} + (hM : M.IsTotallyNonnegRect) (q : ℕ) : M.IsSignConsistentOrder q := by + intro rows rows' cols cols' hrows hrows' hcols hcols' + exact mul_nonneg (hM hrows hcols) (hM hrows' hcols') + +lemma IsTotallyNonnegRect.isSignRegular {M : Matrix ι κ R} + (hM : M.IsTotallyNonnegRect) : M.IsSignRegular := + fun q ↦ hM.isSignConsistentOrder q + +lemma isStrictlySignConsistentOrder_of_posMinors {M : Matrix ι κ R} {q : ℕ} + (hM : + ∀ ⦃rows : Fin q → ι⦄ ⦃cols : Fin q → κ⦄, + StrictMono rows → StrictMono cols → 0 < (M.submatrix rows cols).det) : + M.IsStrictlySignConsistentOrder q := by + intro rows rows' cols cols' hrows hrows' hcols hcols' + exact mul_pos (hM hrows hcols) (hM hrows' hcols') + +end OrderedRing + +lemma IsSignConsistentOrder.minorProduct_nonneg {M : Matrix ι κ R} {q : ℕ} + (hM : M.IsSignConsistentOrder q) {cols : Fin q → κ} (hcols : StrictMono cols) : + ∀ ⦃rows rows' : Fin q → ι⦄, + StrictMono rows → StrictMono rows' → + 0 ≤ (M.submatrix rows cols).det * (M.submatrix rows' cols).det := by + intro rows rows' hrows hrows' + exact hM hrows hrows' hcols hcols + +lemma IsStrictlySignConsistentOrder.minorProduct_pos + {M : Matrix ι κ R} {q : ℕ} (hM : M.IsStrictlySignConsistentOrder q) + {cols : Fin q → κ} (hcols : StrictMono cols) : + ∀ ⦃rows rows' : Fin q → ι⦄, + StrictMono rows → StrictMono rows' → + 0 < (M.submatrix rows cols).det * (M.submatrix rows' cols).det := by + intro rows rows' hrows hrows' + exact hM hrows hrows' hcols hcols + +end Matrix From 5a65f49efd5d62650cf1b0ded63e114a2677a3e5 Mon Sep 17 00:00:00 2001 From: Per Alexandersson Date: Sun, 2 Aug 2026 11:59:15 +0000 Subject: [PATCH 002/141] Add sign-regular closure lemmas --- .../LinearAlgebra/Matrix/SignRegular.lean | 59 +++++++++++++++++++ 1 file changed, 59 insertions(+) diff --git a/RealRooted/Mathlib/LinearAlgebra/Matrix/SignRegular.lean b/RealRooted/Mathlib/LinearAlgebra/Matrix/SignRegular.lean index 1f2a7020..cb17762b 100644 --- a/RealRooted/Mathlib/LinearAlgebra/Matrix/SignRegular.lean +++ b/RealRooted/Mathlib/LinearAlgebra/Matrix/SignRegular.lean @@ -36,6 +36,65 @@ at every order. -/ def IsStrictlySignRegular (M : Matrix ι κ R) : Prop := ∀ q, M.IsStrictlySignConsistentOrder q +protected lemma IsSignConsistentOrder.submatrix + {ι' κ' : Type*} [Preorder ι'] [Preorder κ'] {M : Matrix ι κ R} {q : ℕ} + (hM : M.IsSignConsistentOrder q) {rows : ι' → ι} {cols : κ' → κ} + (hrows : StrictMono rows) (hcols : StrictMono cols) : + (M.submatrix rows cols).IsSignConsistentOrder q := by + intro rows₁ rows₂ cols₁ cols₂ hrows₁ hrows₂ hcols₁ hcols₂ + simpa using hM (hrows.comp hrows₁) (hrows.comp hrows₂) + (hcols.comp hcols₁) (hcols.comp hcols₂) + +protected lemma IsStrictlySignConsistentOrder.submatrix + {ι' κ' : Type*} [Preorder ι'] [Preorder κ'] {M : Matrix ι κ R} {q : ℕ} + (hM : M.IsStrictlySignConsistentOrder q) {rows : ι' → ι} {cols : κ' → κ} + (hrows : StrictMono rows) (hcols : StrictMono cols) : + (M.submatrix rows cols).IsStrictlySignConsistentOrder q := by + intro rows₁ rows₂ cols₁ cols₂ hrows₁ hrows₂ hcols₁ hcols₂ + simpa using hM (hrows.comp hrows₁) (hrows.comp hrows₂) + (hcols.comp hcols₁) (hcols.comp hcols₂) + +protected lemma IsSignConsistentOrder.transpose {M : Matrix ι κ R} {q : ℕ} + (hM : M.IsSignConsistentOrder q) : M.transpose.IsSignConsistentOrder q := by + intro rows rows' cols cols' hrows hrows' hcols hcols' + have hdet (r : Fin q → κ) (c : Fin q → ι) : + (M.transpose.submatrix r c).det = (M.submatrix c r).det := by + rw [← Matrix.det_transpose (M.submatrix c r), Matrix.transpose_submatrix] + rw [hdet rows cols, hdet rows' cols'] + exact hM hcols hcols' hrows hrows' + +protected lemma IsStrictlySignConsistentOrder.transpose {M : Matrix ι κ R} {q : ℕ} + (hM : M.IsStrictlySignConsistentOrder q) : + M.transpose.IsStrictlySignConsistentOrder q := by + intro rows rows' cols cols' hrows hrows' hcols hcols' + have hdet (r : Fin q → κ) (c : Fin q → ι) : + (M.transpose.submatrix r c).det = (M.submatrix c r).det := by + rw [← Matrix.det_transpose (M.submatrix c r), Matrix.transpose_submatrix] + rw [hdet rows cols, hdet rows' cols'] + exact hM hcols hcols' hrows hrows' + +protected lemma IsSignRegular.submatrix + {ι' κ' : Type*} [Preorder ι'] [Preorder κ'] {M : Matrix ι κ R} + (hM : M.IsSignRegular) {rows : ι' → ι} {cols : κ' → κ} + (hrows : StrictMono rows) (hcols : StrictMono cols) : + (M.submatrix rows cols).IsSignRegular := + fun q ↦ (hM q).submatrix hrows hcols + +protected lemma IsStrictlySignRegular.submatrix + {ι' κ' : Type*} [Preorder ι'] [Preorder κ'] {M : Matrix ι κ R} + (hM : M.IsStrictlySignRegular) {rows : ι' → ι} {cols : κ' → κ} + (hrows : StrictMono rows) (hcols : StrictMono cols) : + (M.submatrix rows cols).IsStrictlySignRegular := + fun q ↦ (hM q).submatrix hrows hcols + +protected lemma IsSignRegular.transpose {M : Matrix ι κ R} + (hM : M.IsSignRegular) : M.transpose.IsSignRegular := + fun q ↦ (hM q).transpose + +protected lemma IsStrictlySignRegular.transpose {M : Matrix ι κ R} + (hM : M.IsStrictlySignRegular) : M.transpose.IsStrictlySignRegular := + fun q ↦ (hM q).transpose + protected lemma IsStrictlySignConsistentOrder.toSignConsistentOrder {M : Matrix ι κ R} {q : ℕ} (hM : M.IsStrictlySignConsistentOrder q) : M.IsSignConsistentOrder q := by From 480347041fa322586698dffc7fb7576c6c953129 Mon Sep 17 00:00:00 2001 From: Per Alexandersson Date: Sun, 2 Aug 2026 12:00:33 +0000 Subject: [PATCH 003/141] Orient sign-consistent minors --- .../LinearAlgebra/Matrix/SignRegular.lean | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/RealRooted/Mathlib/LinearAlgebra/Matrix/SignRegular.lean b/RealRooted/Mathlib/LinearAlgebra/Matrix/SignRegular.lean index cb17762b..dfaff489 100644 --- a/RealRooted/Mathlib/LinearAlgebra/Matrix/SignRegular.lean +++ b/RealRooted/Mathlib/LinearAlgebra/Matrix/SignRegular.lean @@ -145,4 +145,32 @@ lemma IsStrictlySignConsistentOrder.minorProduct_pos intro rows rows' hrows hrows' exact hM hrows hrows' hcols hcols +section LinearOrder + +variable {S : Type*} [CommRing S] [LinearOrder S] [IsStrictOrderedRing S] + +lemma IsSignConsistentOrder.minor_nonneg_of_pos + {M : Matrix ι κ S} {q : ℕ} (hM : M.IsSignConsistentOrder q) + {rows₀ : Fin q → ι} {cols₀ : Fin q → κ} + (hrows₀ : StrictMono rows₀) (hcols₀ : StrictMono cols₀) + (href : 0 < (M.submatrix rows₀ cols₀).det) + {rows : Fin q → ι} {cols : Fin q → κ} + (hrows : StrictMono rows) (hcols : StrictMono cols) : + 0 ≤ (M.submatrix rows cols).det := + nonneg_of_mul_nonneg_left + (hM hrows hrows₀ hcols hcols₀) href + +lemma IsSignConsistentOrder.minor_nonpos_of_neg + {M : Matrix ι κ S} {q : ℕ} (hM : M.IsSignConsistentOrder q) + {rows₀ : Fin q → ι} {cols₀ : Fin q → κ} + (hrows₀ : StrictMono rows₀) (hcols₀ : StrictMono cols₀) + (href : (M.submatrix rows₀ cols₀).det < 0) + {rows : Fin q → ι} {cols : Fin q → κ} + (hrows : StrictMono rows) (hcols : StrictMono cols) : + (M.submatrix rows cols).det ≤ 0 := + nonpos_of_mul_nonneg_left + (hM hrows hrows₀ hcols hcols₀) href + +end LinearOrder + end Matrix From d80295e8186cff72f3d9cd247cef79e043a55659 Mon Sep 17 00:00:00 2001 From: Per Alexandersson Date: Sun, 2 Aug 2026 14:07:43 +0000 Subject: [PATCH 004/141] Scope sign-regular index orders --- RealRooted/Mathlib/LinearAlgebra/Matrix/SignRegular.lean | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/RealRooted/Mathlib/LinearAlgebra/Matrix/SignRegular.lean b/RealRooted/Mathlib/LinearAlgebra/Matrix/SignRegular.lean index dfaff489..64a77476 100644 --- a/RealRooted/Mathlib/LinearAlgebra/Matrix/SignRegular.lean +++ b/RealRooted/Mathlib/LinearAlgebra/Matrix/SignRegular.lean @@ -108,13 +108,14 @@ protected lemma IsStrictlySignRegular.toSignRegular {M : Matrix ι κ R} section OrderedRing variable [IsStrictOrderedRing R] +variable {ι' κ' : Type*} [PartialOrder ι'] [PartialOrder κ'] -lemma IsTotallyNonnegRect.isSignConsistentOrder {M : Matrix ι κ R} +lemma IsTotallyNonnegRect.isSignConsistentOrder {M : Matrix ι' κ' R} (hM : M.IsTotallyNonnegRect) (q : ℕ) : M.IsSignConsistentOrder q := by intro rows rows' cols cols' hrows hrows' hcols hcols' exact mul_nonneg (hM hrows hcols) (hM hrows' hcols') -lemma IsTotallyNonnegRect.isSignRegular {M : Matrix ι κ R} +lemma IsTotallyNonnegRect.isSignRegular {M : Matrix ι' κ' R} (hM : M.IsTotallyNonnegRect) : M.IsSignRegular := fun q ↦ hM.isSignConsistentOrder q From 9e310e61d8edf6249aa9d32d040b5fe3e4b7e2d9 Mon Sep 17 00:00:00 2001 From: Per Alexandersson Date: Sun, 2 Aug 2026 20:33:25 +0000 Subject: [PATCH 005/141] Add Karlin Gaussian identity limit --- RealRooted.lean | 1 + .../LinearAlgebra/Matrix/Gaussian.lean | 86 +++++++++++++++++++ 2 files changed, 87 insertions(+) create mode 100644 RealRooted/Mathlib/LinearAlgebra/Matrix/Gaussian.lean diff --git a/RealRooted.lean b/RealRooted.lean index 6037251d..a0ba8e30 100644 --- a/RealRooted.lean +++ b/RealRooted.lean @@ -202,6 +202,7 @@ import RealRooted.Mathlib.Data.List.Zip import RealRooted.Mathlib.Data.Nat.Cast.Basic import RealRooted.Mathlib.Data.Nat.Choose.Cast import RealRooted.Mathlib.LinearAlgebra.Matrix.Determinant.Basic +import RealRooted.Mathlib.LinearAlgebra.Matrix.Gaussian import RealRooted.Mathlib.LinearAlgebra.Matrix.KernelSignVariation import RealRooted.Mathlib.LinearAlgebra.Matrix.SignRegular import RealRooted.Mathlib.LinearAlgebra.Matrix.SignVariation diff --git a/RealRooted/Mathlib/LinearAlgebra/Matrix/Gaussian.lean b/RealRooted/Mathlib/LinearAlgebra/Matrix/Gaussian.lean new file mode 100644 index 00000000..6f0555f9 --- /dev/null +++ b/RealRooted/Mathlib/LinearAlgebra/Matrix/Gaussian.lean @@ -0,0 +1,86 @@ +import Mathlib.Analysis.SpecialFunctions.Exp +import Mathlib.Topology.Instances.Matrix +import RealRooted.Mathlib.LinearAlgebra.Matrix.SignRegular + +/-! +# Karlin's finite Gaussian matrices + +This file starts the formalization of Karlin, *Total Positivity*, Vol. I, +Chapter V, Section 1, Proposition 1.1. Karlin uses the Gaussian matrix + +`F(a) i j = exp (-a * (i - j) ^ 2)`. + +The proposition has two parts: `F(a)` tends to the identity as `a` tends to +positive infinity, and `F(a)` is strictly totally positive when `a > 0`. +This file proves the first part. The second part requires the source's +strict-total-positivity theorem for the exponential kernel `exp (x * y)`; +it is intentionally not assumed here. +-/ + +public section + +open Filter Topology + +namespace Matrix + +/-- Karlin's finite Gaussian matrix from Proposition V.1.1. -/ +noncomputable def gaussianMatrix (n : ℕ) (a : ℝ) : Matrix (Fin n) (Fin n) ℝ := + fun i j => Real.exp (-a * (((i : ℕ) : ℝ) - ((j : ℕ) : ℝ)) ^ 2) + +@[simp] lemma gaussianMatrix_apply (n : ℕ) (a : ℝ) (i j : Fin n) : + gaussianMatrix n a i j = + Real.exp (-a * (((i : ℕ) : ℝ) - ((j : ℕ) : ℝ)) ^ 2) := + rfl + +@[simp] lemma gaussianMatrix_apply_self (n : ℕ) (a : ℝ) (i : Fin n) : + gaussianMatrix n a i i = 1 := by + simp + +lemma gaussianMatrix_apply_pos (n : ℕ) (a : ℝ) (i j : Fin n) : + 0 < gaussianMatrix n a i j := + Real.exp_pos _ + +/-- Karlin's Gaussian matrix converges entrywise to the identity as its +parameter tends to positive infinity. -/ +theorem tendsto_gaussianMatrix_atTop (n : ℕ) : + Tendsto (gaussianMatrix n) atTop + (𝓝 (1 : Matrix (Fin n) (Fin n) ℝ)) := by + change Tendsto + (fun a => (gaussianMatrix n a : Fin n → Fin n → ℝ)) atTop + (𝓝 ((1 : Matrix (Fin n) (Fin n) ℝ) : Fin n → Fin n → ℝ)) + apply tendsto_pi_nhds.2 + intro i + apply tendsto_pi_nhds.2 + intro j + by_cases hij : i = j + · subst j + simp only [gaussianMatrix_apply_self, one_apply, if_pos] + exact tendsto_const_nhds + · have hcast : + (((i : ℕ) : ℝ) - ((j : ℕ) : ℝ)) ≠ 0 := by + rw [sub_ne_zero] + intro heq + have hval : (i : ℕ) = (j : ℕ) := by + exact_mod_cast heq + exact hij (Fin.ext hval) + have hsquare : + 0 < (((i : ℕ) : ℝ) - ((j : ℕ) : ℝ)) ^ 2 := + sq_pos_of_ne_zero hcast + have hneg : + -(((i : ℕ) : ℝ) - ((j : ℕ) : ℝ)) ^ 2 < 0 := + neg_lt_zero.mpr hsquare + have hlinear : + Tendsto + (fun a : ℝ => + a * -(((i : ℕ) : ℝ) - ((j : ℕ) : ℝ)) ^ 2) + atTop atBot := + tendsto_id.atTop_mul_const_of_neg hneg + have hexp := + Real.tendsto_exp_atBot.comp hlinear + convert hexp using 1 + · funext a + simp only [Function.comp_apply, gaussianMatrix_apply] + ring_nf + · simp [hij] + +end Matrix From 97a97ebd70feaa1ee92796a088ccdedcae98fe1d Mon Sep 17 00:00:00 2001 From: Per Alexandersson Date: Sun, 2 Aug 2026 20:38:47 +0000 Subject: [PATCH 006/141] Factor Karlin Gaussian minors --- .../LinearAlgebra/Matrix/Gaussian.lean | 72 +++++++++++++++++++ 1 file changed, 72 insertions(+) diff --git a/RealRooted/Mathlib/LinearAlgebra/Matrix/Gaussian.lean b/RealRooted/Mathlib/LinearAlgebra/Matrix/Gaussian.lean index 6f0555f9..5b385b0d 100644 --- a/RealRooted/Mathlib/LinearAlgebra/Matrix/Gaussian.lean +++ b/RealRooted/Mathlib/LinearAlgebra/Matrix/Gaussian.lean @@ -83,4 +83,76 @@ theorem tendsto_gaussianMatrix_atTop (n : ℕ) : ring_nf · simp [hij] +/-- The finite restriction of Karlin's exponential kernel `exp (x * y)`. -/ +noncomputable def exponentialKernelMatrix {q : ℕ} + (x y : Fin q → ℝ) : Matrix (Fin q) (Fin q) ℝ := + fun i j => Real.exp (x i * y j) + +@[simp] lemma exponentialKernelMatrix_apply {q : ℕ} + (x y : Fin q → ℝ) (i j : Fin q) : + exponentialKernelMatrix x y i j = Real.exp (x i * y j) := + rfl + +/-- A Gaussian minor is an exponential-kernel minor times positive row and +column factors. This is the algebraic reduction in Karlin's proof of +Proposition V.1.1. -/ +theorem det_gaussianMatrix_submatrix_eq {n q : ℕ} (a : ℝ) + (rows cols : Fin q → Fin n) : + ((gaussianMatrix n a).submatrix rows cols).det = + (∏ i, Real.exp (-a * (((rows i : Fin n) : ℕ) : ℝ) ^ 2)) * + (∏ j, Real.exp (-a * (((cols j : Fin n) : ℕ) : ℝ) ^ 2)) * + (exponentialKernelMatrix + (fun i => 2 * a * (((rows i : Fin n) : ℕ) : ℝ)) + (fun j => (((cols j : Fin n) : ℕ) : ℝ))).det := by + let rowFactor : Fin q → ℝ := + fun i => Real.exp (-a * (((rows i : Fin n) : ℕ) : ℝ) ^ 2) + let colFactor : Fin q → ℝ := + fun j => Real.exp (-a * (((cols j : Fin n) : ℕ) : ℝ) ^ 2) + let E : Matrix (Fin q) (Fin q) ℝ := + exponentialKernelMatrix + (fun i => 2 * a * (((rows i : Fin n) : ℕ) : ℝ)) + (fun j => (((cols j : Fin n) : ℕ) : ℝ)) + have hmatrix : + (gaussianMatrix n a).submatrix rows cols = + of fun i j => rowFactor i * (colFactor j * E i j) := by + ext i j + simp only [submatrix_apply, gaussianMatrix_apply, of_apply, rowFactor, + colFactor, E, exponentialKernelMatrix_apply] + rw [show + -a * ((((rows i : Fin n) : ℕ) : ℝ) - + (((cols j : Fin n) : ℕ) : ℝ)) ^ 2 = + -a * (((rows i : Fin n) : ℕ) : ℝ) ^ 2 + + (-a * (((cols j : Fin n) : ℕ) : ℝ) ^ 2 + + (2 * a * (((rows i : Fin n) : ℕ) : ℝ)) * + (((cols j : Fin n) : ℕ) : ℝ)) by ring] + rw [Real.exp_add, Real.exp_add] + have hcol : + (of fun i j => colFactor j * E i j).det = + (∏ j, colFactor j) * E.det := + det_mul_row colFactor E + rw [hmatrix, det_mul_column] + change + (∏ i, rowFactor i) * + (of fun i j => colFactor j * E i j).det = + _ + rw [hcol] + dsimp only [rowFactor, colFactor, E] + ring + +/-- Transfer positivity from the exponential-kernel minor to the corresponding +Gaussian minor. + +The hypothesis is intentionally explicit: it is exactly Karlin III.1's +strict-total-positivity input. Keeping that analytic boundary visible lets us +formalize the Gaussian factorization without assuming Proposition V.1.1. -/ +theorem det_gaussianMatrix_submatrix_pos_of_exponentialKernel {n q : ℕ} + (a : ℝ) (rows cols : Fin q → Fin n) + (hkernel : + 0 < (exponentialKernelMatrix + (fun i => 2 * a * (((rows i : Fin n) : ℕ) : ℝ)) + (fun j => (((cols j : Fin n) : ℕ) : ℝ))).det) : + 0 < ((gaussianMatrix n a).submatrix rows cols).det := by + rw [det_gaussianMatrix_submatrix_eq] + positivity + end Matrix From 685adf59fd289083e4b83de17e2ed6044cc38ac8 Mon Sep 17 00:00:00 2001 From: Per Alexandersson Date: Sun, 2 Aug 2026 20:41:25 +0000 Subject: [PATCH 007/141] Generalize exponential kernel matrix --- .../Mathlib/LinearAlgebra/Matrix/Gaussian.lean | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/RealRooted/Mathlib/LinearAlgebra/Matrix/Gaussian.lean b/RealRooted/Mathlib/LinearAlgebra/Matrix/Gaussian.lean index 5b385b0d..2f348f49 100644 --- a/RealRooted/Mathlib/LinearAlgebra/Matrix/Gaussian.lean +++ b/RealRooted/Mathlib/LinearAlgebra/Matrix/Gaussian.lean @@ -1,6 +1,8 @@ import Mathlib.Analysis.SpecialFunctions.Exp +import Mathlib.LinearAlgebra.Matrix.Determinant.Basic +import Mathlib.Tactic.Positivity +import Mathlib.Tactic.Ring import Mathlib.Topology.Instances.Matrix -import RealRooted.Mathlib.LinearAlgebra.Matrix.SignRegular /-! # Karlin's finite Gaussian matrices @@ -84,13 +86,13 @@ theorem tendsto_gaussianMatrix_atTop (n : ℕ) : · simp [hij] /-- The finite restriction of Karlin's exponential kernel `exp (x * y)`. -/ -noncomputable def exponentialKernelMatrix {q : ℕ} - (x y : Fin q → ℝ) : Matrix (Fin q) (Fin q) ℝ := +noncomputable def exponentialKernelMatrix {i j : Type*} + (x : i → ℝ) (y : j → ℝ) : Matrix i j ℝ := fun i j => Real.exp (x i * y j) -@[simp] lemma exponentialKernelMatrix_apply {q : ℕ} - (x y : Fin q → ℝ) (i j : Fin q) : - exponentialKernelMatrix x y i j = Real.exp (x i * y j) := +@[simp] lemma exponentialKernelMatrix_apply {i j : Type*} + (x : i → ℝ) (y : j → ℝ) (r : i) (c : j) : + exponentialKernelMatrix x y r c = Real.exp (x r * y c) := rfl /-- A Gaussian minor is an exponential-kernel minor times positive row and From ab5ee59eafef494649cb12a2c0c6ea6644a06450 Mon Sep 17 00:00:00 2001 From: Per Alexandersson Date: Sun, 2 Aug 2026 20:58:23 +0000 Subject: [PATCH 008/141] Prove exponential Wronskian positivity --- RealRooted.lean | 1 + .../LinearAlgebra/Matrix/Gaussian.lean | 90 ++++++++++++++++++- .../Mathlib/LinearAlgebra/Vandermonde.lean | 27 ++++++ 3 files changed, 117 insertions(+), 1 deletion(-) create mode 100644 RealRooted/Mathlib/LinearAlgebra/Vandermonde.lean diff --git a/RealRooted.lean b/RealRooted.lean index a0ba8e30..cc5e851f 100644 --- a/RealRooted.lean +++ b/RealRooted.lean @@ -208,6 +208,7 @@ import RealRooted.Mathlib.LinearAlgebra.Matrix.SignRegular import RealRooted.Mathlib.LinearAlgebra.Matrix.SignVariation import RealRooted.Mathlib.LinearAlgebra.Matrix.TotallyNonneg import RealRooted.Mathlib.LinearAlgebra.Matrix.VariationDiminishing +import RealRooted.Mathlib.LinearAlgebra.Vandermonde import RealRooted.MatrixInterlacing import RealRooted.MultiaffineReciprocalRight import RealRooted.Multiaffine diff --git a/RealRooted/Mathlib/LinearAlgebra/Matrix/Gaussian.lean b/RealRooted/Mathlib/LinearAlgebra/Matrix/Gaussian.lean index 2f348f49..aaceb220 100644 --- a/RealRooted/Mathlib/LinearAlgebra/Matrix/Gaussian.lean +++ b/RealRooted/Mathlib/LinearAlgebra/Matrix/Gaussian.lean @@ -1,8 +1,9 @@ -import Mathlib.Analysis.SpecialFunctions.Exp +import Mathlib.Analysis.SpecialFunctions.ExpDeriv import Mathlib.LinearAlgebra.Matrix.Determinant.Basic import Mathlib.Tactic.Positivity import Mathlib.Tactic.Ring import Mathlib.Topology.Instances.Matrix +import RealRooted.Mathlib.LinearAlgebra.Vandermonde /-! # Karlin's finite Gaussian matrices @@ -95,6 +96,93 @@ noncomputable def exponentialKernelMatrix {i j : Type*} exponentialKernelMatrix x y r c = Real.exp (x r * y c) := rfl +/-- Translating all first coordinates multiplies the exponential-kernel +determinant by an explicit positive column factor. -/ +theorem det_exponentialKernelMatrix_add_const_left {q : ℕ} + (x y : Fin q → ℝ) (c : ℝ) : + (exponentialKernelMatrix (fun i => x i + c) y).det = + (∏ j, Real.exp (c * y j)) * + (exponentialKernelMatrix x y).det := by + have hmatrix : + exponentialKernelMatrix (fun i => x i + c) y = + of fun i j => + Real.exp (c * y j) * exponentialKernelMatrix x y i j := by + ext i j + simp only [exponentialKernelMatrix_apply, of_apply] + rw [show (x i + c) * y j = c * y j + x i * y j by ring, + Real.exp_add] + rw [hmatrix, det_mul_row] + +/-- Translating all second coordinates multiplies the exponential-kernel +determinant by an explicit positive row factor. -/ +theorem det_exponentialKernelMatrix_add_const_right {q : ℕ} + (x y : Fin q → ℝ) (c : ℝ) : + (exponentialKernelMatrix x (fun j => y j + c)).det = + (∏ i, Real.exp (x i * c)) * + (exponentialKernelMatrix x y).det := by + have hmatrix : + exponentialKernelMatrix x (fun j => y j + c) = + of fun i j => + Real.exp (x i * c) * exponentialKernelMatrix x y i j := by + ext i j + simp only [exponentialKernelMatrix_apply, of_apply] + rw [show x i * (y j + c) = x i * c + x i * y j by ring, + Real.exp_add] + rw [hmatrix, det_mul_column] + +theorem det_exponentialKernelMatrix_add_const_left_pos_iff {q : ℕ} + (x y : Fin q → ℝ) (c : ℝ) : + 0 < (exponentialKernelMatrix (fun i => x i + c) y).det ↔ + 0 < (exponentialKernelMatrix x y).det := by + rw [det_exponentialKernelMatrix_add_const_left] + exact mul_pos_iff_of_pos_left (by positivity) + +theorem det_exponentialKernelMatrix_add_const_right_pos_iff {q : ℕ} + (x y : Fin q → ℝ) (c : ℝ) : + 0 < (exponentialKernelMatrix x (fun j => y j + c)).det ↔ + 0 < (exponentialKernelMatrix x y).det := by + rw [det_exponentialKernelMatrix_add_const_right] + exact mul_pos_iff_of_pos_left (by positivity) + +/-- The transpose of the Wronskian matrix of the functions +`t ↦ exp (y i * t)`. -/ +noncomputable def exponentialWronskianMatrix {q : ℕ} + (y : Fin q → ℝ) (t : ℝ) : Matrix (Fin q) (Fin q) ℝ := + fun i j => iteratedDeriv (j : ℕ) (fun s => Real.exp (y i * s)) t + +@[simp] lemma exponentialWronskianMatrix_apply {q : ℕ} + (y : Fin q → ℝ) (t : ℝ) (i j : Fin q) : + exponentialWronskianMatrix y t i j = + y i ^ (j : ℕ) * Real.exp (t * y i) := by + rw [exponentialWronskianMatrix, + congrFun (iteratedDeriv_exp_const_mul (j : ℕ) (y i)) t] + congr 2 + exact mul_comm _ _ + +/-- Karlin's exponential Wronskian is a positive exponential factor times a +Vandermonde determinant. -/ +theorem det_exponentialWronskianMatrix_eq {q : ℕ} + (y : Fin q → ℝ) (t : ℝ) : + (exponentialWronskianMatrix y t).det = + (∏ i, Real.exp (t * y i)) * (vandermonde y).det := by + have hmatrix : + exponentialWronskianMatrix y t = + of fun i j => + Real.exp (t * y i) * vandermonde y i j := by + ext i j + simp only [exponentialWronskianMatrix_apply, of_apply, + vandermonde_apply] + ring + rw [hmatrix, det_mul_column] + +/-- The exponential Wronskian has the positive orientation required in +Karlin's extended-determinant argument. -/ +theorem det_exponentialWronskianMatrix_pos {q : ℕ} + {y : Fin q → ℝ} (hy : StrictMono y) (t : ℝ) : + 0 < (exponentialWronskianMatrix y t).det := by + rw [det_exponentialWronskianMatrix_eq] + exact mul_pos (by positivity) (det_vandermonde_pos_of_strictMono hy) + /-- A Gaussian minor is an exponential-kernel minor times positive row and column factors. This is the algebraic reduction in Karlin's proof of Proposition V.1.1. -/ diff --git a/RealRooted/Mathlib/LinearAlgebra/Vandermonde.lean b/RealRooted/Mathlib/LinearAlgebra/Vandermonde.lean new file mode 100644 index 00000000..34a02cb5 --- /dev/null +++ b/RealRooted/Mathlib/LinearAlgebra/Vandermonde.lean @@ -0,0 +1,27 @@ +import Mathlib.LinearAlgebra.Vandermonde + +/-! +# Ordered Vandermonde determinants + +This file adds the strict positivity consequence of the Vandermonde +determinant formula for a strictly increasing family. +-/ + +public section + +namespace Matrix + +variable {R : Type*} [CommRing R] [LinearOrder R] [IsStrictOrderedRing R] + +/-- A Vandermonde determinant on a strictly increasing family is positive. -/ +theorem det_vandermonde_pos_of_strictMono {q : ℕ} + {y : Fin q → R} (hy : StrictMono y) : + 0 < (vandermonde y).det := by + rw [det_vandermonde] + apply Finset.prod_pos + intro i hi + apply Finset.prod_pos + intro j hj + exact sub_pos.mpr (hy (Finset.mem_Ioi.mp hj)) + +end Matrix From bace71e9359260fb335fce6644ef948496197ed5 Mon Sep 17 00:00:00 2001 From: Per Alexandersson Date: Sun, 2 Aug 2026 21:04:00 +0000 Subject: [PATCH 009/141] Add adjacent-row determinant reduction --- .../Matrix/Determinant/Basic.lean | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/RealRooted/Mathlib/LinearAlgebra/Matrix/Determinant/Basic.lean b/RealRooted/Mathlib/LinearAlgebra/Matrix/Determinant/Basic.lean index a3cf55b2..90584009 100644 --- a/RealRooted/Mathlib/LinearAlgebra/Matrix/Determinant/Basic.lean +++ b/RealRooted/Mathlib/LinearAlgebra/Matrix/Determinant/Basic.lean @@ -33,4 +33,26 @@ theorem transpose_mulVec_alternating_det_submatrix_succAbove {q : ℕ} simp_rw [hminor] at hdet simpa [mulVec, dotProduct, A, mul_comm, mul_left_comm, mul_assoc] using hdet +/-- Expanding a matrix with constant first column after adjacent row subtraction. -/ +theorem det_eq_det_adjacentRowDiff_of_firstColumn_eq_one {n : ℕ} + (A : Matrix (Fin (n + 1)) (Fin (n + 1)) R) + (hA : ∀ i, A i 0 = 1) : + A.det = + (Matrix.of fun (i j : Fin n) => + A i.succ j.succ - A i.castSucc j.succ).det := by + let B : Matrix (Fin (n + 1)) (Fin (n + 1)) R := + fun i j => Fin.cases (A 0 j) + (fun k => A k.succ j - A k.castSucc j) i + have hdet : A.det = B.det := by + apply det_eq_of_forall_row_eq_smul_add_pred (fun _ => 1) + · intro j + simp [B] + · intro i j + simp [B] + rw [hdet, det_succ_column_zero, Fin.sum_univ_succ] + simp [B, hA] + apply congrArg det + ext i j + rfl + end Matrix From bd2b50b2bb3c8c93e629ec6163451d69fc08da21 Mon Sep 17 00:00:00 2001 From: Per Alexandersson Date: Sun, 2 Aug 2026 21:06:59 +0000 Subject: [PATCH 010/141] Deduplicate transpose minor proofs --- .../LinearAlgebra/Matrix/SignRegular.lean | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/RealRooted/Mathlib/LinearAlgebra/Matrix/SignRegular.lean b/RealRooted/Mathlib/LinearAlgebra/Matrix/SignRegular.lean index 64a77476..11a7dbea 100644 --- a/RealRooted/Mathlib/LinearAlgebra/Matrix/SignRegular.lean +++ b/RealRooted/Mathlib/LinearAlgebra/Matrix/SignRegular.lean @@ -54,23 +54,23 @@ protected lemma IsStrictlySignConsistentOrder.submatrix simpa using hM (hrows.comp hrows₁) (hrows.comp hrows₂) (hcols.comp hcols₁) (hcols.comp hcols₂) +omit [PartialOrder R] [Preorder ι] [Preorder κ] in +private lemma det_transpose_submatrix + (M : Matrix ι κ R) {q : ℕ} (rows : Fin q → κ) (cols : Fin q → ι) : + (M.transpose.submatrix rows cols).det = (M.submatrix cols rows).det := by + rw [← Matrix.det_transpose (M.submatrix cols rows), Matrix.transpose_submatrix] + protected lemma IsSignConsistentOrder.transpose {M : Matrix ι κ R} {q : ℕ} (hM : M.IsSignConsistentOrder q) : M.transpose.IsSignConsistentOrder q := by intro rows rows' cols cols' hrows hrows' hcols hcols' - have hdet (r : Fin q → κ) (c : Fin q → ι) : - (M.transpose.submatrix r c).det = (M.submatrix c r).det := by - rw [← Matrix.det_transpose (M.submatrix c r), Matrix.transpose_submatrix] - rw [hdet rows cols, hdet rows' cols'] + rw [det_transpose_submatrix M rows cols, det_transpose_submatrix M rows' cols'] exact hM hcols hcols' hrows hrows' protected lemma IsStrictlySignConsistentOrder.transpose {M : Matrix ι κ R} {q : ℕ} (hM : M.IsStrictlySignConsistentOrder q) : M.transpose.IsStrictlySignConsistentOrder q := by intro rows rows' cols cols' hrows hrows' hcols hcols' - have hdet (r : Fin q → κ) (c : Fin q → ι) : - (M.transpose.submatrix r c).det = (M.submatrix c r).det := by - rw [← Matrix.det_transpose (M.submatrix c r), Matrix.transpose_submatrix] - rw [hdet rows cols, hdet rows' cols'] + rw [det_transpose_submatrix M rows cols, det_transpose_submatrix M rows' cols'] exact hM hcols hcols' hrows hrows' protected lemma IsSignRegular.submatrix From 5a81d7dc08aa2d30a7e9110d8f5cc163effcbc73 Mon Sep 17 00:00:00 2001 From: Per Alexandersson Date: Sun, 2 Aug 2026 21:10:59 +0000 Subject: [PATCH 011/141] Add exponential difference integral identity --- .../SpecialFunctions/ExpIntegral.lean | 30 +++++++++++++++++++ .../LinearAlgebra/Matrix/Gaussian.lean | 2 +- 2 files changed, 31 insertions(+), 1 deletion(-) create mode 100644 RealRooted/Mathlib/Analysis/SpecialFunctions/ExpIntegral.lean diff --git a/RealRooted/Mathlib/Analysis/SpecialFunctions/ExpIntegral.lean b/RealRooted/Mathlib/Analysis/SpecialFunctions/ExpIntegral.lean new file mode 100644 index 00000000..2a929393 --- /dev/null +++ b/RealRooted/Mathlib/Analysis/SpecialFunctions/ExpIntegral.lean @@ -0,0 +1,30 @@ +module + +public import Mathlib.Analysis.SpecialFunctions.ExpDeriv +public import Mathlib.MeasureTheory.Integral.IntervalIntegral.FundThmCalculus + +/-! +# Interval integrals of exponential functions + +This file contains a fundamental-theorem-of-calculus identity for exponential +differences. +-/ + +public section + +open scoped Interval + +namespace Real + +/-- The integral form of the adjacent-difference identity for the exponential kernel. -/ +theorem intervalIntegral_mul_exp_mul (a b z : ℝ) : + (∫ t in a..b, z * exp (t * z)) = exp (b * z) - exp (a * z) := by + apply intervalIntegral.integral_eq_sub_of_hasDerivAt + · intro t _ + simpa [mul_comm] using + (Real.hasDerivAt_exp (t * z)).comp t ((hasDerivAt_id t).mul_const z) + · exact + (continuous_const.mul + (Real.continuous_exp.comp (continuous_id.mul continuous_const))).intervalIntegrable _ _ + +end Real diff --git a/RealRooted/Mathlib/LinearAlgebra/Matrix/Gaussian.lean b/RealRooted/Mathlib/LinearAlgebra/Matrix/Gaussian.lean index aaceb220..1f873d8e 100644 --- a/RealRooted/Mathlib/LinearAlgebra/Matrix/Gaussian.lean +++ b/RealRooted/Mathlib/LinearAlgebra/Matrix/Gaussian.lean @@ -1,4 +1,4 @@ -import Mathlib.Analysis.SpecialFunctions.ExpDeriv +import RealRooted.Mathlib.Analysis.SpecialFunctions.ExpIntegral import Mathlib.LinearAlgebra.Matrix.Determinant.Basic import Mathlib.Tactic.Positivity import Mathlib.Tactic.Ring From 81c45c34410aa1316df7d69722d5f562876078cb Mon Sep 17 00:00:00 2001 From: Per Alexandersson Date: Sun, 2 Aug 2026 21:14:01 +0000 Subject: [PATCH 012/141] Commute one determinant row with interval integrals --- .../Matrix/Determinant/Integral.lean | 38 +++++++++++++++++++ .../LinearAlgebra/Matrix/Gaussian.lean | 1 + 2 files changed, 39 insertions(+) create mode 100644 RealRooted/Mathlib/LinearAlgebra/Matrix/Determinant/Integral.lean diff --git a/RealRooted/Mathlib/LinearAlgebra/Matrix/Determinant/Integral.lean b/RealRooted/Mathlib/LinearAlgebra/Matrix/Determinant/Integral.lean new file mode 100644 index 00000000..addeb6ae --- /dev/null +++ b/RealRooted/Mathlib/LinearAlgebra/Matrix/Determinant/Integral.lean @@ -0,0 +1,38 @@ +module + +public import Mathlib.LinearAlgebra.Matrix.Determinant.Basic +public import Mathlib.MeasureTheory.Integral.IntervalIntegral.Basic +public import Mathlib.Topology.Algebra.Module.FiniteDimension + +/-! +# Determinants and interval integrals + +This file records how determinant multilinearity interacts with interval +integrals in one matrix row. +-/ + +public section + +open scoped Interval + +namespace Matrix + +private noncomputable def detUpdateRowLinearMap + {n : Type*} [DecidableEq n] [Fintype n] (M : Matrix n n ℝ) (i : n) : + (n → ℝ) →ₗ[ℝ] ℝ := + { toFun := fun row => (M.updateRow i row).det + map_add' := fun u v => det_updateRow_add M i u v + map_smul' := fun c u => by + simpa only [smul_eq_mul] using det_updateRow_smul M i c u } + +/-- A determinant commutes with an interval integral in one fixed row. -/ +theorem det_updateRow_intervalIntegral + {n : Type*} [DecidableEq n] [Fintype n] (M : Matrix n n ℝ) (i : n) + (f : ℝ → n → ℝ) (a b : ℝ) + (hf : IntervalIntegrable f MeasureTheory.volume a b) : + (M.updateRow i (∫ t in a..b, f t)).det = + ∫ t in a..b, (M.updateRow i (f t)).det := by + let L := (detUpdateRowLinearMap M i).toContinuousLinearMap + exact (L.intervalIntegral_comp_comm hf).symm + +end Matrix diff --git a/RealRooted/Mathlib/LinearAlgebra/Matrix/Gaussian.lean b/RealRooted/Mathlib/LinearAlgebra/Matrix/Gaussian.lean index 1f873d8e..c74c784c 100644 --- a/RealRooted/Mathlib/LinearAlgebra/Matrix/Gaussian.lean +++ b/RealRooted/Mathlib/LinearAlgebra/Matrix/Gaussian.lean @@ -1,4 +1,5 @@ import RealRooted.Mathlib.Analysis.SpecialFunctions.ExpIntegral +import RealRooted.Mathlib.LinearAlgebra.Matrix.Determinant.Integral import Mathlib.LinearAlgebra.Matrix.Determinant.Basic import Mathlib.Tactic.Positivity import Mathlib.Tactic.Ring From 4c63332a976a800ad841da32102bfb9609b1f52f Mon Sep 17 00:00:00 2001 From: Per Alexandersson Date: Sun, 2 Aug 2026 21:15:34 +0000 Subject: [PATCH 013/141] Deduplicate Gaussian determinant imports --- RealRooted/Mathlib/LinearAlgebra/Matrix/Gaussian.lean | 1 - 1 file changed, 1 deletion(-) diff --git a/RealRooted/Mathlib/LinearAlgebra/Matrix/Gaussian.lean b/RealRooted/Mathlib/LinearAlgebra/Matrix/Gaussian.lean index c74c784c..f8ce8ff5 100644 --- a/RealRooted/Mathlib/LinearAlgebra/Matrix/Gaussian.lean +++ b/RealRooted/Mathlib/LinearAlgebra/Matrix/Gaussian.lean @@ -1,6 +1,5 @@ import RealRooted.Mathlib.Analysis.SpecialFunctions.ExpIntegral import RealRooted.Mathlib.LinearAlgebra.Matrix.Determinant.Integral -import Mathlib.LinearAlgebra.Matrix.Determinant.Basic import Mathlib.Tactic.Positivity import Mathlib.Tactic.Ring import Mathlib.Topology.Instances.Matrix From 1ce350ea796c3e9fae303dc634bd3b78f42ec6ce Mon Sep 17 00:00:00 2001 From: Per Alexandersson Date: Sun, 2 Aug 2026 21:18:35 +0000 Subject: [PATCH 014/141] Bridge exponential row differences to integrals --- RealRooted/Mathlib/LinearAlgebra/Matrix/Gaussian.lean | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/RealRooted/Mathlib/LinearAlgebra/Matrix/Gaussian.lean b/RealRooted/Mathlib/LinearAlgebra/Matrix/Gaussian.lean index f8ce8ff5..15d3b0c8 100644 --- a/RealRooted/Mathlib/LinearAlgebra/Matrix/Gaussian.lean +++ b/RealRooted/Mathlib/LinearAlgebra/Matrix/Gaussian.lean @@ -22,6 +22,8 @@ it is intentionally not assumed here. public section +open scoped Interval + open Filter Topology namespace Matrix @@ -96,6 +98,15 @@ noncomputable def exponentialKernelMatrix {i j : Type*} exponentialKernelMatrix x y r c = Real.exp (x r * y c) := rfl +/-- Adjacent exponential-kernel row differences are interval integrals. -/ +lemma exponentialKernelMatrix_succ_sub_castSucc_eq_intervalIntegral {n : ℕ} + (x : Fin (n + 1) → ℝ) (y : Fin n → ℝ) (i j : Fin n) : + exponentialKernelMatrix x y i.succ j - + exponentialKernelMatrix x y i.castSucc j = + ∫ t in x i.castSucc..x i.succ, y j * Real.exp (t * y j) := by + rw [Real.intervalIntegral_mul_exp_mul] + rfl + /-- Translating all first coordinates multiplies the exponential-kernel determinant by an explicit positive column factor. -/ theorem det_exponentialKernelMatrix_add_const_left {q : ℕ} From 6efed9bd342ae0b7c0932ab44fef94fd75f96254 Mon Sep 17 00:00:00 2001 From: Per Alexandersson Date: Sun, 2 Aug 2026 21:23:44 +0000 Subject: [PATCH 015/141] Integrate determinants over row products --- .../Matrix/Determinant/Integral.lean | 49 +++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/RealRooted/Mathlib/LinearAlgebra/Matrix/Determinant/Integral.lean b/RealRooted/Mathlib/LinearAlgebra/Matrix/Determinant/Integral.lean index addeb6ae..5e6f3a51 100644 --- a/RealRooted/Mathlib/LinearAlgebra/Matrix/Determinant/Integral.lean +++ b/RealRooted/Mathlib/LinearAlgebra/Matrix/Determinant/Integral.lean @@ -2,6 +2,7 @@ module public import Mathlib.LinearAlgebra.Matrix.Determinant.Basic public import Mathlib.MeasureTheory.Integral.IntervalIntegral.Basic +public import Mathlib.MeasureTheory.Integral.Pi public import Mathlib.Topology.Algebra.Module.FiniteDimension /-! @@ -35,4 +36,52 @@ theorem det_updateRow_intervalIntegral let L := (detUpdateRowLinearMap M i).toContinuousLinearMap exact (L.intervalIntegral_comp_comm hf).symm +/-- The determinant of rowwise integrals is the integral of the pointwise determinant. -/ +theorem det_integral_rows_eq_integral_det + {n E : Type*} [DecidableEq n] [Fintype n] [MeasurableSpace E] + (μ : n → MeasureTheory.Measure E) [∀ i, MeasureTheory.SigmaFinite (μ i)] + (f : n → E → n → ℝ) + (hf : ∀ i j, MeasureTheory.Integrable (fun x => f i x j) (μ i)) : + (Matrix.of fun i j => ∫ x, f i x j ∂μ i).det = + ∫ x : n → E, + (Matrix.of fun i j => f i (x i) j).det ∂MeasureTheory.Measure.pi μ := by + have hprod (σ : Equiv.Perm n) : + MeasureTheory.Integrable + (fun x : n → E => ∏ i, f i (x i) (σ i)) (MeasureTheory.Measure.pi μ) := + MeasureTheory.Integrable.fintype_prod fun i => hf i (σ i) + calc + (Matrix.of fun i j => ∫ x, f i x j ∂μ i).det = + ∑ σ : Equiv.Perm n, ((Equiv.Perm.sign σ : ℤ) : ℝ) * + ∏ i, ∫ x, f i x (σ i) ∂μ i := by + rw [← Matrix.det_transpose, Matrix.det_apply] + simp_rw [Units.smul_def, ← Int.cast_smul_eq_zsmul ℝ] + rfl + _ = ∑ σ : Equiv.Perm n, ((Equiv.Perm.sign σ : ℤ) : ℝ) * + ∫ x : n → E, ∏ i, f i (x i) (σ i) ∂MeasureTheory.Measure.pi μ := by + apply Finset.sum_congr rfl + intro σ _ + apply congrArg (((Equiv.Perm.sign σ : ℤ) : ℝ) * ·) + exact (MeasureTheory.integral_fintype_prod_eq_prod + (fun i x => f i x (σ i))).symm + _ = ∑ σ : Equiv.Perm n, + ∫ x : n → E, ((Equiv.Perm.sign σ : ℤ) : ℝ) * + ∏ i, f i (x i) (σ i) ∂MeasureTheory.Measure.pi μ := by + apply Finset.sum_congr rfl + intro σ _ + rw [MeasureTheory.integral_const_mul] + _ = ∫ x : n → E, ∑ σ : Equiv.Perm n, + ((Equiv.Perm.sign σ : ℤ) : ℝ) * + ∏ i, f i (x i) (σ i) ∂MeasureTheory.Measure.pi μ := by + symm + apply MeasureTheory.integral_finset_sum + intro σ _ + exact (hprod σ).const_mul _ + _ = ∫ x : n → E, + (Matrix.of fun i j => f i (x i) j).det ∂MeasureTheory.Measure.pi μ := by + apply congrArg fun g : (n → E) → ℝ => ∫ x, g x ∂MeasureTheory.Measure.pi μ + funext x + rw [← Matrix.det_transpose, Matrix.det_apply] + simp_rw [Units.smul_def, ← Int.cast_smul_eq_zsmul ℝ] + rfl + end Matrix From 86dcb14e5b56b9f552a46bf6afbc9abaf2a15019 Mon Sep 17 00:00:00 2001 From: Per Alexandersson Date: Sun, 2 Aug 2026 21:25:25 +0000 Subject: [PATCH 016/141] Deduplicate row determinant expansions --- .../LinearAlgebra/Matrix/Determinant/Basic.lean | 6 ++++++ .../Matrix/Determinant/Integral.lean | 16 +++++++++------- 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/RealRooted/Mathlib/LinearAlgebra/Matrix/Determinant/Basic.lean b/RealRooted/Mathlib/LinearAlgebra/Matrix/Determinant/Basic.lean index 90584009..3c9794a1 100644 --- a/RealRooted/Mathlib/LinearAlgebra/Matrix/Determinant/Basic.lean +++ b/RealRooted/Mathlib/LinearAlgebra/Matrix/Determinant/Basic.lean @@ -10,6 +10,12 @@ variable {n R : Type*} [DecidableEq n] [Fintype n] [CommRing R] -- TODO: Replace `det_zero` @[simp] lemma det_zero' [Nonempty n] : (0 : Matrix n n R).det = 0 := det_zero ‹_› +/-- The Leibniz formula for a determinant, with rows indexed before columns. -/ +theorem det_apply_row (M : Matrix n n R) : + M.det = ∑ σ : Equiv.Perm n, Equiv.Perm.sign σ • ∏ i, M i (σ i) := by + rw [← Matrix.det_transpose, Matrix.det_apply] + rfl + /-- The alternating vector of maximal row-deletion minors of a rectangular matrix lies in the kernel of its transpose. This is the Laplace expansion of the matrix obtained by adjoining a duplicate of any chosen column. -/ diff --git a/RealRooted/Mathlib/LinearAlgebra/Matrix/Determinant/Integral.lean b/RealRooted/Mathlib/LinearAlgebra/Matrix/Determinant/Integral.lean index 5e6f3a51..b2d97ba7 100644 --- a/RealRooted/Mathlib/LinearAlgebra/Matrix/Determinant/Integral.lean +++ b/RealRooted/Mathlib/LinearAlgebra/Matrix/Determinant/Integral.lean @@ -45,6 +45,12 @@ theorem det_integral_rows_eq_integral_det (Matrix.of fun i j => ∫ x, f i x j ∂μ i).det = ∫ x : n → E, (Matrix.of fun i j => f i (x i) j).det ∂MeasureTheory.Measure.pi μ := by + have hdet (M : Matrix n n ℝ) : + M.det = ∑ σ : Equiv.Perm n, ((Equiv.Perm.sign σ : ℤ) : ℝ) * + ∏ i, M i (σ i) := by + rw [Matrix.det_apply_row] + simp_rw [Units.smul_def, ← Int.cast_smul_eq_zsmul ℝ] + rfl have hprod (σ : Equiv.Perm n) : MeasureTheory.Integrable (fun x : n → E => ∏ i, f i (x i) (σ i)) (MeasureTheory.Measure.pi μ) := @@ -52,10 +58,8 @@ theorem det_integral_rows_eq_integral_det calc (Matrix.of fun i j => ∫ x, f i x j ∂μ i).det = ∑ σ : Equiv.Perm n, ((Equiv.Perm.sign σ : ℤ) : ℝ) * - ∏ i, ∫ x, f i x (σ i) ∂μ i := by - rw [← Matrix.det_transpose, Matrix.det_apply] - simp_rw [Units.smul_def, ← Int.cast_smul_eq_zsmul ℝ] - rfl + ∏ i, ∫ x, f i x (σ i) ∂μ i := + hdet _ _ = ∑ σ : Equiv.Perm n, ((Equiv.Perm.sign σ : ℤ) : ℝ) * ∫ x : n → E, ∏ i, f i (x i) (σ i) ∂MeasureTheory.Measure.pi μ := by apply Finset.sum_congr rfl @@ -80,8 +84,6 @@ theorem det_integral_rows_eq_integral_det (Matrix.of fun i j => f i (x i) j).det ∂MeasureTheory.Measure.pi μ := by apply congrArg fun g : (n → E) → ℝ => ∫ x, g x ∂MeasureTheory.Measure.pi μ funext x - rw [← Matrix.det_transpose, Matrix.det_apply] - simp_rw [Units.smul_def, ← Int.cast_smul_eq_zsmul ℝ] - rfl + exact (hdet _).symm end Matrix From 38d1156757851adc04a3061cdf6f8397e813feba Mon Sep 17 00:00:00 2001 From: Per Alexandersson Date: Sun, 2 Aug 2026 21:27:03 +0000 Subject: [PATCH 017/141] Prove product determinant integrability --- .../Matrix/Determinant/Integral.lean | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/RealRooted/Mathlib/LinearAlgebra/Matrix/Determinant/Integral.lean b/RealRooted/Mathlib/LinearAlgebra/Matrix/Determinant/Integral.lean index b2d97ba7..5f411417 100644 --- a/RealRooted/Mathlib/LinearAlgebra/Matrix/Determinant/Integral.lean +++ b/RealRooted/Mathlib/LinearAlgebra/Matrix/Determinant/Integral.lean @@ -36,6 +36,28 @@ theorem det_updateRow_intervalIntegral let L := (detUpdateRowLinearMap M i).toContinuousLinearMap exact (L.intervalIntegral_comp_comm hf).symm +/-- A pointwise determinant is integrable under a product measure when every entry is. -/ +theorem integrable_det_pi + {n E : Type*} [DecidableEq n] [Fintype n] [MeasurableSpace E] + (μ : n → MeasureTheory.Measure E) [∀ i, MeasureTheory.SigmaFinite (μ i)] + (f : n → E → n → ℝ) + (hf : ∀ i j, MeasureTheory.Integrable (fun x => f i x j) (μ i)) : + MeasureTheory.Integrable + (fun x : n → E => (Matrix.of fun i j => f i (x i) j).det) + (MeasureTheory.Measure.pi μ) := by + have hfun : + (fun x : n → E => (Matrix.of fun i j => f i (x i) j).det) = + fun x => ∑ σ : Equiv.Perm n, ((Equiv.Perm.sign σ : ℤ) : ℝ) * + ∏ i, f i (x i) (σ i) := by + funext x + rw [Matrix.det_apply_row] + simp_rw [Units.smul_def, ← Int.cast_smul_eq_zsmul ℝ] + rfl + rw [hfun] + apply MeasureTheory.integrable_finset_sum Finset.univ + intro σ _ + exact (MeasureTheory.Integrable.fintype_prod fun i => hf i (σ i)).const_mul _ + /-- The determinant of rowwise integrals is the integral of the pointwise determinant. -/ theorem det_integral_rows_eq_integral_det {n E : Type*} [DecidableEq n] [Fintype n] [MeasurableSpace E] From 517840879b3c9262663ca4a758228a09d63261dc Mon Sep 17 00:00:00 2001 From: Per Alexandersson Date: Sun, 2 Aug 2026 21:29:45 +0000 Subject: [PATCH 018/141] Specialize determinant integrals to adjacent intervals --- .../LinearAlgebra/Matrix/Gaussian.lean | 36 ++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/RealRooted/Mathlib/LinearAlgebra/Matrix/Gaussian.lean b/RealRooted/Mathlib/LinearAlgebra/Matrix/Gaussian.lean index 15d3b0c8..db90d481 100644 --- a/RealRooted/Mathlib/LinearAlgebra/Matrix/Gaussian.lean +++ b/RealRooted/Mathlib/LinearAlgebra/Matrix/Gaussian.lean @@ -24,7 +24,7 @@ public section open scoped Interval -open Filter Topology +open Filter MeasureTheory Topology namespace Matrix @@ -107,6 +107,40 @@ lemma exponentialKernelMatrix_succ_sub_castSucc_eq_intervalIntegral {n : ℕ} rw [Real.intervalIntegral_mul_exp_mul] rfl +/-- The adjacent-difference determinant is a restricted-volume product integral. -/ +theorem det_adjacentRowDiff_exponentialKernelMatrix_eq_integral {n : ℕ} + (x : Fin (n + 1) → ℝ) (y : Fin n → ℝ) (hx : StrictMono x) : + (Matrix.of fun i j => + exponentialKernelMatrix x y i.succ j - + exponentialKernelMatrix x y i.castSucc j).det = + ∫ t : Fin n → ℝ, + (Matrix.of fun i j => y j * Real.exp (t i * y j)).det + ∂Measure.pi (fun i => + volume.restrict (Set.Ioc (x i.castSucc) (x i.succ))) := by + let μ : Fin n → Measure ℝ := fun i => + volume.restrict (Set.Ioc (x i.castSucc) (x i.succ)) + let f : Fin n → ℝ → Fin n → ℝ := fun _ t j => + y j * Real.exp (t * y j) + have hf : ∀ i j, Integrable (fun t => f i t j) (μ i) := by + intro i j + change IntegrableOn (fun t => f i t j) + (Set.Ioc (x i.castSucc) (x i.succ)) volume + rw [← intervalIntegrable_iff_integrableOn_Ioc_of_le + (hx i.castSucc_lt_succ).le] + exact + (continuous_const.mul + (Real.continuous_exp.comp (continuous_id.mul continuous_const))).intervalIntegrable _ _ + have hmatrix : + Matrix.of (fun i j => + exponentialKernelMatrix x y i.succ j - + exponentialKernelMatrix x y i.castSucc j) = + Matrix.of fun i j => ∫ t, f i t j ∂μ i := by + ext i j + simp only [Matrix.of_apply] + rw [exponentialKernelMatrix_succ_sub_castSucc_eq_intervalIntegral] + rw [intervalIntegral.integral_of_le (hx i.castSucc_lt_succ).le] + rw [hmatrix, det_integral_rows_eq_integral_det μ f hf] + /-- Translating all first coordinates multiplies the exponential-kernel determinant by an explicit positive column factor. -/ theorem det_exponentialKernelMatrix_add_const_left {q : ℕ} From 07572843b7b9a6098ab080df49106c0ffb79ed59 Mon Sep 17 00:00:00 2001 From: Per Alexandersson Date: Sun, 2 Aug 2026 21:30:43 +0000 Subject: [PATCH 019/141] Clarify row determinant integrability name --- .../Mathlib/LinearAlgebra/Matrix/Determinant/Integral.lean | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RealRooted/Mathlib/LinearAlgebra/Matrix/Determinant/Integral.lean b/RealRooted/Mathlib/LinearAlgebra/Matrix/Determinant/Integral.lean index 5f411417..a3197dd0 100644 --- a/RealRooted/Mathlib/LinearAlgebra/Matrix/Determinant/Integral.lean +++ b/RealRooted/Mathlib/LinearAlgebra/Matrix/Determinant/Integral.lean @@ -37,7 +37,7 @@ theorem det_updateRow_intervalIntegral exact (L.intervalIntegral_comp_comm hf).symm /-- A pointwise determinant is integrable under a product measure when every entry is. -/ -theorem integrable_det_pi +theorem integrable_det_rows {n E : Type*} [DecidableEq n] [Fintype n] [MeasurableSpace E] (μ : n → MeasureTheory.Measure E) [∀ i, MeasureTheory.SigmaFinite (μ i)] (f : n → E → n → ℝ) From 1490ce99167199153ce71536ad9989502e346083 Mon Sep 17 00:00:00 2001 From: Per Alexandersson Date: Sun, 2 Aug 2026 21:36:56 +0000 Subject: [PATCH 020/141] Prove adjacent exponential determinant positivity --- .../LinearAlgebra/Matrix/Gaussian.lean | 62 +++++++++++++++++++ 1 file changed, 62 insertions(+) diff --git a/RealRooted/Mathlib/LinearAlgebra/Matrix/Gaussian.lean b/RealRooted/Mathlib/LinearAlgebra/Matrix/Gaussian.lean index db90d481..f2581c36 100644 --- a/RealRooted/Mathlib/LinearAlgebra/Matrix/Gaussian.lean +++ b/RealRooted/Mathlib/LinearAlgebra/Matrix/Gaussian.lean @@ -290,4 +290,66 @@ theorem det_gaussianMatrix_submatrix_pos_of_exponentialKernel {n q : ℕ} rw [det_gaussianMatrix_submatrix_eq] positivity +/-- The adjacent-difference determinant is positive under the smaller-minor hypothesis. -/ +theorem det_adjacentRowDiff_exponentialKernelMatrix_pos {n : ℕ} + (x : Fin (n + 1) → ℝ) (y : Fin n → ℝ) (hx : StrictMono x) + (hy : ∀ j, 0 < y j) + (hdet : ∀ t : Fin n → ℝ, StrictMono t → + 0 < (exponentialKernelMatrix t y).det) : + 0 < (Matrix.of fun i j => + exponentialKernelMatrix x y i.succ j - + exponentialKernelMatrix x y i.castSucc j).det := by + rw [det_adjacentRowDiff_exponentialKernelMatrix_eq_integral x y hx] + let μ : Fin n → Measure ℝ := fun i => + volume.restrict (Set.Ioc (x i.castSucc) (x i.succ)) + let f : Fin n → ℝ → Fin n → ℝ := fun _ t j => + y j * Real.exp (t * y j) + have hf : ∀ i j, Integrable (fun t => f i t j) (μ i) := by + intro i j + change IntegrableOn (fun t => f i t j) + (Set.Ioc (x i.castSucc) (x i.succ)) volume + rw [← intervalIntegrable_iff_integrableOn_Ioc_of_le + (hx i.castSucc_lt_succ).le] + exact + (continuous_const.mul + (Real.continuous_exp.comp (continuous_id.mul continuous_const))).intervalIntegrable _ _ + have hbox : ∀ᵐ t ∂Measure.pi μ, + ∀ i, t i ∈ Set.Ioc (x i.castSucc) (x i.succ) := by + rw [Filter.eventually_all] + intro i + exact Measure.tendsto_eval_ae_ae.eventually + (ae_restrict_mem measurableSet_Ioc) + have hpoint_pos : ∀ᵐ t ∂Measure.pi μ, + 0 < (Matrix.of fun i j => f i (t i) j).det := by + filter_upwards [hbox] with t ht + have htmono : StrictMono t := by + intro i j hij + have hindex : i.succ ≤ j.castSucc := + Fin.mk_le_mk.mpr (Nat.succ_le_of_lt hij) + exact (ht i).2.trans_lt ((hx.monotone hindex).trans_lt (ht j).1) + rw [show Matrix.of (fun i j => f i (t i) j) = + Matrix.of fun i j => + y j * exponentialKernelMatrix t y i j by + ext i j + rfl, + Matrix.det_mul_row] + exact mul_pos (Finset.prod_pos fun j _ => hy j) (hdet t htmono) + have hdetInt : Integrable + (fun t : Fin n → ℝ => (Matrix.of fun i j => f i (t i) j).det) + (Measure.pi μ) := + integrable_det_rows μ f hf + have hsupp : Function.support + (fun t : Fin n → ℝ => (Matrix.of fun i j => f i (t i) j).det) =ᵐ[Measure.pi μ] + Set.univ := hpoint_pos.mono fun t ht => by + apply propext + change ((Matrix.of fun i j => f i (t i) j).det ≠ 0) ↔ True + exact iff_true_intro ht.ne' + rw [integral_pos_iff_support_of_nonneg_ae + (hpoint_pos.mono fun _ h => h.le) hdetInt, + measure_congr hsupp, Measure.pi_univ] + rw [pos_iff_ne_zero, Finset.prod_ne_zero_iff] + intro i _ + simp [μ, Real.volume_Ioc] + exact hx i.castSucc_lt_succ + end Matrix From 368d7df576738f43856467d5aa4dd27ac6b9495c Mon Sep 17 00:00:00 2001 From: Per Alexandersson Date: Sun, 2 Aug 2026 21:39:58 +0000 Subject: [PATCH 021/141] Prove exponential kernel strict positivity --- RealRooted.lean | 2 ++ .../LinearAlgebra/Matrix/Gaussian.lean | 30 +++++++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/RealRooted.lean b/RealRooted.lean index cc5e851f..3ea4653c 100644 --- a/RealRooted.lean +++ b/RealRooted.lean @@ -195,6 +195,7 @@ import RealRooted.Mathlib.Algebra.Polynomial.Homogenize import RealRooted.Mathlib.Algebra.Polynomial.Roots import RealRooted.Mathlib.Algebra.Polynomial.Splits import RealRooted.Mathlib.Analysis.Complex.OpenMapping +import RealRooted.Mathlib.Analysis.SpecialFunctions.ExpIntegral import RealRooted.Mathlib.Analysis.SpecialFunctions.Trigonometric.Basic import RealRooted.Mathlib.Combinatorics.Enumerative.OrderedSubsetPairs import RealRooted.Mathlib.Data.List.Interleave @@ -202,6 +203,7 @@ import RealRooted.Mathlib.Data.List.Zip import RealRooted.Mathlib.Data.Nat.Cast.Basic import RealRooted.Mathlib.Data.Nat.Choose.Cast import RealRooted.Mathlib.LinearAlgebra.Matrix.Determinant.Basic +import RealRooted.Mathlib.LinearAlgebra.Matrix.Determinant.Integral import RealRooted.Mathlib.LinearAlgebra.Matrix.Gaussian import RealRooted.Mathlib.LinearAlgebra.Matrix.KernelSignVariation import RealRooted.Mathlib.LinearAlgebra.Matrix.SignRegular diff --git a/RealRooted/Mathlib/LinearAlgebra/Matrix/Gaussian.lean b/RealRooted/Mathlib/LinearAlgebra/Matrix/Gaussian.lean index f2581c36..d14394c5 100644 --- a/RealRooted/Mathlib/LinearAlgebra/Matrix/Gaussian.lean +++ b/RealRooted/Mathlib/LinearAlgebra/Matrix/Gaussian.lean @@ -352,4 +352,34 @@ theorem det_adjacentRowDiff_exponentialKernelMatrix_pos {n : ℕ} simp [μ, Real.volume_Ioc] exact hx i.castSucc_lt_succ +/-- Strictly ordered exponential-kernel minors are positive. -/ +theorem det_exponentialKernelMatrix_pos {q : ℕ} + {x y : Fin q → ℝ} (hx : StrictMono x) (hy : StrictMono y) : + 0 < (exponentialKernelMatrix x y).det := by + induction q with + | zero => simp + | succ n ih => + let y0 : Fin (n + 1) → ℝ := fun j => y j - y 0 + have hy0 : StrictMono y0 := fun _ _ hij => + sub_lt_sub_right (hy hij) _ + have hy00 : y0 0 = 0 := by simp [y0] + rw [show y = fun j => y0 j + y 0 by + funext j + simp [y0]] + apply (det_exponentialKernelMatrix_add_const_right_pos_iff + x y0 (y 0)).2 + have hfirst : ∀ i, exponentialKernelMatrix x y0 i 0 = 1 := by + intro i + change Real.exp (x i * y0 0) = 1 + rw [hy00] + simp + rw [det_eq_det_adjacentRowDiff_of_firstColumn_eq_one _ hfirst] + let yTail : Fin n → ℝ := fun j => y0 j.succ + have hyTail : StrictMono yTail := hy0.comp Fin.strictMono_succ + have hyTail_pos : ∀ j, 0 < yTail j := by + intro j + exact hy00 ▸ hy0 (by simp) + exact det_adjacentRowDiff_exponentialKernelMatrix_pos + x yTail hx hyTail_pos (fun t ht => ih ht hyTail) + end Matrix From 5c1033280953c19aca7582ac608ffa34bf11ff29 Mon Sep 17 00:00:00 2001 From: Per Alexandersson Date: Sun, 2 Aug 2026 21:43:54 +0000 Subject: [PATCH 022/141] Golf ordered-box monotonicity proof --- RealRooted/Mathlib/LinearAlgebra/Matrix/Gaussian.lean | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/RealRooted/Mathlib/LinearAlgebra/Matrix/Gaussian.lean b/RealRooted/Mathlib/LinearAlgebra/Matrix/Gaussian.lean index d14394c5..a72c88db 100644 --- a/RealRooted/Mathlib/LinearAlgebra/Matrix/Gaussian.lean +++ b/RealRooted/Mathlib/LinearAlgebra/Matrix/Gaussian.lean @@ -324,9 +324,8 @@ theorem det_adjacentRowDiff_exponentialKernelMatrix_pos {n : ℕ} filter_upwards [hbox] with t ht have htmono : StrictMono t := by intro i j hij - have hindex : i.succ ≤ j.castSucc := - Fin.mk_le_mk.mpr (Nat.succ_le_of_lt hij) - exact (ht i).2.trans_lt ((hx.monotone hindex).trans_lt (ht j).1) + exact (ht i).2.trans_lt + ((hx.monotone (Fin.mk_le_mk.mpr (Nat.succ_le_of_lt hij))).trans_lt (ht j).1) rw [show Matrix.of (fun i j => f i (t i) j) = Matrix.of fun i j => y j * exponentialKernelMatrix t y i j by From c71d27d4c5a1c39e046bbdad2fa8247faeddbe48 Mon Sep 17 00:00:00 2001 From: Per Alexandersson Date: Sun, 2 Aug 2026 21:49:42 +0000 Subject: [PATCH 023/141] Prove Gaussian strict minor positivity --- .../LinearAlgebra/Matrix/Gaussian.lean | 21 ++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/RealRooted/Mathlib/LinearAlgebra/Matrix/Gaussian.lean b/RealRooted/Mathlib/LinearAlgebra/Matrix/Gaussian.lean index a72c88db..ebf368ea 100644 --- a/RealRooted/Mathlib/LinearAlgebra/Matrix/Gaussian.lean +++ b/RealRooted/Mathlib/LinearAlgebra/Matrix/Gaussian.lean @@ -15,9 +15,8 @@ Chapter V, Section 1, Proposition 1.1. Karlin uses the Gaussian matrix The proposition has two parts: `F(a)` tends to the identity as `a` tends to positive infinity, and `F(a)` is strictly totally positive when `a > 0`. -This file proves the first part. The second part requires the source's -strict-total-positivity theorem for the exponential kernel `exp (x * y)`; -it is intentionally not assumed here. +This file proves Gaussian convergence and strict positivity of all strictly +ordered finite minors using Karlin.s exponential-kernel argument. -/ public section @@ -381,4 +380,20 @@ theorem det_exponentialKernelMatrix_pos {q : ℕ} exact det_adjacentRowDiff_exponentialKernelMatrix_pos x yTail hx hyTail_pos (fun t ht => ih ht hyTail) +/-- Strictly ordered minors of Karlin's Gaussian matrix are positive. -/ +theorem det_gaussianMatrix_submatrix_pos {n q : ℕ} + (a : ℝ) (rows cols : Fin q → Fin n) (ha : 0 < a) + (hrows : StrictMono rows) (hcols : StrictMono cols) : + 0 < ((gaussianMatrix n a).submatrix rows cols).det := by + apply det_gaussianMatrix_submatrix_pos_of_exponentialKernel a rows cols + apply det_exponentialKernelMatrix_pos + · intro i j hij + have hrowsVal : (rows i).val < (rows j).val := hrows hij + exact mul_lt_mul_of_pos_left (by + exact_mod_cast hrowsVal) (by positivity) + · intro i j hij + have hcolsVal : (cols i).val < (cols j).val := hcols hij + change ((cols i).val : ℝ) < ((cols j).val : ℝ) + exact_mod_cast hcolsVal + end Matrix From 3e0370ec318c701c89cd168c7c318087907c2311 Mon Sep 17 00:00:00 2001 From: Per Alexandersson Date: Sun, 2 Aug 2026 22:05:00 +0000 Subject: [PATCH 024/141] Simplify Gaussian minor monotonicity proof --- RealRooted/Mathlib/LinearAlgebra/Matrix/Gaussian.lean | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/RealRooted/Mathlib/LinearAlgebra/Matrix/Gaussian.lean b/RealRooted/Mathlib/LinearAlgebra/Matrix/Gaussian.lean index ebf368ea..96c56cd5 100644 --- a/RealRooted/Mathlib/LinearAlgebra/Matrix/Gaussian.lean +++ b/RealRooted/Mathlib/LinearAlgebra/Matrix/Gaussian.lean @@ -387,13 +387,8 @@ theorem det_gaussianMatrix_submatrix_pos {n q : ℕ} 0 < ((gaussianMatrix n a).submatrix rows cols).det := by apply det_gaussianMatrix_submatrix_pos_of_exponentialKernel a rows cols apply det_exponentialKernelMatrix_pos - · intro i j hij - have hrowsVal : (rows i).val < (rows j).val := hrows hij - exact mul_lt_mul_of_pos_left (by - exact_mod_cast hrowsVal) (by positivity) - · intro i j hij - have hcolsVal : (cols i).val < (cols j).val := hcols hij - change ((cols i).val : ℝ) < ((cols j).val : ℝ) - exact_mod_cast hcolsVal + · exact (Nat.strictMono_cast.comp + (Fin.val_strictMono.comp hrows)).const_mul (by positivity) + · exact Nat.strictMono_cast.comp (Fin.val_strictMono.comp hcols) end Matrix From 2338090a340d809b56f01ab5efe6b07cbf6a03e8 Mon Sep 17 00:00:00 2001 From: Per Alexandersson Date: Sun, 2 Aug 2026 22:12:43 +0000 Subject: [PATCH 025/141] Add rectangular determinant expansion --- RealRooted.lean | 1 + .../Matrix/Determinant/CauchyBinet.lean | 31 +++++++++++++++++++ 2 files changed, 32 insertions(+) create mode 100644 RealRooted/Mathlib/LinearAlgebra/Matrix/Determinant/CauchyBinet.lean diff --git a/RealRooted.lean b/RealRooted.lean index 3ea4653c..a4219f7d 100644 --- a/RealRooted.lean +++ b/RealRooted.lean @@ -203,6 +203,7 @@ import RealRooted.Mathlib.Data.List.Zip import RealRooted.Mathlib.Data.Nat.Cast.Basic import RealRooted.Mathlib.Data.Nat.Choose.Cast import RealRooted.Mathlib.LinearAlgebra.Matrix.Determinant.Basic +import RealRooted.Mathlib.LinearAlgebra.Matrix.Determinant.CauchyBinet import RealRooted.Mathlib.LinearAlgebra.Matrix.Determinant.Integral import RealRooted.Mathlib.LinearAlgebra.Matrix.Gaussian import RealRooted.Mathlib.LinearAlgebra.Matrix.KernelSignVariation diff --git a/RealRooted/Mathlib/LinearAlgebra/Matrix/Determinant/CauchyBinet.lean b/RealRooted/Mathlib/LinearAlgebra/Matrix/Determinant/CauchyBinet.lean new file mode 100644 index 00000000..540f7e52 --- /dev/null +++ b/RealRooted/Mathlib/LinearAlgebra/Matrix/Determinant/CauchyBinet.lean @@ -0,0 +1,31 @@ +import RealRooted.Mathlib.LinearAlgebra.Matrix.Determinant.Basic + +/-! +# Cauchy-Binet determinant expansions + +This file develops the rectangular Cauchy-Binet formula. The first theorem +expands a selected minor of a rectangular product into the Leibniz sum over +permutations and all intermediate-index maps. The remaining Cauchy-Binet step +groups the injective maps by their ordered image and proves that noninjective +terms cancel. +-/ + +open scoped BigOperators + +namespace Matrix + +/-- Leibniz expansion of a selected minor of a rectangular matrix product. -/ +theorem det_submatrix_mul_eq_sum_perm_fun + {R : Type*} [CommRing R] {l n m q : ℕ} + (L : Matrix (Fin l) (Fin n) R) + (A : Matrix (Fin n) (Fin m) R) + (rows : Fin q → Fin l) (cols : Fin q → Fin m) : + ((L * A).submatrix rows cols).det = + ∑ σ : Equiv.Perm (Fin q), Equiv.Perm.sign σ • + ∑ f : Fin q → Fin n, + ∏ i, L (rows (σ i)) (f i) * A (f i) (cols i) := by + rw [Matrix.det_apply] + simp only [Matrix.submatrix_apply, Matrix.mul_apply, Finset.prod_univ_sum] + simp + +end Matrix From 8fe98b92e7b5554ca9a796c291ca91cb1a581950 Mon Sep 17 00:00:00 2001 From: Per Alexandersson Date: Sun, 2 Aug 2026 22:16:09 +0000 Subject: [PATCH 026/141] Reassemble rectangular determinant expansion --- .../Matrix/Determinant/CauchyBinet.lean | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/RealRooted/Mathlib/LinearAlgebra/Matrix/Determinant/CauchyBinet.lean b/RealRooted/Mathlib/LinearAlgebra/Matrix/Determinant/CauchyBinet.lean index 540f7e52..0c436de6 100644 --- a/RealRooted/Mathlib/LinearAlgebra/Matrix/Determinant/CauchyBinet.lean +++ b/RealRooted/Mathlib/LinearAlgebra/Matrix/Determinant/CauchyBinet.lean @@ -28,4 +28,36 @@ theorem det_submatrix_mul_eq_sum_perm_fun simp only [Matrix.submatrix_apply, Matrix.mul_apply, Finset.prod_univ_sum] simp +/-- Reassemble the permutation sum for each intermediate-index map. -/ +theorem det_submatrix_mul_eq_sum_fun_det + {R : Type*} [CommRing R] {l n m q : ℕ} + (L : Matrix (Fin l) (Fin n) R) + (A : Matrix (Fin n) (Fin m) R) + (rows : Fin q → Fin l) (cols : Fin q → Fin m) : + ((L * A).submatrix rows cols).det = + ∑ f : Fin q → Fin n, + (L.submatrix rows f).det * ∏ i, A (f i) (cols i) := by + rw [det_submatrix_mul_eq_sum_perm_fun] + simp_rw [Finset.smul_sum] + rw [Finset.sum_comm] + apply Fintype.sum_congr + intro f + rw [Matrix.det_apply] + simp only [Matrix.submatrix_apply, Finset.prod_mul_distrib] + rw [Finset.sum_mul] + simp_rw [smul_mul_assoc] + +/-- A submatrix with a noninjective column selector has zero determinant. -/ +theorem det_submatrix_eq_zero_of_not_injective_right + {R : Type*} [CommRing R] {l n q : ℕ} + (L : Matrix (Fin l) (Fin n) R) + (rows : Fin q → Fin l) (f : Fin q → Fin n) + (hf : ¬ Function.Injective f) : + (L.submatrix rows f).det = 0 := by + obtain ⟨i, j, hfij, hij⟩ := Function.not_injective_iff.mp hf + apply Matrix.det_zero_of_column_eq hij + intro k + simp only [Matrix.submatrix_apply] + rw [hfij] + end Matrix From 28beaa6cc5cc7116a0cb56f9e1a7cdca727dfe5d Mon Sep 17 00:00:00 2001 From: Per Alexandersson Date: Sun, 2 Aug 2026 22:18:53 +0000 Subject: [PATCH 027/141] Generalize noninjective submatrix determinant lemma --- .../LinearAlgebra/Matrix/Determinant/Basic.lean | 12 ++++++++++++ .../Matrix/Determinant/CauchyBinet.lean | 13 ------------- 2 files changed, 12 insertions(+), 13 deletions(-) diff --git a/RealRooted/Mathlib/LinearAlgebra/Matrix/Determinant/Basic.lean b/RealRooted/Mathlib/LinearAlgebra/Matrix/Determinant/Basic.lean index 3c9794a1..66ef1195 100644 --- a/RealRooted/Mathlib/LinearAlgebra/Matrix/Determinant/Basic.lean +++ b/RealRooted/Mathlib/LinearAlgebra/Matrix/Determinant/Basic.lean @@ -61,4 +61,16 @@ theorem det_eq_det_adjacentRowDiff_of_firstColumn_eq_one {n : ℕ} ext i j rfl +/-- A submatrix with a noninjective column selector has zero determinant. -/ +theorem det_submatrix_eq_zero_of_not_injective_right + {R m κ q : Type*} [CommRing R] [DecidableEq q] [Fintype q] + (L : Matrix m κ R) (rows : q → m) (f : q → κ) + (hf : ¬ Function.Injective f) : + (L.submatrix rows f).det = 0 := by + obtain ⟨i, j, hfij, hij⟩ := Function.not_injective_iff.mp hf + apply Matrix.det_zero_of_column_eq hij + intro k + simp only [Matrix.submatrix_apply] + rw [hfij] + end Matrix diff --git a/RealRooted/Mathlib/LinearAlgebra/Matrix/Determinant/CauchyBinet.lean b/RealRooted/Mathlib/LinearAlgebra/Matrix/Determinant/CauchyBinet.lean index 0c436de6..d0903146 100644 --- a/RealRooted/Mathlib/LinearAlgebra/Matrix/Determinant/CauchyBinet.lean +++ b/RealRooted/Mathlib/LinearAlgebra/Matrix/Determinant/CauchyBinet.lean @@ -47,17 +47,4 @@ theorem det_submatrix_mul_eq_sum_fun_det rw [Finset.sum_mul] simp_rw [smul_mul_assoc] -/-- A submatrix with a noninjective column selector has zero determinant. -/ -theorem det_submatrix_eq_zero_of_not_injective_right - {R : Type*} [CommRing R] {l n q : ℕ} - (L : Matrix (Fin l) (Fin n) R) - (rows : Fin q → Fin l) (f : Fin q → Fin n) - (hf : ¬ Function.Injective f) : - (L.submatrix rows f).det = 0 := by - obtain ⟨i, j, hfij, hij⟩ := Function.not_injective_iff.mp hf - apply Matrix.det_zero_of_column_eq hij - intro k - simp only [Matrix.submatrix_apply] - rw [hfij] - end Matrix From 5e0ad905460514bbd617c10c84a68c2a3b11e318 Mon Sep 17 00:00:00 2001 From: Per Alexandersson Date: Sun, 2 Aug 2026 22:26:50 +0000 Subject: [PATCH 028/141] Factor injective maps through ordered images --- .../Matrix/Determinant/CauchyBinet.lean | 44 ++++++++++++++++--- 1 file changed, 39 insertions(+), 5 deletions(-) diff --git a/RealRooted/Mathlib/LinearAlgebra/Matrix/Determinant/CauchyBinet.lean b/RealRooted/Mathlib/LinearAlgebra/Matrix/Determinant/CauchyBinet.lean index d0903146..06b7ac49 100644 --- a/RealRooted/Mathlib/LinearAlgebra/Matrix/Determinant/CauchyBinet.lean +++ b/RealRooted/Mathlib/LinearAlgebra/Matrix/Determinant/CauchyBinet.lean @@ -3,11 +3,10 @@ import RealRooted.Mathlib.LinearAlgebra.Matrix.Determinant.Basic /-! # Cauchy-Binet determinant expansions -This file develops the rectangular Cauchy-Binet formula. The first theorem -expands a selected minor of a rectangular product into the Leibniz sum over -permutations and all intermediate-index maps. The remaining Cauchy-Binet step -groups the injective maps by their ordered image and proves that noninjective -terms cancel. +This file develops the rectangular Cauchy-Binet formula. It expands a selected +minor into a sum over intermediate maps, proves that noninjective maps vanish, +and factors each injective map through the increasing enumeration of its image. +The remaining step transports permutation signs and groups by image subset. -/ open scoped BigOperators @@ -48,3 +47,38 @@ theorem det_submatrix_mul_eq_sum_fun_det simp_rw [smul_mul_assoc] end Matrix + +private theorem coe_ofFinEmb_eq_range {q : ℕ} {I : Type*} (f : Fin q ↪ I) : + (Set.powersetCard.ofFinEmb q I f : Set I) = Set.range f := by + ext x + simp [Set.powersetCard.ofFinEmb, Set.powersetCard.map] + +/-- An injective finite map into a linear order factors through the increasing +enumeration of its image and a permutation of its domain. -/ +theorem Set.powersetCard.exists_orderEmb_comp_perm_eq_of_injective + {q : ℕ} {I : Type*} [LinearOrder I] + (f : Fin q → I) (hf : Function.Injective f) : + ∃ s : Set.powersetCard I q, ∃ p : Equiv.Perm (Fin q), + ∀ i, Set.powersetCard.ofFinEmbEquiv.symm s (p i) = f i := by + let emb : Fin q ↪ I := ⟨f, hf⟩ + let s : Set.powersetCard I q := Set.powersetCard.ofFinEmb q I emb + let e : Fin q ↪o I := Set.powersetCard.ofFinEmbEquiv.symm s + have hfRange : Set.range f = (s : Set I) := + (coe_ofFinEmb_eq_range emb).symm + have heRange : Set.range e = (s : Set I) := by + calc + Set.range e = (Set.powersetCard.ofFinEmb q I e.toEmbedding : Set I) := + (coe_ofFinEmb_eq_range e.toEmbedding).symm + _ = (s : Set I) := congrArg + (fun t : Set.powersetCard I q => (t : Set I)) + (Set.powersetCard.ofFinEmbEquiv.apply_symm_apply s) + have hRange : Set.range f = Set.range e := hfRange.trans heRange.symm + let ee : Fin q ≃ Set.range e := Equiv.ofInjective e e.injective + let p : Equiv.Perm (Fin q) := + (Equiv.ofInjective f hf).trans + ((Equiv.setCongr hRange).trans ee.symm) + refine ⟨s, p, ?_⟩ + intro i + have hfi : f i ∈ Set.range e := hRange ▸ ⟨i, rfl⟩ + change e (ee.symm ⟨f i, hfi⟩) = f i + exact congrArg Subtype.val (ee.apply_symm_apply ⟨f i, hfi⟩) From 87f21105a223872ab0b519b68aeb95d1e1d06ea7 Mon Sep 17 00:00:00 2001 From: Per Alexandersson Date: Sun, 2 Aug 2026 22:29:56 +0000 Subject: [PATCH 029/141] Prove uniqueness of ordered image factorization --- .../Matrix/Determinant/CauchyBinet.lean | 47 +++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/RealRooted/Mathlib/LinearAlgebra/Matrix/Determinant/CauchyBinet.lean b/RealRooted/Mathlib/LinearAlgebra/Matrix/Determinant/CauchyBinet.lean index 06b7ac49..d872720d 100644 --- a/RealRooted/Mathlib/LinearAlgebra/Matrix/Determinant/CauchyBinet.lean +++ b/RealRooted/Mathlib/LinearAlgebra/Matrix/Determinant/CauchyBinet.lean @@ -82,3 +82,50 @@ theorem Set.powersetCard.exists_orderEmb_comp_perm_eq_of_injective have hfi : f i ∈ Set.range e := hRange ▸ ⟨i, rfl⟩ change e (ee.symm ⟨f i, hfi⟩) = f i exact congrArg Subtype.val (ee.apply_symm_apply ⟨f i, hfi⟩) + +private theorem range_orderEmbOfPowersetCard {q : ℕ} {I : Type*} + [LinearOrder I] (s : Set.powersetCard I q) : + Set.range (Set.powersetCard.ofFinEmbEquiv.symm s) = (s : Set I) := by + let e : Fin q ↪o I := Set.powersetCard.ofFinEmbEquiv.symm s + calc + Set.range e = (Set.powersetCard.ofFinEmb q I e.toEmbedding : Set I) := + (coe_ofFinEmb_eq_range e.toEmbedding).symm + _ = (s : Set I) := congrArg + (fun t : Set.powersetCard I q => (t : Set I)) + (Set.powersetCard.ofFinEmbEquiv.apply_symm_apply s) + +private theorem range_comp_perm {q : ℕ} {I : Type*} + (e : Fin q → I) (p : Equiv.Perm (Fin q)) : + Set.range (fun i => e (p i)) = Set.range e := by + ext x + constructor + · rintro ⟨i, rfl⟩ + exact ⟨p i, rfl⟩ + · rintro ⟨j, rfl⟩ + exact ⟨p.symm j, by simp⟩ + +/-- The ordered-image/permutation representation of an injective finite map is +unique. -/ +theorem Set.powersetCard.orderEmb_comp_perm_injective + {q : ℕ} {I : Type*} [LinearOrder I] : + Function.Injective + (fun z : Set.powersetCard I q × Equiv.Perm (Fin q) => + fun i => Set.powersetCard.ofFinEmbEquiv.symm z.1 (z.2 i)) := by + rintro ⟨s, p⟩ ⟨t, r⟩ h + let es : Fin q ↪o I := Set.powersetCard.ofFinEmbEquiv.symm s + let et : Fin q ↪o I := Set.powersetCard.ofFinEmbEquiv.symm t + have hst : s = t := by + apply Subtype.ext + apply Finset.coe_injective + calc + (s : Set I) = Set.range es := (range_orderEmbOfPowersetCard s).symm + _ = Set.range (fun i => es (p i)) := (range_comp_perm es p).symm + _ = Set.range (fun i => et (r i)) := congrArg Set.range h + _ = Set.range et := range_comp_perm et r + _ = (t : Set I) := range_orderEmbOfPowersetCard t + subst t + have hpr : p = r := by + apply Equiv.ext + intro i + exact es.injective (congrFun h i) + exact Prod.ext rfl hpr From ae50ccb85a7193e469c722117b4c9b84360cf646 Mon Sep 17 00:00:00 2001 From: Per Alexandersson Date: Sun, 2 Aug 2026 22:31:35 +0000 Subject: [PATCH 030/141] Deduplicate ordered image range proof --- .../Matrix/Determinant/CauchyBinet.lean | 33 ++++++++----------- 1 file changed, 14 insertions(+), 19 deletions(-) diff --git a/RealRooted/Mathlib/LinearAlgebra/Matrix/Determinant/CauchyBinet.lean b/RealRooted/Mathlib/LinearAlgebra/Matrix/Determinant/CauchyBinet.lean index d872720d..13b6121e 100644 --- a/RealRooted/Mathlib/LinearAlgebra/Matrix/Determinant/CauchyBinet.lean +++ b/RealRooted/Mathlib/LinearAlgebra/Matrix/Determinant/CauchyBinet.lean @@ -53,6 +53,18 @@ private theorem coe_ofFinEmb_eq_range {q : ℕ} {I : Type*} (f : Fin q ↪ I) : ext x simp [Set.powersetCard.ofFinEmb, Set.powersetCard.map] + +private theorem range_orderEmbOfPowersetCard {q : ℕ} {I : Type*} + [LinearOrder I] (s : Set.powersetCard I q) : + Set.range (Set.powersetCard.ofFinEmbEquiv.symm s) = (s : Set I) := by + let e : Fin q ↪o I := Set.powersetCard.ofFinEmbEquiv.symm s + calc + Set.range e = (Set.powersetCard.ofFinEmb q I e.toEmbedding : Set I) := + (coe_ofFinEmb_eq_range e.toEmbedding).symm + _ = (s : Set I) := congrArg + (fun t : Set.powersetCard I q => (t : Set I)) + (Set.powersetCard.ofFinEmbEquiv.apply_symm_apply s) + /-- An injective finite map into a linear order factors through the increasing enumeration of its image and a permutation of its domain. -/ theorem Set.powersetCard.exists_orderEmb_comp_perm_eq_of_injective @@ -65,14 +77,8 @@ theorem Set.powersetCard.exists_orderEmb_comp_perm_eq_of_injective let e : Fin q ↪o I := Set.powersetCard.ofFinEmbEquiv.symm s have hfRange : Set.range f = (s : Set I) := (coe_ofFinEmb_eq_range emb).symm - have heRange : Set.range e = (s : Set I) := by - calc - Set.range e = (Set.powersetCard.ofFinEmb q I e.toEmbedding : Set I) := - (coe_ofFinEmb_eq_range e.toEmbedding).symm - _ = (s : Set I) := congrArg - (fun t : Set.powersetCard I q => (t : Set I)) - (Set.powersetCard.ofFinEmbEquiv.apply_symm_apply s) - have hRange : Set.range f = Set.range e := hfRange.trans heRange.symm + have hRange : Set.range f = Set.range e := + hfRange.trans (range_orderEmbOfPowersetCard s).symm let ee : Fin q ≃ Set.range e := Equiv.ofInjective e e.injective let p : Equiv.Perm (Fin q) := (Equiv.ofInjective f hf).trans @@ -83,17 +89,6 @@ theorem Set.powersetCard.exists_orderEmb_comp_perm_eq_of_injective change e (ee.symm ⟨f i, hfi⟩) = f i exact congrArg Subtype.val (ee.apply_symm_apply ⟨f i, hfi⟩) -private theorem range_orderEmbOfPowersetCard {q : ℕ} {I : Type*} - [LinearOrder I] (s : Set.powersetCard I q) : - Set.range (Set.powersetCard.ofFinEmbEquiv.symm s) = (s : Set I) := by - let e : Fin q ↪o I := Set.powersetCard.ofFinEmbEquiv.symm s - calc - Set.range e = (Set.powersetCard.ofFinEmb q I e.toEmbedding : Set I) := - (coe_ofFinEmb_eq_range e.toEmbedding).symm - _ = (s : Set I) := congrArg - (fun t : Set.powersetCard I q => (t : Set I)) - (Set.powersetCard.ofFinEmbEquiv.apply_symm_apply s) - private theorem range_comp_perm {q : ℕ} {I : Type*} (e : Fin q → I) (p : Equiv.Perm (Fin q)) : Set.range (fun i => e (p i)) = Set.range e := by From 2d846d26984ba84379c6be9c1188237d95f08d5b Mon Sep 17 00:00:00 2001 From: Per Alexandersson Date: Sun, 2 Aug 2026 22:34:01 +0000 Subject: [PATCH 031/141] Reindex embedding sums by ordered images --- .../Matrix/Determinant/CauchyBinet.lean | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/RealRooted/Mathlib/LinearAlgebra/Matrix/Determinant/CauchyBinet.lean b/RealRooted/Mathlib/LinearAlgebra/Matrix/Determinant/CauchyBinet.lean index 13b6121e..038e8800 100644 --- a/RealRooted/Mathlib/LinearAlgebra/Matrix/Determinant/CauchyBinet.lean +++ b/RealRooted/Mathlib/LinearAlgebra/Matrix/Determinant/CauchyBinet.lean @@ -124,3 +124,43 @@ theorem Set.powersetCard.orderEmb_comp_perm_injective intro i exact es.injective (congrFun h i) exact Prod.ext rfl hpr + +/-- Ordered image and a domain permutation parameterize all finite embeddings. -/ +noncomputable def Set.powersetCard.orderEmbPermEquivEmbedding + {q : ℕ} {I : Type*} [LinearOrder I] : + Set.powersetCard I q × Equiv.Perm (Fin q) ≃ (Fin q ↪ I) := + Equiv.ofBijective + (fun z => + ⟨fun i => Set.powersetCard.ofFinEmbEquiv.symm z.1 (z.2 i), + (Set.powersetCard.ofFinEmbEquiv.symm z.1).injective.comp + z.2.injective⟩) + ⟨by + intro z w h + apply Set.powersetCard.orderEmb_comp_perm_injective + funext i + exact congrArg (fun g : Fin q ↪ I => g i) h, + by + intro f + obtain ⟨s, p, h⟩ := + Set.powersetCard.exists_orderEmb_comp_perm_eq_of_injective f f.injective + refine ⟨(s, p), ?_⟩ + ext i + exact h i⟩ + +/-- Reindex a sum over finite embeddings by ordered image and permutation. -/ +theorem Set.powersetCard.sum_embedding_eq_sum_orderEmb_perm + {q : ℕ} {I M : Type*} [LinearOrder I] [Fintype I] [AddCommMonoid M] + (g : (Fin q ↪ I) → M) : + ∑ f : Fin q ↪ I, g f = + ∑ s : Set.powersetCard I q, ∑ p : Equiv.Perm (Fin q), + g ⟨fun i => Set.powersetCard.ofFinEmbEquiv.symm s (p i), + (Set.powersetCard.ofFinEmbEquiv.symm s).injective.comp p.injective⟩ := by + calc + ∑ f : Fin q ↪ I, g f = + ∑ z, g (Set.powersetCard.orderEmbPermEquivEmbedding z) := + (Set.powersetCard.orderEmbPermEquivEmbedding.sum_comp g).symm + _ = ∑ s : Set.powersetCard I q, ∑ p : Equiv.Perm (Fin q), + g (Set.powersetCard.orderEmbPermEquivEmbedding (s, p)) := + Fintype.sum_prod_type _ + _ = _ := by + simp [Set.powersetCard.orderEmbPermEquivEmbedding] From fecbbec4a47ea26dba4075ea1347359099256659 Mon Sep 17 00:00:00 2001 From: Per Alexandersson Date: Sun, 2 Aug 2026 22:44:05 +0000 Subject: [PATCH 032/141] Prove selected-minor Cauchy-Binet identity --- .../Matrix/Determinant/CauchyBinet.lean | 87 +++++++++++++++++++ 1 file changed, 87 insertions(+) diff --git a/RealRooted/Mathlib/LinearAlgebra/Matrix/Determinant/CauchyBinet.lean b/RealRooted/Mathlib/LinearAlgebra/Matrix/Determinant/CauchyBinet.lean index 038e8800..15e4b777 100644 --- a/RealRooted/Mathlib/LinearAlgebra/Matrix/Determinant/CauchyBinet.lean +++ b/RealRooted/Mathlib/LinearAlgebra/Matrix/Determinant/CauchyBinet.lean @@ -164,3 +164,90 @@ theorem Set.powersetCard.sum_embedding_eq_sum_orderEmb_perm Fintype.sum_prod_type _ _ = _ := by simp [Set.powersetCard.orderEmbPermEquivEmbedding] + +private noncomputable def injectiveFunctionEquivEmbedding + {q : ℕ} {I : Type*} : + {f : Fin q → I // Function.Injective f} ≃ (Fin q ↪ I) := + Equiv.ofBijective + (fun f : {f : Fin q → I // Function.Injective f} => + ⟨f.1, f.2⟩) + ⟨by + intro f g h + exact Subtype.ext (congrArg Function.Embedding.toFun h), + by + intro f + exact ⟨⟨f, f.injective⟩, rfl⟩⟩ + +private theorem sum_function_eq_sum_embedding_of_zero_noninjective + {q : ℕ} {I M : Type*} [Fintype I] [AddCommMonoid M] + (g : (Fin q → I) → M) + (hzero : ∀ f, ¬ Function.Injective f → g f = 0) : + ∑ f : Fin q → I, g f = ∑ e : Fin q ↪ I, g e := by + classical + let s : Finset (Fin q → I) := Finset.univ.filter Function.Injective + calc + ∑ f : Fin q → I, g f = ∑ f ∈ s, g f := by + symm + apply Finset.sum_subset (Finset.filter_subset _ _) + intro f _ hf + apply hzero f + intro hfinj + exact hf (Finset.mem_filter.mpr ⟨Finset.mem_univ f, hfinj⟩) + _ = ∑ f : {f : Fin q → I // Function.Injective f}, g f := + Finset.sum_subtype s (by simp [s]) g + _ = ∑ f : Fin q ↪ I, g f := by + exact Fintype.sum_equiv injectiveFunctionEquivEmbedding _ _ fun _ => rfl + +private theorem sum_perm_det_submatrix_comp_mul_prod_eq + {R : Type*} [CommRing R] {l n m q : ℕ} + (L : Matrix (Fin l) (Fin n) R) + (A : Matrix (Fin n) (Fin m) R) + (rows : Fin q → Fin l) (cols : Fin q → Fin m) + (e : Fin q → Fin n) : + (∑ p : Equiv.Perm (Fin q), + (L.submatrix rows (fun i => e (p i))).det * + ∏ i, A (e (p i)) (cols i)) = + (L.submatrix rows e).det * (A.submatrix e cols).det := by + calc + (∑ p : Equiv.Perm (Fin q), + (L.submatrix rows (fun i => e (p i))).det * + ∏ i, A (e (p i)) (cols i)) = + (L.submatrix rows e).det * + ∑ p : Equiv.Perm (Fin q), + Equiv.Perm.sign p • ∏ i, A (e (p i)) (cols i) := by + rw [Finset.mul_sum] + apply Fintype.sum_congr + intro p + rw [show L.submatrix rows (fun i => e (p i)) = + (L.submatrix rows e).submatrix id p by rfl] + rw [Matrix.det_permute', Units.smul_def, + ← Int.cast_smul_eq_zsmul R] + simp [mul_comm, mul_assoc] + _ = (L.submatrix rows e).det * (A.submatrix e cols).det := by + congr 1 + rw [Matrix.det_apply] + rfl + +/-- Rectangular Cauchy--Binet for selected square minors. -/ +theorem Matrix.det_submatrix_mul_eq_sum_powersetCard + {R : Type*} [CommRing R] {l n m q : ℕ} + (L : Matrix (Fin l) (Fin n) R) + (A : Matrix (Fin n) (Fin m) R) + (rows : Fin q → Fin l) (cols : Fin q → Fin m) : + ((L * A).submatrix rows cols).det = + ∑ s : Set.powersetCard (Fin n) q, + (L.submatrix rows + (Set.powersetCard.ofFinEmbEquiv.symm s)).det * + (A.submatrix + (Set.powersetCard.ofFinEmbEquiv.symm s) cols).det := by + classical + rw [Matrix.det_submatrix_mul_eq_sum_fun_det] + rw [sum_function_eq_sum_embedding_of_zero_noninjective] + · rw [Set.powersetCard.sum_embedding_eq_sum_orderEmb_perm] + apply Fintype.sum_congr + intro s + simpa using sum_perm_det_submatrix_comp_mul_prod_eq L A rows cols + (Set.powersetCard.ofFinEmbEquiv.symm s) + · intro f hf + rw [Matrix.det_submatrix_eq_zero_of_not_injective_right L rows f hf, + zero_mul] From ce4ec90d2ed21434aa1a6125fbb1745ad0e80f8e Mon Sep 17 00:00:00 2001 From: Per Alexandersson Date: Sun, 2 Aug 2026 22:48:23 +0000 Subject: [PATCH 033/141] Deduplicate Cauchy-Binet sum proof --- .../Matrix/Determinant/CauchyBinet.lean | 40 ++++++------------- 1 file changed, 12 insertions(+), 28 deletions(-) diff --git a/RealRooted/Mathlib/LinearAlgebra/Matrix/Determinant/CauchyBinet.lean b/RealRooted/Mathlib/LinearAlgebra/Matrix/Determinant/CauchyBinet.lean index 15e4b777..a686224f 100644 --- a/RealRooted/Mathlib/LinearAlgebra/Matrix/Determinant/CauchyBinet.lean +++ b/RealRooted/Mathlib/LinearAlgebra/Matrix/Determinant/CauchyBinet.lean @@ -165,38 +165,25 @@ theorem Set.powersetCard.sum_embedding_eq_sum_orderEmb_perm _ = _ := by simp [Set.powersetCard.orderEmbPermEquivEmbedding] -private noncomputable def injectiveFunctionEquivEmbedding - {q : ℕ} {I : Type*} : - {f : Fin q → I // Function.Injective f} ≃ (Fin q ↪ I) := - Equiv.ofBijective - (fun f : {f : Fin q → I // Function.Injective f} => - ⟨f.1, f.2⟩) - ⟨by - intro f g h - exact Subtype.ext (congrArg Function.Embedding.toFun h), - by - intro f - exact ⟨⟨f, f.injective⟩, rfl⟩⟩ - private theorem sum_function_eq_sum_embedding_of_zero_noninjective {q : ℕ} {I M : Type*} [Fintype I] [AddCommMonoid M] (g : (Fin q → I) → M) (hzero : ∀ f, ¬ Function.Injective f → g f = 0) : ∑ f : Fin q → I, g f = ∑ e : Fin q ↪ I, g e := by classical - let s : Finset (Fin q → I) := Finset.univ.filter Function.Injective calc - ∑ f : Fin q → I, g f = ∑ f ∈ s, g f := by - symm - apply Finset.sum_subset (Finset.filter_subset _ _) - intro f _ hf - apply hzero f - intro hfinj - exact hf (Finset.mem_filter.mpr ⟨Finset.mem_univ f, hfinj⟩) - _ = ∑ f : {f : Fin q → I // Function.Injective f}, g f := - Finset.sum_subtype s (by simp [s]) g + ∑ f : Fin q → I, g f = + (∑ f : {f : Fin q → I // Function.Injective f}, g f) + + ∑ f : {f : Fin q → I // ¬ Function.Injective f}, g f := + (Fintype.sum_subtype_add_sum_subtype Function.Injective g).symm + _ = ∑ f : {f : Fin q → I // Function.Injective f}, g f := by + rw [show (∑ f : {f : Fin q → I // ¬ Function.Injective f}, g f) = 0 by + apply Finset.sum_eq_zero + intro f _ + exact hzero f f.property, add_zero] _ = ∑ f : Fin q ↪ I, g f := by - exact Fintype.sum_equiv injectiveFunctionEquivEmbedding _ _ fun _ => rfl + exact Fintype.sum_equiv + (Equiv.subtypeInjectiveEquivEmbedding (Fin q) I) _ _ fun _ => rfl private theorem sum_perm_det_submatrix_comp_mul_prod_eq {R : Type*} [CommRing R] {l n m q : ℕ} @@ -209,10 +196,7 @@ private theorem sum_perm_det_submatrix_comp_mul_prod_eq ∏ i, A (e (p i)) (cols i)) = (L.submatrix rows e).det * (A.submatrix e cols).det := by calc - (∑ p : Equiv.Perm (Fin q), - (L.submatrix rows (fun i => e (p i))).det * - ∏ i, A (e (p i)) (cols i)) = - (L.submatrix rows e).det * + _ = (L.submatrix rows e).det * ∑ p : Equiv.Perm (Fin q), Equiv.Perm.sign p • ∏ i, A (e (p i)) (cols i) := by rw [Finset.mul_sum] From e93697f2b35327665278eb18f2af85f54d07cb75 Mon Sep 17 00:00:00 2001 From: Per Alexandersson Date: Sun, 2 Aug 2026 22:54:34 +0000 Subject: [PATCH 034/141] Extract ordered nonzero minor from full rank --- .../Matrix/Determinant/CauchyBinet.lean | 58 +++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/RealRooted/Mathlib/LinearAlgebra/Matrix/Determinant/CauchyBinet.lean b/RealRooted/Mathlib/LinearAlgebra/Matrix/Determinant/CauchyBinet.lean index a686224f..3d2cf72e 100644 --- a/RealRooted/Mathlib/LinearAlgebra/Matrix/Determinant/CauchyBinet.lean +++ b/RealRooted/Mathlib/LinearAlgebra/Matrix/Determinant/CauchyBinet.lean @@ -1,4 +1,5 @@ import RealRooted.Mathlib.LinearAlgebra.Matrix.Determinant.Basic +import Mathlib.LinearAlgebra.Basis.VectorSpace /-! # Cauchy-Binet determinant expansions @@ -235,3 +236,60 @@ theorem Matrix.det_submatrix_mul_eq_sum_powersetCard · intro f hf rw [Matrix.det_submatrix_eq_zero_of_not_injective_right L rows f hf, zero_mul] + +private theorem selected_mulVec_injective + {R : Type*} [Field R] {n m q : ℕ} + (A : Matrix (Fin n) (Fin m) R) + (hA : Function.Injective A.mulVec) + (cols : Fin q → Fin m) (hcols : StrictMono cols) : + Function.Injective (A.submatrix id cols).mulVec := by + rw [Matrix.mulVec_injective_iff] + have h := (Matrix.mulVec_injective_iff.mp hA).comp cols hcols.injective + simpa [Matrix.col, Function.comp_def] using h + +private theorem exists_left_inverse_matrix + {R : Type*} [Field R] {n q : ℕ} + (B : Matrix (Fin n) (Fin q) R) + (hB : Function.Injective B.mulVec) : + ∃ C : Matrix (Fin q) (Fin n) R, C * B = 1 := by + let f := Matrix.toLin' B + have hf : LinearMap.ker f = ⊥ := by + rw [LinearMap.ker_eq_bot] + intro x y hxy + apply hB + simpa only [f, Matrix.toLin'_apply] using hxy + obtain ⟨g, hg⟩ := f.exists_leftInverse_of_injective hf + refine ⟨LinearMap.toMatrix' g, ?_⟩ + rw [← LinearMap.toMatrix'_toLin' B, ← LinearMap.toMatrix'_comp, hg, + ← Matrix.toLin'_one, LinearMap.toMatrix'_toLin'] + +/-- Full column rank gives a nonzero maximal minor on strictly increasing rows. + +The proof restricts the independent columns, takes a linear left inverse, and +applies rectangular Cauchy--Binet to the resulting identity matrix. -/ +theorem Matrix.exists_ordered_minor_ne_zero_of_mulVec_injective + {R : Type*} [Field R] {n m q : ℕ} + (A : Matrix (Fin n) (Fin m) R) + (hA : Function.Injective A.mulVec) + (cols : Fin q → Fin m) (hcols : StrictMono cols) : + ∃ rows : Fin q → Fin n, StrictMono rows ∧ + (A.submatrix rows cols).det ≠ 0 := by + classical + let B := A.submatrix id cols + have hB : Function.Injective B.mulVec := by + exact selected_mulVec_injective A hA cols hcols + obtain ⟨C, hCB⟩ := exists_left_inverse_matrix B hB + have hsum : + (∑ s : Set.powersetCard (Fin n) q, + (C.submatrix id + (Set.powersetCard.ofFinEmbEquiv.symm s)).det * + (B.submatrix + (Set.powersetCard.ofFinEmbEquiv.symm s) id).det) ≠ 0 := by + rw [← Matrix.det_submatrix_mul_eq_sum_powersetCard C B id id, hCB] + simp + obtain ⟨s, _, hs⟩ := Finset.exists_ne_zero_of_sum_ne_zero + (s := Finset.univ) (by simpa using hsum) + refine ⟨Set.powersetCard.ofFinEmbEquiv.symm s, + (Set.powersetCard.ofFinEmbEquiv.symm s).strictMono, ?_⟩ + have hminor := (mul_ne_zero_iff.mp hs).2 + simpa [B] using hminor From b7765b507d08fa0c872298c6946178cfe937b0df Mon Sep 17 00:00:00 2001 From: Per Alexandersson Date: Sun, 2 Aug 2026 23:07:21 +0000 Subject: [PATCH 035/141] Formalize Karlin Gaussian strictification --- RealRooted.lean | 1 + .../Matrix/SignRegularStrictification.lean | 111 ++++++++++++++++++ 2 files changed, 112 insertions(+) create mode 100644 RealRooted/Mathlib/LinearAlgebra/Matrix/SignRegularStrictification.lean diff --git a/RealRooted.lean b/RealRooted.lean index a4219f7d..d23495bc 100644 --- a/RealRooted.lean +++ b/RealRooted.lean @@ -208,6 +208,7 @@ import RealRooted.Mathlib.LinearAlgebra.Matrix.Determinant.Integral import RealRooted.Mathlib.LinearAlgebra.Matrix.Gaussian import RealRooted.Mathlib.LinearAlgebra.Matrix.KernelSignVariation import RealRooted.Mathlib.LinearAlgebra.Matrix.SignRegular +import RealRooted.Mathlib.LinearAlgebra.Matrix.SignRegularStrictification import RealRooted.Mathlib.LinearAlgebra.Matrix.SignVariation import RealRooted.Mathlib.LinearAlgebra.Matrix.TotallyNonneg import RealRooted.Mathlib.LinearAlgebra.Matrix.VariationDiminishing diff --git a/RealRooted/Mathlib/LinearAlgebra/Matrix/SignRegularStrictification.lean b/RealRooted/Mathlib/LinearAlgebra/Matrix/SignRegularStrictification.lean new file mode 100644 index 00000000..452d1fd7 --- /dev/null +++ b/RealRooted/Mathlib/LinearAlgebra/Matrix/SignRegularStrictification.lean @@ -0,0 +1,111 @@ +import RealRooted.Mathlib.LinearAlgebra.Matrix.Determinant.CauchyBinet +import RealRooted.Mathlib.LinearAlgebra.Matrix.Gaussian +import RealRooted.Mathlib.LinearAlgebra.Matrix.SignRegular + +/-! +# Strictifying sign-consistent matrices + +This file formalizes Karlin, *Total Positivity*, Vol. I, Chapter V, Section 1, +Proposition 1.2. A positive-minor left factor strictifies a full-column-rank +sign-consistent matrix. Karlin applies this with a Gaussian matrix and then +lets the Gaussian parameter tend to infinity. +-/ + +public section + +open Filter +open scoped BigOperators Topology + +namespace Matrix + +/-- Positive ordered minors in a left factor strictify a full-column-rank +sign-consistent right factor. -/ +theorem IsSignConsistentOrder.isStrictlySignConsistentOrder_mul_of_posMinors + {S : Type*} [Field S] [LinearOrder S] [IsStrictOrderedRing S] + {l n m q : ℕ} {L : Matrix (Fin l) (Fin n) S} + {A : Matrix (Fin n) (Fin m) S} + (hA : A.IsSignConsistentOrder q) + (hAinj : Function.Injective A.mulVec) + (hL : ∀ ⦃rows : Fin q → Fin l⦄ ⦃cols : Fin q → Fin n⦄, + StrictMono rows → StrictMono cols → + 0 < (L.submatrix rows cols).det) : + (L * A).IsStrictlySignConsistentOrder q := by + classical + intro rows rows' cols cols' hrows hrows' hcols hcols' + obtain ⟨rows₀, hrows₀, href_ne⟩ := + exists_ordered_minor_ne_zero_of_mulVec_injective A hAinj cols hcols + rcases lt_or_gt_of_ne href_ne with href_neg | href_pos + · have hneg : ∀ ⦃r : Fin q → Fin l⦄ ⦃c : Fin q → Fin m⦄, + StrictMono r → StrictMono c → + ((L * A).submatrix r c).det < 0 := by + intro r c hr hc + rw [det_submatrix_mul_eq_sum_powersetCard] + apply Finset.sum_neg' + · intro s _ + exact mul_nonpos_of_nonneg_of_nonpos + (hL hr (Set.powersetCard.ofFinEmbEquiv.symm s).strictMono).le + (hA.minor_nonpos_of_neg hrows₀ hcols href_neg + (Set.powersetCard.ofFinEmbEquiv.symm s).strictMono hc) + · obtain ⟨r₁, hr₁, href₁_ne⟩ := + exists_ordered_minor_ne_zero_of_mulVec_injective A hAinj c hc + have href₁_neg : (A.submatrix r₁ c).det < 0 := + lt_of_le_of_ne + (hA.minor_nonpos_of_neg hrows₀ hcols href_neg hr₁ hc) href₁_ne + let e := OrderEmbedding.ofStrictMono r₁ hr₁ + refine ⟨Set.powersetCard.ofFinEmbEquiv e, Finset.mem_univ _, ?_⟩ + simpa [e] using mul_neg_of_pos_of_neg (hL hr hr₁) href₁_neg + exact mul_pos_of_neg_of_neg (hneg hrows hcols) (hneg hrows' hcols') + · have hpos : ∀ ⦃r : Fin q → Fin l⦄ ⦃c : Fin q → Fin m⦄, + StrictMono r → StrictMono c → + 0 < ((L * A).submatrix r c).det := by + intro r c hr hc + rw [det_submatrix_mul_eq_sum_powersetCard] + apply Finset.sum_pos' + · intro s _ + exact mul_nonneg + (hL hr (Set.powersetCard.ofFinEmbEquiv.symm s).strictMono).le + (hA.minor_nonneg_of_pos hrows₀ hcols href_pos + (Set.powersetCard.ofFinEmbEquiv.symm s).strictMono hc) + · obtain ⟨r₁, hr₁, href₁_ne⟩ := + exists_ordered_minor_ne_zero_of_mulVec_injective A hAinj c hc + have href₁_pos : 0 < (A.submatrix r₁ c).det := + lt_of_le_of_ne + (hA.minor_nonneg_of_pos hrows₀ hcols href_pos hr₁ hc) href₁_ne.symm + let e := OrderEmbedding.ofStrictMono r₁ hr₁ + refine ⟨Set.powersetCard.ofFinEmbEquiv e, Finset.mem_univ _, ?_⟩ + simpa [e] using mul_pos (hL hr hr₁) href₁_pos + exact mul_pos (hpos hrows hcols) (hpos hrows' hcols') + +/-- Karlin's Gaussian left multiplier strictifies a full-column-rank +sign-consistent matrix. -/ +theorem IsSignConsistentOrder.isStrictlySignConsistentOrder_gaussianMatrix_mul + {n m q : ℕ} {A : Matrix (Fin n) (Fin m) ℝ} + (hA : A.IsSignConsistentOrder q) (hAinj : Function.Injective A.mulVec) + {a : ℝ} (ha : 0 < a) : + (gaussianMatrix n a * A).IsStrictlySignConsistentOrder q := by + apply hA.isStrictlySignConsistentOrder_mul_of_posMinors hAinj + intro rows cols hrows hcols + exact det_gaussianMatrix_submatrix_pos a rows cols ha hrows hcols + +/-- Gaussian left multiplication converges entrywise to the original matrix. -/ +theorem tendsto_gaussianMatrix_mul_atTop {n m : ℕ} + (A : Matrix (Fin n) (Fin m) ℝ) : + Tendsto (fun a => gaussianMatrix n a * A) atTop (𝓝 A) := by + rw [tendsto_pi_nhds] + intro i + rw [tendsto_pi_nhds] + intro j + have hG : ∀ k, Tendsto (fun a => gaussianMatrix n a i k) atTop + (𝓝 ((1 : Matrix (Fin n) (Fin n) ℝ) i k)) := by + intro k + exact tendsto_pi_nhds.mp + (tendsto_pi_nhds.mp (tendsto_gaussianMatrix_atTop n) i) k + have hsum : + Tendsto (fun a => ∑ k, gaussianMatrix n a i k * A k j) atTop + (𝓝 (∑ k, (1 : Matrix (Fin n) (Fin n) ℝ) i k * A k j)) := + tendsto_finset_sum Finset.univ (fun k _ => (hG k).mul_const (A k j)) + rw [show (∑ k, (1 : Matrix (Fin n) (Fin n) ℝ) i k * A k j) = A i j by + rw [← Matrix.mul_apply, Matrix.one_mul]] at hsum + simpa only [Matrix.mul_apply] using hsum + +end Matrix From b0adb9e35cdf4a840c5b69b837a8f0b049c5cfc4 Mon Sep 17 00:00:00 2001 From: Per Alexandersson Date: Sun, 2 Aug 2026 23:09:40 +0000 Subject: [PATCH 036/141] Reuse matrix multiplication continuity --- .../Matrix/SignRegularStrictification.lean | 22 +++++-------------- 1 file changed, 5 insertions(+), 17 deletions(-) diff --git a/RealRooted/Mathlib/LinearAlgebra/Matrix/SignRegularStrictification.lean b/RealRooted/Mathlib/LinearAlgebra/Matrix/SignRegularStrictification.lean index 452d1fd7..5b0d9dc0 100644 --- a/RealRooted/Mathlib/LinearAlgebra/Matrix/SignRegularStrictification.lean +++ b/RealRooted/Mathlib/LinearAlgebra/Matrix/SignRegularStrictification.lean @@ -14,7 +14,7 @@ lets the Gaussian parameter tend to infinity. public section open Filter -open scoped BigOperators Topology +open scoped Topology namespace Matrix @@ -91,21 +91,9 @@ theorem IsSignConsistentOrder.isStrictlySignConsistentOrder_gaussianMatrix_mul theorem tendsto_gaussianMatrix_mul_atTop {n m : ℕ} (A : Matrix (Fin n) (Fin m) ℝ) : Tendsto (fun a => gaussianMatrix n a * A) atTop (𝓝 A) := by - rw [tendsto_pi_nhds] - intro i - rw [tendsto_pi_nhds] - intro j - have hG : ∀ k, Tendsto (fun a => gaussianMatrix n a i k) atTop - (𝓝 ((1 : Matrix (Fin n) (Fin n) ℝ) i k)) := by - intro k - exact tendsto_pi_nhds.mp - (tendsto_pi_nhds.mp (tendsto_gaussianMatrix_atTop n) i) k - have hsum : - Tendsto (fun a => ∑ k, gaussianMatrix n a i k * A k j) atTop - (𝓝 (∑ k, (1 : Matrix (Fin n) (Fin n) ℝ) i k * A k j)) := - tendsto_finset_sum Finset.univ (fun k _ => (hG k).mul_const (A k j)) - rw [show (∑ k, (1 : Matrix (Fin n) (Fin n) ℝ) i k * A k j) = A i j by - rw [← Matrix.mul_apply, Matrix.one_mul]] at hsum - simpa only [Matrix.mul_apply] using hsum + have hmul : Continuous (fun M : Matrix (Fin n) (Fin n) ℝ => M * A) := + continuous_id.matrix_mul continuous_const + simpa only [Matrix.one_mul] using + hmul.continuousAt.tendsto.comp (tendsto_gaussianMatrix_atTop n) end Matrix From d6819a7419da9c7553590b0e2a940000cec12b7a Mon Sep 17 00:00:00 2001 From: Per Alexandersson Date: Sun, 2 Aug 2026 23:21:31 +0000 Subject: [PATCH 037/141] Prove lower semicontinuity of sign variations --- RealRooted.lean | 1 + .../Matrix/SignVariationTopology.lean | 85 +++++++++++++++++++ 2 files changed, 86 insertions(+) create mode 100644 RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariationTopology.lean diff --git a/RealRooted.lean b/RealRooted.lean index d23495bc..41806314 100644 --- a/RealRooted.lean +++ b/RealRooted.lean @@ -210,6 +210,7 @@ import RealRooted.Mathlib.LinearAlgebra.Matrix.KernelSignVariation import RealRooted.Mathlib.LinearAlgebra.Matrix.SignRegular import RealRooted.Mathlib.LinearAlgebra.Matrix.SignRegularStrictification import RealRooted.Mathlib.LinearAlgebra.Matrix.SignVariation +import RealRooted.Mathlib.LinearAlgebra.Matrix.SignVariationTopology import RealRooted.Mathlib.LinearAlgebra.Matrix.TotallyNonneg import RealRooted.Mathlib.LinearAlgebra.Matrix.VariationDiminishing import RealRooted.Mathlib.LinearAlgebra.Vandermonde diff --git a/RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariationTopology.lean b/RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariationTopology.lean new file mode 100644 index 00000000..5bd5d722 --- /dev/null +++ b/RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariationTopology.lean @@ -0,0 +1,85 @@ +import RealRooted.Mathlib.LinearAlgebra.Matrix.SignVariation + +/-! +# Topological properties of sign variations + +This file proves the finite-dimensional lower semicontinuity of sign variations. +The result supplies the limit step in Karlin's Gaussian approximation argument +for variation-diminishing matrices. +-/ + +open Filter Topology + +namespace Fin + +/-- Sign variations cannot increase when a convergent net reaches its limit. -/ +theorem signVariations_le_of_tendsto + {α : Type*} {l : Filter α} [l.NeBot] {n r : ℕ} + {f : α → Fin n → ℝ} {x : Fin n → ℝ} + (hf : Tendsto f l (𝓝 x)) + (hr : ∀ᶠ a in l, signVariations (f a) ≤ r) : + signVariations x ≤ r := by + let raw : List SignType := List.ofFn (SignType.sign ∘ x) + let nz : List SignType := raw.filter (· ≠ 0) + let d : List SignType := nz.destutter (· ≠ ·) + have hsub : d.Sublist raw := + (List.destutter_sublist (· ≠ ·) nz).trans List.filter_sublist + obtain ⟨e, he⟩ := + List.sublist_iff_exists_fin_orderEmbedding_get_eq.mp hsub + have hrawlen : raw.length = n := by + simp [raw] + let eN : Fin d.length ↪o Fin n := + e.trans (Fin.castOrderIso hrawlen).toOrderEmbedding + have he' (k : Fin d.length) : + d.get k = SignType.sign (x (eN k)) := by + rw [he k] + simp only [raw, List.get_ofFn, Function.comp_apply] + congr 2 + have hd_ne (z : SignType) (hz : z ∈ d) : z ≠ 0 := by + have hz_nz : z ∈ nz := + (List.destutter_sublist (· ≠ ·) nz).mem hz + exact of_decide_eq_true (List.mem_filter.mp hz_nz).2 + have hd_filter : d.filter (· ≠ 0) = d := by + rw [List.filter_eq_self] + intro z hz + simp [hd_ne z hz] + have hsign : + ∀ k : Fin d.length, + ∀ᶠ a in l, d.get k = SignType.sign (f a (eN k)) := by + intro k + have hk := tendsto_pi_nhds.mp hf (eN k) + have hxne : x (eN k) ≠ 0 := by + rw [← sign_ne_zero, ← he' k] + exact hd_ne _ (List.get_mem d k) + rcases lt_or_gt_of_ne hxne with hxneg | hxpos + · filter_upwards [hk.eventually_lt_const hxneg] with a ha + rw [he' k, sign_neg hxneg, sign_neg ha] + · filter_upwards [hk.eventually_const_lt hxpos] with a ha + rw [he' k, sign_pos hxpos, sign_pos ha] + have hmono : + ∀ᶠ a in l, signVariations x ≤ signVariations (f a) := by + filter_upwards [Filter.eventually_all.mpr hsign] with a ha + let rawA : List SignType := List.ofFn (SignType.sign ∘ f a) + have hrawAlen : rawA.length = n := by + simp [rawA] + let eA : Fin d.length ↪o Fin rawA.length := + eN.trans (Fin.castOrderIso hrawAlen.symm).toOrderEmbedding + have hdsub : d.Sublist rawA := by + apply List.sublist_iff_exists_fin_orderEmbedding_get_eq.mpr + refine ⟨eA, ?_⟩ + intro k + rw [ha k] + simp only [rawA, List.get_ofFn, Function.comp_apply] + congr 2 + have hdsub_nz : d.Sublist (rawA.filter (· ≠ 0)) := by + have h := hdsub.filter (· ≠ 0) + rw [hd_filter] at h + exact h + have hlen := + (List.isChain_destutter (· ≠ ·) nz).length_le_length_destutter_ne hdsub_nz + simpa only [signVariations, List.signVariations, List.map_ofFn, d, nz, raw, + rawA] using Nat.sub_le_sub_right hlen 1 + obtain ⟨a, hxa, har⟩ := (hmono.and hr).exists + exact hxa.trans har + +end Fin From b99e0e5ace96644a3633787197052c8ea18a22f6 Mon Sep 17 00:00:00 2001 From: Per Alexandersson Date: Sun, 2 Aug 2026 23:28:32 +0000 Subject: [PATCH 038/141] Prove full-rank sign variation bound --- RealRooted.lean | 1 + .../Matrix/SignRegularVariation.lean | 39 +++++++++++++++++++ 2 files changed, 40 insertions(+) create mode 100644 RealRooted/Mathlib/LinearAlgebra/Matrix/SignRegularVariation.lean diff --git a/RealRooted.lean b/RealRooted.lean index 41806314..b0980992 100644 --- a/RealRooted.lean +++ b/RealRooted.lean @@ -209,6 +209,7 @@ import RealRooted.Mathlib.LinearAlgebra.Matrix.Gaussian import RealRooted.Mathlib.LinearAlgebra.Matrix.KernelSignVariation import RealRooted.Mathlib.LinearAlgebra.Matrix.SignRegular import RealRooted.Mathlib.LinearAlgebra.Matrix.SignRegularStrictification +import RealRooted.Mathlib.LinearAlgebra.Matrix.SignRegularVariation import RealRooted.Mathlib.LinearAlgebra.Matrix.SignVariation import RealRooted.Mathlib.LinearAlgebra.Matrix.SignVariationTopology import RealRooted.Mathlib.LinearAlgebra.Matrix.TotallyNonneg diff --git a/RealRooted/Mathlib/LinearAlgebra/Matrix/SignRegularVariation.lean b/RealRooted/Mathlib/LinearAlgebra/Matrix/SignRegularVariation.lean new file mode 100644 index 00000000..e57ca6f0 --- /dev/null +++ b/RealRooted/Mathlib/LinearAlgebra/Matrix/SignRegularVariation.lean @@ -0,0 +1,39 @@ +import RealRooted.Mathlib.LinearAlgebra.Matrix.SignRegularStrictification +import RealRooted.Mathlib.LinearAlgebra.Matrix.SignVariationTopology +import RealRooted.Mathlib.LinearAlgebra.Matrix.VariationDiminishing + +/-! +# Variation bounds for sign-consistent matrices + +This file proves the full-column-rank case of the variation bound in Karlin, +*Total Positivity*, Volume I, Chapter V, Section 1, Theorem 1.3. Gaussian +strictification reduces the result to the strict maximal-minor theorem, and +lower semicontinuity of sign variations passes the bound to the limit. +-/ + +open Filter Topology + +/-- A full-column-rank sign-consistent matrix has at most `q - 1` sign +variations in every vector in its range. -/ +theorem Matrix.IsSignConsistentOrder.signVariations_mulVec_le_card_sub_one + {m q : ℕ} {A : Matrix (Fin m) (Fin q) ℝ} + (hA : A.IsSignConsistentOrder q) + (hqm : q ≤ m) + (hAinj : Function.Injective A.mulVec) + (x : Fin q → ℝ) : + Fin.signVariations (A.mulVec x) ≤ q - 1 := by + refine Fin.signVariations_le_of_tendsto + (l := (atTop : Filter ℝ)) + (f := fun a => (Matrix.gaussianMatrix m a * A).mulVec x) ?_ ?_ + · have hcont : Continuous + (fun M : Matrix (Fin m) (Fin q) ℝ => M.mulVec x) := + continuous_id.matrix_mulVec continuous_const + exact hcont.continuousAt.tendsto.comp + (Matrix.tendsto_gaussianMatrix_mul_atTop A) + · filter_upwards [Filter.eventually_gt_atTop (0 : ℝ)] with a ha + have hstrict : + (Matrix.gaussianMatrix m a * A).IsStrictlySignConsistentOrder q := + hA.isStrictlySignConsistentOrder_gaussianMatrix_mul hAinj ha + exact Matrix.signVariations_mulVec_le_card_sub_one_of_strictMaximalMinors + hqm (fun ⦃rows rows'⦄ hrows hrows' => + hstrict hrows hrows' strictMono_id strictMono_id) x From 845e1612e207af3e4ec901d6e091cb0099e74aef Mon Sep 17 00:00:00 2001 From: Per Alexandersson Date: Sun, 2 Aug 2026 23:33:17 +0000 Subject: [PATCH 039/141] Golf sign variation limit proofs --- .../Mathlib/LinearAlgebra/Matrix/SignRegularVariation.lean | 2 +- .../Mathlib/LinearAlgebra/Matrix/SignVariationTopology.lean | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/RealRooted/Mathlib/LinearAlgebra/Matrix/SignRegularVariation.lean b/RealRooted/Mathlib/LinearAlgebra/Matrix/SignRegularVariation.lean index e57ca6f0..e2f8e23e 100644 --- a/RealRooted/Mathlib/LinearAlgebra/Matrix/SignRegularVariation.lean +++ b/RealRooted/Mathlib/LinearAlgebra/Matrix/SignRegularVariation.lean @@ -11,7 +11,7 @@ strictification reduces the result to the strict maximal-minor theorem, and lower semicontinuity of sign variations passes the bound to the limit. -/ -open Filter Topology +open Filter /-- A full-column-rank sign-consistent matrix has at most `q - 1` sign variations in every vector in its range. -/ diff --git a/RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariationTopology.lean b/RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariationTopology.lean index 5bd5d722..45787abd 100644 --- a/RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariationTopology.lean +++ b/RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariationTopology.lean @@ -72,9 +72,7 @@ theorem signVariations_le_of_tendsto simp only [rawA, List.get_ofFn, Function.comp_apply] congr 2 have hdsub_nz : d.Sublist (rawA.filter (· ≠ 0)) := by - have h := hdsub.filter (· ≠ 0) - rw [hd_filter] at h - exact h + simpa only [hd_filter] using hdsub.filter (· ≠ 0) have hlen := (List.isChain_destutter (· ≠ ·) nz).length_le_length_destutter_ne hdsub_nz simpa only [signVariations, List.signVariations, List.map_ofFn, d, nz, raw, From 99bdbb66e91e9f97fb7eb91ddf02517ad4881399 Mon Sep 17 00:00:00 2001 From: Per Alexandersson Date: Sun, 2 Aug 2026 23:48:21 +0000 Subject: [PATCH 040/141] Preserve alternating rows under kernel perturbation --- RealRooted.lean | 1 + .../Matrix/SignRegularRankDeficient.lean | 43 +++++++++++++++++++ 2 files changed, 44 insertions(+) create mode 100644 RealRooted/Mathlib/LinearAlgebra/Matrix/SignRegularRankDeficient.lean diff --git a/RealRooted.lean b/RealRooted.lean index b0980992..73f4e399 100644 --- a/RealRooted.lean +++ b/RealRooted.lean @@ -208,6 +208,7 @@ import RealRooted.Mathlib.LinearAlgebra.Matrix.Determinant.Integral import RealRooted.Mathlib.LinearAlgebra.Matrix.Gaussian import RealRooted.Mathlib.LinearAlgebra.Matrix.KernelSignVariation import RealRooted.Mathlib.LinearAlgebra.Matrix.SignRegular +import RealRooted.Mathlib.LinearAlgebra.Matrix.SignRegularRankDeficient import RealRooted.Mathlib.LinearAlgebra.Matrix.SignRegularStrictification import RealRooted.Mathlib.LinearAlgebra.Matrix.SignRegularVariation import RealRooted.Mathlib.LinearAlgebra.Matrix.SignVariation diff --git a/RealRooted/Mathlib/LinearAlgebra/Matrix/SignRegularRankDeficient.lean b/RealRooted/Mathlib/LinearAlgebra/Matrix/SignRegularRankDeficient.lean new file mode 100644 index 00000000..7feb1402 --- /dev/null +++ b/RealRooted/Mathlib/LinearAlgebra/Matrix/SignRegularRankDeficient.lean @@ -0,0 +1,43 @@ +import RealRooted.Mathlib.LinearAlgebra.Matrix.SignRegularVariation + +/-! +# Rank-deficient sign-consistent matrices + +This file develops the rank-deficient induction in Karlin, *Total Positivity*, +Volume I, Chapter V, Section 1, Theorem 1.3. The first lemmas formalize the +selected-row kernel perturbation in equations (1.1) on printed pages 221--222. +-/ + +/-- Perturbing coefficients along a vector annihilated by a selected-row +submatrix does not change the selected coordinates of the matrix-vector +product. -/ +theorem Matrix.mulVec_add_smul_apply_eq_of_submatrix_mulVec_eq_zero + {n m k : ℕ} (A : Matrix (Fin n) (Fin m) ℝ) + (rows : Fin k → Fin n) (c z : Fin m → ℝ) + (hz : (A.submatrix rows id).mulVec z = 0) + (t : ℝ) (i : Fin k) : + A.mulVec (c + t • z) (rows i) = A.mulVec c (rows i) := by + have hzi : A.mulVec z (rows i) = 0 := by + have hi := congrFun hz i + simpa only [Matrix.mulVec, dotProduct, Matrix.submatrix_apply, id_eq, + Pi.zero_apply] using hi + rw [Matrix.mulVec_add, Matrix.mulVec_smul] + simp [hzi] + +/-- A selected alternating witness survives Karlin's kernel perturbation of +the coefficient vector. -/ +theorem Fin.StrictlyAlternates.matrix_mulVec_add_smul + {n m k : ℕ} {A : Matrix (Fin n) (Fin m) ℝ} + {rows : Fin (k + 1) → Fin n} {c z : Fin m → ℝ} + (h : StrictlyAlternates (fun i => A.mulVec c (rows i))) + (hz : (A.submatrix rows id).mulVec z = 0) + (t : ℝ) : + StrictlyAlternates (fun i => A.mulVec (c + t • z) (rows i)) := by + intro i + change + A.mulVec (c + t • z) (rows i.castSucc) * + A.mulVec (c + t • z) (rows i.succ) < 0 + have heq (j : Fin (k + 1)) := + A.mulVec_add_smul_apply_eq_of_submatrix_mulVec_eq_zero rows c z hz t j + rw [heq i.castSucc, heq i.succ] + exact h i From 9c22309912af7cdb7649e84d972cebbb243c6fbc Mon Sep 17 00:00:00 2001 From: Per Alexandersson Date: Sun, 2 Aug 2026 23:57:43 +0000 Subject: [PATCH 041/141] Extract ordered nonzero rank minors --- .../Matrix/SignRegularRankDeficient.lean | 53 +++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/RealRooted/Mathlib/LinearAlgebra/Matrix/SignRegularRankDeficient.lean b/RealRooted/Mathlib/LinearAlgebra/Matrix/SignRegularRankDeficient.lean index 7feb1402..6383ef02 100644 --- a/RealRooted/Mathlib/LinearAlgebra/Matrix/SignRegularRankDeficient.lean +++ b/RealRooted/Mathlib/LinearAlgebra/Matrix/SignRegularRankDeficient.lean @@ -1,3 +1,5 @@ +import Mathlib.LinearAlgebra.LinearIndependent.Lemmas +import Mathlib.LinearAlgebra.Matrix.Rank import RealRooted.Mathlib.LinearAlgebra.Matrix.SignRegularVariation /-! @@ -8,6 +10,57 @@ Volume I, Chapter V, Section 1, Theorem 1.3. The first lemmas formalize the selected-row kernel perturbation in equations (1.1) on printed pages 221--222. -/ +/-- A finite matrix of rank `r` has a nonzero `r`-minor with increasingly +ordered row and column selectors. -/ +theorem Matrix.exists_ordered_minor_ne_zero_of_rank_eq + {R : Type*} [Field R] {n m r : ℕ} + (A : Matrix (Fin n) (Fin m) R) (hrank : A.rank = r) : + ∃ rows : Fin r → Fin n, ∃ cols : Fin r → Fin m, + StrictMono rows ∧ StrictMono cols ∧ + (A.submatrix rows cols).det ≠ 0 := by + obtain ⟨κ, a, ha, hspan, hli⟩ := + exists_linearIndependent' R A.col + letI : Finite κ := Finite.of_injective a ha + letI : Fintype κ := Fintype.ofFinite κ + have hcard : Fintype.card κ = r := by + calc + Fintype.card κ = + Module.finrank R (Submodule.span R (Set.range (A.col ∘ a))) := + (finrank_span_eq_card hli).symm + _ = Module.finrank R (Submodule.span R (Set.range A.col)) := by + rw [hspan] + _ = A.rank := (Matrix.rank_eq_finrank_span_cols A).symm + _ = r := hrank + let e : Fin r ≃ κ := (Fintype.equivFinOfCardEq hcard).symm + let f : Fin r → Fin m := a ∘ e + have hf : Function.Injective f := ha.comp e.injective + have hli_f : LinearIndependent R (A.col ∘ f) := by + simpa only [f, Function.comp_assoc] using hli.comp e e.injective + let B : Matrix (Fin n) (Fin r) R := A.submatrix id f + have hB : Function.Injective B.mulVec := by + rw [Matrix.mulVec_injective_iff] + change LinearIndependent R (A.col ∘ f) + exact hli_f + obtain ⟨rows, hrows, hdet⟩ := + Matrix.exists_ordered_minor_ne_zero_of_mulVec_injective + B hB id strictMono_id + have hdetf : (A.submatrix rows f).det ≠ 0 := by + simpa only [B, Matrix.submatrix_submatrix, Function.id_comp, + Function.comp_id] using hdet + obtain ⟨s, p, hp⟩ := + Set.powersetCard.exists_orderEmb_comp_perm_eq_of_injective f hf + let cols : Fin r ↪o Fin m := Set.powersetCard.ofFinEmbEquiv.symm s + refine ⟨rows, cols, hrows, cols.strictMono, ?_⟩ + intro hzero + apply hdetf + have hmatrix : + A.submatrix rows f = + (A.submatrix rows cols).submatrix id p := by + ext i j + simp only [Matrix.submatrix_apply, id_eq] + rw [hp j] + rw [hmatrix, Matrix.det_permute', hzero, mul_zero] + /-- Perturbing coefficients along a vector annihilated by a selected-row submatrix does not change the selected coordinates of the matrix-vector product. -/ From 3c419fbf85ba8871962ddb6149fffa28e5da3f5b Mon Sep 17 00:00:00 2001 From: Per Alexandersson Date: Mon, 3 Aug 2026 00:04:12 +0000 Subject: [PATCH 042/141] Golf rank minor independence proof --- .../LinearAlgebra/Matrix/SignRegularRankDeficient.lean | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/RealRooted/Mathlib/LinearAlgebra/Matrix/SignRegularRankDeficient.lean b/RealRooted/Mathlib/LinearAlgebra/Matrix/SignRegularRankDeficient.lean index 6383ef02..7c014721 100644 --- a/RealRooted/Mathlib/LinearAlgebra/Matrix/SignRegularRankDeficient.lean +++ b/RealRooted/Mathlib/LinearAlgebra/Matrix/SignRegularRankDeficient.lean @@ -34,13 +34,11 @@ theorem Matrix.exists_ordered_minor_ne_zero_of_rank_eq let e : Fin r ≃ κ := (Fintype.equivFinOfCardEq hcard).symm let f : Fin r → Fin m := a ∘ e have hf : Function.Injective f := ha.comp e.injective - have hli_f : LinearIndependent R (A.col ∘ f) := by - simpa only [f, Function.comp_assoc] using hli.comp e e.injective let B : Matrix (Fin n) (Fin r) R := A.submatrix id f have hB : Function.Injective B.mulVec := by rw [Matrix.mulVec_injective_iff] change LinearIndependent R (A.col ∘ f) - exact hli_f + simpa only [f, Function.comp_assoc] using hli.comp e e.injective obtain ⟨rows, hrows, hdet⟩ := Matrix.exists_ordered_minor_ne_zero_of_mulVec_injective B hB id strictMono_id From 0333da3de78cf89505f7b1a92312549f38278983 Mon Sep 17 00:00:00 2001 From: Per Alexandersson Date: Mon, 3 Aug 2026 00:13:16 +0000 Subject: [PATCH 043/141] Preserve rank after deleting an avoided column --- .../Matrix/SignRegularRankDeficient.lean | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/RealRooted/Mathlib/LinearAlgebra/Matrix/SignRegularRankDeficient.lean b/RealRooted/Mathlib/LinearAlgebra/Matrix/SignRegularRankDeficient.lean index 7c014721..a0e8efaa 100644 --- a/RealRooted/Mathlib/LinearAlgebra/Matrix/SignRegularRankDeficient.lean +++ b/RealRooted/Mathlib/LinearAlgebra/Matrix/SignRegularRankDeficient.lean @@ -59,6 +59,32 @@ theorem Matrix.exists_ordered_minor_ne_zero_of_rank_eq rw [hp j] rw [hmatrix, Matrix.det_permute', hzero, mul_zero] +/-- Deleting a column preserves the rank when a maximal nonzero minor avoids +that column. This is the common rank step in Karlin's two deficient-rank +branches; the cofactor and rank-basis kernel constructions remain separate. -/ +theorem Matrix.rank_deleteColumn_eq_of_minor_ne_zero + {R : Type*} [Field R] {n k r : ℕ} + (A : Matrix (Fin n) (Fin (k + 1)) R) + (hrank : A.rank = r) (j0 : Fin (k + 1)) + (rows : Fin r → Fin n) (cols : Fin r → Fin k) + (hminor : + (A.submatrix rows (j0.succAbove ∘ cols)).det ≠ 0) : + (A.submatrix id j0.succAbove).rank = r := by + let A' : Matrix (Fin n) (Fin k) R := A.submatrix id j0.succAbove + have hdet : (A'.submatrix rows cols).det ≠ 0 := by + simpa only [A', Matrix.submatrix_submatrix, Function.id_comp] using hminor + have hunit : IsUnit (A'.submatrix rows cols) := by + rw [Matrix.isUnit_iff_isUnit_det, isUnit_iff_ne_zero] + exact hdet + apply le_antisymm + · calc + A'.rank ≤ A.rank := Matrix.rank_submatrix_le A id j0.succAbove + _ = r := hrank + · calc + r = (A'.submatrix rows cols).rank := by + simpa using (Matrix.rank_of_isUnit _ hunit).symm + _ ≤ A'.rank := Matrix.rank_submatrix_le A' rows cols + /-- Perturbing coefficients along a vector annihilated by a selected-row submatrix does not change the selected coordinates of the matrix-vector product. -/ From a640e3e689478cb768f473d278c345be40c84be8 Mon Sep 17 00:00:00 2001 From: Per Alexandersson Date: Mon, 3 Aug 2026 00:20:22 +0000 Subject: [PATCH 044/141] Prove the signed row-cofactor kernel identity --- .../Matrix/SignRegularRankDeficient.lean | 54 +++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/RealRooted/Mathlib/LinearAlgebra/Matrix/SignRegularRankDeficient.lean b/RealRooted/Mathlib/LinearAlgebra/Matrix/SignRegularRankDeficient.lean index a0e8efaa..80e142b3 100644 --- a/RealRooted/Mathlib/LinearAlgebra/Matrix/SignRegularRankDeficient.lean +++ b/RealRooted/Mathlib/LinearAlgebra/Matrix/SignRegularRankDeficient.lean @@ -59,6 +59,60 @@ theorem Matrix.exists_ordered_minor_ne_zero_of_rank_eq rw [hp j] rw [hmatrix, Matrix.det_permute', hzero, mul_zero] +/-- Karlin's signed row-cofactor vector of a singular square matrix lies in +its kernel. The retained-row equations come from the rectangular alternating +minor identity; Laplace expansion supplies the omitted-row equation. -/ +theorem Matrix.mulVec_signedRowCofactor_eq_zero_of_det_eq_zero + {R : Type*} [CommRing R] {k : ℕ} + (B : Matrix (Fin (k + 1)) (Fin (k + 1)) R) + (i0 : Fin (k + 1)) (hdet : B.det = 0) : + B.mulVec (fun j => (-1 : R) ^ (i0 + j : ℕ) * + (B.submatrix i0.succAbove j.succAbove).det) = 0 := by + let C : Matrix (Fin (k + 1)) (Fin k) R := + (B.submatrix i0.succAbove id).transpose + have hminor (j : Fin (k + 1)) : + (C.submatrix j.succAbove id).det = + (B.submatrix i0.succAbove j.succAbove).det := by + have hmatrix : + C.submatrix j.succAbove id = + (B.submatrix i0.succAbove j.succAbove).transpose := by + ext a b + rfl + rw [hmatrix, Matrix.det_transpose] + let z0 : Fin (k + 1) → R := fun j => + (-1 : R) ^ (j : ℕ) * (B.submatrix i0.succAbove j.succAbove).det + let z : Fin (k + 1) → R := fun j => + (-1 : R) ^ (i0 + j : ℕ) * (B.submatrix i0.succAbove j.succAbove).det + have hremoved0 : (B.submatrix i0.succAbove id).mulVec z0 = 0 := by + have hkernel := + Matrix.transpose_mulVec_alternating_det_submatrix_succAbove C + simp_rw [hminor] at hkernel + simpa only [C, Matrix.transpose_transpose, z0] using hkernel + have hz : z = (-1 : R) ^ (i0 : ℕ) • z0 := by + funext j + simp only [z, z0, Pi.smul_apply, smul_eq_mul, pow_add] + ring + have hremoved : (B.submatrix i0.succAbove id).mulVec z = 0 := by + rw [hz, Matrix.mulVec_smul, hremoved0, smul_zero] + change B.mulVec z = 0 + apply funext + refine Fin.succAboveCases i0 ?_ (fun i => ?_) + · simp only [Matrix.mulVec, dotProduct, z, Pi.zero_apply] + calc + ∑ j : Fin (k + 1), B i0 j * ((-1 : R) ^ + ((i0 : ℕ) + (j : ℕ)) * + (B.submatrix i0.succAbove j.succAbove).det) = + ∑ j : Fin (k + 1), (-1 : R) ^ ((i0 : ℕ) + (j : ℕ)) * + B i0 j * (B.submatrix i0.succAbove j.succAbove).det := by + apply Finset.sum_congr rfl + intro j _ + ring + _ = B.det := (Matrix.det_succ_row B i0).symm + _ = 0 := hdet + · have hi := congrFun hremoved i + simpa only [Matrix.mulVec, dotProduct, Matrix.submatrix_apply, id_eq, + Pi.zero_apply] using hi + /-- Deleting a column preserves the rank when a maximal nonzero minor avoids that column. This is the common rank step in Karlin's two deficient-rank branches; the cofactor and rank-basis kernel constructions remain separate. -/ From 4e2824529e144a3a0197109031854e14605ebb01 Mon Sep 17 00:00:00 2001 From: Per Alexandersson Date: Mon, 3 Aug 2026 00:22:26 +0000 Subject: [PATCH 045/141] Golf signed cofactor kernel proof --- .../Matrix/SignRegularRankDeficient.lean | 28 ++++++++----------- 1 file changed, 12 insertions(+), 16 deletions(-) diff --git a/RealRooted/Mathlib/LinearAlgebra/Matrix/SignRegularRankDeficient.lean b/RealRooted/Mathlib/LinearAlgebra/Matrix/SignRegularRankDeficient.lean index 80e142b3..1faa89b6 100644 --- a/RealRooted/Mathlib/LinearAlgebra/Matrix/SignRegularRankDeficient.lean +++ b/RealRooted/Mathlib/LinearAlgebra/Matrix/SignRegularRankDeficient.lean @@ -68,31 +68,27 @@ theorem Matrix.mulVec_signedRowCofactor_eq_zero_of_det_eq_zero (i0 : Fin (k + 1)) (hdet : B.det = 0) : B.mulVec (fun j => (-1 : R) ^ (i0 + j : ℕ) * (B.submatrix i0.succAbove j.succAbove).det) = 0 := by - let C : Matrix (Fin (k + 1)) (Fin k) R := - (B.submatrix i0.succAbove id).transpose + let C : Matrix (Fin k) (Fin (k + 1)) R := + B.submatrix i0.succAbove id have hminor (j : Fin (k + 1)) : - (C.submatrix j.succAbove id).det = + ((C.transpose).submatrix j.succAbove id).det = (B.submatrix i0.succAbove j.succAbove).det := by - have hmatrix : - C.submatrix j.succAbove id = - (B.submatrix i0.succAbove j.succAbove).transpose := by - ext a b - rfl - rw [hmatrix, Matrix.det_transpose] + rw [← Matrix.det_transpose] + rfl let z0 : Fin (k + 1) → R := fun j => (-1 : R) ^ (j : ℕ) * (B.submatrix i0.succAbove j.succAbove).det let z : Fin (k + 1) → R := fun j => (-1 : R) ^ (i0 + j : ℕ) * (B.submatrix i0.succAbove j.succAbove).det - have hremoved0 : (B.submatrix i0.succAbove id).mulVec z0 = 0 := by + have hremoved0 : C.mulVec z0 = 0 := by have hkernel := - Matrix.transpose_mulVec_alternating_det_submatrix_succAbove C + Matrix.transpose_mulVec_alternating_det_submatrix_succAbove C.transpose + rw [Matrix.transpose_transpose] at hkernel simp_rw [hminor] at hkernel - simpa only [C, Matrix.transpose_transpose, z0] using hkernel + simpa only [z0] using hkernel have hz : z = (-1 : R) ^ (i0 : ℕ) • z0 := by funext j - simp only [z, z0, Pi.smul_apply, smul_eq_mul, pow_add] - ring - have hremoved : (B.submatrix i0.succAbove id).mulVec z = 0 := by + simp only [z, z0, Pi.smul_apply, smul_eq_mul, pow_add, mul_assoc] + have hremoved : C.mulVec z = 0 := by rw [hz, Matrix.mulVec_smul, hremoved0, smul_zero] change B.mulVec z = 0 apply funext @@ -110,7 +106,7 @@ theorem Matrix.mulVec_signedRowCofactor_eq_zero_of_det_eq_zero _ = B.det := (Matrix.det_succ_row B i0).symm _ = 0 := hdet · have hi := congrFun hremoved i - simpa only [Matrix.mulVec, dotProduct, Matrix.submatrix_apply, id_eq, + simpa only [C, Matrix.mulVec, dotProduct, Matrix.submatrix_apply, id_eq, Pi.zero_apply] using hi /-- Deleting a column preserves the rank when a maximal nonzero minor avoids From 729561ead2e05e4dda7aab94100efde8ed31e254 Mon Sep 17 00:00:00 2001 From: Per Alexandersson Date: Mon, 3 Aug 2026 00:28:02 +0000 Subject: [PATCH 046/141] Compose Karlin's nonzero-cofactor branch --- .../Matrix/SignRegularRankDeficient.lean | 65 +++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/RealRooted/Mathlib/LinearAlgebra/Matrix/SignRegularRankDeficient.lean b/RealRooted/Mathlib/LinearAlgebra/Matrix/SignRegularRankDeficient.lean index 1faa89b6..5270ed12 100644 --- a/RealRooted/Mathlib/LinearAlgebra/Matrix/SignRegularRankDeficient.lean +++ b/RealRooted/Mathlib/LinearAlgebra/Matrix/SignRegularRankDeficient.lean @@ -135,6 +135,71 @@ theorem Matrix.rank_deleteColumn_eq_of_minor_ne_zero simpa using (Matrix.rank_of_isUnit _ hunit).symm _ ≤ A'.rank := Matrix.rank_submatrix_le A' rows cols +/-- Karlin's nonzero-cofactor branch for a selected singular square submatrix. +The displayed signed cofactor vector witnesses the selected-row kernel, while +the same cofactor proves both the ambient rank and rank-preserving deletion. -/ +theorem Matrix.signedRowCofactor_spec_of_minor_ne_zero + {R : Type*} [Field R] {n k : ℕ} + (A : Matrix (Fin n) (Fin (k + 1)) R) + (rows : Fin (k + 1) → Fin n) (i0 j0 : Fin (k + 1)) + (hrank_lt : A.rank < k + 1) + (hminor : + ((A.submatrix rows id).submatrix + i0.succAbove j0.succAbove).det ≠ 0) : + let B := A.submatrix rows id + let z : Fin (k + 1) → R := fun j => + (-1 : R) ^ (i0 + j : ℕ) * + (B.submatrix i0.succAbove j.succAbove).det + A.rank = k ∧ B.mulVec z = 0 ∧ z j0 ≠ 0 ∧ + (A.submatrix id j0.succAbove).rank = k := by + let B : Matrix (Fin (k + 1)) (Fin (k + 1)) R := + A.submatrix rows id + let z : Fin (k + 1) → R := fun j => + (-1 : R) ^ (i0 + j : ℕ) * + (B.submatrix i0.succAbove j.succAbove).det + change A.rank = k ∧ B.mulVec z = 0 ∧ z j0 ≠ 0 ∧ + (A.submatrix id j0.succAbove).rank = k + have hunit : IsUnit (B.submatrix i0.succAbove j0.succAbove) := by + rw [Matrix.isUnit_iff_isUnit_det, isUnit_iff_ne_zero] + simpa only [B] using hminor + have hcofactor_rank : + (B.submatrix i0.succAbove j0.succAbove).rank = k := by + simpa using Matrix.rank_of_isUnit _ hunit + have hrank_ge : k ≤ A.rank := by + calc + k = (B.submatrix i0.succAbove j0.succAbove).rank := + hcofactor_rank.symm + _ ≤ B.rank := + Matrix.rank_submatrix_le B i0.succAbove j0.succAbove + _ ≤ A.rank := Matrix.rank_submatrix_le A rows id + have hrank : A.rank = k := by + lia + have hdet : B.det = 0 := by + by_contra hdet + have hBunit : IsUnit B := by + rw [Matrix.isUnit_iff_isUnit_det, isUnit_iff_ne_zero] + exact hdet + have hBrank : B.rank = k + 1 := by + simpa using Matrix.rank_of_isUnit B hBunit + have hle : B.rank ≤ A.rank := Matrix.rank_submatrix_le A rows id + rw [hBrank, hrank] at hle + lia + have hkernel : B.mulVec z = 0 := by + exact B.mulVec_signedRowCofactor_eq_zero_of_det_eq_zero i0 hdet + have hzj0 : z j0 ≠ 0 := by + exact mul_ne_zero (pow_ne_zero _ (by simp)) (by + simpa only [B] using hminor) + have hminorA : + (A.submatrix (rows ∘ i0.succAbove) + (j0.succAbove ∘ (id : Fin k → Fin k))).det ≠ 0 := by + simpa only [B, Matrix.submatrix_submatrix, Function.id_comp, + Function.comp_id] using hminor + have hdelete : + (A.submatrix id j0.succAbove).rank = k := + A.rank_deleteColumn_eq_of_minor_ne_zero hrank j0 + (rows ∘ i0.succAbove) id hminorA + exact ⟨hrank, hkernel, hzj0, hdelete⟩ + /-- Perturbing coefficients along a vector annihilated by a selected-row submatrix does not change the selected coordinates of the matrix-vector product. -/ From 927ec4343d7a33160c5701c9bfff533193ec667f Mon Sep 17 00:00:00 2001 From: Per Alexandersson Date: Mon, 3 Aug 2026 00:32:11 +0000 Subject: [PATCH 047/141] Reassemble deleted-column matrix products --- .../Matrix/SignRegularRankDeficient.lean | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/RealRooted/Mathlib/LinearAlgebra/Matrix/SignRegularRankDeficient.lean b/RealRooted/Mathlib/LinearAlgebra/Matrix/SignRegularRankDeficient.lean index 5270ed12..38823a1f 100644 --- a/RealRooted/Mathlib/LinearAlgebra/Matrix/SignRegularRankDeficient.lean +++ b/RealRooted/Mathlib/LinearAlgebra/Matrix/SignRegularRankDeficient.lean @@ -200,6 +200,36 @@ theorem Matrix.signedRowCofactor_spec_of_minor_ne_zero (rows ∘ i0.succAbove) id hminorA exact ⟨hrank, hkernel, hzj0, hdelete⟩ +/-- A zero coefficient can be removed together with its matrix column when +forming a matrix-vector product. -/ +theorem Matrix.mulVec_eq_deleteColumn_mulVec_removeNth_of_apply_eq_zero + {R : Type*} [NonUnitalNonAssocSemiring R] {n k : ℕ} + (A : Matrix (Fin n) (Fin (k + 1)) R) + (d : Fin (k + 1) → R) (j0 : Fin (k + 1)) + (hd : d j0 = 0) : + A.mulVec d = + (A.submatrix id j0.succAbove).mulVec (Fin.removeNth j0 d) := by + funext i + simp only [Matrix.mulVec, dotProduct, Matrix.submatrix_apply, id_eq, + Fin.removeNth] + rw [Fin.sum_univ_succAbove (fun j => A i j * d j) j0, hd] + simp + +/-- Karlin's coefficient cancellation: perturb by the unique scalar that +zeros coordinate `j0`, then remove that coordinate and its matrix column. -/ +theorem Matrix.mulVec_add_neg_div_smul_eq_deleteColumn_mulVec + {R : Type*} [Field R] {n k : ℕ} + (A : Matrix (Fin n) (Fin (k + 1)) R) + (c z : Fin (k + 1) → R) (j0 : Fin (k + 1)) + (hz : z j0 ≠ 0) : + A.mulVec (c + (-c j0 / z j0) • z) = + (A.submatrix id j0.succAbove).mulVec + (Fin.removeNth j0 (c + (-c j0 / z j0) • z)) := by + apply A.mulVec_eq_deleteColumn_mulVec_removeNth_of_apply_eq_zero + simp only [Pi.add_apply, Pi.smul_apply, smul_eq_mul] + field_simp + ring + /-- Perturbing coefficients along a vector annihilated by a selected-row submatrix does not change the selected coordinates of the matrix-vector product. -/ From 4a407cada3c0f3cd6740a01e266cf81cff3467c3 Mon Sep 17 00:00:00 2001 From: Per Alexandersson Date: Mon, 3 Aug 2026 00:36:40 +0000 Subject: [PATCH 048/141] Golf coefficient cancellation proofs --- .../LinearAlgebra/Matrix/SignRegularRankDeficient.lean | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/RealRooted/Mathlib/LinearAlgebra/Matrix/SignRegularRankDeficient.lean b/RealRooted/Mathlib/LinearAlgebra/Matrix/SignRegularRankDeficient.lean index 38823a1f..2526816b 100644 --- a/RealRooted/Mathlib/LinearAlgebra/Matrix/SignRegularRankDeficient.lean +++ b/RealRooted/Mathlib/LinearAlgebra/Matrix/SignRegularRankDeficient.lean @@ -212,13 +212,13 @@ theorem Matrix.mulVec_eq_deleteColumn_mulVec_removeNth_of_apply_eq_zero funext i simp only [Matrix.mulVec, dotProduct, Matrix.submatrix_apply, id_eq, Fin.removeNth] - rw [Fin.sum_univ_succAbove (fun j => A i j * d j) j0, hd] - simp + rw [Fin.sum_univ_succAbove (fun j => A i j * d j) j0, hd, mul_zero, + zero_add] /-- Karlin's coefficient cancellation: perturb by the unique scalar that zeros coordinate `j0`, then remove that coordinate and its matrix column. -/ theorem Matrix.mulVec_add_neg_div_smul_eq_deleteColumn_mulVec - {R : Type*} [Field R] {n k : ℕ} + {R : Type*} [DivisionRing R] {n k : ℕ} (A : Matrix (Fin n) (Fin (k + 1)) R) (c z : Fin (k + 1) → R) (j0 : Fin (k + 1)) (hz : z j0 ≠ 0) : @@ -227,8 +227,7 @@ theorem Matrix.mulVec_add_neg_div_smul_eq_deleteColumn_mulVec (Fin.removeNth j0 (c + (-c j0 / z j0) • z)) := by apply A.mulVec_eq_deleteColumn_mulVec_removeNth_of_apply_eq_zero simp only [Pi.add_apply, Pi.smul_apply, smul_eq_mul] - field_simp - ring + rw [div_mul_cancel₀ _ hz, add_neg_cancel] /-- Perturbing coefficients along a vector annihilated by a selected-row submatrix does not change the selected coordinates of the matrix-vector From 355e53dd0ef6bc3591951b60b9de16c261cb73c4 Mon Sep 17 00:00:00 2001 From: Per Alexandersson Date: Mon, 3 Aug 2026 00:57:52 +0000 Subject: [PATCH 049/141] Prove Karlin cofactor branch contradiction --- .../Matrix/SignRegularRankDeficient.lean | 59 ++++++++++++++ .../LinearAlgebra/Matrix/SignVariation.lean | 78 +++++++++++++++++++ 2 files changed, 137 insertions(+) diff --git a/RealRooted/Mathlib/LinearAlgebra/Matrix/SignRegularRankDeficient.lean b/RealRooted/Mathlib/LinearAlgebra/Matrix/SignRegularRankDeficient.lean index 2526816b..34d509ff 100644 --- a/RealRooted/Mathlib/LinearAlgebra/Matrix/SignRegularRankDeficient.lean +++ b/RealRooted/Mathlib/LinearAlgebra/Matrix/SignRegularRankDeficient.lean @@ -262,3 +262,62 @@ theorem Fin.StrictlyAlternates.matrix_mulVec_add_smul A.mulVec_add_smul_apply_eq_of_submatrix_mulVec_eq_zero rows c z hz t j rw [heq i.castSucc, heq i.succ] exact h i + +/-- Karlin's nonzero-cofactor branch contradicts the full-rank variation +bound after the source-prescribed coefficient cancellation and column +deletion. -/ +theorem Matrix.IsSignConsistentOrder.not_strictlyAlternates_mulVec_of_cofactor_ne_zero + {n k : ℕ} {A : Matrix (Fin n) (Fin (k + 1)) ℝ} + (hA : A.IsSignConsistentOrder k) + (hk : 0 < k) + (rows : Fin (k + 1) → Fin n) (hrows : StrictMono rows) + (i0 j0 : Fin (k + 1)) + (hrank_lt : A.rank < k + 1) + (hminor : + ((A.submatrix rows id).submatrix + i0.succAbove j0.succAbove).det ≠ 0) + (c : Fin (k + 1) → ℝ) + (hAlt : Fin.StrictlyAlternates (fun i => A.mulVec c (rows i))) : + False := by + let B : Matrix (Fin (k + 1)) (Fin (k + 1)) ℝ := + A.submatrix rows id + let z : Fin (k + 1) → ℝ := fun j => + (-1 : ℝ) ^ (i0 + j : ℕ) * + (B.submatrix i0.succAbove j.succAbove).det + let A' : Matrix (Fin n) (Fin k) ℝ := + A.submatrix id j0.succAbove + have hcase : + A.rank = k ∧ B.mulVec z = 0 ∧ z j0 ≠ 0 ∧ A'.rank = k := by + simpa only [B, z, A'] using + A.signedRowCofactor_spec_of_minor_ne_zero + rows i0 j0 hrank_lt hminor + obtain ⟨_hrank, hkernel, hzj0, hdelete⟩ := hcase + let t0 : ℝ := -c j0 / z j0 + let d : Fin (k + 1) → ℝ := c + t0 • z + let d' : Fin k → ℝ := Fin.removeNth j0 d + have hAltPert : + Fin.StrictlyAlternates (fun i => A.mulVec d (rows i)) := by + simpa only [d, t0] using + hAlt.matrix_mulVec_add_smul hkernel t0 + have hreassemble : A.mulVec d = A'.mulVec d' := by + simpa only [A', d', d, t0] using + A.mulVec_add_neg_div_smul_eq_deleteColumn_mulVec c z j0 hzj0 + have hAltDeleted : + Fin.StrictlyAlternates (fun i => A'.mulVec d' (rows i)) := by + rw [hreassemble] at hAltPert + exact hAltPert + have hA' : A'.IsSignConsistentOrder k := by + exact hA.submatrix strictMono_id (Fin.strictMono_succAbove j0) + have hk1n : k + 1 ≤ n := by + simpa using Fintype.card_le_of_injective rows hrows.injective + have hkn : k ≤ n := (Nat.le_succ k).trans hk1n + have hA'inj : Function.Injective A'.mulVec := by + rw [Matrix.mulVec_injective_iff, + linearIndependent_iff_card_eq_finrank_span] + simpa only [Fintype.card_fin, Set.finrank] using + hdelete.symm.trans (Matrix.rank_eq_finrank_span_cols A') + have hlower : k ≤ Fin.signVariations (A'.mulVec d') := + hAltDeleted.le_signVariations_of_strictMono hrows + have hupper : Fin.signVariations (A'.mulVec d') ≤ k - 1 := + hA'.signVariations_mulVec_le_card_sub_one hkn hA'inj d' + lia diff --git a/RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariation.lean b/RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariation.lean index e1c483eb..3d7940b7 100644 --- a/RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariation.lean +++ b/RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariation.lean @@ -578,6 +578,84 @@ lemma exists_strictMono_strictlyAlternates_of_le_signVariations · rw [← he0, ← he1] exact hkd +/-- A strictly alternating subsequence gives a lower bound on the sign +variations of the ambient vector. -/ +lemma StrictlyAlternates.le_signVariations_of_strictMono + {m q : ℕ} {y : Fin m → ℝ} + {rows : Fin (q + 1) → Fin m} + (h : StrictlyAlternates (fun i => y (rows i))) + (hrows : StrictMono rows) : q ≤ signVariations y := by + by_cases hq : q = 0 + · simp [hq] + have hnonzero : ∀ i, y (rows i) ≠ 0 := by + intro i + refine Fin.cases ?_ (fun j => ?_) i + · intro hy + let i0 : Fin q := ⟨0, Nat.pos_of_ne_zero hq⟩ + have hneg := h i0 + change y (rows 0) * y (rows i0.succ) < 0 at hneg + rw [hy, zero_mul] at hneg + exact (lt_irrefl 0) hneg + · intro hy + have hneg := h j + change y (rows j.castSucc) * y (rows j.succ) < 0 at hneg + rw [hy, mul_zero] at hneg + exact (lt_irrefl 0) hneg + have hsub : + (List.ofFn (SignType.sign ∘ fun i => y (rows i))).Sublist + (List.ofFn (SignType.sign ∘ y)) := by + rw [List.sublist_iff_exists_fin_orderEmbedding_get_eq] + let e0 : + Fin (List.ofFn (SignType.sign ∘ fun i => y (rows i))).length ≃o + Fin (q + 1) := + Fin.castOrderIso List.length_ofFn + let e1 : Fin (q + 1) ↪o Fin m := + OrderEmbedding.ofStrictMono rows hrows + let e2 : + Fin m ≃o Fin (List.ofFn (SignType.sign ∘ y)).length := + (Fin.castOrderIso List.length_ofFn).symm + refine ⟨e0.toOrderEmbedding.trans (e1.trans e2.toOrderEmbedding), ?_⟩ + intro i + simp only [List.get_ofFn, e0, e1, e2, RelEmbedding.trans_apply, + Function.comp_apply] + apply congrArg SignType.sign + apply congrArg y + apply Fin.ext + rfl + have hfilter : + (List.ofFn (SignType.sign ∘ fun i => y (rows i))).filter + (· ≠ 0) = + List.ofFn (SignType.sign ∘ fun i => y (rows i)) := by + rw [List.filter_eq_self] + intro s hs + simp only [List.mem_ofFn] at hs + obtain ⟨i, rfl⟩ := hs + simp [Function.comp_apply, hnonzero i] + have hsub_nonzero : + (List.ofFn (SignType.sign ∘ fun i => y (rows i))).Sublist + ((List.ofFn (SignType.sign ∘ y)).filter (· ≠ 0)) := by + rw [← hfilter] + exact List.Sublist.filter (· ≠ 0) hsub + have hchain : + (List.ofFn (SignType.sign ∘ fun i => y (rows i))).IsChain + (· ≠ ·) := by + rw [List.isChain_ofFn] + intro i hi + change SignType.sign (y (rows ⟨i, by lia⟩)) ≠ + SignType.sign (y (rows ⟨i + 1, hi⟩)) + have hneg := h (⟨i, by lia⟩ : Fin q) + change y (rows ⟨i, by lia⟩) * y (rows ⟨i + 1, hi⟩) < 0 at hneg + rcases mul_neg_iff.mp hneg with ⟨hleft, hright⟩ | ⟨hleft, hright⟩ + · rw [sign_pos hleft, sign_neg hright] + simp + · rw [sign_neg hleft, sign_pos hright] + simp + have hlen := + List.IsChain.length_le_length_destutter_ne hsub_nonzero hchain + rw [signVariations, List.signVariations, List.map_ofFn] + simp only [List.length_ofFn] at hlen + lia + /-- Two nonzero strictly alternating vectors have pointwise products of one strict sign. -/ lemma StrictlyAlternates.pointwise_mul_pos_or_neg {n : ℕ} From dc345f34f64f0a61a76126cd4662a25f8cb66349 Mon Sep 17 00:00:00 2001 From: Per Alexandersson Date: Mon, 3 Aug 2026 01:09:28 +0000 Subject: [PATCH 050/141] Construct Karlin supported column relation --- .../Matrix/SignRegularRankDeficient.lean | 138 ++++++++++++++++++ 1 file changed, 138 insertions(+) diff --git a/RealRooted/Mathlib/LinearAlgebra/Matrix/SignRegularRankDeficient.lean b/RealRooted/Mathlib/LinearAlgebra/Matrix/SignRegularRankDeficient.lean index 34d509ff..a44010f3 100644 --- a/RealRooted/Mathlib/LinearAlgebra/Matrix/SignRegularRankDeficient.lean +++ b/RealRooted/Mathlib/LinearAlgebra/Matrix/SignRegularRankDeficient.lean @@ -321,3 +321,141 @@ theorem Matrix.IsSignConsistentOrder.not_strictlyAlternates_mulVec_of_cofactor_n have hupper : Fin.signVariations (A'.mulVec d') ≤ k - 1 := hA'.signVariations_mulVec_le_card_sub_one hkn hA'inj d' lia + +/-- Extending a coefficient vector by zero along an injective column selector +turns the ambient matrix-vector product into the selected-column product. -/ +theorem Matrix.mulVec_extend_eq_submatrix_mulVec + {R : Type*} [NonUnitalNonAssocSemiring R] + {ι κ κ' : Type*} [Fintype κ] [Fintype κ'] + (A : Matrix ι κ R) (cols : κ' → κ) + (hcols : Function.Injective cols) (d : κ' → R) : + A.mulVec (Function.extend cols d 0) = + (A.submatrix id cols).mulVec d := by + classical + funext i + simp only [Matrix.mulVec, dotProduct, Matrix.submatrix_apply, id_eq] + symm + apply Fintype.sum_of_injective cols hcols + · intro j hj + rw [Function.extend_apply' d (0 : κ → R) j hj, Pi.zero_apply, + mul_zero] + · intro j + rw [hcols.extend_apply d (0 : κ → R) j] + +/-- Karlin's repaired Case B relation after coordinates of the omitted column +in the retained column basis have been chosen. The relation is explicitly +supported on those basis columns and `j0`; no arbitrary kernel coordinate is +prescribed. -/ +theorem Matrix.supportedColumnRelation_spec + {R : Type*} [Field R] {n m r : ℕ} + (A : Matrix (Fin n) (Fin (m + 1)) R) + (j0 : Fin (m + 1)) + (rows : Fin r → Fin n) (cols : Fin r → Fin m) + (hcols : Function.Injective cols) + (hrank : A.rank = r) + (hminor : + (A.submatrix rows (j0.succAbove ∘ cols)).det ≠ 0) + (d : Fin r → R) + (hcombo : + (A.submatrix id (j0.succAbove ∘ cols)).mulVec d = A.col j0) : + let selected := j0.succAbove ∘ cols + let z : Fin (m + 1) → R := + Function.extend selected d 0 - Pi.single j0 1 + A.mulVec z = 0 ∧ z j0 = -1 ∧ + (A.submatrix id j0.succAbove).rank = r := by + let selected : Fin r → Fin (m + 1) := j0.succAbove ∘ cols + let z : Fin (m + 1) → R := + Function.extend selected d 0 - Pi.single j0 1 + change A.mulVec z = 0 ∧ z j0 = -1 ∧ + (A.submatrix id j0.succAbove).rank = r + have hselected : Function.Injective selected := + (Fin.strictMono_succAbove j0).injective.comp hcols + have hj0 : j0 ∉ Set.range selected := by + rintro ⟨i, hi⟩ + exact (Fin.succAbove_ne j0 (cols i)) (by + simpa only [selected, Function.comp_apply] using hi) + have hkernel : A.mulVec z = 0 := by + calc + A.mulVec z = + A.mulVec (Function.extend selected d 0) - + A.mulVec (Pi.single j0 1) := by + change + A.mulVec + (Function.extend selected d 0 - Pi.single j0 1) = + A.mulVec (Function.extend selected d 0) - + A.mulVec (Pi.single j0 1) + rw [Matrix.mulVec_sub] + _ = (A.submatrix id selected).mulVec d - A.col j0 := by + rw [A.mulVec_extend_eq_submatrix_mulVec selected hselected d, + Matrix.mulVec_single_one] + _ = 0 := by + simpa only [selected] using sub_eq_zero.mpr hcombo + have hzj0 : z j0 = -1 := by + simp only [z, Pi.sub_apply] + rw [Function.extend_apply' d (0 : Fin (m + 1) → R) j0 hj0, + Pi.zero_apply, Pi.single_eq_same, zero_sub] + have hdelete : + (A.submatrix id j0.succAbove).rank = r := + A.rank_deleteColumn_eq_of_minor_ne_zero hrank j0 rows cols hminor + exact ⟨hkernel, hzj0, hdelete⟩ + +/-- A rank-sized minor avoiding `j0` produces Karlin's supported Case B +relation and proves that deleting `j0` preserves rank. The printed source's +arbitrary-coordinate assertion is not used: equality of the retained and +ambient column-space dimensions supplies the omitted column's coordinates. -/ +theorem Matrix.exists_supportedColumnRelation_of_minor_ne_zero + {R : Type*} [Field R] {n m r : ℕ} + (A : Matrix (Fin n) (Fin (m + 1)) R) + (j0 : Fin (m + 1)) + (rows : Fin r → Fin n) (cols : Fin r → Fin m) + (hcols : Function.Injective cols) + (hrank : A.rank = r) + (hminor : + (A.submatrix rows (j0.succAbove ∘ cols)).det ≠ 0) : + ∃ d : Fin r → R, + let selected := j0.succAbove ∘ cols + let z : Fin (m + 1) → R := + Function.extend selected d 0 - Pi.single j0 1 + A.mulVec z = 0 ∧ z j0 = -1 ∧ + (A.submatrix id j0.succAbove).rank = r := by + let selected : Fin r → Fin (m + 1) := j0.succAbove ∘ cols + let B : Matrix (Fin n) (Fin r) R := A.submatrix id selected + have hunit : IsUnit (B.submatrix rows id) := by + rw [Matrix.isUnit_iff_isUnit_det, isUnit_iff_ne_zero] + simpa only [B, selected, Matrix.submatrix_submatrix, + Function.id_comp, Function.comp_id] using hminor + have hminor_rank : (B.submatrix rows id).rank = r := by + simpa using Matrix.rank_of_isUnit _ hunit + have hBge : r ≤ B.rank := by + calc + r = (B.submatrix rows id).rank := hminor_rank.symm + _ ≤ B.rank := Matrix.rank_submatrix_le B rows id + have hBle : B.rank ≤ r := by + calc + B.rank ≤ A.rank := Matrix.rank_submatrix_le A id selected + _ = r := hrank + have hBrank : B.rank = r := le_antisymm hBle hBge + have hrange_le : + LinearMap.range B.mulVecLin ≤ LinearMap.range A.mulVecLin := by + rw [Matrix.range_mulVecLin, Matrix.range_mulVecLin] + apply Submodule.span_mono + rintro _ ⟨j, rfl⟩ + refine ⟨selected j, ?_⟩ + rfl + have hrange : + LinearMap.range B.mulVecLin = LinearMap.range A.mulVecLin := + Submodule.eq_of_le_of_finrank_le hrange_le (by + change A.rank ≤ B.rank + rw [hrank, hBrank]) + have hcol : A.col j0 ∈ LinearMap.range B.mulVecLin := by + rw [hrange] + refine ⟨Pi.single j0 1, ?_⟩ + simpa only [Matrix.mulVecLin_apply] using A.mulVec_single_one j0 + obtain ⟨d, hd⟩ := hcol + refine ⟨d, ?_⟩ + have hcombo : + (A.submatrix id (j0.succAbove ∘ cols)).mulVec d = + A.col j0 := by + simpa only [B, selected, Matrix.mulVecLin_apply] using hd + exact A.supportedColumnRelation_spec j0 rows cols hcols hrank + hminor d hcombo From 9865b3caac255d1b5384905a0c19aff922a1dc94 Mon Sep 17 00:00:00 2001 From: Per Alexandersson Date: Mon, 3 Aug 2026 01:18:57 +0000 Subject: [PATCH 051/141] Golf Karlin supported relation proofs --- .../Matrix/SignRegularRankDeficient.lean | 30 ++++++------------- 1 file changed, 9 insertions(+), 21 deletions(-) diff --git a/RealRooted/Mathlib/LinearAlgebra/Matrix/SignRegularRankDeficient.lean b/RealRooted/Mathlib/LinearAlgebra/Matrix/SignRegularRankDeficient.lean index a44010f3..6a1a3825 100644 --- a/RealRooted/Mathlib/LinearAlgebra/Matrix/SignRegularRankDeficient.lean +++ b/RealRooted/Mathlib/LinearAlgebra/Matrix/SignRegularRankDeficient.lean @@ -375,29 +375,18 @@ theorem Matrix.supportedColumnRelation_spec exact (Fin.succAbove_ne j0 (cols i)) (by simpa only [selected, Function.comp_apply] using hi) have hkernel : A.mulVec z = 0 := by - calc - A.mulVec z = - A.mulVec (Function.extend selected d 0) - - A.mulVec (Pi.single j0 1) := by - change - A.mulVec - (Function.extend selected d 0 - Pi.single j0 1) = - A.mulVec (Function.extend selected d 0) - - A.mulVec (Pi.single j0 1) - rw [Matrix.mulVec_sub] - _ = (A.submatrix id selected).mulVec d - A.col j0 := by - rw [A.mulVec_extend_eq_submatrix_mulVec selected hselected d, - Matrix.mulVec_single_one] - _ = 0 := by - simpa only [selected] using sub_eq_zero.mpr hcombo + change A.mulVec + (Function.extend selected d 0 - Pi.single j0 1) = 0 + rw [Matrix.mulVec_sub, + A.mulVec_extend_eq_submatrix_mulVec selected hselected d, + Matrix.mulVec_single_one] + simpa only [selected] using sub_eq_zero.mpr hcombo have hzj0 : z j0 = -1 := by simp only [z, Pi.sub_apply] rw [Function.extend_apply' d (0 : Fin (m + 1) → R) j0 hj0, Pi.zero_apply, Pi.single_eq_same, zero_sub] - have hdelete : - (A.submatrix id j0.succAbove).rank = r := - A.rank_deleteColumn_eq_of_minor_ne_zero hrank j0 rows cols hminor - exact ⟨hkernel, hzj0, hdelete⟩ + exact ⟨hkernel, hzj0, + A.rank_deleteColumn_eq_of_minor_ne_zero hrank j0 rows cols hminor⟩ /-- A rank-sized minor avoiding `j0` produces Karlin's supported Case B relation and proves that deleting `j0` preserves rank. The printed source's @@ -440,8 +429,7 @@ theorem Matrix.exists_supportedColumnRelation_of_minor_ne_zero rw [Matrix.range_mulVecLin, Matrix.range_mulVecLin] apply Submodule.span_mono rintro _ ⟨j, rfl⟩ - refine ⟨selected j, ?_⟩ - rfl + exact ⟨selected j, rfl⟩ have hrange : LinearMap.range B.mulVecLin = LinearMap.range A.mulVecLin := Submodule.eq_of_le_of_finrank_le hrange_le (by From 10db3a133aa4f9eadd507c5600e8a2507f624b11 Mon Sep 17 00:00:00 2001 From: Per Alexandersson Date: Mon, 3 Aug 2026 01:23:54 +0000 Subject: [PATCH 052/141] Add Karlin rank deletion induction adapter --- .../Matrix/SignRegularRankDeficient.lean | 50 +++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/RealRooted/Mathlib/LinearAlgebra/Matrix/SignRegularRankDeficient.lean b/RealRooted/Mathlib/LinearAlgebra/Matrix/SignRegularRankDeficient.lean index 6a1a3825..9253e53a 100644 --- a/RealRooted/Mathlib/LinearAlgebra/Matrix/SignRegularRankDeficient.lean +++ b/RealRooted/Mathlib/LinearAlgebra/Matrix/SignRegularRankDeficient.lean @@ -447,3 +447,53 @@ theorem Matrix.exists_supportedColumnRelation_of_minor_ne_zero simpa only [B, selected, Matrix.mulVecLin_apply] using hd exact A.supportedColumnRelation_spec j0 rows cols hcols hrank hminor d hcombo + +/-- Karlin's deficient-rank induction adapter. The printed proof's claim that +an arbitrary kernel coordinate can be prescribed is false in general. Instead, +we choose `j0` outside the columns of a nonzero rank-sized minor. That minor +survives deletion of `j0`, while the selected columns express column `j0` +and produce a kernel relation whose `j0` coordinate is nonzero. -/ +theorem Matrix.exists_kernelVector_apply_ne_zero_rank_deleteColumn_eq_of_rank_lt + {R : Type*} [Field R] {n k r : ℕ} + (A : Matrix (Fin n) (Fin (k + 1)) R) + (hrank : A.rank = r) (hrank_lt : r < k + 1) : + ∃ j0 : Fin (k + 1), ∃ z : Fin (k + 1) → R, + A.mulVec z = 0 ∧ z j0 ≠ 0 ∧ + (A.submatrix id j0.succAbove).rank = r := by + classical + obtain ⟨rows, cols, _, hcols, hminor⟩ := + A.exists_ordered_minor_ne_zero_of_rank_eq hrank + have hcols_not_surj : ¬ Function.Surjective cols := by + intro hsurj + have hcard := Fintype.card_le_of_surjective cols hsurj + have : k + 1 ≤ r := by + simpa only [Fintype.card_fin] using hcard + exact (not_le_of_gt hrank_lt) this + obtain ⟨j0, hj0⟩ : ∃ j0, ∀ i, cols i ≠ j0 := by + simpa only [Function.Surjective, not_forall, not_exists] using + hcols_not_surj + choose cols' hcols' using fun i => + Fin.exists_succAbove_eq (hj0 i) + have hcols_factor : j0.succAbove ∘ cols' = cols := by + funext i + exact hcols' i + have hcols'_inj : Function.Injective cols' := by + intro i j hij + apply hcols.injective + calc + cols i = j0.succAbove (cols' i) := + (congrFun hcols_factor i).symm + _ = j0.succAbove (cols' j) := congrArg j0.succAbove hij + _ = cols j := congrFun hcols_factor j + have hminor' : + (A.submatrix rows (j0.succAbove ∘ cols')).det ≠ 0 := by + rw [hcols_factor] + exact hminor + obtain ⟨d, hkernel, hzj0, hdelete⟩ := + A.exists_supportedColumnRelation_of_minor_ne_zero + j0 rows cols' hcols'_inj hrank hminor' + refine ⟨j0, + Function.extend (j0.succAbove ∘ cols') d 0 - Pi.single j0 1, + hkernel, ?_, hdelete⟩ + rw [hzj0] + exact neg_ne_zero.mpr one_ne_zero From e84bbbbd677702637927317dbb53b6e3466b1f02 Mon Sep 17 00:00:00 2001 From: Per Alexandersson Date: Mon, 3 Aug 2026 01:33:39 +0000 Subject: [PATCH 053/141] Prove Karlin deficient-rank induction step --- .../Matrix/SignRegularRankDeficient.lean | 53 +++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/RealRooted/Mathlib/LinearAlgebra/Matrix/SignRegularRankDeficient.lean b/RealRooted/Mathlib/LinearAlgebra/Matrix/SignRegularRankDeficient.lean index 9253e53a..117ab735 100644 --- a/RealRooted/Mathlib/LinearAlgebra/Matrix/SignRegularRankDeficient.lean +++ b/RealRooted/Mathlib/LinearAlgebra/Matrix/SignRegularRankDeficient.lean @@ -497,3 +497,56 @@ theorem Matrix.exists_kernelVector_apply_ne_zero_rank_deleteColumn_eq_of_rank_lt hkernel, ?_, hdelete⟩ rw [hzj0] exact neg_ne_zero.mpr one_ne_zero + +/-- Karlin's positive-rank, one-column induction step for the deficient-rank +case. The induction hypothesis handles the rank-preserving matrix obtained by +deleting the column selected by +`exists_kernelVector_apply_ne_zero_rank_deleteColumn_eq_of_rank_lt`. -/ +theorem Matrix.IsSignConsistentOrder.signVariations_mulVec_le_rank_sub_one_of_induction + {n k r : ℕ} + {A : Matrix (Fin n) (Fin (k + 1)) ℝ} + (hA : A.IsSignConsistentOrder r) + (hrank : A.rank = r) + (hrank_lt : r < k + 1) + (hr : 0 < r) + (hind : + ∀ {B : Matrix (Fin n) (Fin k) ℝ}, + B.IsSignConsistentOrder r → + B.rank = r → + ∀ d : Fin k → ℝ, + Fin.signVariations (B.mulVec d) ≤ r - 1) + (c : Fin (k + 1) → ℝ) : + Fin.signVariations (A.mulVec c) ≤ r - 1 := by + by_contra hle + have hbad : r ≤ Fin.signVariations (A.mulVec c) := by + lia + obtain ⟨rows, hrows, halt⟩ := + Fin.exists_strictMono_strictlyAlternates_of_le_signVariations hr hbad + obtain ⟨j0, z, hkernel, hzj0, hdelete⟩ := + A.exists_kernelVector_apply_ne_zero_rank_deleteColumn_eq_of_rank_lt + hrank hrank_lt + have hzrows : (A.submatrix rows id).mulVec z = 0 := by + funext i + simpa only [Matrix.mulVec, Matrix.submatrix_apply, id_eq, + Pi.zero_apply] using congrFun hkernel (rows i) + let t : ℝ := -c j0 / z j0 + let d : Fin (k + 1) → ℝ := c + t • z + let B : Matrix (Fin n) (Fin k) ℝ := + A.submatrix id j0.succAbove + let d' : Fin k → ℝ := Fin.removeNth j0 d + have halt_d : + Fin.StrictlyAlternates (fun i => A.mulVec d (rows i)) := by + simpa only [d, t] using halt.matrix_mulVec_add_smul hzrows t + have hcancel : A.mulVec d = B.mulVec d' := by + simpa only [d, t, B, d'] using + A.mulVec_add_neg_div_smul_eq_deleteColumn_mulVec c z j0 hzj0 + have halt_delete : + Fin.StrictlyAlternates (fun i => B.mulVec d' (rows i)) := by + rw [← hcancel] + exact halt_d + have hB : B.IsSignConsistentOrder r := by + dsimp only [B] + exact hA.submatrix strictMono_id (Fin.strictMono_succAbove j0) + have hupper := hind hB hdelete d' + have hlower := halt_delete.le_signVariations_of_strictMono hrows + lia From df0924cb4d2a9819a716f3e662f548f1f591c7fe Mon Sep 17 00:00:00 2001 From: Per Alexandersson Date: Mon, 3 Aug 2026 01:38:30 +0000 Subject: [PATCH 054/141] Golf Karlin rank induction adapters --- .../Matrix/SignRegularRankDeficient.lean | 24 +++++++------------ 1 file changed, 9 insertions(+), 15 deletions(-) diff --git a/RealRooted/Mathlib/LinearAlgebra/Matrix/SignRegularRankDeficient.lean b/RealRooted/Mathlib/LinearAlgebra/Matrix/SignRegularRankDeficient.lean index 117ab735..12cb8a0c 100644 --- a/RealRooted/Mathlib/LinearAlgebra/Matrix/SignRegularRankDeficient.lean +++ b/RealRooted/Mathlib/LinearAlgebra/Matrix/SignRegularRankDeficient.lean @@ -465,10 +465,9 @@ theorem Matrix.exists_kernelVector_apply_ne_zero_rank_deleteColumn_eq_of_rank_lt A.exists_ordered_minor_ne_zero_of_rank_eq hrank have hcols_not_surj : ¬ Function.Surjective cols := by intro hsurj - have hcard := Fintype.card_le_of_surjective cols hsurj - have : k + 1 ≤ r := by - simpa only [Fintype.card_fin] using hcard - exact (not_le_of_gt hrank_lt) this + apply not_le_of_gt hrank_lt + simpa only [Fintype.card_fin] using + Fintype.card_le_of_surjective cols hsurj obtain ⟨j0, hj0⟩ : ∃ j0, ∀ i, cols i ≠ j0 := by simpa only [Function.Surjective, not_forall, not_exists] using hcols_not_surj @@ -480,15 +479,11 @@ theorem Matrix.exists_kernelVector_apply_ne_zero_rank_deleteColumn_eq_of_rank_lt have hcols'_inj : Function.Injective cols' := by intro i j hij apply hcols.injective - calc - cols i = j0.succAbove (cols' i) := - (congrFun hcols_factor i).symm - _ = j0.succAbove (cols' j) := congrArg j0.succAbove hij - _ = cols j := congrFun hcols_factor j + rw [← hcols_factor] + exact congrArg j0.succAbove hij have hminor' : (A.submatrix rows (j0.succAbove ∘ cols')).det ≠ 0 := by - rw [hcols_factor] - exact hminor + simpa only [hcols_factor] using hminor obtain ⟨d, hkernel, hzj0, hdelete⟩ := A.exists_supportedColumnRelation_of_minor_ne_zero j0 rows cols' hcols'_inj hrank hminor' @@ -536,7 +531,7 @@ theorem Matrix.IsSignConsistentOrder.signVariations_mulVec_le_rank_sub_one_of_in let d' : Fin k → ℝ := Fin.removeNth j0 d have halt_d : Fin.StrictlyAlternates (fun i => A.mulVec d (rows i)) := by - simpa only [d, t] using halt.matrix_mulVec_add_smul hzrows t + simpa only [d] using halt.matrix_mulVec_add_smul hzrows t have hcancel : A.mulVec d = B.mulVec d' := by simpa only [d, t, B, d'] using A.mulVec_add_neg_div_smul_eq_deleteColumn_mulVec c z j0 hzj0 @@ -544,9 +539,8 @@ theorem Matrix.IsSignConsistentOrder.signVariations_mulVec_le_rank_sub_one_of_in Fin.StrictlyAlternates (fun i => B.mulVec d' (rows i)) := by rw [← hcancel] exact halt_d - have hB : B.IsSignConsistentOrder r := by - dsimp only [B] - exact hA.submatrix strictMono_id (Fin.strictMono_succAbove j0) + have hB : B.IsSignConsistentOrder r := + hA.submatrix strictMono_id (Fin.strictMono_succAbove j0) have hupper := hind hB hdelete d' have hlower := halt_delete.le_signVariations_of_strictMono hrows lia From 317b01fc5234c83d1bf2382d430bf598b6876cef Mon Sep 17 00:00:00 2001 From: Per Alexandersson Date: Mon, 3 Aug 2026 01:48:50 +0000 Subject: [PATCH 055/141] Prove Karlin rank-sensitive variation bound --- .../Matrix/SignRegularRankDeficient.lean | 47 +++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/RealRooted/Mathlib/LinearAlgebra/Matrix/SignRegularRankDeficient.lean b/RealRooted/Mathlib/LinearAlgebra/Matrix/SignRegularRankDeficient.lean index 12cb8a0c..7f510c25 100644 --- a/RealRooted/Mathlib/LinearAlgebra/Matrix/SignRegularRankDeficient.lean +++ b/RealRooted/Mathlib/LinearAlgebra/Matrix/SignRegularRankDeficient.lean @@ -544,3 +544,50 @@ theorem Matrix.IsSignConsistentOrder.signVariations_mulVec_le_rank_sub_one_of_in have hupper := hind hB hdelete d' have hlower := halt_delete.le_signVariations_of_strictMono hrows lia + +/-- Karlin's rank-sensitive variation bound, proved by induction on the number +of columns. Rank zero gives the zero linear map, full column rank uses the +injective variation bound, and positive deficient rank uses the +rank-preserving deletion step. -/ +theorem Matrix.IsSignConsistentOrder.signVariations_mulVec_le_rank_sub_one + {n m r : ℕ} + {A : Matrix (Fin n) (Fin m) ℝ} + (hA : A.IsSignConsistentOrder r) + (hrank : A.rank = r) + (c : Fin m → ℝ) : + Fin.signVariations (A.mulVec c) ≤ r - 1 := by + induction m generalizing r with + | zero => + have hrzero : r = 0 := by + have hrle : r ≤ 0 := hrank ▸ A.rank_le_width + exact Nat.eq_zero_of_le_zero hrle + rw [hrzero] at hrank ⊢ + have hlin : A.mulVecLin = 0 := + LinearMap.range_eq_bot.mp (Submodule.finrank_eq_zero.mp hrank) + rw [← Matrix.mulVecLin_apply, hlin, LinearMap.zero_apply, + Fin.signVariations_zero] + | succ k ih => + by_cases hrzero : r = 0 + · rw [hrzero] at hrank ⊢ + have hlin : A.mulVecLin = 0 := + LinearMap.range_eq_bot.mp + (Submodule.finrank_eq_zero.mp hrank) + rw [← Matrix.mulVecLin_apply, hlin, LinearMap.zero_apply, + Fin.signVariations_zero] + · have hrpos : 0 < r := Nat.pos_of_ne_zero hrzero + have hrle : r ≤ k + 1 := by + rw [← hrank] + exact A.rank_le_width + by_cases hrfull : r = k + 1 + · rw [hrfull] at hA hrank ⊢ + have hkn : k + 1 ≤ n := hrank ▸ A.rank_le_height + have hAinj : Function.Injective A.mulVec := by + rw [Matrix.mulVec_injective_iff, + linearIndependent_iff_card_eq_finrank_span] + simpa only [Fintype.card_fin, Set.finrank] using + hrank.symm.trans (Matrix.rank_eq_finrank_span_cols A) + exact hA.signVariations_mulVec_le_card_sub_one hkn hAinj c + · have hrlt : r < k + 1 := lt_of_le_of_ne hrle hrfull + exact hA.signVariations_mulVec_le_rank_sub_one_of_induction + hrank hrlt hrpos + (fun {B} hB hBrank d => ih hB hBrank d) c From 6ad9dbaee3ea091a28f49c1a644e7e81af38fc5a Mon Sep 17 00:00:00 2001 From: Per Alexandersson Date: Mon, 3 Aug 2026 02:12:17 +0000 Subject: [PATCH 056/141] Preserve sign consistency under TN right products --- .../Matrix/SignRegularRankDeficient.lean | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/RealRooted/Mathlib/LinearAlgebra/Matrix/SignRegularRankDeficient.lean b/RealRooted/Mathlib/LinearAlgebra/Matrix/SignRegularRankDeficient.lean index 7f510c25..675b8e06 100644 --- a/RealRooted/Mathlib/LinearAlgebra/Matrix/SignRegularRankDeficient.lean +++ b/RealRooted/Mathlib/LinearAlgebra/Matrix/SignRegularRankDeficient.lean @@ -591,3 +591,43 @@ theorem Matrix.IsSignConsistentOrder.signVariations_mulVec_le_rank_sub_one exact hA.signVariations_mulVec_le_rank_sub_one_of_induction hrank hrlt hrpos (fun {B} hB hBrank d => ih hB hBrank d) c + +/-- Right multiplication by a totally nonnegative rectangular matrix preserves +the common weak sign of ordered minors of a fixed size. Cauchy--Binet expands +the product of two output minors as a double sum; sign consistency controls the +left-minor products and total nonnegativity controls the right-minor products. -/ +theorem Matrix.IsSignConsistentOrder.mul_of_right_isTotallyNonnegRect + {l n m r : ℕ} + {L : Matrix (Fin l) (Fin n) ℝ} + {A : Matrix (Fin n) (Fin m) ℝ} + (hL : L.IsSignConsistentOrder r) + (hA : A.IsTotallyNonnegRect) : + (L * A).IsSignConsistentOrder r := by + classical + intro rows rows' cols cols' hrows hrows' hcols hcols' + rw [Matrix.det_submatrix_mul_eq_sum_powersetCard L A rows cols, + Matrix.det_submatrix_mul_eq_sum_powersetCard L A rows' cols'] + rw [Finset.sum_mul] + apply Finset.sum_nonneg + intro s hs + rw [Finset.mul_sum] + apply Finset.sum_nonneg + intro t ht + let es : Fin r ↪o Fin n := + Set.powersetCard.ofFinEmbEquiv.symm s + let et : Fin r ↪o Fin n := + Set.powersetCard.ofFinEmbEquiv.symm t + have hsign : + 0 ≤ + (L.submatrix rows es).det * + (L.submatrix rows' et).det := + hL hrows hrows' es.strictMono et.strictMono + have hright : + 0 ≤ + (A.submatrix es cols).det * + (A.submatrix et cols').det := + mul_nonneg + (hA es.strictMono hcols) + (hA et.strictMono hcols') + simpa only [es, et, mul_assoc, mul_left_comm, mul_comm] using + mul_nonneg hsign hright From 8b862f637c6cc4b1be11b4a9c6dafdc0052f7759 Mon Sep 17 00:00:00 2001 From: Per Alexandersson Date: Mon, 3 Aug 2026 02:18:50 +0000 Subject: [PATCH 057/141] Deduplicate Karlin rank-zero induction case --- .../Matrix/SignRegularRankDeficient.lean | 25 +++++++++++-------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/RealRooted/Mathlib/LinearAlgebra/Matrix/SignRegularRankDeficient.lean b/RealRooted/Mathlib/LinearAlgebra/Matrix/SignRegularRankDeficient.lean index 675b8e06..37658bba 100644 --- a/RealRooted/Mathlib/LinearAlgebra/Matrix/SignRegularRankDeficient.lean +++ b/RealRooted/Mathlib/LinearAlgebra/Matrix/SignRegularRankDeficient.lean @@ -556,24 +556,28 @@ theorem Matrix.IsSignConsistentOrder.signVariations_mulVec_le_rank_sub_one (hrank : A.rank = r) (c : Fin m → ℝ) : Fin.signVariations (A.mulVec c) ≤ r - 1 := by + have hzero : + ∀ {p : ℕ} {B : Matrix (Fin n) (Fin p) ℝ}, + B.rank = 0 → + ∀ d : Fin p → ℝ, + Fin.signVariations (B.mulVec d) ≤ 0 := by + intro p B hBrank d + have hlin : B.mulVecLin = 0 := + LinearMap.range_eq_bot.mp + (Submodule.finrank_eq_zero.mp hBrank) + rw [← Matrix.mulVecLin_apply, hlin, LinearMap.zero_apply, + Fin.signVariations_zero] induction m generalizing r with | zero => have hrzero : r = 0 := by have hrle : r ≤ 0 := hrank ▸ A.rank_le_width exact Nat.eq_zero_of_le_zero hrle rw [hrzero] at hrank ⊢ - have hlin : A.mulVecLin = 0 := - LinearMap.range_eq_bot.mp (Submodule.finrank_eq_zero.mp hrank) - rw [← Matrix.mulVecLin_apply, hlin, LinearMap.zero_apply, - Fin.signVariations_zero] + exact hzero hrank c | succ k ih => by_cases hrzero : r = 0 · rw [hrzero] at hrank ⊢ - have hlin : A.mulVecLin = 0 := - LinearMap.range_eq_bot.mp - (Submodule.finrank_eq_zero.mp hrank) - rw [← Matrix.mulVecLin_apply, hlin, LinearMap.zero_apply, - Fin.signVariations_zero] + exact hzero hrank c · have hrpos : 0 < r := Nat.pos_of_ne_zero hrzero have hrle : r ≤ k + 1 := by rw [← hrank] @@ -589,8 +593,7 @@ theorem Matrix.IsSignConsistentOrder.signVariations_mulVec_le_rank_sub_one exact hA.signVariations_mulVec_le_card_sub_one hkn hAinj c · have hrlt : r < k + 1 := lt_of_le_of_ne hrle hrfull exact hA.signVariations_mulVec_le_rank_sub_one_of_induction - hrank hrlt hrpos - (fun {B} hB hBrank d => ih hB hBrank d) c + hrank hrlt hrpos ih c /-- Right multiplication by a totally nonnegative rectangular matrix preserves the common weak sign of ordered minors of a fixed size. Cauchy--Binet expands From 29951150e1ffe08a5772d1e1e1f1080eeffd1bfb Mon Sep 17 00:00:00 2001 From: Per Alexandersson Date: Mon, 3 Aug 2026 02:27:09 +0000 Subject: [PATCH 058/141] Prove monotone incidence minor formula --- .../Matrix/SignRegularRankDeficient.lean | 70 +++++++++++++++++++ 1 file changed, 70 insertions(+) diff --git a/RealRooted/Mathlib/LinearAlgebra/Matrix/SignRegularRankDeficient.lean b/RealRooted/Mathlib/LinearAlgebra/Matrix/SignRegularRankDeficient.lean index 37658bba..ccd4bb16 100644 --- a/RealRooted/Mathlib/LinearAlgebra/Matrix/SignRegularRankDeficient.lean +++ b/RealRooted/Mathlib/LinearAlgebra/Matrix/SignRegularRankDeficient.lean @@ -634,3 +634,73 @@ theorem Matrix.IsSignConsistentOrder.mul_of_right_isTotallyNonnegRect (hA et.strictMono hcols') simpa only [es, et, mul_assoc, mul_left_comm, mul_comm] using mul_nonneg hsign hright + +/-- The minor of a monotone weighted incidence matrix is diagonal or singular. + +This is the elementary incidence-matrix step in Karlin's proof of Theorem V.1.4: +monotonicity forces every nonzero Leibniz term to use the identity permutation. +Thus no separate combinatorial model is needed for this bridge. -/ +theorem Matrix.det_submatrix_monotoneWeightedIncidence + {n q r : ℕ} + (block : Fin n → Fin q) + (hblock : Monotone block) + (weight : Fin n → ℝ) + {rows : Fin r → Fin n} + {cols : Fin r → Fin q} + (hrows : StrictMono rows) + (hcols : StrictMono cols) : + (Matrix.submatrix + (fun j s => if block j = s then weight j else 0) + rows cols).det = + if ∀ i, block (rows i) = cols i then + ∏ i, weight (rows i) + else 0 := by + classical + let W : Matrix (Fin n) (Fin q) ℝ := + fun j s => if block j = s then weight j else 0 + change (W.submatrix rows cols).det = + if ∀ i, block (rows i) = cols i then + ∏ i, weight (rows i) + else 0 + by_cases hdiag : ∀ i, block (rows i) = cols i + · rw [if_pos hdiag] + have hmatrix : + W.submatrix rows cols = + Matrix.diagonal (fun i => weight (rows i)) := by + ext i j + simp only [Matrix.submatrix_apply, W] + rw [hdiag i] + by_cases hij : i = j + · subst j + simp + · have hc : cols i ≠ cols j := fun h => + hij (hcols.injective h) + simp [hc, hij] + rw [hmatrix, Matrix.det_diagonal] + · rw [if_neg hdiag, Matrix.det_apply'] + apply Finset.sum_eq_zero + intro sigma hsigma + by_cases hterm : ∀ i, block (rows (sigma i)) = cols i + · have hsigma_mono : StrictMono sigma := by + intro i j hij + by_contra hnot + have hle : sigma j ≤ sigma i := le_of_not_gt hnot + have hf : + block (rows (sigma j)) ≤ block (rows (sigma i)) := + (hblock.comp hrows.monotone) hle + rw [hterm j, hterm i] at hf + exact (not_le_of_gt (hcols hij)) hf + have hsigma_eq : ∀ i, sigma i = i := fun i => + le_antisymm (hsigma_mono.le_id i) (hsigma_mono.id_le i) + exfalso + apply hdiag + intro i + simpa only [hsigma_eq i] using hterm i + · simp only [not_forall] at hterm + obtain ⟨i, hi⟩ := hterm + have hprod : + (∏ j, (W.submatrix rows cols) (sigma j) j) = 0 := by + apply Finset.prod_eq_zero (Finset.mem_univ i) + simp only [Matrix.submatrix_apply, W] + rw [if_neg hi] + rw [hprod, mul_zero] From a2a4c8fbbaf2d9d72b79eba88c2c0858ee2e3d2e Mon Sep 17 00:00:00 2001 From: Per Alexandersson Date: Mon, 3 Aug 2026 02:28:21 +0000 Subject: [PATCH 059/141] Show monotone incidence matrices are TN --- .../Matrix/SignRegularRankDeficient.lean | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/RealRooted/Mathlib/LinearAlgebra/Matrix/SignRegularRankDeficient.lean b/RealRooted/Mathlib/LinearAlgebra/Matrix/SignRegularRankDeficient.lean index ccd4bb16..0379142a 100644 --- a/RealRooted/Mathlib/LinearAlgebra/Matrix/SignRegularRankDeficient.lean +++ b/RealRooted/Mathlib/LinearAlgebra/Matrix/SignRegularRankDeficient.lean @@ -704,3 +704,20 @@ theorem Matrix.det_submatrix_monotoneWeightedIncidence simp only [Matrix.submatrix_apply, W] rw [if_neg hi] rw [hprod, mul_zero] + +/-- A monotone weighted incidence matrix is totally nonnegative when its weights are +nonnegative. -/ +theorem Matrix.isTotallyNonnegRect_monotoneWeightedIncidence + {n q : ℕ} + (block : Fin n → Fin q) + (hblock : Monotone block) + (weight : Fin n → ℝ) + (hweight : ∀ j, 0 ≤ weight j) : + Matrix.IsTotallyNonnegRect + (fun j s => if block j = s then weight j else 0) := by + intro r rows cols hrows hcols + rw [Matrix.det_submatrix_monotoneWeightedIncidence + block hblock weight hrows hcols] + split_ifs + · exact Finset.prod_nonneg fun i _ => hweight (rows i) + · exact le_rfl From 57d595e4e843f32028ebf6d2b8452dd5b9db0493 Mon Sep 17 00:00:00 2001 From: Per Alexandersson Date: Mon, 3 Aug 2026 02:33:11 +0000 Subject: [PATCH 060/141] Define weighted incidence aggregation --- .../Matrix/SignRegularRankDeficient.lean | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/RealRooted/Mathlib/LinearAlgebra/Matrix/SignRegularRankDeficient.lean b/RealRooted/Mathlib/LinearAlgebra/Matrix/SignRegularRankDeficient.lean index 0379142a..d81bdd6b 100644 --- a/RealRooted/Mathlib/LinearAlgebra/Matrix/SignRegularRankDeficient.lean +++ b/RealRooted/Mathlib/LinearAlgebra/Matrix/SignRegularRankDeficient.lean @@ -721,3 +721,35 @@ theorem Matrix.isTotallyNonnegRect_monotoneWeightedIncidence split_ifs · exact Finset.prod_nonneg fun i _ => hweight (rows i) · exact le_rfl + +/-- The weighted incidence matrix associated with a block map. -/ +def Matrix.weightedIncidence + {n q : ℕ} + (block : Fin n → Fin q) + (weight : Fin n → ℝ) : + Matrix (Fin n) (Fin q) ℝ := + fun j s => if block j = s then weight j else 0 + +@[simp] +theorem Matrix.weightedIncidence_apply + {n q : ℕ} + (block : Fin n → Fin q) + (weight : Fin n → ℝ) + (j : Fin n) + (s : Fin q) : + Matrix.weightedIncidence block weight j s = + if block j = s then weight j else 0 := + rfl + +/-- Right multiplication by a weighted incidence matrix forms weighted block sums. -/ +theorem Matrix.mul_weightedIncidence_apply + {l n q : ℕ} + (L : Matrix (Fin l) (Fin n) ℝ) + (block : Fin n → Fin q) + (weight : Fin n → ℝ) + (i : Fin l) + (s : Fin q) : + (L * Matrix.weightedIncidence block weight) i s = + ∑ j with block j = s, L i j * weight j := by + rw [Matrix.mul_apply, Finset.sum_filter] + simp only [Matrix.weightedIncidence_apply, mul_ite, mul_zero] From 4808221b5674057f4af43e238fb6ef4270806e9d Mon Sep 17 00:00:00 2001 From: Per Alexandersson Date: Mon, 3 Aug 2026 02:36:23 +0000 Subject: [PATCH 061/141] Deduplicate weighted incidence matrices --- .../Matrix/SignRegularRankDeficient.lean | 53 +++++++++---------- 1 file changed, 26 insertions(+), 27 deletions(-) diff --git a/RealRooted/Mathlib/LinearAlgebra/Matrix/SignRegularRankDeficient.lean b/RealRooted/Mathlib/LinearAlgebra/Matrix/SignRegularRankDeficient.lean index d81bdd6b..8a371608 100644 --- a/RealRooted/Mathlib/LinearAlgebra/Matrix/SignRegularRankDeficient.lean +++ b/RealRooted/Mathlib/LinearAlgebra/Matrix/SignRegularRankDeficient.lean @@ -635,6 +635,25 @@ theorem Matrix.IsSignConsistentOrder.mul_of_right_isTotallyNonnegRect simpa only [es, et, mul_assoc, mul_left_comm, mul_comm] using mul_nonneg hsign hright +/-- The weighted incidence matrix associated with a block map. -/ +def Matrix.weightedIncidence + {n q : ℕ} + (block : Fin n → Fin q) + (weight : Fin n → ℝ) : + Matrix (Fin n) (Fin q) ℝ := + fun j s => if block j = s then weight j else 0 + +@[simp] +theorem Matrix.weightedIncidence_apply + {n q : ℕ} + (block : Fin n → Fin q) + (weight : Fin n → ℝ) + (j : Fin n) + (s : Fin q) : + Matrix.weightedIncidence block weight j s = + if block j = s then weight j else 0 := + rfl + /-- The minor of a monotone weighted incidence matrix is diagonal or singular. This is the elementary incidence-matrix step in Karlin's proof of Theorem V.1.4: @@ -649,15 +668,12 @@ theorem Matrix.det_submatrix_monotoneWeightedIncidence {cols : Fin r → Fin q} (hrows : StrictMono rows) (hcols : StrictMono cols) : - (Matrix.submatrix - (fun j s => if block j = s then weight j else 0) - rows cols).det = + ((Matrix.weightedIncidence block weight).submatrix rows cols).det = if ∀ i, block (rows i) = cols i then ∏ i, weight (rows i) else 0 := by classical - let W : Matrix (Fin n) (Fin q) ℝ := - fun j s => if block j = s then weight j else 0 + let W := Matrix.weightedIncidence block weight change (W.submatrix rows cols).det = if ∀ i, block (rows i) = cols i then ∏ i, weight (rows i) @@ -668,7 +684,8 @@ theorem Matrix.det_submatrix_monotoneWeightedIncidence W.submatrix rows cols = Matrix.diagonal (fun i => weight (rows i)) := by ext i j - simp only [Matrix.submatrix_apply, W] + simp only [Matrix.submatrix_apply, W, + Matrix.weightedIncidence_apply] rw [hdiag i] by_cases hij : i = j · subst j @@ -701,7 +718,8 @@ theorem Matrix.det_submatrix_monotoneWeightedIncidence have hprod : (∏ j, (W.submatrix rows cols) (sigma j) j) = 0 := by apply Finset.prod_eq_zero (Finset.mem_univ i) - simp only [Matrix.submatrix_apply, W] + simp only [Matrix.submatrix_apply, W, + Matrix.weightedIncidence_apply] rw [if_neg hi] rw [hprod, mul_zero] @@ -714,7 +732,7 @@ theorem Matrix.isTotallyNonnegRect_monotoneWeightedIncidence (weight : Fin n → ℝ) (hweight : ∀ j, 0 ≤ weight j) : Matrix.IsTotallyNonnegRect - (fun j s => if block j = s then weight j else 0) := by + (Matrix.weightedIncidence block weight) := by intro r rows cols hrows hcols rw [Matrix.det_submatrix_monotoneWeightedIncidence block hblock weight hrows hcols] @@ -722,25 +740,6 @@ theorem Matrix.isTotallyNonnegRect_monotoneWeightedIncidence · exact Finset.prod_nonneg fun i _ => hweight (rows i) · exact le_rfl -/-- The weighted incidence matrix associated with a block map. -/ -def Matrix.weightedIncidence - {n q : ℕ} - (block : Fin n → Fin q) - (weight : Fin n → ℝ) : - Matrix (Fin n) (Fin q) ℝ := - fun j s => if block j = s then weight j else 0 - -@[simp] -theorem Matrix.weightedIncidence_apply - {n q : ℕ} - (block : Fin n → Fin q) - (weight : Fin n → ℝ) - (j : Fin n) - (s : Fin q) : - Matrix.weightedIncidence block weight j s = - if block j = s then weight j else 0 := - rfl - /-- Right multiplication by a weighted incidence matrix forms weighted block sums. -/ theorem Matrix.mul_weightedIncidence_apply {l n q : ℕ} From abdc80db29c2fa172d3808903d94f5d69f019f7a Mon Sep 17 00:00:00 2001 From: Per Alexandersson Date: Mon, 3 Aug 2026 02:39:16 +0000 Subject: [PATCH 062/141] Reconstruct weighted block coefficients --- .../Matrix/SignRegularRankDeficient.lean | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/RealRooted/Mathlib/LinearAlgebra/Matrix/SignRegularRankDeficient.lean b/RealRooted/Mathlib/LinearAlgebra/Matrix/SignRegularRankDeficient.lean index 8a371608..4d825fd3 100644 --- a/RealRooted/Mathlib/LinearAlgebra/Matrix/SignRegularRankDeficient.lean +++ b/RealRooted/Mathlib/LinearAlgebra/Matrix/SignRegularRankDeficient.lean @@ -752,3 +752,21 @@ theorem Matrix.mul_weightedIncidence_apply ∑ j with block j = s, L i j * weight j := by rw [Matrix.mul_apply, Finset.sum_filter] simp only [Matrix.weightedIncidence_apply, mul_ite, mul_zero] + +/-- Multiplying an aggregated matrix by block coefficients reconstructs the +corresponding coefficient vector before multiplication by the original matrix. -/ +theorem Matrix.mul_weightedIncidence_mulVec + {l n q : ℕ} + (L : Matrix (Fin l) (Fin n) ℝ) + (block : Fin n → Fin q) + (weight : Fin n → ℝ) + (sign : Fin q → ℝ) : + (L * Matrix.weightedIncidence block weight).mulVec sign = + L.mulVec (fun j => weight j * sign (block j)) := by + have hincidence : + (Matrix.weightedIncidence block weight).mulVec sign = + fun j => weight j * sign (block j) := by + ext j + simp [Matrix.mulVec, dotProduct] + rw [← Matrix.mulVec_mulVec sign L + (Matrix.weightedIncidence block weight), hincidence] From 801a08145e523a561d4c882c7ab38cec9265fcc7 Mon Sep 17 00:00:00 2001 From: Per Alexandersson Date: Mon, 3 Aug 2026 02:41:54 +0000 Subject: [PATCH 063/141] Preserve signs under weighted aggregation --- .../Matrix/SignRegularRankDeficient.lean | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/RealRooted/Mathlib/LinearAlgebra/Matrix/SignRegularRankDeficient.lean b/RealRooted/Mathlib/LinearAlgebra/Matrix/SignRegularRankDeficient.lean index 4d825fd3..1138aa4b 100644 --- a/RealRooted/Mathlib/LinearAlgebra/Matrix/SignRegularRankDeficient.lean +++ b/RealRooted/Mathlib/LinearAlgebra/Matrix/SignRegularRankDeficient.lean @@ -770,3 +770,18 @@ theorem Matrix.mul_weightedIncidence_mulVec simp [Matrix.mulVec, dotProduct] rw [← Matrix.mulVec_mulVec sign L (Matrix.weightedIncidence block weight), hincidence] + +/-- Aggregating consecutive columns with nonnegative weights preserves sign +consistency of every fixed order. -/ +theorem Matrix.IsSignConsistentOrder.mul_weightedIncidence + {l n q r : ℕ} + {L : Matrix (Fin l) (Fin n) ℝ} + (hL : L.IsSignConsistentOrder r) + (block : Fin n → Fin q) + (hblock : Monotone block) + (weight : Fin n → ℝ) + (hweight : ∀ j, 0 ≤ weight j) : + (L * Matrix.weightedIncidence block weight).IsSignConsistentOrder r := + hL.mul_of_right_isTotallyNonnegRect + (Matrix.isTotallyNonnegRect_monotoneWeightedIncidence + block hblock weight hweight) From d9bdef59caa611174c259c460ae6ed8309bec496 Mon Sep 17 00:00:00 2001 From: Per Alexandersson Date: Mon, 3 Aug 2026 02:44:48 +0000 Subject: [PATCH 064/141] Extract weighted incidence mulVec formula --- .../Matrix/SignRegularRankDeficient.lean | 21 +++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/RealRooted/Mathlib/LinearAlgebra/Matrix/SignRegularRankDeficient.lean b/RealRooted/Mathlib/LinearAlgebra/Matrix/SignRegularRankDeficient.lean index 1138aa4b..239da6e0 100644 --- a/RealRooted/Mathlib/LinearAlgebra/Matrix/SignRegularRankDeficient.lean +++ b/RealRooted/Mathlib/LinearAlgebra/Matrix/SignRegularRankDeficient.lean @@ -753,6 +753,19 @@ theorem Matrix.mul_weightedIncidence_apply rw [Matrix.mul_apply, Finset.sum_filter] simp only [Matrix.weightedIncidence_apply, mul_ite, mul_zero] +/-- A weighted incidence matrix sends block coefficients to their weighted +pullback along the block map. -/ +@[simp] +theorem Matrix.weightedIncidence_mulVec + {n q : ℕ} + (block : Fin n → Fin q) + (weight : Fin n → ℝ) + (sign : Fin q → ℝ) : + (Matrix.weightedIncidence block weight).mulVec sign = + fun j => weight j * sign (block j) := by + ext j + simp [Matrix.mulVec, dotProduct] + /-- Multiplying an aggregated matrix by block coefficients reconstructs the corresponding coefficient vector before multiplication by the original matrix. -/ theorem Matrix.mul_weightedIncidence_mulVec @@ -763,13 +776,9 @@ theorem Matrix.mul_weightedIncidence_mulVec (sign : Fin q → ℝ) : (L * Matrix.weightedIncidence block weight).mulVec sign = L.mulVec (fun j => weight j * sign (block j)) := by - have hincidence : - (Matrix.weightedIncidence block weight).mulVec sign = - fun j => weight j * sign (block j) := by - ext j - simp [Matrix.mulVec, dotProduct] rw [← Matrix.mulVec_mulVec sign L - (Matrix.weightedIncidence block weight), hincidence] + (Matrix.weightedIncidence block weight), + Matrix.weightedIncidence_mulVec] /-- Aggregating consecutive columns with nonnegative weights preserves sign consistency of every fixed order. -/ From ee10258a0bf239fcfb9c9fd07e5f5d8ed71b424b Mon Sep 17 00:00:00 2001 From: Per Alexandersson Date: Mon, 3 Aug 2026 02:48:02 +0000 Subject: [PATCH 065/141] Assemble Karlin bound from sign blocks --- .../Matrix/SignRegularRankDeficient.lean | 57 +++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/RealRooted/Mathlib/LinearAlgebra/Matrix/SignRegularRankDeficient.lean b/RealRooted/Mathlib/LinearAlgebra/Matrix/SignRegularRankDeficient.lean index 239da6e0..76a22249 100644 --- a/RealRooted/Mathlib/LinearAlgebra/Matrix/SignRegularRankDeficient.lean +++ b/RealRooted/Mathlib/LinearAlgebra/Matrix/SignRegularRankDeficient.lean @@ -794,3 +794,60 @@ theorem Matrix.IsSignConsistentOrder.mul_weightedIncidence hL.mul_of_right_isTotallyNonnegRect (Matrix.isTotallyNonnegRect_monotoneWeightedIncidence block hblock weight hweight) + +/-- Data expressing a finite vector as nonnegative weights pulled back from an +ordered collection of sign blocks. + +This is an interface for the elementary finite-list decomposition in Karlin's +proof, not an assertion that the decomposition already exists. Separating the +data keeps the matrix argument independent of how maximal same-sign blocks are +constructed and avoids formalizing an unrelated combinatorial model. -/ +structure Fin.SignBlockDecomposition {n : ℕ} (c : Fin n → ℝ) where + numBlocks : ℕ + numBlocks_pos : 0 < numBlocks + block : Fin n → Fin numBlocks + block_mono : Monotone block + weight : Fin n → ℝ + weight_nonneg : ∀ j, 0 ≤ weight j + coeff : Fin numBlocks → ℝ + reconstruct : ∀ j, weight j * coeff (block j) = c j + numBlocks_sub_one : numBlocks - 1 = Fin.signVariations c + +/-- Karlin's V.1.4 inequality from explicit sign-block decomposition data. + +The only remaining finite combinatorial step for the general theorem is to +construct `Fin.SignBlockDecomposition c`; all matrix and rank arguments are +discharged here. -/ +theorem Matrix.IsSignRegular.signVariations_mulVec_le_of_signBlockDecomposition + {l n : ℕ} + {A : Matrix (Fin l) (Fin n) ℝ} + (hA : A.IsSignRegular) + (c : Fin n → ℝ) + (d : Fin.SignBlockDecomposition c) : + Fin.signVariations (A.mulVec c) ≤ Fin.signVariations c := by + let B := + A * Matrix.weightedIncidence d.block d.weight + have hBsign : B.IsSignConsistentOrder B.rank := by + simpa only [B] using + (hA B.rank).mul_weightedIncidence + d.block d.block_mono d.weight d.weight_nonneg + have hbound : + Fin.signVariations (B.mulVec d.coeff) ≤ B.rank - 1 := + hBsign.signVariations_mulVec_le_rank_sub_one rfl d.coeff + have hmul : B.mulVec d.coeff = A.mulVec c := by + calc + B.mulVec d.coeff = + A.mulVec (fun j => d.weight j * d.coeff (d.block j)) := by + simpa only [B] using + Matrix.mul_weightedIncidence_mulVec + A d.block d.weight d.coeff + _ = A.mulVec c := + congrArg A.mulVec (funext fun j => d.reconstruct j) + calc + Fin.signVariations (A.mulVec c) = + Fin.signVariations (B.mulVec d.coeff) := + congrArg Fin.signVariations hmul.symm + _ ≤ B.rank - 1 := hbound + _ ≤ d.numBlocks - 1 := + (Nat.sub_le_sub_right B.rank_le_width) 1 + _ = Fin.signVariations c := d.numBlocks_sub_one From 1e3662baadd46f69fb50f204ca871e70e85783db Mon Sep 17 00:00:00 2001 From: Per Alexandersson Date: Mon, 3 Aug 2026 02:50:08 +0000 Subject: [PATCH 066/141] Define prefix sign variations --- .../Matrix/SignRegularRankDeficient.lean | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/RealRooted/Mathlib/LinearAlgebra/Matrix/SignRegularRankDeficient.lean b/RealRooted/Mathlib/LinearAlgebra/Matrix/SignRegularRankDeficient.lean index 76a22249..6b67b7bc 100644 --- a/RealRooted/Mathlib/LinearAlgebra/Matrix/SignRegularRankDeficient.lean +++ b/RealRooted/Mathlib/LinearAlgebra/Matrix/SignRegularRankDeficient.lean @@ -851,3 +851,30 @@ theorem Matrix.IsSignRegular.signVariations_mulVec_le_of_signBlockDecomposition _ ≤ d.numBlocks - 1 := (Nat.sub_le_sub_right B.rank_le_width) 1 _ = Fin.signVariations c := d.numBlocks_sub_one + +/-- The sign variation of the prefix ending at `j`. -/ +def Fin.prefixSignVariations + {n : ℕ} (c : Fin n → ℝ) (j : Fin n) : ℕ := + ((List.ofFn c).take (j + 1)).signVariations + +/-- Prefix sign variation is at most the index of the prefix endpoint. -/ +theorem Fin.prefixSignVariations_le_val + {n : ℕ} (c : Fin n → ℝ) (j : Fin n) : + Fin.prefixSignVariations c j ≤ j := by + unfold Fin.prefixSignVariations + calc + ((List.ofFn c).take (j + 1)).signVariations ≤ + ((List.ofFn c).take (j + 1)).length - 1 := + List.signVariations_le_length_sub_one _ + _ = j := by simp + +/-- At the final index, prefix sign variation is full-vector sign variation. -/ +theorem Fin.prefixSignVariations_last + {n : ℕ} (c : Fin (n + 1) → ℝ) : + Fin.prefixSignVariations c (Fin.last n) = + Fin.signVariations c := by + rw [Fin.prefixSignVariations, Fin.signVariations] + have hlength : + (Fin.last n : ℕ) + 1 = (List.ofFn c).length := by + simp + rw [hlength, List.take_length] From a83fa4acf3886ab5799fe9e9940a9b52fbc9fead Mon Sep 17 00:00:00 2001 From: Per Alexandersson Date: Mon, 3 Aug 2026 02:52:11 +0000 Subject: [PATCH 067/141] Move sign block API to sign variation --- .../Matrix/SignRegularRankDeficient.lean | 45 ------------------- .../LinearAlgebra/Matrix/SignVariation.lean | 45 +++++++++++++++++++ 2 files changed, 45 insertions(+), 45 deletions(-) diff --git a/RealRooted/Mathlib/LinearAlgebra/Matrix/SignRegularRankDeficient.lean b/RealRooted/Mathlib/LinearAlgebra/Matrix/SignRegularRankDeficient.lean index 6b67b7bc..d205b66f 100644 --- a/RealRooted/Mathlib/LinearAlgebra/Matrix/SignRegularRankDeficient.lean +++ b/RealRooted/Mathlib/LinearAlgebra/Matrix/SignRegularRankDeficient.lean @@ -795,24 +795,6 @@ theorem Matrix.IsSignConsistentOrder.mul_weightedIncidence (Matrix.isTotallyNonnegRect_monotoneWeightedIncidence block hblock weight hweight) -/-- Data expressing a finite vector as nonnegative weights pulled back from an -ordered collection of sign blocks. - -This is an interface for the elementary finite-list decomposition in Karlin's -proof, not an assertion that the decomposition already exists. Separating the -data keeps the matrix argument independent of how maximal same-sign blocks are -constructed and avoids formalizing an unrelated combinatorial model. -/ -structure Fin.SignBlockDecomposition {n : ℕ} (c : Fin n → ℝ) where - numBlocks : ℕ - numBlocks_pos : 0 < numBlocks - block : Fin n → Fin numBlocks - block_mono : Monotone block - weight : Fin n → ℝ - weight_nonneg : ∀ j, 0 ≤ weight j - coeff : Fin numBlocks → ℝ - reconstruct : ∀ j, weight j * coeff (block j) = c j - numBlocks_sub_one : numBlocks - 1 = Fin.signVariations c - /-- Karlin's V.1.4 inequality from explicit sign-block decomposition data. The only remaining finite combinatorial step for the general theorem is to @@ -851,30 +833,3 @@ theorem Matrix.IsSignRegular.signVariations_mulVec_le_of_signBlockDecomposition _ ≤ d.numBlocks - 1 := (Nat.sub_le_sub_right B.rank_le_width) 1 _ = Fin.signVariations c := d.numBlocks_sub_one - -/-- The sign variation of the prefix ending at `j`. -/ -def Fin.prefixSignVariations - {n : ℕ} (c : Fin n → ℝ) (j : Fin n) : ℕ := - ((List.ofFn c).take (j + 1)).signVariations - -/-- Prefix sign variation is at most the index of the prefix endpoint. -/ -theorem Fin.prefixSignVariations_le_val - {n : ℕ} (c : Fin n → ℝ) (j : Fin n) : - Fin.prefixSignVariations c j ≤ j := by - unfold Fin.prefixSignVariations - calc - ((List.ofFn c).take (j + 1)).signVariations ≤ - ((List.ofFn c).take (j + 1)).length - 1 := - List.signVariations_le_length_sub_one _ - _ = j := by simp - -/-- At the final index, prefix sign variation is full-vector sign variation. -/ -theorem Fin.prefixSignVariations_last - {n : ℕ} (c : Fin (n + 1) → ℝ) : - Fin.prefixSignVariations c (Fin.last n) = - Fin.signVariations c := by - rw [Fin.prefixSignVariations, Fin.signVariations] - have hlength : - (Fin.last n : ℕ) + 1 = (List.ofFn c).length := by - simp - rw [hlength, List.take_length] diff --git a/RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariation.lean b/RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariation.lean index 3d7940b7..669ec43d 100644 --- a/RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariation.lean +++ b/RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariation.lean @@ -778,3 +778,48 @@ lemma strictlyAlternates_alternating (n : ℕ) {ε : ℝ} (hε : 0 < ε) : _ < 0 := neg_lt_zero.mpr (sq_pos_of_pos hε) end Fin + +/-- Data expressing a finite vector as nonnegative weights pulled back from an +ordered collection of sign blocks. + +This is an interface for the elementary finite-list decomposition in Karlin's +proof, not an assertion that the decomposition already exists. Separating the +data keeps later matrix arguments independent of how maximal same-sign blocks +are constructed and avoids formalizing an unrelated combinatorial model. -/ +structure Fin.SignBlockDecomposition {n : ℕ} (c : Fin n → ℝ) where + numBlocks : ℕ + numBlocks_pos : 0 < numBlocks + block : Fin n → Fin numBlocks + block_mono : Monotone block + weight : Fin n → ℝ + weight_nonneg : ∀ j, 0 ≤ weight j + coeff : Fin numBlocks → ℝ + reconstruct : ∀ j, weight j * coeff (block j) = c j + numBlocks_sub_one : numBlocks - 1 = Fin.signVariations c + +/-- The sign variation of the prefix ending at `j`. -/ +def Fin.prefixSignVariations + {n : ℕ} (c : Fin n → ℝ) (j : Fin n) : ℕ := + ((List.ofFn c).take (j + 1)).signVariations + +/-- Prefix sign variation is at most the index of the prefix endpoint. -/ +theorem Fin.prefixSignVariations_le_val + {n : ℕ} (c : Fin n → ℝ) (j : Fin n) : + Fin.prefixSignVariations c j ≤ j := by + unfold Fin.prefixSignVariations + calc + ((List.ofFn c).take (j + 1)).signVariations ≤ + ((List.ofFn c).take (j + 1)).length - 1 := + List.signVariations_le_length_sub_one _ + _ = j := by simp + +/-- At the final index, prefix sign variation is full-vector sign variation. -/ +theorem Fin.prefixSignVariations_last + {n : ℕ} (c : Fin (n + 1) → ℝ) : + Fin.prefixSignVariations c (Fin.last n) = + Fin.signVariations c := by + rw [Fin.prefixSignVariations, Fin.signVariations] + have hlength : + (Fin.last n : ℕ) + 1 = (List.ofFn c).length := by + simp + rw [hlength, List.take_length] From 81ae5d59dc50f6939b7c43ea9f076f2d8c58bb22 Mon Sep 17 00:00:00 2001 From: Per Alexandersson Date: Mon, 3 Aug 2026 02:56:40 +0000 Subject: [PATCH 068/141] Prove prefix sign variation monotonicity --- .../LinearAlgebra/Matrix/SignVariation.lean | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariation.lean b/RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariation.lean index 669ec43d..e0b34405 100644 --- a/RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariation.lean +++ b/RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariation.lean @@ -779,6 +779,36 @@ lemma strictlyAlternates_alternating (n : ℕ) {ε : ℝ} (hε : 0 < ε) : end Fin +/-- Sign variation is monotone under taking a list prefix. -/ +theorem List.signVariations_mono_of_prefix + {R : Type*} [Zero R] [LinearOrder R] + {l₁ l₂ : List R} (h : l₁ <+: l₂) : + l₁.signVariations ≤ l₂.signVariations := by + rw [List.signVariations, List.signVariations] + let s₁ : List SignType := + (l₁.map SignType.sign).filter (· ≠ 0) + let s₂ : List SignType := + (l₂.map SignType.sign).filter (· ≠ 0) + change (s₁.destutter (· ≠ ·)).length - 1 ≤ + (s₂.destutter (· ≠ ·)).length - 1 + have hsign : s₁ <+: s₂ := + (h.map SignType.sign).filter (· ≠ 0) + have hsub : s₁.destutter (· ≠ ·) <+ s₂ := + (List.destutter_sublist + (fun x y : SignType => x ≠ y) s₁).trans hsign.sublist + have hchain : + (s₁.destutter (· ≠ ·)).IsChain (· ≠ ·) := + List.isChain_destutter (fun x y : SignType => x ≠ y) s₁ + exact (Nat.sub_le_sub_right + (List.IsChain.length_le_length_destutter_ne hsub hchain)) 1 + +/-- Taking a list prefix cannot increase sign variation. -/ +theorem List.signVariations_take_le + {R : Type*} [Zero R] [LinearOrder R] + (l : List R) (k : ℕ) : + (l.take k).signVariations ≤ l.signVariations := + List.signVariations_mono_of_prefix (List.take_prefix k l) + /-- Data expressing a finite vector as nonnegative weights pulled back from an ordered collection of sign blocks. @@ -823,3 +853,12 @@ theorem Fin.prefixSignVariations_last (Fin.last n : ℕ) + 1 = (List.ofFn c).length := by simp rw [hlength, List.take_length] + +/-- Prefix sign variation is monotone in the prefix endpoint. -/ +theorem Fin.monotone_prefixSignVariations + {n : ℕ} (c : Fin n → ℝ) : + Monotone (Fin.prefixSignVariations c) := by + intro i j hij + unfold Fin.prefixSignVariations + apply List.signVariations_mono_of_prefix + exact List.take_prefix_take_left (Nat.add_le_add_right hij 1) From 4901e7f1ac569c66dfc9fd8155d272958fa0c19e Mon Sep 17 00:00:00 2001 From: Per Alexandersson Date: Mon, 3 Aug 2026 02:57:25 +0000 Subject: [PATCH 069/141] Define monotone sign block indices --- .../LinearAlgebra/Matrix/SignVariation.lean | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariation.lean b/RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariation.lean index e0b34405..0ca7aac2 100644 --- a/RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariation.lean +++ b/RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariation.lean @@ -862,3 +862,24 @@ theorem Fin.monotone_prefixSignVariations unfold Fin.prefixSignVariations apply List.signVariations_mono_of_prefix exact List.take_prefix_take_left (Nat.add_le_add_right hij 1) + +/-- Prefix sign variation is bounded by full-vector sign variation. -/ +theorem Fin.prefixSignVariations_le_signVariations + {n : ℕ} (c : Fin n → ℝ) (j : Fin n) : + Fin.prefixSignVariations c j ≤ Fin.signVariations c := by + unfold Fin.prefixSignVariations Fin.signVariations + exact List.signVariations_take_le _ _ + +/-- The sign-block index of an entry, counted by prefix sign variation. -/ +def Fin.signBlockIndex + {n : ℕ} (c : Fin n → ℝ) (j : Fin n) : + Fin (Fin.signVariations c + 1) := + ⟨Fin.prefixSignVariations c j, + Nat.lt_succ_of_le (Fin.prefixSignVariations_le_signVariations c j)⟩ + +/-- Sign-block indices are monotone in the original index. -/ +theorem Fin.monotone_signBlockIndex + {n : ℕ} (c : Fin n → ℝ) : + Monotone (Fin.signBlockIndex c) := by + intro i j hij + exact Fin.monotone_prefixSignVariations c hij From d17a5a551fe0e2fc879fc71434cfadb2ee45396d Mon Sep 17 00:00:00 2001 From: Per Alexandersson Date: Mon, 3 Aug 2026 02:59:34 +0000 Subject: [PATCH 070/141] Bound adjacent prefix sign variations --- .../LinearAlgebra/Matrix/SignVariation.lean | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariation.lean b/RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariation.lean index 0ca7aac2..ef97a53e 100644 --- a/RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariation.lean +++ b/RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariation.lean @@ -809,6 +809,16 @@ theorem List.signVariations_take_le (l.take k).signVariations ≤ l.signVariations := List.signVariations_mono_of_prefix (List.take_prefix k l) +/-- Appending one real entry increases sign variation by at most one. -/ +theorem List.signVariations_append_singleton_le_succ + (l : List ℝ) (x : ℝ) : + (l ++ [x]).signVariations ≤ l.signVariations + 1 := by + rcases le_total 0 x with hx | hx + · apply List.signVariations_append_nonneg_le_succ + simpa using hx + · apply List.signVariations_append_nonpos_le_succ + simpa using hx + /-- Data expressing a finite vector as nonnegative weights pulled back from an ordered collection of sign blocks. @@ -883,3 +893,16 @@ theorem Fin.monotone_signBlockIndex Monotone (Fin.signBlockIndex c) := by intro i j hij exact Fin.monotone_prefixSignVariations c hij + +/-- Adjacent prefix sign-variation values differ by at most one. -/ +theorem Fin.prefixSignVariations_succ_le + {n : ℕ} (c : Fin (n + 1) → ℝ) (i : Fin n) : + Fin.prefixSignVariations c i.succ ≤ + Fin.prefixSignVariations c i.castSucc + 1 := by + unfold Fin.prefixSignVariations + have hindex : + (i : ℕ) + 1 < (List.ofFn c).length := by + simp [i.isLt] + rw [show (i.succ : ℕ) + 1 = ((i : ℕ) + 1) + 1 by simp, + List.take_succ_eq_append_getElem hindex] + exact List.signVariations_append_singleton_le_succ _ _ From 36519653393a025a74c3e81127c2876d979b40e4 Mon Sep 17 00:00:00 2001 From: Per Alexandersson Date: Mon, 3 Aug 2026 03:01:05 +0000 Subject: [PATCH 071/141] Add sign block index simp lemmas --- .../LinearAlgebra/Matrix/SignVariation.lean | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariation.lean b/RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariation.lean index ef97a53e..e6d84d8e 100644 --- a/RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariation.lean +++ b/RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariation.lean @@ -887,6 +887,21 @@ def Fin.signBlockIndex ⟨Fin.prefixSignVariations c j, Nat.lt_succ_of_le (Fin.prefixSignVariations_le_signVariations c j)⟩ +@[simp] +theorem Fin.val_signBlockIndex + {n : ℕ} (c : Fin n → ℝ) (j : Fin n) : + (Fin.signBlockIndex c j : ℕ) = + Fin.prefixSignVariations c j := + rfl + +@[simp] +theorem Fin.signBlockIndex_last + {n : ℕ} (c : Fin (n + 1) → ℝ) : + Fin.signBlockIndex c (Fin.last n) = + Fin.last (Fin.signVariations c) := by + apply Fin.ext + exact Fin.prefixSignVariations_last c + /-- Sign-block indices are monotone in the original index. -/ theorem Fin.monotone_signBlockIndex {n : ℕ} (c : Fin n → ℝ) : From d4df0a6ef9d1b36f1d5237fbf2a32d68f527d587 Mon Sep 17 00:00:00 2001 From: Per Alexandersson Date: Mon, 3 Aug 2026 03:05:30 +0000 Subject: [PATCH 072/141] Preserve prefixes under destuttering --- RealRooted/Mathlib/Data/List/Destutter.lean | 66 +++++++++++++++++++ .../LinearAlgebra/Matrix/SignVariation.lean | 19 ++---- 2 files changed, 71 insertions(+), 14 deletions(-) create mode 100644 RealRooted/Mathlib/Data/List/Destutter.lean diff --git a/RealRooted/Mathlib/Data/List/Destutter.lean b/RealRooted/Mathlib/Data/List/Destutter.lean new file mode 100644 index 00000000..6d6a98b3 --- /dev/null +++ b/RealRooted/Mathlib/Data/List/Destutter.lean @@ -0,0 +1,66 @@ +module + +public import Mathlib.Data.List.Destutter + +/-! +# Additional lemmas about list destuttering + +This file contains compatibility lemmas intended for upstreaming to +`Mathlib.Data.List.Destutter`. +-/ + +public section + +namespace List + +private theorem singleton_prefix_destutter' + {α : Type*} (R : α → α → Prop) [DecidableRel R] + (a : α) (l : List α) : + [a] <+: l.destutter' R a := by + induction l generalizing a with + | nil => + exact ⟨[], rfl⟩ + | cons b l ih => + rw [List.destutter'_cons] + by_cases hab : R a b + · rw [if_pos hab] + exact ⟨l.destutter' R b, rfl⟩ + · rw [if_neg hab] + exact ih a + +private theorem destutter'_prefix_append + {α : Type*} (R : α → α → Prop) [DecidableRel R] + (a : α) (l t : List α) : + l.destutter' R a <+: (l ++ t).destutter' R a := by + induction l generalizing a with + | nil => + exact singleton_prefix_destutter' R a t + | cons b l ih => + simp only [List.cons_append] + rw [List.destutter'_cons, List.destutter'_cons] + by_cases hab : R a b + · rw [if_pos hab, if_pos hab] + rcases ih b with ⟨u, hu⟩ + exact ⟨u, by simpa using congrArg (List.cons a) hu⟩ + · rw [if_neg hab, if_neg hab] + exact ih a + +private theorem destutter_prefix_append + {α : Type*} (R : α → α → Prop) [DecidableRel R] + (l t : List α) : + l.destutter R <+: (l ++ t).destutter R := by + cases l with + | nil => + exact ⟨t.destutter R, by simp⟩ + | cons a l => + exact destutter'_prefix_append R a l t + +/-- Destuttering preserves list prefixhood. -/ +theorem IsPrefix.destutter + {α : Type*} {R : α → α → Prop} [DecidableRel R] + {l₁ l₂ : List α} (h : l₁ <+: l₂) : + l₁.destutter R <+: l₂.destutter R := by + rcases h with ⟨t, rfl⟩ + exact destutter_prefix_append R l₁ t + +end List diff --git a/RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariation.lean b/RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariation.lean index e6d84d8e..aa93eaae 100644 --- a/RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariation.lean +++ b/RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariation.lean @@ -4,6 +4,7 @@ public import Mathlib.Algebra.Polynomial.RuleOfSigns public import Mathlib.Data.List.ChainOfFn public import Mathlib.Data.List.NodupEquivFin public import Mathlib.Tactic +public import RealRooted.Mathlib.Data.List.Destutter /-! # Sign variations of finite vectors @@ -785,22 +786,12 @@ theorem List.signVariations_mono_of_prefix {l₁ l₂ : List R} (h : l₁ <+: l₂) : l₁.signVariations ≤ l₂.signVariations := by rw [List.signVariations, List.signVariations] - let s₁ : List SignType := - (l₁.map SignType.sign).filter (· ≠ 0) - let s₂ : List SignType := - (l₂.map SignType.sign).filter (· ≠ 0) - change (s₁.destutter (· ≠ ·)).length - 1 ≤ - (s₂.destutter (· ≠ ·)).length - 1 - have hsign : s₁ <+: s₂ := + have hsign : + (l₁.map SignType.sign).filter (· ≠ 0) <+: + (l₂.map SignType.sign).filter (· ≠ 0) := (h.map SignType.sign).filter (· ≠ 0) - have hsub : s₁.destutter (· ≠ ·) <+ s₂ := - (List.destutter_sublist - (fun x y : SignType => x ≠ y) s₁).trans hsign.sublist - have hchain : - (s₁.destutter (· ≠ ·)).IsChain (· ≠ ·) := - List.isChain_destutter (fun x y : SignType => x ≠ y) s₁ exact (Nat.sub_le_sub_right - (List.IsChain.length_le_length_destutter_ne hsub hchain)) 1 + (hsign.destutter (R := fun x y : SignType => x ≠ y)).length_le) 1 /-- Taking a list prefix cannot increase sign variation. -/ theorem List.signVariations_take_le From 03b3cfff63efb5d6895669d9a9eb950e5ddb1583 Mon Sep 17 00:00:00 2001 From: Per Alexandersson Date: Mon, 3 Aug 2026 03:07:06 +0000 Subject: [PATCH 073/141] Preserve final element under destuttering --- RealRooted/Mathlib/Data/List/Destutter.lean | 43 +++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/RealRooted/Mathlib/Data/List/Destutter.lean b/RealRooted/Mathlib/Data/List/Destutter.lean index 6d6a98b3..71fee61d 100644 --- a/RealRooted/Mathlib/Data/List/Destutter.lean +++ b/RealRooted/Mathlib/Data/List/Destutter.lean @@ -63,4 +63,47 @@ theorem IsPrefix.destutter rcases h with ⟨t, rfl⟩ exact destutter_prefix_append R l₁ t +private theorem getLast?_cons_of_ne_nil + {α : Type*} {a : α} {l : List α} (hl : l ≠ []) : + (a :: l).getLast? = l.getLast? := by + rw [List.getLast?_cons, List.getLast?_eq_some_getLast hl] + simp + +private theorem getLast?_destutter'_ne + {α : Type*} [DecidableEq α] (a : α) (l : List α) : + (l.destutter' (· ≠ ·) a).getLast? = + (a :: l).getLast? := by + induction l generalizing a with + | nil => + simp + | cons b l ih => + rw [List.destutter'_cons] + by_cases hab : a ≠ b + · rw [if_pos hab] + calc + (a :: l.destutter' (· ≠ ·) b).getLast? = + (l.destutter' (· ≠ ·) b).getLast? := + getLast?_cons_of_ne_nil (List.destutter'_ne_nil _ _) + _ = (b :: l).getLast? := ih b + _ = (a :: b :: l).getLast? := + (getLast?_cons_of_ne_nil (by simp)).symm + · have hab_eq : a = b := not_ne_iff.mp hab + subst b + rw [if_neg (by simp)] + calc + (l.destutter' (· ≠ ·) a).getLast? = + (a :: l).getLast? := ih a + _ = (a :: a :: l).getLast? := + (getLast?_cons_of_ne_nil (by simp)).symm + +/-- Destuttering by disequality preserves the final element. -/ +theorem getLast?_destutter_ne + {α : Type*} [DecidableEq α] (l : List α) : + (l.destutter (· ≠ ·)).getLast? = l.getLast? := by + cases l with + | nil => + simp + | cons a l => + exact getLast?_destutter'_ne a l + end List From 57ef0dab8d592adbd61a22eecf4fc3ae3a91df9e Mon Sep 17 00:00:00 2001 From: Per Alexandersson Date: Mon, 3 Aug 2026 03:22:00 +0000 Subject: [PATCH 074/141] Prove constant sign on sign-block fibers --- .../SpecialFunctions/ExpIntegral.lean | 2 +- RealRooted/Mathlib/Data/List/Destutter.lean | 27 +++++- .../Matrix/Determinant/CauchyBinet.lean | 4 +- .../Matrix/Determinant/Integral.lean | 4 +- .../LinearAlgebra/Matrix/SignVariation.lean | 84 +++++++++++++++++++ 5 files changed, 113 insertions(+), 8 deletions(-) diff --git a/RealRooted/Mathlib/Analysis/SpecialFunctions/ExpIntegral.lean b/RealRooted/Mathlib/Analysis/SpecialFunctions/ExpIntegral.lean index 2a929393..23e1803d 100644 --- a/RealRooted/Mathlib/Analysis/SpecialFunctions/ExpIntegral.lean +++ b/RealRooted/Mathlib/Analysis/SpecialFunctions/ExpIntegral.lean @@ -21,7 +21,7 @@ theorem intervalIntegral_mul_exp_mul (a b z : ℝ) : (∫ t in a..b, z * exp (t * z)) = exp (b * z) - exp (a * z) := by apply intervalIntegral.integral_eq_sub_of_hasDerivAt · intro t _ - simpa [mul_comm] using + simpa [Function.comp_def, mul_comm] using (Real.hasDerivAt_exp (t * z)).comp t ((hasDerivAt_id t).mul_const z) · exact (continuous_const.mul diff --git a/RealRooted/Mathlib/Data/List/Destutter.lean b/RealRooted/Mathlib/Data/List/Destutter.lean index 71fee61d..bfe91114 100644 --- a/RealRooted/Mathlib/Data/List/Destutter.lean +++ b/RealRooted/Mathlib/Data/List/Destutter.lean @@ -63,7 +63,7 @@ theorem IsPrefix.destutter rcases h with ⟨t, rfl⟩ exact destutter_prefix_append R l₁ t -private theorem getLast?_cons_of_ne_nil +private theorem getLast?_cons_eq_tail_of_ne_nil {α : Type*} {a : α} {l : List α} (hl : l ≠ []) : (a :: l).getLast? = l.getLast? := by rw [List.getLast?_cons, List.getLast?_eq_some_getLast hl] @@ -83,10 +83,10 @@ private theorem getLast?_destutter'_ne calc (a :: l.destutter' (· ≠ ·) b).getLast? = (l.destutter' (· ≠ ·) b).getLast? := - getLast?_cons_of_ne_nil (List.destutter'_ne_nil _ _) + getLast?_cons_eq_tail_of_ne_nil (List.destutter'_ne_nil _ _) _ = (b :: l).getLast? := ih b _ = (a :: b :: l).getLast? := - (getLast?_cons_of_ne_nil (by simp)).symm + (getLast?_cons_eq_tail_of_ne_nil (by simp)).symm · have hab_eq : a = b := not_ne_iff.mp hab subst b rw [if_neg (by simp)] @@ -94,7 +94,7 @@ private theorem getLast?_destutter'_ne (l.destutter' (· ≠ ·) a).getLast? = (a :: l).getLast? := ih a _ = (a :: a :: l).getLast? := - (getLast?_cons_of_ne_nil (by simp)).symm + (getLast?_cons_eq_tail_of_ne_nil (by simp)).symm /-- Destuttering by disequality preserves the final element. -/ theorem getLast?_destutter_ne @@ -106,4 +106,23 @@ theorem getLast?_destutter_ne | cons a l => exact getLast?_destutter'_ne a l +/-- Equal-length destuttered prefixes have the same final element. -/ +theorem IsPrefix.getLast?_eq_of_destutter_length_le + {α : Type*} [DecidableEq α] + {l₁ l₂ : List α} (h : l₁ <+: l₂) + (hlen : + (l₂.destutter (· ≠ ·)).length ≤ + (l₁.destutter (· ≠ ·)).length) : + l₁.getLast? = l₂.getLast? := by + have heq : + l₁.destutter (· ≠ ·) = + l₂.destutter (· ≠ ·) := + (h.destutter (R := fun x y : α => x ≠ y)).eq_of_length_le hlen + calc + l₁.getLast? = (l₁.destutter (· ≠ ·)).getLast? := + (List.getLast?_destutter_ne l₁).symm + _ = (l₂.destutter (· ≠ ·)).getLast? := + congrArg List.getLast? heq + _ = l₂.getLast? := List.getLast?_destutter_ne l₂ + end List diff --git a/RealRooted/Mathlib/LinearAlgebra/Matrix/Determinant/CauchyBinet.lean b/RealRooted/Mathlib/LinearAlgebra/Matrix/Determinant/CauchyBinet.lean index 3d2cf72e..ffa7d92b 100644 --- a/RealRooted/Mathlib/LinearAlgebra/Matrix/Determinant/CauchyBinet.lean +++ b/RealRooted/Mathlib/LinearAlgebra/Matrix/Determinant/CauchyBinet.lean @@ -1,5 +1,7 @@ import RealRooted.Mathlib.LinearAlgebra.Matrix.Determinant.Basic import Mathlib.LinearAlgebra.Basis.VectorSpace +import Mathlib.LinearAlgebra.Matrix.ToLin +import Mathlib.Order.Hom.PowersetCard /-! # Cauchy-Binet determinant expansions @@ -207,7 +209,7 @@ private theorem sum_perm_det_submatrix_comp_mul_prod_eq (L.submatrix rows e).submatrix id p by rfl] rw [Matrix.det_permute', Units.smul_def, ← Int.cast_smul_eq_zsmul R] - simp [mul_comm, mul_assoc] + ac_rfl _ = (L.submatrix rows e).det * (A.submatrix e cols).det := by congr 1 rw [Matrix.det_apply] diff --git a/RealRooted/Mathlib/LinearAlgebra/Matrix/Determinant/Integral.lean b/RealRooted/Mathlib/LinearAlgebra/Matrix/Determinant/Integral.lean index a3197dd0..8add6f6d 100644 --- a/RealRooted/Mathlib/LinearAlgebra/Matrix/Determinant/Integral.lean +++ b/RealRooted/Mathlib/LinearAlgebra/Matrix/Determinant/Integral.lean @@ -1,6 +1,6 @@ module -public import Mathlib.LinearAlgebra.Matrix.Determinant.Basic +public import RealRooted.Mathlib.LinearAlgebra.Matrix.Determinant.Basic public import Mathlib.MeasureTheory.Integral.IntervalIntegral.Basic public import Mathlib.MeasureTheory.Integral.Pi public import Mathlib.Topology.Algebra.Module.FiniteDimension @@ -24,7 +24,7 @@ private noncomputable def detUpdateRowLinearMap { toFun := fun row => (M.updateRow i row).det map_add' := fun u v => det_updateRow_add M i u v map_smul' := fun c u => by - simpa only [smul_eq_mul] using det_updateRow_smul M i c u } + simpa only [RingHom.id_apply, smul_eq_mul] using det_updateRow_smul M i c u } /-- A determinant commutes with an interval integral in one fixed row. -/ theorem det_updateRow_intervalIntegral diff --git a/RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariation.lean b/RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariation.lean index aa93eaae..ee7236b0 100644 --- a/RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariation.lean +++ b/RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariation.lean @@ -800,6 +800,17 @@ theorem List.signVariations_take_le (l.take k).signVariations ≤ l.signVariations := List.signVariations_mono_of_prefix (List.take_prefix k l) +/-- A nonzero endpoint survives sign filtering as the final sign of its prefix. -/ +theorem List.getLast?_filter_sign_take_succ + {R : Type*} [Zero R] [LinearOrder R] + (l : List R) {i : ℕ} (hi : i < l.length) + (hne : l[i] ≠ 0) : + (((l.take (i + 1)).map SignType.sign).filter + (· ≠ 0)).getLast? = some (SignType.sign l[i]) := by + rw [List.take_succ_eq_append_getElem hi, List.map_append, + List.filter_append] + simp [sign_ne_zero.mpr hne] + /-- Appending one real entry increases sign variation by at most one. -/ theorem List.signVariations_append_singleton_le_succ (l : List ℝ) (x : ℝ) : @@ -912,3 +923,76 @@ theorem Fin.prefixSignVariations_succ_le rw [show (i.succ : ℕ) + 1 = ((i : ℕ) + 1) + 1 by simp, List.take_succ_eq_append_getElem hindex] exact List.signVariations_append_singleton_le_succ _ _ + +/-- Nonzero entries with equal block index and ordered indices have equal signs. -/ +theorem Fin.sign_eq_of_le_of_signBlockIndex_eq + {n : ℕ} (c : Fin n → ℝ) {i j : Fin n} + (hij : i ≤ j) (hi : c i ≠ 0) (hj : c j ≠ 0) + (hblock : Fin.signBlockIndex c i = Fin.signBlockIndex c j) : + SignType.sign (c i) = SignType.sign (c j) := by + let l := List.ofFn c + let sᵢ := + ((l.take (i + 1)).map SignType.sign).filter (· ≠ 0) + let sⱼ := + ((l.take (j + 1)).map SignType.sign).filter (· ≠ 0) + have hprefix : sᵢ <+: sⱼ := + ((List.take_prefix_take_left + (Nat.add_le_add_right hij 1)).map SignType.sign).filter (· ≠ 0) + have hprefEq : + Fin.prefixSignVariations c i = + Fin.prefixSignVariations c j := by + have hval := congrArg Fin.val hblock + simpa using hval + have hvariationEq : + (sᵢ.destutter (· ≠ ·)).length - 1 = + (sⱼ.destutter (· ≠ ·)).length - 1 := by + change (l.take (i + 1)).signVariations = + (l.take (j + 1)).signVariations at hprefEq + change (sᵢ.destutter (· ≠ ·)).length - 1 = + (sⱼ.destutter (· ≠ ·)).length - 1 at hprefEq + exact hprefEq + have hiIndex : (i : ℕ) < l.length := by simp [l] + have hjIndex : (j : ℕ) < l.length := by simp [l] + have hiValue : l[i] ≠ 0 := by simpa [l] using hi + have hjValue : l[j] ≠ 0 := by simpa [l] using hj + have hlastI : + sᵢ.getLast? = some (SignType.sign (c i)) := by + simpa [sᵢ, l] using + List.getLast?_filter_sign_take_succ l hiIndex hiValue + have hlastJ : + sⱼ.getLast? = some (SignType.sign (c j)) := by + simpa [sⱼ, l] using + List.getLast?_filter_sign_take_succ l hjIndex hjValue + have hdestIne : sᵢ.destutter (· ≠ ·) ≠ [] := by + intro hd + have hlastDest := List.getLast?_destutter_ne sᵢ + rw [hd, hlastI] at hlastDest + simp at hlastDest + have hdestJne : sⱼ.destutter (· ≠ ·) ≠ [] := by + intro hd + have hlastDest := List.getLast?_destutter_ne sⱼ + rw [hd, hlastJ] at hlastDest + simp at hlastDest + have hdestIPos : 0 < (sᵢ.destutter (· ≠ ·)).length := + List.length_pos_iff.mpr hdestIne + have hdestJPos : 0 < (sⱼ.destutter (· ≠ ·)).length := + List.length_pos_iff.mpr hdestJne + have hlengthEq : + (sᵢ.destutter (· ≠ ·)).length = + (sⱼ.destutter (· ≠ ·)).length := by + lia + have hlastEq : sᵢ.getLast? = sⱼ.getLast? := + hprefix.getLast?_eq_of_destutter_length_le hlengthEq.ge + rw [hlastI, hlastJ] at hlastEq + exact Option.some.inj hlastEq + +/-- Nonzero entries in the same sign block have equal signs. -/ +theorem Fin.sign_eq_of_signBlockIndex_eq + {n : ℕ} (c : Fin n → ℝ) {i j : Fin n} + (hi : c i ≠ 0) (hj : c j ≠ 0) + (hblock : Fin.signBlockIndex c i = Fin.signBlockIndex c j) : + SignType.sign (c i) = SignType.sign (c j) := by + rcases le_total i j with hij | hji + · exact Fin.sign_eq_of_le_of_signBlockIndex_eq c hij hi hj hblock + · exact (Fin.sign_eq_of_le_of_signBlockIndex_eq + c hji hj hi hblock.symm).symm From e9aed1a3f54030f0fa39048d0e2d010a6914f00d Mon Sep 17 00:00:00 2001 From: Per Alexandersson Date: Mon, 3 Aug 2026 03:31:41 +0000 Subject: [PATCH 075/141] Clean up sign-block proof and repair clean builds --- RealRooted.lean | 1 + RealRooted/Mathlib/Data/List/Destutter.lean | 8 ++----- .../Matrix/Determinant/CauchyBinet.lean | 5 +++-- .../LinearAlgebra/Matrix/Gaussian.lean | 5 +++-- .../LinearAlgebra/Matrix/SignVariation.lean | 21 ++++++++----------- 5 files changed, 18 insertions(+), 22 deletions(-) diff --git a/RealRooted.lean b/RealRooted.lean index 73f4e399..4039625a 100644 --- a/RealRooted.lean +++ b/RealRooted.lean @@ -198,6 +198,7 @@ import RealRooted.Mathlib.Analysis.Complex.OpenMapping import RealRooted.Mathlib.Analysis.SpecialFunctions.ExpIntegral import RealRooted.Mathlib.Analysis.SpecialFunctions.Trigonometric.Basic import RealRooted.Mathlib.Combinatorics.Enumerative.OrderedSubsetPairs +import RealRooted.Mathlib.Data.List.Destutter import RealRooted.Mathlib.Data.List.Interleave import RealRooted.Mathlib.Data.List.Zip import RealRooted.Mathlib.Data.Nat.Cast.Basic diff --git a/RealRooted/Mathlib/Data/List/Destutter.lean b/RealRooted/Mathlib/Data/List/Destutter.lean index bfe91114..2c30fa66 100644 --- a/RealRooted/Mathlib/Data/List/Destutter.lean +++ b/RealRooted/Mathlib/Data/List/Destutter.lean @@ -118,11 +118,7 @@ theorem IsPrefix.getLast?_eq_of_destutter_length_le l₁.destutter (· ≠ ·) = l₂.destutter (· ≠ ·) := (h.destutter (R := fun x y : α => x ≠ y)).eq_of_length_le hlen - calc - l₁.getLast? = (l₁.destutter (· ≠ ·)).getLast? := - (List.getLast?_destutter_ne l₁).symm - _ = (l₂.destutter (· ≠ ·)).getLast? := - congrArg List.getLast? heq - _ = l₂.getLast? := List.getLast?_destutter_ne l₂ + simpa only [List.getLast?_destutter_ne] using + congrArg List.getLast? heq end List diff --git a/RealRooted/Mathlib/LinearAlgebra/Matrix/Determinant/CauchyBinet.lean b/RealRooted/Mathlib/LinearAlgebra/Matrix/Determinant/CauchyBinet.lean index ffa7d92b..ca2d5f73 100644 --- a/RealRooted/Mathlib/LinearAlgebra/Matrix/Determinant/CauchyBinet.lean +++ b/RealRooted/Mathlib/LinearAlgebra/Matrix/Determinant/CauchyBinet.lean @@ -208,7 +208,7 @@ private theorem sum_perm_det_submatrix_comp_mul_prod_eq rw [show L.submatrix rows (fun i => e (p i)) = (L.submatrix rows e).submatrix id p by rfl] rw [Matrix.det_permute', Units.smul_def, - ← Int.cast_smul_eq_zsmul R] + ← Int.cast_smul_eq_zsmul R, smul_eq_mul] ac_rfl _ = (L.submatrix rows e).det * (A.submatrix e cols).det := by congr 1 @@ -247,7 +247,8 @@ private theorem selected_mulVec_injective Function.Injective (A.submatrix id cols).mulVec := by rw [Matrix.mulVec_injective_iff] have h := (Matrix.mulVec_injective_iff.mp hA).comp cols hcols.injective - simpa [Matrix.col, Function.comp_def] using h + simpa only [Matrix.col_apply, Matrix.submatrix_apply, id_eq, + Function.comp_apply] using h private theorem exists_left_inverse_matrix {R : Type*} [Field R] {n q : ℕ} diff --git a/RealRooted/Mathlib/LinearAlgebra/Matrix/Gaussian.lean b/RealRooted/Mathlib/LinearAlgebra/Matrix/Gaussian.lean index 96c56cd5..20ccd282 100644 --- a/RealRooted/Mathlib/LinearAlgebra/Matrix/Gaussian.lean +++ b/RealRooted/Mathlib/LinearAlgebra/Matrix/Gaussian.lean @@ -361,9 +361,10 @@ theorem det_exponentialKernelMatrix_pos {q : ℕ} have hy0 : StrictMono y0 := fun _ _ hij => sub_lt_sub_right (hy hij) _ have hy00 : y0 0 = 0 := by simp [y0] - rw [show y = fun j => y0 j + y 0 by + have hy_eq : y = fun j => y0 j + y 0 := by funext j - simp [y0]] + simp [y0] + rw [hy_eq] apply (det_exponentialKernelMatrix_add_const_right_pos_iff x y0 (y 0)).2 have hfirst : ∀ i, exponentialKernelMatrix x y0 i 0 = 1 := by diff --git a/RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariation.lean b/RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariation.lean index ee7236b0..e94de2b9 100644 --- a/RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariation.lean +++ b/RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariation.lean @@ -883,6 +883,7 @@ theorem Fin.prefixSignVariations_le_signVariations exact List.signVariations_take_le _ _ /-- The sign-block index of an entry, counted by prefix sign variation. -/ +@[expose] def Fin.signBlockIndex {n : ℕ} (c : Fin n → ℝ) (j : Fin n) : Fin (Fin.signVariations c + 1) := @@ -941,8 +942,7 @@ theorem Fin.sign_eq_of_le_of_signBlockIndex_eq have hprefEq : Fin.prefixSignVariations c i = Fin.prefixSignVariations c j := by - have hval := congrArg Fin.val hblock - simpa using hval + simpa only [Fin.val_signBlockIndex] using congrArg Fin.val hblock have hvariationEq : (sᵢ.destutter (· ≠ ·)).length - 1 = (sⱼ.destutter (· ≠ ·)).length - 1 := by @@ -963,20 +963,17 @@ theorem Fin.sign_eq_of_le_of_signBlockIndex_eq sⱼ.getLast? = some (SignType.sign (c j)) := by simpa [sⱼ, l] using List.getLast?_filter_sign_take_succ l hjIndex hjValue - have hdestIne : sᵢ.destutter (· ≠ ·) ≠ [] := by + have hdestIPos : 0 < (sᵢ.destutter (· ≠ ·)).length := by + rw [List.length_pos_iff] intro hd have hlastDest := List.getLast?_destutter_ne sᵢ rw [hd, hlastI] at hlastDest simp at hlastDest - have hdestJne : sⱼ.destutter (· ≠ ·) ≠ [] := by - intro hd - have hlastDest := List.getLast?_destutter_ne sⱼ - rw [hd, hlastJ] at hlastDest - simp at hlastDest - have hdestIPos : 0 < (sᵢ.destutter (· ≠ ·)).length := - List.length_pos_iff.mpr hdestIne - have hdestJPos : 0 < (sⱼ.destutter (· ≠ ·)).length := - List.length_pos_iff.mpr hdestJne + have hdestLengthLe : + (sᵢ.destutter (· ≠ ·)).length ≤ + (sⱼ.destutter (· ≠ ·)).length := + (hprefix.destutter + (R := fun x y : SignType => x ≠ y)).length_le have hlengthEq : (sᵢ.destutter (· ≠ ·)).length = (sⱼ.destutter (· ≠ ·)).length := by From 6bb98e717086069cb1f728df0f9235a0445c6098 Mon Sep 17 00:00:00 2001 From: Per Alexandersson Date: Mon, 3 Aug 2026 03:36:42 +0000 Subject: [PATCH 076/141] Construct the finite sign-block decomposition --- .../LinearAlgebra/Matrix/SignVariation.lean | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariation.lean b/RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariation.lean index e94de2b9..05d054f9 100644 --- a/RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariation.lean +++ b/RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariation.lean @@ -993,3 +993,41 @@ theorem Fin.sign_eq_of_signBlockIndex_eq · exact Fin.sign_eq_of_le_of_signBlockIndex_eq c hij hi hj hblock · exact (Fin.sign_eq_of_le_of_signBlockIndex_eq c hji hj hi hblock.symm).symm + +/-- The same-sign block decomposition used in Karlin's variation theorem. + +Zero entries receive weight zero. Each block containing a nonzero entry uses +the sign of one such entry as its coefficient; the same-block sign theorem +makes this choice independent of the representative for reconstruction. -/ +noncomputable def Fin.signBlockDecomposition + {n : ℕ} (c : Fin n → ℝ) : Fin.SignBlockDecomposition c := by + classical + let coeff : Fin (Fin.signVariations c + 1) → ℝ := fun b => + if h : ∃ j, c j ≠ 0 ∧ Fin.signBlockIndex c j = b then + (SignType.sign (c h.choose) : ℝ) + else 0 + refine + { numBlocks := Fin.signVariations c + 1 + numBlocks_pos := Nat.succ_pos _ + block := Fin.signBlockIndex c + block_mono := Fin.monotone_signBlockIndex c + weight := fun j => |c j| + weight_nonneg := fun j => abs_nonneg (c j) + coeff := coeff + reconstruct := ?_ + numBlocks_sub_one := by simp } + intro j + by_cases hj : c j = 0 + · simp [hj] + · have hex : + ∃ k, c k ≠ 0 ∧ + Fin.signBlockIndex c k = Fin.signBlockIndex c j := + ⟨j, hj, rfl⟩ + change |c j| * coeff (Fin.signBlockIndex c j) = c j + rw [show coeff (Fin.signBlockIndex c j) = + (SignType.sign (c hex.choose) : ℝ) by + simp only [coeff, dif_pos hex]] + have hspec := hex.choose_spec + have hsign := + Fin.sign_eq_of_signBlockIndex_eq c hj hspec.1 hspec.2.symm + rw [← hsign, abs_mul_sign] From 05974d265b882915357b08e1e5b5247e58d131a7 Mon Sep 17 00:00:00 2001 From: Per Alexandersson Date: Mon, 3 Aug 2026 03:38:16 +0000 Subject: [PATCH 077/141] Derive sign-regular variation diminution --- .../LinearAlgebra/Matrix/SignRegularRankDeficient.lean | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/RealRooted/Mathlib/LinearAlgebra/Matrix/SignRegularRankDeficient.lean b/RealRooted/Mathlib/LinearAlgebra/Matrix/SignRegularRankDeficient.lean index d205b66f..13f2b1cf 100644 --- a/RealRooted/Mathlib/LinearAlgebra/Matrix/SignRegularRankDeficient.lean +++ b/RealRooted/Mathlib/LinearAlgebra/Matrix/SignRegularRankDeficient.lean @@ -833,3 +833,12 @@ theorem Matrix.IsSignRegular.signVariations_mulVec_le_of_signBlockDecomposition _ ≤ d.numBlocks - 1 := (Nat.sub_le_sub_right B.rank_le_width) 1 _ = Fin.signVariations c := d.numBlocks_sub_one + +/-- Karlin's variation-diminishing theorem for sign-regular matrices. -/ +theorem Matrix.IsSignRegular.signVariations_mulVec_le + {l n : ℕ} + {A : Matrix (Fin l) (Fin n) ℝ} + (hA : A.IsSignRegular) (c : Fin n → ℝ) : + Fin.signVariations (A.mulVec c) ≤ Fin.signVariations c := + hA.signVariations_mulVec_le_of_signBlockDecomposition + c (Fin.signBlockDecomposition c) From 5a46e8a7dc27149065b162b72eba68f4fd7a7ca7 Mon Sep 17 00:00:00 2001 From: Per Alexandersson Date: Mon, 3 Aug 2026 03:42:36 +0000 Subject: [PATCH 078/141] Golf finite sign-block reconstruction --- .../Mathlib/LinearAlgebra/Matrix/SignVariation.lean | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariation.lean b/RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariation.lean index 05d054f9..bcaa19da 100644 --- a/RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariation.lean +++ b/RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariation.lean @@ -1015,10 +1015,10 @@ noncomputable def Fin.signBlockDecomposition weight_nonneg := fun j => abs_nonneg (c j) coeff := coeff reconstruct := ?_ - numBlocks_sub_one := by simp } + numBlocks_sub_one := Nat.add_sub_cancel _ _ } intro j by_cases hj : c j = 0 - · simp [hj] + · simp only [hj, abs_zero, zero_mul] · have hex : ∃ k, c k ≠ 0 ∧ Fin.signBlockIndex c k = Fin.signBlockIndex c j := @@ -1026,8 +1026,7 @@ noncomputable def Fin.signBlockDecomposition change |c j| * coeff (Fin.signBlockIndex c j) = c j rw [show coeff (Fin.signBlockIndex c j) = (SignType.sign (c hex.choose) : ℝ) by - simp only [coeff, dif_pos hex]] - have hspec := hex.choose_spec - have hsign := - Fin.sign_eq_of_signBlockIndex_eq c hj hspec.1 hspec.2.symm - rw [← hsign, abs_mul_sign] + simp only [coeff, dif_pos hex], + ← Fin.sign_eq_of_signBlockIndex_eq c hj + hex.choose_spec.1 hex.choose_spec.2.symm, + abs_mul_sign] From ab138597d2bffce13b66e60c32880b47962bf9b5 Mon Sep 17 00:00:00 2001 From: Per Alexandersson Date: Mon, 3 Aug 2026 03:46:08 +0000 Subject: [PATCH 079/141] Derive rectangular TN variation diminution --- .../LinearAlgebra/Matrix/SignRegularRankDeficient.lean | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/RealRooted/Mathlib/LinearAlgebra/Matrix/SignRegularRankDeficient.lean b/RealRooted/Mathlib/LinearAlgebra/Matrix/SignRegularRankDeficient.lean index 13f2b1cf..9b602123 100644 --- a/RealRooted/Mathlib/LinearAlgebra/Matrix/SignRegularRankDeficient.lean +++ b/RealRooted/Mathlib/LinearAlgebra/Matrix/SignRegularRankDeficient.lean @@ -842,3 +842,12 @@ theorem Matrix.IsSignRegular.signVariations_mulVec_le Fin.signVariations (A.mulVec c) ≤ Fin.signVariations c := hA.signVariations_mulVec_le_of_signBlockDecomposition c (Fin.signBlockDecomposition c) + +/-- Karlin's forward variation-diminishing theorem for finite rectangular +totally nonnegative matrices. -/ +theorem Matrix.IsTotallyNonnegRect.signVariations_mulVec_le + {l n : ℕ} + {A : Matrix (Fin l) (Fin n) ℝ} + (hA : A.IsTotallyNonnegRect) (c : Fin n → ℝ) : + Fin.signVariations (A.mulVec c) ≤ Fin.signVariations c := + hA.isSignRegular.signVariations_mulVec_le c From 46dd182ff6f254c6af1b93767b2d27e512440cd7 Mon Sep 17 00:00:00 2001 From: Per Alexandersson Date: Mon, 3 Aug 2026 03:48:25 +0000 Subject: [PATCH 080/141] Preserve nonzero signs under finite convergence --- .../Matrix/SignVariationTopology.lean | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariationTopology.lean b/RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariationTopology.lean index 45787abd..ac59783c 100644 --- a/RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariationTopology.lean +++ b/RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariationTopology.lean @@ -12,6 +12,29 @@ open Filter Topology namespace Fin +/-- Nonzero coordinates eventually retain their signs under convergence. -/ +theorem eventually_sign_eq_of_tendsto + {α : Type*} {l : Filter α} [l.NeBot] {n : ℕ} + {f : α → Fin n → ℝ} {x : Fin n → ℝ} + (hf : Tendsto f l (𝓝 x)) : + ∀ᶠ a in l, ∀ i, x i ≠ 0 → + SignType.sign (f a i) = SignType.sign (x i) := by + have hsign : + ∀ i : Fin n, ∀ᶠ a in l, x i ≠ 0 → + SignType.sign (f a i) = SignType.sign (x i) := by + intro i + by_cases hi : x i = 0 + · exact Filter.Eventually.of_forall fun _ hne => (hne hi).elim + · have hiLimit := tendsto_pi_nhds.mp hf i + rcases lt_or_gt_of_ne hi with hneg | hpos + · filter_upwards [hiLimit.eventually_lt_const hneg] with a ha + intro + rw [sign_neg ha, sign_neg hneg] + · filter_upwards [hiLimit.eventually_const_lt hpos] with a ha + intro + rw [sign_pos ha, sign_pos hpos] + exact Filter.eventually_all.mpr hsign + /-- Sign variations cannot increase when a convergent net reaches its limit. -/ theorem signVariations_le_of_tendsto {α : Type*} {l : Filter α} [l.NeBot] {n r : ℕ} From 0b97f768ba9acc0197508c55aaf0d904e1b70cbe Mon Sep 17 00:00:00 2001 From: Per Alexandersson Date: Mon, 3 Aug 2026 03:49:49 +0000 Subject: [PATCH 081/141] Reuse finite convergence sign stability --- .../Matrix/SignVariationTopology.lean | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariationTopology.lean b/RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariationTopology.lean index ac59783c..dda4e187 100644 --- a/RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariationTopology.lean +++ b/RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariationTopology.lean @@ -67,21 +67,17 @@ theorem signVariations_le_of_tendsto intro z hz simp [hd_ne z hz] have hsign : - ∀ k : Fin d.length, - ∀ᶠ a in l, d.get k = SignType.sign (f a (eN k)) := by + ∀ᶠ a in l, ∀ k : Fin d.length, + d.get k = SignType.sign (f a (eN k)) := by + filter_upwards [eventually_sign_eq_of_tendsto hf] with a ha intro k - have hk := tendsto_pi_nhds.mp hf (eN k) have hxne : x (eN k) ≠ 0 := by rw [← sign_ne_zero, ← he' k] exact hd_ne _ (List.get_mem d k) - rcases lt_or_gt_of_ne hxne with hxneg | hxpos - · filter_upwards [hk.eventually_lt_const hxneg] with a ha - rw [he' k, sign_neg hxneg, sign_neg ha] - · filter_upwards [hk.eventually_const_lt hxpos] with a ha - rw [he' k, sign_pos hxpos, sign_pos ha] + rw [he' k, ha (eN k) hxne] have hmono : ∀ᶠ a in l, signVariations x ≤ signVariations (f a) := by - filter_upwards [Filter.eventually_all.mpr hsign] with a ha + filter_upwards [hsign] with a ha let rawA : List SignType := List.ofFn (SignType.sign ∘ f a) have hrawAlen : rawA.length = n := by simp [rawA] From 9440b6bececae24ebb90ddfdf20504686e9b128b Mon Sep 17 00:00:00 2001 From: Per Alexandersson Date: Mon, 3 Aug 2026 03:57:29 +0000 Subject: [PATCH 082/141] Prove nodal sign insertion invariance --- RealRooted/Mathlib/Data/List/Destutter.lean | 37 +++++++++++++++++++ .../LinearAlgebra/Matrix/SignVariation.lean | 14 +++++++ 2 files changed, 51 insertions(+) diff --git a/RealRooted/Mathlib/Data/List/Destutter.lean b/RealRooted/Mathlib/Data/List/Destutter.lean index 2c30fa66..bc6cd1e1 100644 --- a/RealRooted/Mathlib/Data/List/Destutter.lean +++ b/RealRooted/Mathlib/Data/List/Destutter.lean @@ -121,4 +121,41 @@ theorem IsPrefix.getLast?_eq_of_destutter_length_le simpa only [List.getLast?_destutter_ne] using congrArg List.getLast? heq +private theorem destutter'_append_cons_self_ne + {α : Type*} [DecidableEq α] (b a : α) (l₁ l₂ : List α) : + (l₁ ++ a :: a :: l₂).destutter' (· ≠ ·) b = + (l₁ ++ a :: l₂).destutter' (· ≠ ·) b := by + induction l₁ generalizing b with + | nil => + by_cases hba : b = a + · subst b + simp + · simp [hba] + | cons c l ih => + simp only [cons_append, List.destutter'_cons] + by_cases hbc : b ≠ c + · rw [if_pos hbc, if_pos hbc, ih c] + · rw [if_neg hbc, if_neg hbc, ih b] + +/-- Deleting one of two adjacent equal entries does not change disequality destuttering. -/ +theorem destutter_append_cons_self_ne + {α : Type*} [DecidableEq α] (l₁ l₂ : List α) (a : α) : + (l₁ ++ a :: a :: l₂).destutter (· ≠ ·) = + (l₁ ++ a :: l₂).destutter (· ≠ ·) := by + cases l₁ with + | nil => + simp [List.destutter_cons'] + | cons b l => + simp only [cons_append, List.destutter_cons'] + exact destutter'_append_cons_self_ne b a l l₂ + +/-- Deleting the second of two adjacent equal entries after a fixed entry does not change +disequality destuttering. -/ +theorem destutter_append_cons_cons_self_ne + {α : Type*} [DecidableEq α] (l₁ l₂ : List α) (a b : α) : + (l₁ ++ a :: b :: b :: l₂).destutter (· ≠ ·) = + (l₁ ++ a :: b :: l₂).destutter (· ≠ ·) := by + simpa only [append_assoc, singleton_append] using + destutter_append_cons_self_ne (l₁ ++ [a]) l₂ b + end List diff --git a/RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariation.lean b/RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariation.lean index bcaa19da..127bacdf 100644 --- a/RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariation.lean +++ b/RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariation.lean @@ -357,6 +357,20 @@ lemma signVariations_append_nonpos_le_succ (l₁ l₂ : List ℝ) end List +/-- Inserting any sign between opposite nonzero signs does not change sign variations. + +This is the finite local step used for nodal interior zeros in Karlin's perturbation +argument: after zero signs are removed, the inserted sign either disappears or duplicates +one of its two neighbors. -/ +theorem List.signVariations_insert_between_opposite + (l₁ l₂ : List SignType) (a b z : SignType) + (ha : a ≠ 0) (hb : b ≠ 0) (hab : a ≠ b) : + (l₁ ++ [a, z, b] ++ l₂).signVariations = + (l₁ ++ [a, b] ++ l₂).signVariations := by + fin_cases a <;> fin_cases b <;> fin_cases z <;> + simp_all [List.signVariations, List.destutter_append_cons_self_ne, + List.destutter_append_cons_cons_self_ne] + namespace Fin /-- The number of sign changes in a finite vector, in index order and ignoring From c19d3f66d295842297be96dbb325fa61715f9c1b Mon Sep 17 00:00:00 2001 From: Per Alexandersson Date: Mon, 3 Aug 2026 04:01:06 +0000 Subject: [PATCH 083/141] Bound endpoint sign variation cost --- RealRooted/Mathlib/Data/List/Destutter.lean | 18 ++++++++++++++++++ .../LinearAlgebra/Matrix/SignVariation.lean | 14 ++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/RealRooted/Mathlib/Data/List/Destutter.lean b/RealRooted/Mathlib/Data/List/Destutter.lean index bc6cd1e1..9bd3e8f7 100644 --- a/RealRooted/Mathlib/Data/List/Destutter.lean +++ b/RealRooted/Mathlib/Data/List/Destutter.lean @@ -158,4 +158,22 @@ theorem destutter_append_cons_cons_self_ne simpa only [append_assoc, singleton_append] using destutter_append_cons_self_ne (l₁ ++ [a]) l₂ b +/-- Prepending an entry increases the length after disequality destuttering by at most one. -/ +theorem length_destutter_cons_ne_le_succ + {α : Type*} [DecidableEq α] (a : α) (l : List α) : + ((a :: l).destutter (· ≠ ·)).length ≤ + (l.destutter (· ≠ ·)).length + 1 := by + cases l with + | nil => + simp + | cons b l => + simp only [List.destutter_cons', List.destutter'_cons] + by_cases hab : a ≠ b + · rw [if_pos hab] + simp + · have hab_eq : a = b := not_ne_iff.mp hab + subst b + rw [if_neg (by simp)] + lia + end List diff --git a/RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariation.lean b/RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariation.lean index 127bacdf..a2da0b31 100644 --- a/RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariation.lean +++ b/RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariation.lean @@ -357,6 +357,20 @@ lemma signVariations_append_nonpos_le_succ (l₁ l₂ : List ℝ) end List +/-- Prepending one sign increases the number of sign variations by at most one. -/ +theorem List.signVariations_cons_le_succ (a : SignType) (l : List SignType) : + (a :: l).signVariations ≤ l.signVariations + 1 := by + fin_cases a + · simp [List.signVariations] + · have h := List.length_destutter_cons_ne_le_succ (-1) + ((l.map SignType.sign).filter (· ≠ 0)) + simp [List.signVariations] + lia + · have h := List.length_destutter_cons_ne_le_succ 1 + ((l.map SignType.sign).filter (· ≠ 0)) + simp [List.signVariations] + lia + /-- Inserting any sign between opposite nonzero signs does not change sign variations. This is the finite local step used for nodal interior zeros in Karlin's perturbation From 852b2b0d559e238a395de94eb5a27c90ed414c35 Mon Sep 17 00:00:00 2001 From: Per Alexandersson Date: Mon, 3 Aug 2026 04:02:54 +0000 Subject: [PATCH 084/141] Golf endpoint variation proofs --- RealRooted/Mathlib/Data/List/Destutter.lean | 11 ++--------- .../LinearAlgebra/Matrix/SignVariation.lean | 14 +++++--------- 2 files changed, 7 insertions(+), 18 deletions(-) diff --git a/RealRooted/Mathlib/Data/List/Destutter.lean b/RealRooted/Mathlib/Data/List/Destutter.lean index 9bd3e8f7..14f031ed 100644 --- a/RealRooted/Mathlib/Data/List/Destutter.lean +++ b/RealRooted/Mathlib/Data/List/Destutter.lean @@ -164,16 +164,9 @@ theorem length_destutter_cons_ne_le_succ ((a :: l).destutter (· ≠ ·)).length ≤ (l.destutter (· ≠ ·)).length + 1 := by cases l with - | nil => - simp + | nil => simp | cons b l => simp only [List.destutter_cons', List.destutter'_cons] - by_cases hab : a ≠ b - · rw [if_pos hab] - simp - · have hab_eq : a = b := not_ne_iff.mp hab - subst b - rw [if_neg (by simp)] - lia + split_ifs <;> simp_all end List diff --git a/RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariation.lean b/RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariation.lean index a2da0b31..170f7667 100644 --- a/RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariation.lean +++ b/RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariation.lean @@ -360,15 +360,11 @@ end List /-- Prepending one sign increases the number of sign variations by at most one. -/ theorem List.signVariations_cons_le_succ (a : SignType) (l : List SignType) : (a :: l).signVariations ≤ l.signVariations + 1 := by - fin_cases a - · simp [List.signVariations] - · have h := List.length_destutter_cons_ne_le_succ (-1) - ((l.map SignType.sign).filter (· ≠ 0)) - simp [List.signVariations] - lia - · have h := List.length_destutter_cons_ne_le_succ 1 - ((l.map SignType.sign).filter (· ≠ 0)) - simp [List.signVariations] + have h := List.length_destutter_cons_ne_le_succ (SignType.sign a) + ((l.map SignType.sign).filter (· ≠ 0)) + by_cases ha : SignType.sign a = 0 + · simp [List.signVariations, ha] + · simp [List.signVariations, ha] lia /-- Inserting any sign between opposite nonzero signs does not change sign variations. From 246cbc93517a29fa36a272f0f29bdd6f39072c4e Mon Sep 17 00:00:00 2001 From: Per Alexandersson Date: Mon, 3 Aug 2026 04:05:48 +0000 Subject: [PATCH 085/141] Compose repeated nodal sign insertions --- .../LinearAlgebra/Matrix/SignVariation.lean | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariation.lean b/RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariation.lean index 170f7667..f6fe4874 100644 --- a/RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariation.lean +++ b/RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariation.lean @@ -381,6 +381,35 @@ theorem List.signVariations_insert_between_opposite simp_all [List.signVariations, List.destutter_append_cons_self_ne, List.destutter_append_cons_cons_self_ne] +namespace List + +/-- One insertion at an interior nodal position. + +This inductive relation is structural data recording an explicit list operation, +not an unproved mathematical assertion packaged as a proposition. -/ +inductive NodalInsertion : List SignType → List SignType → Prop + | insert (l₁ l₂ : List SignType) (a b z : SignType) + (ha : a ≠ 0) (hb : b ≠ 0) (hab : a ≠ b) : + NodalInsertion (l₁ ++ [a, b] ++ l₂) (l₁ ++ [a, z, b] ++ l₂) + +/-- Repeated insertions at interior nodal positions preserve sign variations. -/ +theorem signVariations_eq_of_nodalInsertions + {l l' : List SignType} + (h : Relation.ReflTransGen NodalInsertion l l') : + l'.signVariations = l.signVariations := by + induction h with + | refl => rfl + | tail h huv ih => + cases huv with + | insert l₁ l₂ a b z ha hb hab => + calc + (l₁ ++ [a, z, b] ++ l₂).signVariations = + (l₁ ++ [a, b] ++ l₂).signVariations := + signVariations_insert_between_opposite l₁ l₂ a b z ha hb hab + _ = l.signVariations := ih + +end List + namespace Fin /-- The number of sign changes in a finite vector, in index order and ignoring From 9d697625261ae330e7820b04ad3458ab76210fe3 Mon Sep 17 00:00:00 2001 From: Per Alexandersson Date: Mon, 3 Aug 2026 04:07:30 +0000 Subject: [PATCH 086/141] Bound complete nodal perturbation variation --- .../LinearAlgebra/Matrix/SignVariation.lean | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariation.lean b/RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariation.lean index f6fe4874..19f16419 100644 --- a/RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariation.lean +++ b/RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariation.lean @@ -408,6 +408,23 @@ theorem signVariations_eq_of_nodalInsertions signVariations_insert_between_opposite l₁ l₂ a b z ha hb hab _ = l.signVariations := ih +/-- Repeated interior nodal insertions and two arbitrary endpoint insertions increase sign +variations by at most two. -/ +theorem signVariations_endpoints_le_add_two_of_nodalInsertions + {l l' : List SignType} + (h : Relation.ReflTransGen NodalInsertion l l') + (a b : SignType) : + (((a :: l') ++ [b]).signVariations) ≤ l.signVariations + 2 := by + calc + ((a :: l') ++ [b]).signVariations = + (a :: (l' ++ [b])).signVariations := by simp + _ ≤ (l' ++ [b]).signVariations + 1 := + signVariations_cons_le_succ a (l' ++ [b]) + _ ≤ (l'.signVariations + 1) + 1 := + Nat.add_le_add_right (signVariations_append_singleton_le_succ l' b) 1 + _ = l.signVariations + 2 := by + rw [signVariations_eq_of_nodalInsertions h] + end List namespace Fin From 497a08117266ffba71f7d6ae96590fe85c78a2cf Mon Sep 17 00:00:00 2001 From: Per Alexandersson Date: Mon, 3 Aug 2026 04:10:11 +0000 Subject: [PATCH 087/141] Clean up nodal endpoint composition --- RealRooted/Mathlib/Data/List/Destutter.lean | 23 ++++++++++++++++++ .../LinearAlgebra/Matrix/SignVariation.lean | 24 ++++++++++++++----- 2 files changed, 41 insertions(+), 6 deletions(-) diff --git a/RealRooted/Mathlib/Data/List/Destutter.lean b/RealRooted/Mathlib/Data/List/Destutter.lean index 14f031ed..c08af3b4 100644 --- a/RealRooted/Mathlib/Data/List/Destutter.lean +++ b/RealRooted/Mathlib/Data/List/Destutter.lean @@ -169,4 +169,27 @@ theorem length_destutter_cons_ne_le_succ simp only [List.destutter_cons', List.destutter'_cons] split_ifs <;> simp_all +private theorem length_destutter'_append_singleton_ne_le_succ + {α : Type*} [DecidableEq α] (b a : α) (l : List α) : + ((l ++ [a]).destutter' (· ≠ ·) b).length ≤ + (l.destutter' (· ≠ ·) b).length + 1 := by + induction l generalizing b with + | nil => + simp [List.destutter'_cons] + split_ifs <;> simp + | cons c l ih => + simp only [cons_append, List.destutter'_cons] + split_ifs <;> simp_all + +/-- Appending an entry increases the length after disequality destuttering by at most one. -/ +theorem length_destutter_append_singleton_ne_le_succ + {α : Type*} [DecidableEq α] (l : List α) (a : α) : + ((l ++ [a]).destutter (· ≠ ·)).length ≤ + (l.destutter (· ≠ ·)).length + 1 := by + cases l with + | nil => simp + | cons b l => + simp only [cons_append, List.destutter_cons'] + exact length_destutter'_append_singleton_ne_le_succ b a l + end List diff --git a/RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariation.lean b/RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariation.lean index 19f16419..832a977d 100644 --- a/RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariation.lean +++ b/RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariation.lean @@ -367,6 +367,17 @@ theorem List.signVariations_cons_le_succ (a : SignType) (l : List SignType) : · simp [List.signVariations, ha] lia +/-- Appending one sign increases the number of sign variations by at most one. -/ +theorem List.signVariations_append_singleton_signType_le_succ + (l : List SignType) (a : SignType) : + (l ++ [a]).signVariations ≤ l.signVariations + 1 := by + have h := List.length_destutter_append_singleton_ne_le_succ + ((l.map SignType.sign).filter (· ≠ 0)) (SignType.sign a) + by_cases ha : SignType.sign a = 0 + · simp [List.signVariations, ha] + · simp [List.signVariations, ha] + lia + /-- Inserting any sign between opposite nonzero signs does not change sign variations. This is the finite local step used for nodal interior zeros in Karlin's perturbation @@ -414,14 +425,15 @@ theorem signVariations_endpoints_le_add_two_of_nodalInsertions {l l' : List SignType} (h : Relation.ReflTransGen NodalInsertion l l') (a b : SignType) : - (((a :: l') ++ [b]).signVariations) ≤ l.signVariations + 2 := by + ((a :: l') ++ [b]).signVariations ≤ l.signVariations + 2 := by calc - ((a :: l') ++ [b]).signVariations = - (a :: (l' ++ [b])).signVariations := by simp - _ ≤ (l' ++ [b]).signVariations + 1 := - signVariations_cons_le_succ a (l' ++ [b]) + ((a :: l') ++ [b]).signVariations ≤ + (l' ++ [b]).signVariations + 1 := by + simpa only [List.cons_append] using + signVariations_cons_le_succ a (l' ++ [b]) _ ≤ (l'.signVariations + 1) + 1 := - Nat.add_le_add_right (signVariations_append_singleton_le_succ l' b) 1 + Nat.add_le_add_right + (signVariations_append_singleton_signType_le_succ l' b) 1 _ = l.signVariations + 2 := by rw [signVariations_eq_of_nodalInsertions h] From 48600c8539074a10c8523047f754559b31fea25a Mon Sep 17 00:00:00 2001 From: Per Alexandersson Date: Mon, 3 Aug 2026 04:14:06 +0000 Subject: [PATCH 088/141] Preserve nodal insertions under context --- .../LinearAlgebra/Matrix/SignVariation.lean | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariation.lean b/RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariation.lean index 832a977d..880edab1 100644 --- a/RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariation.lean +++ b/RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariation.lean @@ -403,6 +403,28 @@ inductive NodalInsertion : List SignType → List SignType → Prop (ha : a ≠ 0) (hb : b ≠ 0) (hab : a ≠ b) : NodalInsertion (l₁ ++ [a, b] ++ l₂) (l₁ ++ [a, z, b] ++ l₂) +/-- Adding unchanged list context preserves one nodal insertion. -/ +theorem NodalInsertion.append_context + {l l' : List SignType} (h : NodalInsertion l l') + (pre post : List SignType) : + NodalInsertion (pre ++ l ++ post) (pre ++ l' ++ post) := by + cases h with + | insert l₁ l₂ a b z ha hb hab => + simpa only [append_assoc] using + NodalInsertion.insert (pre ++ l₁) (l₂ ++ post) a b z ha hb hab + +/-- Adding unchanged list context preserves repeated nodal insertions. -/ +theorem NodalInsertion.reflTransGen_append_context + {l l' : List SignType} + (h : Relation.ReflTransGen NodalInsertion l l') + (pre post : List SignType) : + Relation.ReflTransGen NodalInsertion + (pre ++ l ++ post) (pre ++ l' ++ post) := by + induction h with + | refl => exact .refl + | tail h huv ih => + exact ih.tail (huv.append_context pre post) + /-- Repeated insertions at interior nodal positions preserve sign variations. -/ theorem signVariations_eq_of_nodalInsertions {l l' : List SignType} From cab1d03f65c0954f0cfc837b1d65babae5919240 Mon Sep 17 00:00:00 2001 From: Per Alexandersson Date: Mon, 3 Aug 2026 04:17:54 +0000 Subject: [PATCH 089/141] Derive opposite signs from strict nodality --- .../Mathlib/LinearAlgebra/Matrix/SignVariation.lean | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariation.lean b/RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariation.lean index 880edab1..6908c462 100644 --- a/RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariation.lean +++ b/RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariation.lean @@ -461,6 +461,15 @@ theorem signVariations_endpoints_le_add_two_of_nodalInsertions end List +/-- A negative product of real numbers has nonzero, opposite signs. -/ +theorem SignType.sign_ne_zero_and_ne_of_mul_neg + {a b : ℝ} (h : a * b < 0) : + SignType.sign a ≠ 0 ∧ SignType.sign b ≠ 0 ∧ + SignType.sign a ≠ SignType.sign b := by + rcases (mul_neg_iff.mp h) with ⟨ha, hb⟩ | ⟨ha, hb⟩ + · simp [SignType.sign, ha, hb, not_lt_of_ge hb.le] + · simp [SignType.sign, ha, hb, not_lt_of_ge ha.le] + namespace Fin /-- The number of sign changes in a finite vector, in index order and ignoring From 1ee508e0c95c9e988f9fe767f5660bd8f723080f Mon Sep 17 00:00:00 2001 From: Per Alexandersson Date: Mon, 3 Aug 2026 04:19:37 +0000 Subject: [PATCH 090/141] Relate succAbove to list erasure --- RealRooted/Mathlib/Data/List/OfFn.lean | 36 +++++++++++++++++++ .../LinearAlgebra/Matrix/SignVariation.lean | 1 + 2 files changed, 37 insertions(+) create mode 100644 RealRooted/Mathlib/Data/List/OfFn.lean diff --git a/RealRooted/Mathlib/Data/List/OfFn.lean b/RealRooted/Mathlib/Data/List/OfFn.lean new file mode 100644 index 00000000..5786d80b --- /dev/null +++ b/RealRooted/Mathlib/Data/List/OfFn.lean @@ -0,0 +1,36 @@ +module + +public import Mathlib.Data.List.OfFn + +/-! +# Additional lemmas about lists of finite functions + +This file contains compatibility lemmas intended for upstreaming to +`Mathlib.Data.List.OfFn`. +-/ + +public section + +namespace List + +/-- Removing a finite-function coordinate agrees with erasing that list index. -/ +theorem ofFn_succAbove_eq_eraseIdx + {α : Type*} {n : ℕ} + (f : Fin (n + 1) → α) (p : Fin (n + 1)) : + List.ofFn (fun i : Fin n => f (p.succAbove i)) = + (List.ofFn f).eraseIdx p := by + apply List.ext_getElem + · simp [List.length_eraseIdx, p.isLt] + · intro i hi₁ hi₂ + simp only [List.getElem_ofFn, List.getElem_eraseIdx] + split + · rw [Fin.succAbove_of_castSucc_lt p _ (by + change i < (p : ℕ) + exact ‹i < (p : ℕ)›)] + congr + · rw [Fin.succAbove_of_le_castSucc p _ (by + change (p : ℕ) ≤ i + exact Nat.le_of_not_gt ‹¬i < (p : ℕ)›)] + congr + +end List diff --git a/RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariation.lean b/RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariation.lean index 6908c462..65fa028d 100644 --- a/RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariation.lean +++ b/RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariation.lean @@ -5,6 +5,7 @@ public import Mathlib.Data.List.ChainOfFn public import Mathlib.Data.List.NodupEquivFin public import Mathlib.Tactic public import RealRooted.Mathlib.Data.List.Destutter +public import RealRooted.Mathlib.Data.List.OfFn /-! # Sign variations of finite vectors From 88cbc81ea7a1621f5c3ec8ba8a0d6e88697e4161 Mon Sep 17 00:00:00 2001 From: Per Alexandersson Date: Mon, 3 Aug 2026 04:21:57 +0000 Subject: [PATCH 091/141] Clean up nodal bridge APIs --- RealRooted/Mathlib/Data/List/OfFn.lean | 2 +- RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariation.lean | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/RealRooted/Mathlib/Data/List/OfFn.lean b/RealRooted/Mathlib/Data/List/OfFn.lean index 5786d80b..f9fa8f5e 100644 --- a/RealRooted/Mathlib/Data/List/OfFn.lean +++ b/RealRooted/Mathlib/Data/List/OfFn.lean @@ -21,7 +21,7 @@ theorem ofFn_succAbove_eq_eraseIdx (List.ofFn f).eraseIdx p := by apply List.ext_getElem · simp [List.length_eraseIdx, p.isLt] - · intro i hi₁ hi₂ + · intro i hi₁ _ simp only [List.getElem_ofFn, List.getElem_eraseIdx] split · rw [Fin.succAbove_of_castSucc_lt p _ (by diff --git a/RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariation.lean b/RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariation.lean index 65fa028d..da61a26f 100644 --- a/RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariation.lean +++ b/RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariation.lean @@ -405,7 +405,7 @@ inductive NodalInsertion : List SignType → List SignType → Prop NodalInsertion (l₁ ++ [a, b] ++ l₂) (l₁ ++ [a, z, b] ++ l₂) /-- Adding unchanged list context preserves one nodal insertion. -/ -theorem NodalInsertion.append_context +protected theorem NodalInsertion.append_context {l l' : List SignType} (h : NodalInsertion l l') (pre post : List SignType) : NodalInsertion (pre ++ l ++ post) (pre ++ l' ++ post) := by @@ -415,7 +415,7 @@ theorem NodalInsertion.append_context NodalInsertion.insert (pre ++ l₁) (l₂ ++ post) a b z ha hb hab /-- Adding unchanged list context preserves repeated nodal insertions. -/ -theorem NodalInsertion.reflTransGen_append_context +protected theorem NodalInsertion.reflTransGen_append_context {l l' : List SignType} (h : Relation.ReflTransGen NodalInsertion l l') (pre post : List SignType) : From 13075c931d7cfb8a97f23afb928fd6ba9ebf5197 Mon Sep 17 00:00:00 2001 From: Per Alexandersson Date: Mon, 3 Aug 2026 04:23:34 +0000 Subject: [PATCH 092/141] Split finite lists into endpoints and interior --- RealRooted/Mathlib/Data/List/OfFn.lean | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/RealRooted/Mathlib/Data/List/OfFn.lean b/RealRooted/Mathlib/Data/List/OfFn.lean index f9fa8f5e..7f205c4a 100644 --- a/RealRooted/Mathlib/Data/List/OfFn.lean +++ b/RealRooted/Mathlib/Data/List/OfFn.lean @@ -33,4 +33,15 @@ theorem ofFn_succAbove_eq_eraseIdx exact Nat.le_of_not_gt ‹¬i < (p : ℕ)›)] congr +/-- A finite-function list splits into its two endpoints and interior coordinates. -/ +theorem ofFn_two_endpoints + {α : Type*} {n : ℕ} (f : Fin (n + 2) → α) : + List.ofFn f = + f 0 :: + (List.ofFn (fun i : Fin n => f i.succ.castSucc) ++ + [f (Fin.last (n + 1))]) := by + rw [List.ofFn_succ, List.ofFn_succ'] + simp only [List.concat_eq_append] + congr + end List From 63ba5144021d7128050ed121d3aa5873438a3820 Mon Sep 17 00:00:00 2001 From: Per Alexandersson Date: Mon, 3 Aug 2026 04:25:59 +0000 Subject: [PATCH 093/141] Transport neighbor triples through succAbove --- RealRooted/Mathlib/Data/Fin/Basic.lean | 54 ++++++++++++++++++++++++++ RealRooted/Mathlib/Data/List/OfFn.lean | 1 + 2 files changed, 55 insertions(+) create mode 100644 RealRooted/Mathlib/Data/Fin/Basic.lean diff --git a/RealRooted/Mathlib/Data/Fin/Basic.lean b/RealRooted/Mathlib/Data/Fin/Basic.lean new file mode 100644 index 00000000..0ff20764 --- /dev/null +++ b/RealRooted/Mathlib/Data/Fin/Basic.lean @@ -0,0 +1,54 @@ +module + +public import Mathlib.Data.Fin.Basic + +/-! +# Additional lemmas about finite indices + +This file contains compatibility lemmas intended for upstreaming to +`Mathlib.Data.Fin.Basic`. +-/ + +public section + +namespace Fin + +/-- `succAbove` preserves three consecutive coordinates lying before the omitted index. -/ +theorem succAbove_triple_eq_castSucc_of_right_lt + {n : ℕ} (p : Fin (n + 3)) (i : Fin n) + (h : i.succ.succ.castSucc < p) : + p.succAbove i.castSucc.castSucc = i.castSucc.castSucc.castSucc ∧ + p.succAbove i.succ.castSucc = i.succ.castSucc.castSucc ∧ + p.succAbove i.succ.succ = i.succ.succ.castSucc := by + have hleft : i.castSucc.castSucc.castSucc < p := by + apply lt_of_le_of_lt _ h + change (i : ℕ) ≤ (i : ℕ) + 2 + lia + have hcenter : i.succ.castSucc.castSucc < p := by + apply lt_of_le_of_lt _ h + change (i : ℕ) + 1 ≤ (i : ℕ) + 2 + lia + exact ⟨Fin.succAbove_of_castSucc_lt p _ hleft, + Fin.succAbove_of_castSucc_lt p _ hcenter, + Fin.succAbove_of_castSucc_lt p _ h⟩ + +/-- `succAbove` shifts three consecutive coordinates lying after the omitted index. -/ +theorem succAbove_triple_eq_succ_of_le_left + {n : ℕ} (p : Fin (n + 3)) (i : Fin n) + (h : p ≤ i.castSucc.castSucc.castSucc) : + p.succAbove i.castSucc.castSucc = i.castSucc.castSucc.succ ∧ + p.succAbove i.succ.castSucc = i.succ.castSucc.succ ∧ + p.succAbove i.succ.succ = i.succ.succ.succ := by + have hcenter : p ≤ i.succ.castSucc.castSucc := by + apply le_trans h + change (i : ℕ) ≤ (i : ℕ) + 1 + lia + have hright : p ≤ i.succ.succ.castSucc := by + apply le_trans h + change (i : ℕ) ≤ (i : ℕ) + 2 + lia + exact ⟨Fin.succAbove_of_le_castSucc p _ h, + Fin.succAbove_of_le_castSucc p _ hcenter, + Fin.succAbove_of_le_castSucc p _ hright⟩ + +end Fin diff --git a/RealRooted/Mathlib/Data/List/OfFn.lean b/RealRooted/Mathlib/Data/List/OfFn.lean index 7f205c4a..de124689 100644 --- a/RealRooted/Mathlib/Data/List/OfFn.lean +++ b/RealRooted/Mathlib/Data/List/OfFn.lean @@ -1,6 +1,7 @@ module public import Mathlib.Data.List.OfFn +public import RealRooted.Mathlib.Data.Fin.Basic /-! # Additional lemmas about lists of finite functions From 24591826f839b7c3a92a977918d30b5fa4f80310 Mon Sep 17 00:00:00 2001 From: Per Alexandersson Date: Mon, 3 Aug 2026 04:27:47 +0000 Subject: [PATCH 094/141] Golf succAbove triple arithmetic --- RealRooted/Mathlib/Data/Fin/Basic.lean | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/RealRooted/Mathlib/Data/Fin/Basic.lean b/RealRooted/Mathlib/Data/Fin/Basic.lean index 0ff20764..c3b4fcc4 100644 --- a/RealRooted/Mathlib/Data/Fin/Basic.lean +++ b/RealRooted/Mathlib/Data/Fin/Basic.lean @@ -20,13 +20,12 @@ theorem succAbove_triple_eq_castSucc_of_right_lt p.succAbove i.castSucc.castSucc = i.castSucc.castSucc.castSucc ∧ p.succAbove i.succ.castSucc = i.succ.castSucc.castSucc ∧ p.succAbove i.succ.succ = i.succ.succ.castSucc := by + change (i : ℕ) + 2 < (p : ℕ) at h have hleft : i.castSucc.castSucc.castSucc < p := by - apply lt_of_le_of_lt _ h - change (i : ℕ) ≤ (i : ℕ) + 2 + change (i : ℕ) < (p : ℕ) lia have hcenter : i.succ.castSucc.castSucc < p := by - apply lt_of_le_of_lt _ h - change (i : ℕ) + 1 ≤ (i : ℕ) + 2 + change (i : ℕ) + 1 < (p : ℕ) lia exact ⟨Fin.succAbove_of_castSucc_lt p _ hleft, Fin.succAbove_of_castSucc_lt p _ hcenter, @@ -39,13 +38,12 @@ theorem succAbove_triple_eq_succ_of_le_left p.succAbove i.castSucc.castSucc = i.castSucc.castSucc.succ ∧ p.succAbove i.succ.castSucc = i.succ.castSucc.succ ∧ p.succAbove i.succ.succ = i.succ.succ.succ := by + change (p : ℕ) ≤ (i : ℕ) at h have hcenter : p ≤ i.succ.castSucc.castSucc := by - apply le_trans h - change (i : ℕ) ≤ (i : ℕ) + 1 + change (p : ℕ) ≤ (i : ℕ) + 1 lia have hright : p ≤ i.succ.succ.castSucc := by - apply le_trans h - change (i : ℕ) ≤ (i : ℕ) + 2 + change (p : ℕ) ≤ (i : ℕ) + 2 lia exact ⟨Fin.succAbove_of_le_castSucc p _ h, Fin.succAbove_of_le_castSucc p _ hcenter, From fbbb8f9fc4d6f20773ff374547cc7c3350c1448b Mon Sep 17 00:00:00 2001 From: Per Alexandersson Date: Mon, 3 Aug 2026 04:29:13 +0000 Subject: [PATCH 095/141] Rule out the nodal deletion splice --- RealRooted/Mathlib/Data/Fin/Basic.lean | 8 ++++++++ .../Mathlib/LinearAlgebra/Matrix/SignVariation.lean | 10 ++++++++++ 2 files changed, 18 insertions(+) diff --git a/RealRooted/Mathlib/Data/Fin/Basic.lean b/RealRooted/Mathlib/Data/Fin/Basic.lean index c3b4fcc4..57753e5e 100644 --- a/RealRooted/Mathlib/Data/Fin/Basic.lean +++ b/RealRooted/Mathlib/Data/Fin/Basic.lean @@ -49,4 +49,12 @@ theorem succAbove_triple_eq_succ_of_le_left Fin.succAbove_of_le_castSucc p _ hcenter, Fin.succAbove_of_le_castSucc p _ hright⟩ +/-- Omitting an interior center sends the corresponding new center to its old right +neighbor. -/ +theorem succAbove_center_eq_right (i : Fin n) : + (i.succ.castSucc.castSucc).succAbove i.succ.castSucc = + i.succ.succ.castSucc := by + rw [Fin.succAbove_of_le_castSucc _ _ le_rfl] + congr + end Fin diff --git a/RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariation.lean b/RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariation.lean index da61a26f..62d72e9b 100644 --- a/RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariation.lean +++ b/RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariation.lean @@ -473,6 +473,16 @@ theorem SignType.sign_ne_zero_and_ne_of_mul_neg namespace Fin +/-- At the splice created by deleting a nodal zero, the new center is nonzero. -/ +theorem succAbove_center_ne_zero_of_mul_neg + {n : ℕ} (x : Fin (n + 3) → ℝ) (i : Fin n) + (h : x i.castSucc.castSucc.castSucc * x i.succ.succ.castSucc < 0) : + x ((i.succ.castSucc.castSucc).succAbove i.succ.castSucc) ≠ 0 := by + rw [Fin.succAbove_center_eq_right] + intro hz + rw [hz, mul_zero] at h + exact lt_irrefl 0 h + /-- The number of sign changes in a finite vector, in index order and ignoring zero entries. -/ def signVariations {R : Type*} [Zero R] [LinearOrder R] {n : ℕ} From aed2ae75bb03e04d14fe23477ad84265525bac17 Mon Sep 17 00:00:00 2001 From: Per Alexandersson Date: Mon, 3 Aug 2026 04:31:01 +0000 Subject: [PATCH 096/141] Handle the second nodal deletion adjacency --- RealRooted/Mathlib/Data/Fin/Basic.lean | 8 ++++++++ .../Mathlib/LinearAlgebra/Matrix/SignVariation.lean | 10 ++++++++++ 2 files changed, 18 insertions(+) diff --git a/RealRooted/Mathlib/Data/Fin/Basic.lean b/RealRooted/Mathlib/Data/Fin/Basic.lean index 57753e5e..5b301f4e 100644 --- a/RealRooted/Mathlib/Data/Fin/Basic.lean +++ b/RealRooted/Mathlib/Data/Fin/Basic.lean @@ -57,4 +57,12 @@ theorem succAbove_center_eq_right (i : Fin n) : rw [Fin.succAbove_of_le_castSucc _ _ le_rfl] congr +/-- Omitting the old right neighbor sends the corresponding new center to its old value. -/ +theorem succAbove_center_eq_left (i : Fin n) : + (i.succ.succ.castSucc).succAbove i.succ.castSucc = + i.succ.castSucc.castSucc := by + rw [Fin.succAbove_of_castSucc_lt _ _ (by + change (i : ℕ) + 1 < (i : ℕ) + 2 + lia)] + end Fin diff --git a/RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariation.lean b/RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariation.lean index 62d72e9b..4b735474 100644 --- a/RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariation.lean +++ b/RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariation.lean @@ -483,6 +483,16 @@ theorem succAbove_center_ne_zero_of_mul_neg rw [hz, mul_zero] at h exact lt_irrefl 0 h +/-- If the omitted nodal zero was the old right neighbor, the new center is nonzero. -/ +theorem succAbove_center_ne_zero_of_omit_right_mul_neg + {n : ℕ} (x : Fin (n + 3) → ℝ) (i : Fin n) + (h : x i.succ.castSucc.castSucc * x i.succ.succ.succ < 0) : + x ((i.succ.succ.castSucc).succAbove i.succ.castSucc) ≠ 0 := by + rw [Fin.succAbove_center_eq_left] + intro hz + rw [hz, zero_mul] at h + exact lt_irrefl 0 h + /-- The number of sign changes in a finite vector, in index order and ignoring zero entries. -/ def signVariations {R : Type*} [Zero R] [LinearOrder R] {n : ℕ} From 1adbaaa6858336ea6220abd73b6bfd8a02611db4 Mon Sep 17 00:00:00 2001 From: Per Alexandersson Date: Mon, 3 Aug 2026 04:32:18 +0000 Subject: [PATCH 097/141] Deduplicate nodal splice contradictions --- .../Mathlib/LinearAlgebra/Matrix/SignVariation.lean | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariation.lean b/RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariation.lean index 4b735474..aca7c40f 100644 --- a/RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariation.lean +++ b/RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariation.lean @@ -479,9 +479,7 @@ theorem succAbove_center_ne_zero_of_mul_neg (h : x i.castSucc.castSucc.castSucc * x i.succ.succ.castSucc < 0) : x ((i.succ.castSucc.castSucc).succAbove i.succ.castSucc) ≠ 0 := by rw [Fin.succAbove_center_eq_right] - intro hz - rw [hz, mul_zero] at h - exact lt_irrefl 0 h + exact right_ne_zero_of_mul (ne_of_lt h) /-- If the omitted nodal zero was the old right neighbor, the new center is nonzero. -/ theorem succAbove_center_ne_zero_of_omit_right_mul_neg @@ -489,9 +487,7 @@ theorem succAbove_center_ne_zero_of_omit_right_mul_neg (h : x i.succ.castSucc.castSucc * x i.succ.succ.succ < 0) : x ((i.succ.succ.castSucc).succAbove i.succ.castSucc) ≠ 0 := by rw [Fin.succAbove_center_eq_left] - intro hz - rw [hz, zero_mul] at h - exact lt_irrefl 0 h + exact left_ne_zero_of_mul (ne_of_lt h) /-- The number of sign changes in a finite vector, in index order and ignoring zero entries. -/ From dd1caa61b76d56f84b49a29af93f6037ad8b5f59 Mon Sep 17 00:00:00 2001 From: Per Alexandersson Date: Mon, 3 Aug 2026 04:34:49 +0000 Subject: [PATCH 098/141] Transport nodality through coordinate deletion --- .../LinearAlgebra/Matrix/SignVariation.lean | 48 +++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariation.lean b/RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariation.lean index aca7c40f..74b8ca6d 100644 --- a/RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariation.lean +++ b/RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariation.lean @@ -489,6 +489,54 @@ theorem succAbove_center_ne_zero_of_omit_right_mul_neg rw [Fin.succAbove_center_eq_left] exact left_ne_zero_of_mul (ne_of_lt h) +/-- Deleting an interior nodal zero preserves strict interior nodality. -/ +theorem interiorNodal_succAbove + {n : ℕ} (x : Fin (n + 3) → ℝ) (k : Fin (n + 1)) + (hk : x k.succ.castSucc = 0) + (hnodal : ∀ j : Fin (n + 1), x j.succ.castSucc = 0 → + x j.castSucc.castSucc * x j.succ.succ < 0) : + ∀ i : Fin n, + x (k.succ.castSucc.succAbove i.succ.castSucc) = 0 → + x (k.succ.castSucc.succAbove i.castSucc.castSucc) * + x (k.succ.castSucc.succAbove i.succ.succ) < 0 := by + intro i hi + by_cases hki : (k : ℕ) < (i : ℕ) + · have hp : k.succ.castSucc ≤ i.castSucc.castSucc.castSucc := by + change (k : ℕ) + 1 ≤ (i : ℕ) + lia + rcases Fin.succAbove_triple_eq_succ_of_le_left k.succ.castSucc i hp with + ⟨hleft, hcenter, hright⟩ + rw [hcenter] at hi + rw [hleft, hright] + simpa using hnodal i.succ (by simpa using hi) + · by_cases hik : (i : ℕ) < (k : ℕ) + · by_cases hnext : (k : ℕ) = (i : ℕ) + 1 + · have hkeq : k = i.succ := by + apply Fin.ext + exact hnext + subst k + have hn := hnodal i.succ (by simpa using hk) + have hne := Fin.succAbove_center_ne_zero_of_omit_right_mul_neg x i + (by simpa using hn) + exact (hne (by simpa using hi)).elim + · have hp : i.succ.succ.castSucc < k.succ.castSucc := by + change (i : ℕ) + 2 < (k : ℕ) + 1 + lia + rcases Fin.succAbove_triple_eq_castSucc_of_right_lt k.succ.castSucc i hp with + ⟨hleft, hcenter, hright⟩ + rw [hcenter] at hi + rw [hleft, hright] + simpa using hnodal i.castSucc (by simpa using hi) + · have hkeq : k = i.castSucc := by + apply Fin.ext + change (k : ℕ) = (i : ℕ) + lia + subst k + have hn := hnodal i.castSucc (by simpa using hk) + have hne := Fin.succAbove_center_ne_zero_of_mul_neg x i + (by simpa using hn) + exact (hne (by simpa using hi)).elim + /-- The number of sign changes in a finite vector, in index order and ignoring zero entries. -/ def signVariations {R : Type*} [Zero R] [LinearOrder R] {n : ℕ} From 39a40dec6617e31f63f5f90223f513296cde1962 Mon Sep 17 00:00:00 2001 From: Per Alexandersson Date: Mon, 3 Aug 2026 04:37:21 +0000 Subject: [PATCH 099/141] Normalize finite sign variation lists --- .../LinearAlgebra/Matrix/SignVariation.lean | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariation.lean b/RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariation.lean index 74b8ca6d..f1d9dc13 100644 --- a/RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariation.lean +++ b/RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariation.lean @@ -358,6 +358,11 @@ lemma signVariations_append_nonpos_le_succ (l₁ l₂ : List ℝ) end List +/-- Taking the sign of a sign is the identity. -/ +@[simp] +theorem SignType.sign_sign (s : SignType) : SignType.sign s = s := by + fin_cases s <;> rfl + /-- Prepending one sign increases the number of sign variations by at most one. -/ theorem List.signVariations_cons_le_succ (a : SignType) (l : List SignType) : (a :: l).signVariations ≤ l.signVariations + 1 := by @@ -395,6 +400,19 @@ theorem List.signVariations_insert_between_opposite namespace List +private theorem filter_map_sign_filter_ne_zero (l : List SignType) : + ((l.filter (· ≠ 0)).map SignType.sign).filter (· ≠ 0) = + (l.map SignType.sign).filter (· ≠ 0) := by + induction l with + | nil => rfl + | cons a l ih => + fin_cases a <;> simp_all + +/-- Filtering zero signs does not change sign variations. -/ +theorem signVariations_filter_ne_zero (l : List SignType) : + (l.filter (· ≠ 0)).signVariations = l.signVariations := by + simp only [List.signVariations, filter_map_sign_filter_ne_zero] + /-- One insertion at an interior nodal position. This inductive relation is structural data recording an explicit list operation, @@ -473,6 +491,19 @@ theorem SignType.sign_ne_zero_and_ne_of_mul_neg namespace Fin +/-- A finite real vector has the same variation count as its explicit sign list. -/ +theorem signVariations_eq_signList {n : ℕ} (x : Fin n → ℝ) : + Fin.signVariations x = + (List.ofFn (SignType.sign ∘ x)).signVariations := by + simp [Fin.signVariations, List.signVariations, Function.comp_def] + +/-- Filtering the explicit sign list computes finite-vector sign variations. -/ +theorem filtered_signList_signVariations {n : ℕ} (x : Fin n → ℝ) : + ((List.ofFn (SignType.sign ∘ x)).filter (· ≠ 0)).signVariations = + Fin.signVariations x := by + rw [List.signVariations_filter_ne_zero] + exact (Fin.signVariations_eq_signList x).symm + /-- At the splice created by deleting a nodal zero, the new center is nonzero. -/ theorem succAbove_center_ne_zero_of_mul_neg {n : ℕ} (x : Fin (n + 3) → ℝ) (i : Fin n) From bfb0d95aae9a2e54764ae7fffea3700bbc8a0068 Mon Sep 17 00:00:00 2001 From: Per Alexandersson Date: Mon, 3 Aug 2026 04:41:34 +0000 Subject: [PATCH 100/141] Simplify sign filtering normalization --- RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariation.lean | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariation.lean b/RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariation.lean index f1d9dc13..a20fbc60 100644 --- a/RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariation.lean +++ b/RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariation.lean @@ -403,10 +403,8 @@ namespace List private theorem filter_map_sign_filter_ne_zero (l : List SignType) : ((l.filter (· ≠ 0)).map SignType.sign).filter (· ≠ 0) = (l.map SignType.sign).filter (· ≠ 0) := by - induction l with - | nil => rfl - | cons a l ih => - fin_cases a <;> simp_all + have hsign : (SignType.sign : SignType → SignType) = id := funext SignType.sign_sign + simp [hsign] /-- Filtering zero signs does not change sign variations. -/ theorem signVariations_filter_ne_zero (l : List SignType) : From 57f1497f8660d533fcab3430edd53869a34772b7 Mon Sep 17 00:00:00 2001 From: Per Alexandersson Date: Mon, 3 Aug 2026 04:42:49 +0000 Subject: [PATCH 101/141] Export finite indexing shims --- RealRooted.lean | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/RealRooted.lean b/RealRooted.lean index 4039625a..6d140c1e 100644 --- a/RealRooted.lean +++ b/RealRooted.lean @@ -21,8 +21,11 @@ import RealRooted.AllCombo import RealRooted.Apolarity import RealRooted.Basic import RealRooted.Bezoutian -import RealRooted.BoundarySpecializationRight +import RealRooted.BorceaBranden.Applications.BidiagonalSymbol +import RealRooted.BorceaBranden.Applications.HomogenizeStable +import RealRooted.BorceaBranden.Applications.UnivariateSymbol import RealRooted.BorceaBranden.BoundarySpecialization +import RealRooted.BorceaBranden.FiniteSymbolBasis import RealRooted.BorceaBranden.FiniteSymbolCoefficient import RealRooted.BorceaBranden.FiniteSymbolContraction import RealRooted.BorceaBranden.FiniteSymbolDegree @@ -32,10 +35,7 @@ import RealRooted.BorceaBranden.FiniteSymbolProduct import RealRooted.BorceaBranden.FiniteSymbolReciprocal import RealRooted.BorceaBranden.FiniteSymbolReconstruction import RealRooted.BorceaBranden.FiniteSymbolReconstructionCore -import RealRooted.BorceaBranden.FiniteSymbolBasis -import RealRooted.BorceaBranden.Applications.BidiagonalSymbol -import RealRooted.BorceaBranden.Applications.HomogenizeStable -import RealRooted.BorceaBranden.Applications.UnivariateSymbol +import RealRooted.BoundarySpecializationRight import RealRooted.CauchyInterlacing import RealRooted.Challenges.AissenSchoenbergWhitney import RealRooted.Challenges.BorceaBranden @@ -198,8 +198,10 @@ import RealRooted.Mathlib.Analysis.Complex.OpenMapping import RealRooted.Mathlib.Analysis.SpecialFunctions.ExpIntegral import RealRooted.Mathlib.Analysis.SpecialFunctions.Trigonometric.Basic import RealRooted.Mathlib.Combinatorics.Enumerative.OrderedSubsetPairs +import RealRooted.Mathlib.Data.Fin.Basic import RealRooted.Mathlib.Data.List.Destutter import RealRooted.Mathlib.Data.List.Interleave +import RealRooted.Mathlib.Data.List.OfFn import RealRooted.Mathlib.Data.List.Zip import RealRooted.Mathlib.Data.Nat.Cast.Basic import RealRooted.Mathlib.Data.Nat.Choose.Cast @@ -218,9 +220,9 @@ import RealRooted.Mathlib.LinearAlgebra.Matrix.TotallyNonneg import RealRooted.Mathlib.LinearAlgebra.Matrix.VariationDiminishing import RealRooted.Mathlib.LinearAlgebra.Vandermonde import RealRooted.MatrixInterlacing -import RealRooted.MultiaffineReciprocalRight import RealRooted.Multiaffine import RealRooted.MultiaffineReciprocal +import RealRooted.MultiaffineReciprocalRight import RealRooted.MultiplierSequence import RealRooted.MultivariateStability import RealRooted.NarayanaTransformation From 446b2d9b8789da950daefac0828f8c4e0fce4f43 Mon Sep 17 00:00:00 2001 From: Per Alexandersson Date: Mon, 3 Aug 2026 04:53:10 +0000 Subject: [PATCH 102/141] Relate interior coordinate deletion --- RealRooted/Mathlib/Data/Fin/Basic.lean | 25 ++++++++++++++++++- .../Matrix/Determinant/CauchyBinet.lean | 6 +++-- 2 files changed, 28 insertions(+), 3 deletions(-) diff --git a/RealRooted/Mathlib/Data/Fin/Basic.lean b/RealRooted/Mathlib/Data/Fin/Basic.lean index 5b301f4e..004dde13 100644 --- a/RealRooted/Mathlib/Data/Fin/Basic.lean +++ b/RealRooted/Mathlib/Data/Fin/Basic.lean @@ -1,6 +1,6 @@ module -public import Mathlib.Data.Fin.Basic +public import Mathlib.Data.Fin.SuccPred /-! # Additional lemmas about finite indices @@ -65,4 +65,27 @@ theorem succAbove_center_eq_left (i : Fin n) : change (i : ℕ) + 1 < (i : ℕ) + 2 lia)] +/-- Deleting an interior full-vector coordinate agrees with deleting its +corresponding interior coordinate. -/ +theorem succAbove_succ_castSucc + {n : ℕ} (k : Fin (n + 1)) (i : Fin n) : + k.succ.castSucc.succAbove i.succ.castSucc = + (k.succAbove i).succ.castSucc := by + by_cases h : i.castSucc < k + · have h' : i.succ.castSucc.castSucc < k.succ.castSucc := by + change (i : ℕ) + 1 < (k : ℕ) + 1 + exact Nat.succ_lt_succ h + rw [Fin.succAbove_of_castSucc_lt _ _ h', + Fin.succAbove_of_castSucc_lt _ _ h] + apply Fin.ext + rfl + · have hki : k ≤ i.castSucc := le_of_not_gt h + have h' : k.succ.castSucc ≤ i.succ.castSucc.castSucc := by + change (k : ℕ) + 1 ≤ (i : ℕ) + 1 + exact Nat.succ_le_succ hki + rw [Fin.succAbove_of_le_castSucc _ _ h', + Fin.succAbove_of_le_castSucc _ _ hki] + apply Fin.ext + rfl + end Fin diff --git a/RealRooted/Mathlib/LinearAlgebra/Matrix/Determinant/CauchyBinet.lean b/RealRooted/Mathlib/LinearAlgebra/Matrix/Determinant/CauchyBinet.lean index ca2d5f73..232ed465 100644 --- a/RealRooted/Mathlib/LinearAlgebra/Matrix/Determinant/CauchyBinet.lean +++ b/RealRooted/Mathlib/LinearAlgebra/Matrix/Determinant/CauchyBinet.lean @@ -247,8 +247,10 @@ private theorem selected_mulVec_injective Function.Injective (A.submatrix id cols).mulVec := by rw [Matrix.mulVec_injective_iff] have h := (Matrix.mulVec_injective_iff.mp hA).comp cols hcols.injective - simpa only [Matrix.col_apply, Matrix.submatrix_apply, id_eq, - Function.comp_apply] using h + rw [show (A.submatrix id cols).col = A.col ∘ cols by + ext j i + rfl] + exact h private theorem exists_left_inverse_matrix {R : Type*} [Field R] {n q : ℕ} From 62868e7221be75be95fe299424611bcfdade3460 Mon Sep 17 00:00:00 2001 From: Per Alexandersson Date: Mon, 3 Aug 2026 04:55:57 +0000 Subject: [PATCH 103/141] Relate interior list deletion --- RealRooted/Mathlib/Data/List/OfFn.lean | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/RealRooted/Mathlib/Data/List/OfFn.lean b/RealRooted/Mathlib/Data/List/OfFn.lean index de124689..904129d6 100644 --- a/RealRooted/Mathlib/Data/List/OfFn.lean +++ b/RealRooted/Mathlib/Data/List/OfFn.lean @@ -34,6 +34,19 @@ theorem ofFn_succAbove_eq_eraseIdx exact Nat.le_of_not_gt ‹¬i < (p : ℕ)›)] congr +/-- Removing an interior coordinate agrees with erasing its interior-list index. -/ +theorem ofFn_interior_succAbove_eq_eraseIdx + {α : Type*} {n : ℕ} + (f : Fin (n + 3) → α) (k : Fin (n + 1)) : + List.ofFn + (fun i : Fin n => + f (k.succ.castSucc.succAbove i.succ.castSucc)) = + (List.ofFn + (fun i : Fin (n + 1) => f i.succ.castSucc)).eraseIdx k := by + simpa only [Fin.succAbove_succ_castSucc] using + List.ofFn_succAbove_eq_eraseIdx + (fun i : Fin (n + 1) => f i.succ.castSucc) k + /-- A finite-function list splits into its two endpoints and interior coordinates. -/ theorem ofFn_two_endpoints {α : Type*} {n : ℕ} (f : Fin (n + 2) → α) : From 2d5f68027aa84a142966ce348691764dcd9f281d Mon Sep 17 00:00:00 2001 From: Per Alexandersson Date: Mon, 3 Aug 2026 04:57:26 +0000 Subject: [PATCH 104/141] Golf interior deletion extensionality --- RealRooted/Mathlib/Data/Fin/Basic.lean | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/RealRooted/Mathlib/Data/Fin/Basic.lean b/RealRooted/Mathlib/Data/Fin/Basic.lean index 004dde13..a3ca5195 100644 --- a/RealRooted/Mathlib/Data/Fin/Basic.lean +++ b/RealRooted/Mathlib/Data/Fin/Basic.lean @@ -77,15 +77,13 @@ theorem succAbove_succ_castSucc exact Nat.succ_lt_succ h rw [Fin.succAbove_of_castSucc_lt _ _ h', Fin.succAbove_of_castSucc_lt _ _ h] - apply Fin.ext - rfl + exact Fin.ext rfl · have hki : k ≤ i.castSucc := le_of_not_gt h have h' : k.succ.castSucc ≤ i.succ.castSucc.castSucc := by change (k : ℕ) + 1 ≤ (i : ℕ) + 1 exact Nat.succ_le_succ hki rw [Fin.succAbove_of_le_castSucc _ _ h', Fin.succAbove_of_le_castSucc _ _ hki] - apply Fin.ext - rfl + exact Fin.ext rfl end Fin From 50efbae163d343b42cd94935f5162695b714667a Mon Sep 17 00:00:00 2001 From: Per Alexandersson Date: Mon, 3 Aug 2026 05:04:14 +0000 Subject: [PATCH 105/141] Reinsert erased nodal signs --- .../LinearAlgebra/Matrix/SignVariation.lean | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariation.lean b/RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariation.lean index a20fbc60..4f99ca73 100644 --- a/RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariation.lean +++ b/RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariation.lean @@ -430,6 +430,36 @@ protected theorem NodalInsertion.append_context simpa only [append_assoc] using NodalInsertion.insert (pre ++ l₁) (l₂ ++ post) a b z ha hb hab +/-- Erasing an entry between opposite nonzero neighbors is one nodal insertion in reverse. -/ +protected theorem NodalInsertion.eraseIdx_succ + (l : List SignType) (i : ℕ) (hi : i + 2 < l.length) + (ha : l[i] ≠ 0) (hb : l[i + 2] ≠ 0) + (hab : l[i] ≠ l[i + 2]) : + NodalInsertion (l.eraseIdx (i + 1)) l := by + induction l generalizing i with + | nil => simp at hi + | cons x l ih => + cases i with + | zero => + cases l with + | nil => simp at hi + | cons z l => + cases l with + | nil => simp at hi + | cons b l => + exact NodalInsertion.insert [] l x b z + (by simpa using ha) (by simpa using hb) + (by simpa using hab) + | succ i => + have h := ih i + (by + simp only [length_cons] at hi + lia) + (by simpa [Nat.succ_eq_add_one] using ha) + (by simpa [Nat.succ_eq_add_one] using hb) + (by simpa [Nat.succ_eq_add_one] using hab) + simpa [Nat.succ_eq_add_one] using h.append_context [x] [] + /-- Adding unchanged list context preserves repeated nodal insertions. -/ protected theorem NodalInsertion.reflTransGen_append_context {l l' : List SignType} From ba7ee77bbba99cb1bc200b6f8ea1d8a13abeb2a6 Mon Sep 17 00:00:00 2001 From: Per Alexandersson Date: Mon, 3 Aug 2026 05:09:33 +0000 Subject: [PATCH 106/141] Preserve filters when erasing rejected entries --- RealRooted.lean | 1 + RealRooted/Mathlib/Data/List/Basic.lean | 30 +++++++++++++++++++ .../LinearAlgebra/Matrix/SignVariation.lean | 1 + 3 files changed, 32 insertions(+) create mode 100644 RealRooted/Mathlib/Data/List/Basic.lean diff --git a/RealRooted.lean b/RealRooted.lean index 6d140c1e..1520c800 100644 --- a/RealRooted.lean +++ b/RealRooted.lean @@ -199,6 +199,7 @@ import RealRooted.Mathlib.Analysis.SpecialFunctions.ExpIntegral import RealRooted.Mathlib.Analysis.SpecialFunctions.Trigonometric.Basic import RealRooted.Mathlib.Combinatorics.Enumerative.OrderedSubsetPairs import RealRooted.Mathlib.Data.Fin.Basic +import RealRooted.Mathlib.Data.List.Basic import RealRooted.Mathlib.Data.List.Destutter import RealRooted.Mathlib.Data.List.Interleave import RealRooted.Mathlib.Data.List.OfFn diff --git a/RealRooted/Mathlib/Data/List/Basic.lean b/RealRooted/Mathlib/Data/List/Basic.lean new file mode 100644 index 00000000..2b357e81 --- /dev/null +++ b/RealRooted/Mathlib/Data/List/Basic.lean @@ -0,0 +1,30 @@ +module + +public import Mathlib.Data.List.Basic + +/-! +# Additional basic list lemmas + +This file contains compatibility lemmas intended for upstreaming to +`Mathlib.Data.List.Basic`. +-/ + +public section + +namespace List + +/-- Erasing an entry rejected by a filter does not change the filtered list. -/ +theorem filter_eraseIdx_eq_of_getElem_not + {α : Type*} {p : α → Bool} {l : List α} {i : ℕ} + (hi : i < l.length) (hpi : ¬p l[i]) : + (l.eraseIdx i).filter p = l.filter p := by + calc + (l.eraseIdx i).filter p = + (l.take i ++ l.drop (i + 1)).filter p := by + rw [List.eraseIdx_eq_take_drop_succ] + _ = (l.take i ++ l[i] :: l.drop (i + 1)).filter p := by + simp only [List.filter_append, List.filter_cons_of_neg hpi] + _ = l.filter p := by + rw [← List.drop_eq_getElem_cons hi, List.take_append_drop] + +end List diff --git a/RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariation.lean b/RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariation.lean index 4f99ca73..025e4b04 100644 --- a/RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariation.lean +++ b/RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariation.lean @@ -5,6 +5,7 @@ public import Mathlib.Data.List.ChainOfFn public import Mathlib.Data.List.NodupEquivFin public import Mathlib.Tactic public import RealRooted.Mathlib.Data.List.Destutter +public import RealRooted.Mathlib.Data.List.Basic public import RealRooted.Mathlib.Data.List.OfFn /-! From 48c3d233ca15276f1b891635d5bb73ad78a8c8e6 Mon Sep 17 00:00:00 2001 From: Per Alexandersson Date: Mon, 3 Aug 2026 05:11:08 +0000 Subject: [PATCH 107/141] Golf filtered erasure proof --- RealRooted/Mathlib/Data/List/Basic.lean | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/RealRooted/Mathlib/Data/List/Basic.lean b/RealRooted/Mathlib/Data/List/Basic.lean index 2b357e81..ad1a2c6f 100644 --- a/RealRooted/Mathlib/Data/List/Basic.lean +++ b/RealRooted/Mathlib/Data/List/Basic.lean @@ -18,13 +18,9 @@ theorem filter_eraseIdx_eq_of_getElem_not {α : Type*} {p : α → Bool} {l : List α} {i : ℕ} (hi : i < l.length) (hpi : ¬p l[i]) : (l.eraseIdx i).filter p = l.filter p := by - calc - (l.eraseIdx i).filter p = - (l.take i ++ l.drop (i + 1)).filter p := by - rw [List.eraseIdx_eq_take_drop_succ] - _ = (l.take i ++ l[i] :: l.drop (i + 1)).filter p := by - simp only [List.filter_append, List.filter_cons_of_neg hpi] - _ = l.filter p := by - rw [← List.drop_eq_getElem_cons hi, List.take_append_drop] + rw [List.eraseIdx_eq_take_drop_succ, List.filter_append] + conv_rhs => + rw [← List.take_append_drop i l, List.filter_append, + List.drop_eq_getElem_cons hi, List.filter_cons_of_neg hpi] end List From 774e3714a6771cda0b8a81316a855381e7c8fa3a Mon Sep 17 00:00:00 2001 From: Per Alexandersson Date: Mon, 3 Aug 2026 05:14:33 +0000 Subject: [PATCH 108/141] Define finite nodal perturbation core --- RealRooted/Mathlib/Data/Fin/Basic.lean | 19 +++++++++++++++++++ .../LinearAlgebra/Matrix/SignVariation.lean | 14 ++++++++++++++ 2 files changed, 33 insertions(+) diff --git a/RealRooted/Mathlib/Data/Fin/Basic.lean b/RealRooted/Mathlib/Data/Fin/Basic.lean index a3ca5195..c8c391a4 100644 --- a/RealRooted/Mathlib/Data/Fin/Basic.lean +++ b/RealRooted/Mathlib/Data/Fin/Basic.lean @@ -86,4 +86,23 @@ theorem succAbove_succ_castSucc Fin.succAbove_of_le_castSucc _ _ hki] exact Fin.ext rfl +/-- Deleting an interior coordinate preserves the first coordinate. -/ +theorem succAbove_zero_of_interior + {n : ℕ} (k : Fin (n + 1)) : + k.succ.castSucc.succAbove (0 : Fin (n + 2)) = 0 := by + rw [Fin.succAbove_of_castSucc_lt _ _ (by + change 0 < (k : ℕ) + 1 + lia)] + exact Fin.ext rfl + +/-- Deleting an interior coordinate preserves the final coordinate. -/ +theorem succAbove_last_of_interior + {n : ℕ} (k : Fin (n + 1)) : + k.succ.castSucc.succAbove (Fin.last (n + 1)) = + Fin.last (n + 2) := by + rw [Fin.succAbove_of_le_castSucc _ _ (by + change (k : ℕ) + 1 ≤ n + 1 + lia)] + exact Fin.ext rfl + end Fin diff --git a/RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariation.lean b/RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariation.lean index 025e4b04..2fcb25bd 100644 --- a/RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariation.lean +++ b/RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariation.lean @@ -533,6 +533,20 @@ theorem filtered_signList_signVariations {n : ℕ} (x : Fin n → ℝ) : rw [List.signVariations_filter_ne_zero] exact (Fin.signVariations_eq_signList x).symm +/-- Perturbed signs at all interior coordinates and at the original nonzero +endpoints. + +Interior zeros are retained because the coordinate-removal induction constructs +them by nodal insertions. A zero original endpoint is omitted and handled by the +final two-endpoint variation bound. -/ +def nodalPerturbationCoreSigns + {n : ℕ} (x y : Fin (n + 2) → ℝ) : List SignType := + (if x 0 = 0 then [] else [SignType.sign (y 0)]) ++ + List.ofFn + (fun i : Fin n => SignType.sign (y i.succ.castSucc)) ++ + (if x (Fin.last (n + 1)) = 0 then [] + else [SignType.sign (y (Fin.last (n + 1)))]) + /-- At the splice created by deleting a nodal zero, the new center is nonzero. -/ theorem succAbove_center_ne_zero_of_mul_neg {n : ℕ} (x : Fin (n + 3) → ℝ) (i : Fin n) From a37334b710891971857c80f13b2d43b32bbcaf0e Mon Sep 17 00:00:00 2001 From: Per Alexandersson Date: Mon, 3 Aug 2026 05:23:43 +0000 Subject: [PATCH 109/141] Move erasure through list context --- RealRooted/Mathlib/Data/List/Basic.lean | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/RealRooted/Mathlib/Data/List/Basic.lean b/RealRooted/Mathlib/Data/List/Basic.lean index ad1a2c6f..0ab04187 100644 --- a/RealRooted/Mathlib/Data/List/Basic.lean +++ b/RealRooted/Mathlib/Data/List/Basic.lean @@ -23,4 +23,16 @@ theorem filter_eraseIdx_eq_of_getElem_not rw [← List.take_append_drop i l, List.filter_append, List.drop_eq_getElem_cons hi, List.filter_cons_of_neg hpi] +/-- Erasing an index inside the middle block of a three-block concatenation. -/ +theorem eraseIdx_append_middle + {α : Type*} (pre middle post : List α) (i : ℕ) + (hi : i < middle.length) : + (pre ++ middle ++ post).eraseIdx (pre.length + i) = + pre ++ middle.eraseIdx i ++ post := by + rw [List.append_assoc, + List.eraseIdx_append_of_length_le (by simp), + Nat.add_sub_cancel_left, + List.eraseIdx_append_of_lt_length hi, + List.append_assoc] + end List From e364e7a017fd46d667bb7a2401454f25ea67b8c3 Mon Sep 17 00:00:00 2001 From: Per Alexandersson Date: Mon, 3 Aug 2026 05:25:58 +0000 Subject: [PATCH 110/141] Clean up list shim namespaces --- RealRooted/Mathlib/Data/List/Basic.lean | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/RealRooted/Mathlib/Data/List/Basic.lean b/RealRooted/Mathlib/Data/List/Basic.lean index 0ab04187..b14f55fe 100644 --- a/RealRooted/Mathlib/Data/List/Basic.lean +++ b/RealRooted/Mathlib/Data/List/Basic.lean @@ -18,10 +18,10 @@ theorem filter_eraseIdx_eq_of_getElem_not {α : Type*} {p : α → Bool} {l : List α} {i : ℕ} (hi : i < l.length) (hpi : ¬p l[i]) : (l.eraseIdx i).filter p = l.filter p := by - rw [List.eraseIdx_eq_take_drop_succ, List.filter_append] + rw [eraseIdx_eq_take_drop_succ, filter_append] conv_rhs => - rw [← List.take_append_drop i l, List.filter_append, - List.drop_eq_getElem_cons hi, List.filter_cons_of_neg hpi] + rw [← take_append_drop i l, filter_append, + drop_eq_getElem_cons hi, filter_cons_of_neg hpi] /-- Erasing an index inside the middle block of a three-block concatenation. -/ theorem eraseIdx_append_middle @@ -29,10 +29,10 @@ theorem eraseIdx_append_middle (hi : i < middle.length) : (pre ++ middle ++ post).eraseIdx (pre.length + i) = pre ++ middle.eraseIdx i ++ post := by - rw [List.append_assoc, - List.eraseIdx_append_of_length_le (by simp), + rw [append_assoc, + eraseIdx_append_of_length_le (by simp), Nat.add_sub_cancel_left, - List.eraseIdx_append_of_lt_length hi, - List.append_assoc] + eraseIdx_append_of_lt_length hi, + append_assoc] end List From 3183bd556ff53c2c9dfe4bf353fec8cf5b9172bb Mon Sep 17 00:00:00 2001 From: Per Alexandersson Date: Mon, 3 Aug 2026 05:32:51 +0000 Subject: [PATCH 111/141] Reinsert nodal signs inside list context --- .../LinearAlgebra/Matrix/SignVariation.lean | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariation.lean b/RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariation.lean index 2fcb25bd..79fea8e9 100644 --- a/RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariation.lean +++ b/RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariation.lean @@ -461,6 +461,22 @@ protected theorem NodalInsertion.eraseIdx_succ (by simpa [Nat.succ_eq_add_one] using hab) simpa [Nat.succ_eq_add_one] using h.append_context [x] [] +/-- Erasing an entry between opposite nonzero neighbors inside a middle block +is one nodal insertion in reverse, with unchanged surrounding context. -/ +protected theorem NodalInsertion.eraseIdx_append_middle + (pre middle post : List SignType) (i : ℕ) + (hi : i + 2 < middle.length) + (ha : middle[i] ≠ 0) (hb : middle[i + 2] ≠ 0) + (hab : middle[i] ≠ middle[i + 2]) : + NodalInsertion + ((pre ++ middle ++ post).eraseIdx (pre.length + (i + 1))) + (pre ++ middle ++ post) := by + have hi' : i + 1 < middle.length := by lia + rw [List.eraseIdx_append_middle pre middle post (i + 1) hi'] + exact + (NodalInsertion.eraseIdx_succ middle i hi ha hb hab).append_context + pre post + /-- Adding unchanged list context preserves repeated nodal insertions. -/ protected theorem NodalInsertion.reflTransGen_append_context {l l' : List SignType} From fc67c4d03e2d8f0d4d6c74e94ed7e4870e5982f6 Mon Sep 17 00:00:00 2001 From: Per Alexandersson Date: Mon, 3 Aug 2026 05:38:49 +0000 Subject: [PATCH 112/141] Handle endpoint nodal sign insertions --- .../LinearAlgebra/Matrix/SignVariation.lean | 67 +++++++++++++++++++ 1 file changed, 67 insertions(+) diff --git a/RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariation.lean b/RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariation.lean index 79fea8e9..32e52a88 100644 --- a/RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariation.lean +++ b/RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariation.lean @@ -477,6 +477,73 @@ protected theorem NodalInsertion.eraseIdx_append_middle (NodalInsertion.eraseIdx_succ middle i hi ha hb hab).append_context pre post +/-- Deleting the first element of a middle block is a nodal insertion when +its left neighbor is a retained singleton and its right neighbor is the +second middle entry. -/ +protected theorem NodalInsertion.eraseIdx_first_middle + (a : SignType) (middle post : List SignType) + (hmiddle : 1 < middle.length) + (ha : a ≠ 0) + (hb : middle[1] ≠ 0) + (hab : a ≠ middle[1]) : + NodalInsertion + ([a] ++ middle.eraseIdx 0 ++ post) + ([a] ++ middle ++ post) := by + have h : + NodalInsertion + (([a] ++ middle ++ post).eraseIdx 1) + ([a] ++ middle ++ post) := by + simpa only [List.nil_append, List.length_nil, zero_add, + List.append_assoc] using + NodalInsertion.eraseIdx_append_middle + [] ([a] ++ middle) post 0 + (by + simp only [List.length_append, List.length_singleton] + lia) + (by simpa using ha) + (by simpa using hb) + (by simpa using hab) + have herase : + ([a] ++ middle ++ post).eraseIdx 1 = + [a] ++ middle.eraseIdx 0 ++ post := by + simpa using + List.eraseIdx_append_middle [a] middle post 0 (by lia) + rw [← herase] + exact h + +/-- Deleting the final entry of a middle block is one nodal insertion in +reverse when its preceding entry and a retained singleton endpoint have +opposite nonzero signs. -/ +protected theorem NodalInsertion.eraseIdx_last_append_singleton + (pre middle : List SignType) (b : SignType) (i : ℕ) + (hlen : middle.length = i + 2) + (ha : middle[i] ≠ 0) (hb : b ≠ 0) + (hab : middle[i] ≠ b) : + NodalInsertion + (pre ++ middle.eraseIdx (i + 1) ++ [b]) + (pre ++ middle ++ [b]) := by + have hi : i < middle.length := by simp [hlen] + have hiErase : i + 1 < middle.length := by simp [hlen] + have hiLocal : i + 2 < (middle ++ [b]).length := by simp [hlen] + have hleft : (middle ++ [b])[i] = middle[i] := + List.getElem_append_left hi + have hright : (middle ++ [b])[i + 2] = b := by + simp [List.getElem_append_right, hlen] + have hfull : + NodalInsertion + ((pre ++ middle ++ [b]).eraseIdx + (pre.length + (i + 1))) + (pre ++ middle ++ [b]) := by + simpa only [List.append_nil, List.append_assoc] using + NodalInsertion.eraseIdx_append_middle + pre (middle ++ [b]) [] i hiLocal + (by rw [hleft]; exact ha) + (by rw [hright]; exact hb) + (by rw [hleft, hright]; exact hab) + rw [List.eraseIdx_append_middle + pre middle [b] (i + 1) hiErase] at hfull + exact hfull + /-- Adding unchanged list context preserves repeated nodal insertions. -/ protected theorem NodalInsertion.reflTransGen_append_context {l l' : List SignType} From a462dfbf356805b93c9fddcd0d814335d01b065a Mon Sep 17 00:00:00 2001 From: Per Alexandersson Date: Mon, 3 Aug 2026 05:41:11 +0000 Subject: [PATCH 113/141] Simplify endpoint nodal insertion proofs --- .../LinearAlgebra/Matrix/SignVariation.lean | 57 +++++++------------ 1 file changed, 22 insertions(+), 35 deletions(-) diff --git a/RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariation.lean b/RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariation.lean index 32e52a88..f86a8650 100644 --- a/RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariation.lean +++ b/RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariation.lean @@ -489,27 +489,20 @@ protected theorem NodalInsertion.eraseIdx_first_middle NodalInsertion ([a] ++ middle.eraseIdx 0 ++ post) ([a] ++ middle ++ post) := by - have h : - NodalInsertion - (([a] ++ middle ++ post).eraseIdx 1) - ([a] ++ middle ++ post) := by - simpa only [List.nil_append, List.length_nil, zero_add, - List.append_assoc] using - NodalInsertion.eraseIdx_append_middle - [] ([a] ++ middle) post 0 - (by - simp only [List.length_append, List.length_singleton] - lia) - (by simpa using ha) - (by simpa using hb) - (by simpa using hab) - have herase : - ([a] ++ middle ++ post).eraseIdx 1 = - [a] ++ middle.eraseIdx 0 ++ post := by - simpa using - List.eraseIdx_append_middle [a] middle post 0 (by lia) - rw [← herase] - exact h + rw [← List.eraseIdx_append_middle [a] middle post 0 (by lia)] + change NodalInsertion + (([a] ++ middle ++ post).eraseIdx 1) + ([a] ++ middle ++ post) + simpa only [List.nil_append, List.length_nil, zero_add, + List.append_assoc] using + NodalInsertion.eraseIdx_append_middle + [] ([a] ++ middle) post 0 + (by + simp only [List.length_append, List.length_singleton] + lia) + (by simpa using ha) + (by simpa using hb) + (by simpa using hab) /-- Deleting the final entry of a middle block is one nodal insertion in reverse when its preceding entry and a retained singleton endpoint have @@ -529,20 +522,14 @@ protected theorem NodalInsertion.eraseIdx_last_append_singleton List.getElem_append_left hi have hright : (middle ++ [b])[i + 2] = b := by simp [List.getElem_append_right, hlen] - have hfull : - NodalInsertion - ((pre ++ middle ++ [b]).eraseIdx - (pre.length + (i + 1))) - (pre ++ middle ++ [b]) := by - simpa only [List.append_nil, List.append_assoc] using - NodalInsertion.eraseIdx_append_middle - pre (middle ++ [b]) [] i hiLocal - (by rw [hleft]; exact ha) - (by rw [hright]; exact hb) - (by rw [hleft, hright]; exact hab) - rw [List.eraseIdx_append_middle - pre middle [b] (i + 1) hiErase] at hfull - exact hfull + rw [← List.eraseIdx_append_middle + pre middle [b] (i + 1) hiErase] + simpa only [List.append_nil, List.append_assoc] using + NodalInsertion.eraseIdx_append_middle + pre (middle ++ [b]) [] i hiLocal + (by rw [hleft]; exact ha) + (by rw [hright]; exact hb) + (by rw [hleft, hright]; exact hab) /-- Adding unchanged list context preserves repeated nodal insertions. -/ protected theorem NodalInsertion.reflTransGen_append_context From 85cf74c91fb8fc975b172b9eaad15ee1e99ec5ae Mon Sep 17 00:00:00 2001 From: Per Alexandersson Date: Mon, 3 Aug 2026 05:59:46 +0000 Subject: [PATCH 114/141] prove nodal sign removal induction step --- RealRooted/SignVariation.lean | 293 ++++++++++++++++++++++++++++++++++ 1 file changed, 293 insertions(+) create mode 100644 RealRooted/SignVariation.lean diff --git a/RealRooted/SignVariation.lean b/RealRooted/SignVariation.lean new file mode 100644 index 00000000..51d83008 --- /dev/null +++ b/RealRooted/SignVariation.lean @@ -0,0 +1,293 @@ + +/-- Remove one interior zero, apply the shorter nodal-insertion chain, and reinsert it. + +This is the finite induction step in the endpoint-perturbation route used in Karlin's +Chapter 8, Section 3 argument. +-/ +theorem Fin.nodalInsertions_coreSigns_remove + {n : ℕ} + (ih : + ∀ {u v : Fin (n + 2) → ℝ}, + (∀ i, u i ≠ 0 → + SignType.sign (v i) = SignType.sign (u i)) → + (∀ i : Fin n, u i.succ.castSucc = 0 → + u i.castSucc.castSucc * u i.succ.succ < 0) → + Relation.ReflTransGen List.NodalInsertion + ((List.ofFn (SignType.sign ∘ u)).filter (· ≠ 0)) + (Fin.nodalPerturbationCoreSigns u v)) + {x y : Fin (n + 3) → ℝ} + (hsign : ∀ i, x i ≠ 0 → + SignType.sign (y i) = SignType.sign (x i)) + (hnodal : ∀ i : Fin (n + 1), x i.succ.castSucc = 0 → + x i.castSucc.castSucc * x i.succ.succ < 0) + (k : Fin (n + 1)) + (hk : x k.succ.castSucc = 0) : + Relation.ReflTransGen List.NodalInsertion + ((List.ofFn (SignType.sign ∘ x)).filter (· ≠ 0)) + (Fin.nodalPerturbationCoreSigns x y) := by + let p : Fin (n + 3) := k.succ.castSucc + let x' : Fin (n + 2) → ℝ := fun i => x (p.succAbove i) + let y' : Fin (n + 2) → ℝ := fun i => y (p.succAbove i) + have hsign' : + ∀ i, x' i ≠ 0 → + SignType.sign (y' i) = SignType.sign (x' i) := by + intro i hi + simpa only [x', y'] using + hsign (p.succAbove i) (by simpa only [x'] using hi) + have hnodal' : + ∀ i : Fin n, x' i.succ.castSucc = 0 → + x' i.castSucc.castSucc * x' i.succ.succ < 0 := by + simpa only [x', p] using + Fin.interiorNodal_succAbove x k hk hnodal + have hrec : + Relation.ReflTransGen List.NodalInsertion + ((List.ofFn (SignType.sign ∘ x')).filter (· ≠ 0)) + (Fin.nodalPerturbationCoreSigns x' y') := + ih hsign' hnodal' + have hofFn : + List.ofFn (fun i => SignType.sign (x' i)) = + (List.ofFn (fun i => SignType.sign (x i))).eraseIdx p := by + simpa only [x', p] using + List.ofFn_succAbove_eq_eraseIdx + (fun i => SignType.sign (x i)) p + have hlenSource : + (p : ℕ) < + (List.ofFn (fun i => SignType.sign (x i))).length := by + simpa only [List.length_ofFn] using p.isLt + have hvalueSource : + (List.ofFn (fun i => SignType.sign (x i)))[(p : ℕ)] = 0 := by + rw [List.getElem_ofFn hlenSource] + change SignType.sign (x p) = 0 + rw [show p = k.succ.castSucc from rfl, hk] + norm_num [SignType.sign] + have hfilteredSource : + ¬(fun z : SignType => decide (z ≠ 0)) + (List.ofFn (fun i => SignType.sign (x i)))[(p : ℕ)] := by + rw [hvalueSource] + decide + have hsource : + (List.ofFn (fun i => SignType.sign (x' i))).filter (· ≠ 0) = + (List.ofFn (fun i => SignType.sign (x i))).filter (· ≠ 0) := by + rw [hofFn] + exact List.filter_eraseIdx_eq_of_getElem_not + hlenSource hfilteredSource + have hxzero : x' 0 = x 0 := by + simp only [x', p, Fin.succAbove_zero_of_interior] + have hyzero : y' 0 = y 0 := by + simp only [y', p, Fin.succAbove_zero_of_interior] + have hxlast : + x' (Fin.last (n + 1)) = x (Fin.last (n + 2)) := by + simp only [x', p, Fin.succAbove_last_of_interior] + have hylast : + y' (Fin.last (n + 1)) = y (Fin.last (n + 2)) := by + simp only [y', p, Fin.succAbove_last_of_interior] + have hinterior : + List.ofFn + (fun i : Fin n => + SignType.sign (y' i.succ.castSucc)) = + (List.ofFn + (fun i : Fin (n + 1) => + SignType.sign (y i.succ.castSucc))).eraseIdx k := by + simpa only [y', p, Function.comp_apply] using + List.ofFn_interior_succAbove_eq_eraseIdx + (SignType.sign ∘ y) k + let left : List SignType := + if x 0 = 0 then [] else [SignType.sign (y 0)] + let middle : List SignType := + List.ofFn + (fun i : Fin (n + 1) => + SignType.sign (y i.succ.castSucc)) + let right : List SignType := + if x (Fin.last (n + 2)) = 0 then [] + else [SignType.sign (y (Fin.last (n + 2)))] + let full : List SignType := left ++ middle ++ right + let reduced : List SignType := + left ++ middle.eraseIdx k ++ right + have hfull : + Fin.nodalPerturbationCoreSigns x y = full := by + rfl + have hreduced : + Fin.nodalPerturbationCoreSigns x' y' = reduced := by + simp only [Fin.nodalPerturbationCoreSigns, reduced, left, middle, + right, hxzero, hyzero, hxlast, hylast, hinterior] + have hmul : + x k.castSucc.castSucc * x k.succ.succ < 0 := + hnodal k hk + have hxleft : x k.castSucc.castSucc ≠ 0 := + left_ne_zero_of_mul (ne_of_lt hmul) + have hxright : x k.succ.succ ≠ 0 := + right_ne_zero_of_mul (ne_of_lt hmul) + have hopposite : + SignType.sign (y k.castSucc.castSucc) ≠ 0 ∧ + SignType.sign (y k.succ.succ) ≠ 0 ∧ + SignType.sign (y k.castSucc.castSucc) ≠ + SignType.sign (y k.succ.succ) := by + simpa only [hsign k.castSucc.castSucc hxleft, + hsign k.succ.succ hxright] using + SignType.sign_ne_zero_and_ne_of_mul_neg hmul + have hstep : List.NodalInsertion reduced full := by + by_cases hn : n = 0 + · subst n + have hkzero : k = 0 := Fin.eq_zero k + subst k + have hleftIndex : + (0 : Fin 1).castSucc.castSucc = (0 : Fin 3) := by + exact Fin.ext rfl + have hcenterIndex : + (0 : Fin 1).succ.castSucc = (1 : Fin 3) := by + exact Fin.ext rfl + have hrightIndex : + (0 : Fin 1).succ.succ = (2 : Fin 3) := by + exact Fin.ext rfl + have hlastIndex : + (Fin.last 2 : Fin 3) = (2 : Fin 3) := by + exact Fin.ext rfl + have hxzero' : x (0 : Fin 3) ≠ 0 := by + simpa only [hleftIndex] using hxleft + have hxtwo : x (2 : Fin 3) ≠ 0 := by + simpa only [hrightIndex] using hxright + have hsignLeft : SignType.sign (y 0) ≠ 0 := by + simpa only [hleftIndex] using hopposite.1 + have hsignTwo : SignType.sign (y 2) ≠ 0 := by + simpa only [hrightIndex] using hopposite.2.1 + have hsignNe : + SignType.sign (y 0) ≠ SignType.sign (y 2) := by + simpa only [hleftIndex, hrightIndex] using hopposite.2.2 + simpa [full, reduced, left, middle, right, hxzero', hxtwo, + hcenterIndex, hlastIndex] using + List.NodalInsertion.insert [] [] + (SignType.sign (y 0)) + (SignType.sign (y 2)) + (SignType.sign (y 1)) + hsignLeft hsignTwo hsignNe + · have hnpos : 0 < n := Nat.pos_of_ne_zero hn + by_cases hkzero : (k : ℕ) = 0 + · have hkeq : k = 0 := Fin.ext hkzero + subst k + have hxzero' : x 0 ≠ 0 := by + simpa using hxleft + have hmiddleLength : 1 < middle.length := by + simp [middle] + lia + simpa [full, reduced, left, hxzero'] using + List.NodalInsertion.eraseIdx_first_middle + (SignType.sign (y 0)) middle right + hmiddleLength + (by simpa using hopposite.1) + (by simpa [middle] using hopposite.2.1) + (by simpa [middle] using hopposite.2.2) + · by_cases hklast : (k : ℕ) = n + · have hkeq : k = Fin.last n := Fin.ext hklast + subst k + have hxlast' : x (Fin.last (n + 2)) ≠ 0 := by + simpa using hxright + have hpred : n - 1 + 1 = n := by lia + have hlen : middle.length = n - 1 + 2 := by + simp [middle] + lia + have hleftBound : n - 1 < middle.length := by + rw [hlen] + lia + have hleftMiddle : + middle[n - 1]'hleftBound = + SignType.sign + (y (Fin.last n).castSucc.castSucc) := by + rw [List.getElem_ofFn hleftBound] + congr 2 + apply Fin.ext + simp only [Fin.val_castSucc, Fin.val_succ, Fin.val_last] + lia + have hrightEndpoint : + SignType.sign (y (Fin.last (n + 2))) = + SignType.sign (y (Fin.last n).succ.succ) := by + congr 2 + have haMiddle : middle[n - 1] ≠ 0 := by + rw [hleftMiddle] + exact hopposite.1 + have hbEndpoint : + SignType.sign (y (Fin.last (n + 2))) ≠ 0 := by + rw [hrightEndpoint] + exact hopposite.2.1 + have habMiddle : + middle[n - 1] ≠ + SignType.sign (y (Fin.last (n + 2))) := by + rw [hleftMiddle, hrightEndpoint] + exact hopposite.2.2 + simpa [full, reduced, right, hxlast', hpred] using + List.NodalInsertion.eraseIdx_last_append_singleton + left middle + (SignType.sign (y (Fin.last (n + 2)))) + (n - 1) hlen haMiddle hbEndpoint habMiddle + · have hkpos : 0 < (k : ℕ) := Nat.pos_of_ne_zero hkzero + have hklt : (k : ℕ) < n := by lia + have hpred : + (k : ℕ) - 1 + 1 = (k : ℕ) := by lia + have hsucc : + (k : ℕ) - 1 + 2 = (k : ℕ) + 1 := by lia + have hleftBound : + (k : ℕ) - 1 < middle.length := by + simp [middle] + have hrightBound : + (k : ℕ) - 1 + 2 < middle.length := by + simp [middle] + lia + have hleftMiddle : + middle[(k : ℕ) - 1]'hleftBound = + SignType.sign (y k.castSucc.castSucc) := by + rw [List.getElem_ofFn hleftBound] + congr 2 + apply Fin.ext + simp only [Fin.val_castSucc, Fin.val_succ] + lia + have hrightMiddle : + middle[(k : ℕ) - 1 + 2]'hrightBound = + SignType.sign (y k.succ.succ) := by + rw [List.getElem_ofFn hrightBound] + congr 2 + apply Fin.ext + simp only [Fin.val_castSucc, Fin.val_succ] + lia + have haMiddle : middle[(k : ℕ) - 1] ≠ 0 := by + rw [hleftMiddle] + exact hopposite.1 + have hbMiddle : middle[(k : ℕ) - 1 + 2] ≠ 0 := by + rw [hrightMiddle] + exact hopposite.2.1 + have habMiddle : + middle[(k : ℕ) - 1] ≠ + middle[(k : ℕ) - 1 + 2] := by + rw [hleftMiddle, hrightMiddle] + exact hopposite.2.2 + have hlocal : + List.NodalInsertion + ((left ++ middle ++ right).eraseIdx + (left.length + ((k : ℕ) - 1 + 1))) + (left ++ middle ++ right) := + List.NodalInsertion.eraseIdx_append_middle + left middle right ((k : ℕ) - 1) + hrightBound haMiddle hbMiddle habMiddle + have hkMiddle : (k : ℕ) < middle.length := by + simpa [middle] using k.isLt + rw [hpred] at hlocal + rw [List.eraseIdx_append_middle + left middle right (k : ℕ) hkMiddle] at hlocal + simpa [full, reduced] using hlocal + have hstepCore : + List.NodalInsertion + (Fin.nodalPerturbationCoreSigns x' y') + (Fin.nodalPerturbationCoreSigns x y) := by + rw [hreduced, hfull] + exact hstep + have hsourceComp : + (List.ofFn (SignType.sign ∘ x')).filter (· ≠ 0) = + (List.ofFn (SignType.sign ∘ x)).filter (· ≠ 0) := by + calc + _ = (List.ofFn (fun i => SignType.sign (x' i))).filter + (· ≠ 0) := by + congr 2 + _ = (List.ofFn (fun i => SignType.sign (x i))).filter + (· ≠ 0) := hsource + _ = _ := by + congr 2 + rw [hsourceComp] at hrec + exact hrec.tail hstepCore From 3ceaa4110f723426836c04a94871d56e73937376 Mon Sep 17 00:00:00 2001 From: Per Alexandersson Date: Mon, 3 Aug 2026 06:01:40 +0000 Subject: [PATCH 115/141] relocate nodal removal proof to owning module --- .../LinearAlgebra/Matrix/SignVariation.lean | 294 ++++++++++++++++++ RealRooted/SignVariation.lean | 293 ----------------- 2 files changed, 294 insertions(+), 293 deletions(-) delete mode 100644 RealRooted/SignVariation.lean diff --git a/RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariation.lean b/RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariation.lean index f86a8650..2ab29f14 100644 --- a/RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariation.lean +++ b/RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariation.lean @@ -681,6 +681,300 @@ theorem interiorNodal_succAbove (by simpa using hn) exact (hne (by simpa using hi)).elim + +/-- Remove one interior zero, apply the shorter nodal-insertion chain, and reinsert it. + +This is the finite induction step in the endpoint-perturbation route used in Karlin's +Chapter 8, Section 3 argument. +-/ +theorem nodalInsertions_coreSigns_remove + {n : ℕ} + (ih : + ∀ {u v : Fin (n + 2) → ℝ}, + (∀ i, u i ≠ 0 → + SignType.sign (v i) = SignType.sign (u i)) → + (∀ i : Fin n, u i.succ.castSucc = 0 → + u i.castSucc.castSucc * u i.succ.succ < 0) → + Relation.ReflTransGen List.NodalInsertion + ((List.ofFn (SignType.sign ∘ u)).filter (· ≠ 0)) + (Fin.nodalPerturbationCoreSigns u v)) + {x y : Fin (n + 3) → ℝ} + (hsign : ∀ i, x i ≠ 0 → + SignType.sign (y i) = SignType.sign (x i)) + (hnodal : ∀ i : Fin (n + 1), x i.succ.castSucc = 0 → + x i.castSucc.castSucc * x i.succ.succ < 0) + (k : Fin (n + 1)) + (hk : x k.succ.castSucc = 0) : + Relation.ReflTransGen List.NodalInsertion + ((List.ofFn (SignType.sign ∘ x)).filter (· ≠ 0)) + (Fin.nodalPerturbationCoreSigns x y) := by + let p : Fin (n + 3) := k.succ.castSucc + let x' : Fin (n + 2) → ℝ := fun i => x (p.succAbove i) + let y' : Fin (n + 2) → ℝ := fun i => y (p.succAbove i) + have hsign' : + ∀ i, x' i ≠ 0 → + SignType.sign (y' i) = SignType.sign (x' i) := by + intro i hi + simpa only [x', y'] using + hsign (p.succAbove i) (by simpa only [x'] using hi) + have hnodal' : + ∀ i : Fin n, x' i.succ.castSucc = 0 → + x' i.castSucc.castSucc * x' i.succ.succ < 0 := by + simpa only [x', p] using + Fin.interiorNodal_succAbove x k hk hnodal + have hrec : + Relation.ReflTransGen List.NodalInsertion + ((List.ofFn (SignType.sign ∘ x')).filter (· ≠ 0)) + (Fin.nodalPerturbationCoreSigns x' y') := + ih hsign' hnodal' + have hofFn : + List.ofFn (fun i => SignType.sign (x' i)) = + (List.ofFn (fun i => SignType.sign (x i))).eraseIdx p := by + simpa only [x', p] using + List.ofFn_succAbove_eq_eraseIdx + (fun i => SignType.sign (x i)) p + have hlenSource : + (p : ℕ) < + (List.ofFn (fun i => SignType.sign (x i))).length := by + simpa only [List.length_ofFn] using p.isLt + have hvalueSource : + (List.ofFn (fun i => SignType.sign (x i)))[(p : ℕ)] = 0 := by + rw [List.getElem_ofFn hlenSource] + change SignType.sign (x p) = 0 + rw [show p = k.succ.castSucc from rfl, hk] + norm_num [SignType.sign] + have hfilteredSource : + ¬(fun z : SignType => decide (z ≠ 0)) + (List.ofFn (fun i => SignType.sign (x i)))[(p : ℕ)] := by + rw [hvalueSource] + decide + have hsource : + (List.ofFn (fun i => SignType.sign (x' i))).filter (· ≠ 0) = + (List.ofFn (fun i => SignType.sign (x i))).filter (· ≠ 0) := by + rw [hofFn] + exact List.filter_eraseIdx_eq_of_getElem_not + hlenSource hfilteredSource + have hxzero : x' 0 = x 0 := by + simp only [x', p, Fin.succAbove_zero_of_interior] + have hyzero : y' 0 = y 0 := by + simp only [y', p, Fin.succAbove_zero_of_interior] + have hxlast : + x' (Fin.last (n + 1)) = x (Fin.last (n + 2)) := by + simp only [x', p, Fin.succAbove_last_of_interior] + have hylast : + y' (Fin.last (n + 1)) = y (Fin.last (n + 2)) := by + simp only [y', p, Fin.succAbove_last_of_interior] + have hinterior : + List.ofFn + (fun i : Fin n => + SignType.sign (y' i.succ.castSucc)) = + (List.ofFn + (fun i : Fin (n + 1) => + SignType.sign (y i.succ.castSucc))).eraseIdx k := by + simpa only [y', p, Function.comp_apply] using + List.ofFn_interior_succAbove_eq_eraseIdx + (SignType.sign ∘ y) k + let left : List SignType := + if x 0 = 0 then [] else [SignType.sign (y 0)] + let middle : List SignType := + List.ofFn + (fun i : Fin (n + 1) => + SignType.sign (y i.succ.castSucc)) + let right : List SignType := + if x (Fin.last (n + 2)) = 0 then [] + else [SignType.sign (y (Fin.last (n + 2)))] + let full : List SignType := left ++ middle ++ right + let reduced : List SignType := + left ++ middle.eraseIdx k ++ right + have hfull : + Fin.nodalPerturbationCoreSigns x y = full := by + rfl + have hreduced : + Fin.nodalPerturbationCoreSigns x' y' = reduced := by + simp only [Fin.nodalPerturbationCoreSigns, reduced, left, middle, + right, hxzero, hyzero, hxlast, hylast, hinterior] + have hmul : + x k.castSucc.castSucc * x k.succ.succ < 0 := + hnodal k hk + have hxleft : x k.castSucc.castSucc ≠ 0 := + left_ne_zero_of_mul (ne_of_lt hmul) + have hxright : x k.succ.succ ≠ 0 := + right_ne_zero_of_mul (ne_of_lt hmul) + have hopposite : + SignType.sign (y k.castSucc.castSucc) ≠ 0 ∧ + SignType.sign (y k.succ.succ) ≠ 0 ∧ + SignType.sign (y k.castSucc.castSucc) ≠ + SignType.sign (y k.succ.succ) := by + simpa only [hsign k.castSucc.castSucc hxleft, + hsign k.succ.succ hxright] using + SignType.sign_ne_zero_and_ne_of_mul_neg hmul + have hstep : List.NodalInsertion reduced full := by + by_cases hn : n = 0 + · subst n + have hkzero : k = 0 := Fin.eq_zero k + subst k + have hleftIndex : + (0 : Fin 1).castSucc.castSucc = (0 : Fin 3) := by + exact Fin.ext rfl + have hcenterIndex : + (0 : Fin 1).succ.castSucc = (1 : Fin 3) := by + exact Fin.ext rfl + have hrightIndex : + (0 : Fin 1).succ.succ = (2 : Fin 3) := by + exact Fin.ext rfl + have hlastIndex : + (Fin.last 2 : Fin 3) = (2 : Fin 3) := by + exact Fin.ext rfl + have hxzero' : x (0 : Fin 3) ≠ 0 := by + simpa only [hleftIndex] using hxleft + have hxtwo : x (2 : Fin 3) ≠ 0 := by + simpa only [hrightIndex] using hxright + have hsignLeft : SignType.sign (y 0) ≠ 0 := by + simpa only [hleftIndex] using hopposite.1 + have hsignTwo : SignType.sign (y 2) ≠ 0 := by + simpa only [hrightIndex] using hopposite.2.1 + have hsignNe : + SignType.sign (y 0) ≠ SignType.sign (y 2) := by + simpa only [hleftIndex, hrightIndex] using hopposite.2.2 + simpa [full, reduced, left, middle, right, hxzero', hxtwo, + hcenterIndex, hlastIndex] using + List.NodalInsertion.insert [] [] + (SignType.sign (y 0)) + (SignType.sign (y 2)) + (SignType.sign (y 1)) + hsignLeft hsignTwo hsignNe + · have hnpos : 0 < n := Nat.pos_of_ne_zero hn + by_cases hkzero : (k : ℕ) = 0 + · have hkeq : k = 0 := Fin.ext hkzero + subst k + have hxzero' : x 0 ≠ 0 := by + simpa using hxleft + have hmiddleLength : 1 < middle.length := by + simp [middle] + lia + simpa [full, reduced, left, hxzero'] using + List.NodalInsertion.eraseIdx_first_middle + (SignType.sign (y 0)) middle right + hmiddleLength + (by simpa using hopposite.1) + (by simpa [middle] using hopposite.2.1) + (by simpa [middle] using hopposite.2.2) + · by_cases hklast : (k : ℕ) = n + · have hkeq : k = Fin.last n := Fin.ext hklast + subst k + have hxlast' : x (Fin.last (n + 2)) ≠ 0 := by + simpa using hxright + have hpred : n - 1 + 1 = n := by lia + have hlen : middle.length = n - 1 + 2 := by + simp [middle] + lia + have hleftBound : n - 1 < middle.length := by + rw [hlen] + lia + have hleftMiddle : + middle[n - 1]'hleftBound = + SignType.sign + (y (Fin.last n).castSucc.castSucc) := by + rw [List.getElem_ofFn hleftBound] + congr 2 + apply Fin.ext + simp only [Fin.val_castSucc, Fin.val_succ, Fin.val_last] + lia + have hrightEndpoint : + SignType.sign (y (Fin.last (n + 2))) = + SignType.sign (y (Fin.last n).succ.succ) := by + congr 2 + have haMiddle : middle[n - 1] ≠ 0 := by + rw [hleftMiddle] + exact hopposite.1 + have hbEndpoint : + SignType.sign (y (Fin.last (n + 2))) ≠ 0 := by + rw [hrightEndpoint] + exact hopposite.2.1 + have habMiddle : + middle[n - 1] ≠ + SignType.sign (y (Fin.last (n + 2))) := by + rw [hleftMiddle, hrightEndpoint] + exact hopposite.2.2 + simpa [full, reduced, right, hxlast', hpred] using + List.NodalInsertion.eraseIdx_last_append_singleton + left middle + (SignType.sign (y (Fin.last (n + 2)))) + (n - 1) hlen haMiddle hbEndpoint habMiddle + · have hkpos : 0 < (k : ℕ) := Nat.pos_of_ne_zero hkzero + have hklt : (k : ℕ) < n := by lia + have hpred : + (k : ℕ) - 1 + 1 = (k : ℕ) := by lia + have hsucc : + (k : ℕ) - 1 + 2 = (k : ℕ) + 1 := by lia + have hleftBound : + (k : ℕ) - 1 < middle.length := by + simp [middle] + have hrightBound : + (k : ℕ) - 1 + 2 < middle.length := by + simp [middle] + lia + have hleftMiddle : + middle[(k : ℕ) - 1]'hleftBound = + SignType.sign (y k.castSucc.castSucc) := by + rw [List.getElem_ofFn hleftBound] + congr 2 + apply Fin.ext + simp only [Fin.val_castSucc, Fin.val_succ] + lia + have hrightMiddle : + middle[(k : ℕ) - 1 + 2]'hrightBound = + SignType.sign (y k.succ.succ) := by + rw [List.getElem_ofFn hrightBound] + congr 2 + apply Fin.ext + simp only [Fin.val_castSucc, Fin.val_succ] + lia + have haMiddle : middle[(k : ℕ) - 1] ≠ 0 := by + rw [hleftMiddle] + exact hopposite.1 + have hbMiddle : middle[(k : ℕ) - 1 + 2] ≠ 0 := by + rw [hrightMiddle] + exact hopposite.2.1 + have habMiddle : + middle[(k : ℕ) - 1] ≠ + middle[(k : ℕ) - 1 + 2] := by + rw [hleftMiddle, hrightMiddle] + exact hopposite.2.2 + have hlocal : + List.NodalInsertion + ((left ++ middle ++ right).eraseIdx + (left.length + ((k : ℕ) - 1 + 1))) + (left ++ middle ++ right) := + List.NodalInsertion.eraseIdx_append_middle + left middle right ((k : ℕ) - 1) + hrightBound haMiddle hbMiddle habMiddle + have hkMiddle : (k : ℕ) < middle.length := by + simpa [middle] using k.isLt + rw [hpred] at hlocal + rw [List.eraseIdx_append_middle + left middle right (k : ℕ) hkMiddle] at hlocal + simpa [full, reduced] using hlocal + have hstepCore : + List.NodalInsertion + (Fin.nodalPerturbationCoreSigns x' y') + (Fin.nodalPerturbationCoreSigns x y) := by + rw [hreduced, hfull] + exact hstep + have hsourceComp : + (List.ofFn (SignType.sign ∘ x')).filter (· ≠ 0) = + (List.ofFn (SignType.sign ∘ x)).filter (· ≠ 0) := by + calc + _ = (List.ofFn (fun i => SignType.sign (x' i))).filter + (· ≠ 0) := by + congr 2 + _ = (List.ofFn (fun i => SignType.sign (x i))).filter + (· ≠ 0) := hsource + _ = _ := by + congr 2 + rw [hsourceComp] at hrec + exact hrec.tail hstepCore + /-- The number of sign changes in a finite vector, in index order and ignoring zero entries. -/ def signVariations {R : Type*} [Zero R] [LinearOrder R] {n : ℕ} diff --git a/RealRooted/SignVariation.lean b/RealRooted/SignVariation.lean deleted file mode 100644 index 51d83008..00000000 --- a/RealRooted/SignVariation.lean +++ /dev/null @@ -1,293 +0,0 @@ - -/-- Remove one interior zero, apply the shorter nodal-insertion chain, and reinsert it. - -This is the finite induction step in the endpoint-perturbation route used in Karlin's -Chapter 8, Section 3 argument. --/ -theorem Fin.nodalInsertions_coreSigns_remove - {n : ℕ} - (ih : - ∀ {u v : Fin (n + 2) → ℝ}, - (∀ i, u i ≠ 0 → - SignType.sign (v i) = SignType.sign (u i)) → - (∀ i : Fin n, u i.succ.castSucc = 0 → - u i.castSucc.castSucc * u i.succ.succ < 0) → - Relation.ReflTransGen List.NodalInsertion - ((List.ofFn (SignType.sign ∘ u)).filter (· ≠ 0)) - (Fin.nodalPerturbationCoreSigns u v)) - {x y : Fin (n + 3) → ℝ} - (hsign : ∀ i, x i ≠ 0 → - SignType.sign (y i) = SignType.sign (x i)) - (hnodal : ∀ i : Fin (n + 1), x i.succ.castSucc = 0 → - x i.castSucc.castSucc * x i.succ.succ < 0) - (k : Fin (n + 1)) - (hk : x k.succ.castSucc = 0) : - Relation.ReflTransGen List.NodalInsertion - ((List.ofFn (SignType.sign ∘ x)).filter (· ≠ 0)) - (Fin.nodalPerturbationCoreSigns x y) := by - let p : Fin (n + 3) := k.succ.castSucc - let x' : Fin (n + 2) → ℝ := fun i => x (p.succAbove i) - let y' : Fin (n + 2) → ℝ := fun i => y (p.succAbove i) - have hsign' : - ∀ i, x' i ≠ 0 → - SignType.sign (y' i) = SignType.sign (x' i) := by - intro i hi - simpa only [x', y'] using - hsign (p.succAbove i) (by simpa only [x'] using hi) - have hnodal' : - ∀ i : Fin n, x' i.succ.castSucc = 0 → - x' i.castSucc.castSucc * x' i.succ.succ < 0 := by - simpa only [x', p] using - Fin.interiorNodal_succAbove x k hk hnodal - have hrec : - Relation.ReflTransGen List.NodalInsertion - ((List.ofFn (SignType.sign ∘ x')).filter (· ≠ 0)) - (Fin.nodalPerturbationCoreSigns x' y') := - ih hsign' hnodal' - have hofFn : - List.ofFn (fun i => SignType.sign (x' i)) = - (List.ofFn (fun i => SignType.sign (x i))).eraseIdx p := by - simpa only [x', p] using - List.ofFn_succAbove_eq_eraseIdx - (fun i => SignType.sign (x i)) p - have hlenSource : - (p : ℕ) < - (List.ofFn (fun i => SignType.sign (x i))).length := by - simpa only [List.length_ofFn] using p.isLt - have hvalueSource : - (List.ofFn (fun i => SignType.sign (x i)))[(p : ℕ)] = 0 := by - rw [List.getElem_ofFn hlenSource] - change SignType.sign (x p) = 0 - rw [show p = k.succ.castSucc from rfl, hk] - norm_num [SignType.sign] - have hfilteredSource : - ¬(fun z : SignType => decide (z ≠ 0)) - (List.ofFn (fun i => SignType.sign (x i)))[(p : ℕ)] := by - rw [hvalueSource] - decide - have hsource : - (List.ofFn (fun i => SignType.sign (x' i))).filter (· ≠ 0) = - (List.ofFn (fun i => SignType.sign (x i))).filter (· ≠ 0) := by - rw [hofFn] - exact List.filter_eraseIdx_eq_of_getElem_not - hlenSource hfilteredSource - have hxzero : x' 0 = x 0 := by - simp only [x', p, Fin.succAbove_zero_of_interior] - have hyzero : y' 0 = y 0 := by - simp only [y', p, Fin.succAbove_zero_of_interior] - have hxlast : - x' (Fin.last (n + 1)) = x (Fin.last (n + 2)) := by - simp only [x', p, Fin.succAbove_last_of_interior] - have hylast : - y' (Fin.last (n + 1)) = y (Fin.last (n + 2)) := by - simp only [y', p, Fin.succAbove_last_of_interior] - have hinterior : - List.ofFn - (fun i : Fin n => - SignType.sign (y' i.succ.castSucc)) = - (List.ofFn - (fun i : Fin (n + 1) => - SignType.sign (y i.succ.castSucc))).eraseIdx k := by - simpa only [y', p, Function.comp_apply] using - List.ofFn_interior_succAbove_eq_eraseIdx - (SignType.sign ∘ y) k - let left : List SignType := - if x 0 = 0 then [] else [SignType.sign (y 0)] - let middle : List SignType := - List.ofFn - (fun i : Fin (n + 1) => - SignType.sign (y i.succ.castSucc)) - let right : List SignType := - if x (Fin.last (n + 2)) = 0 then [] - else [SignType.sign (y (Fin.last (n + 2)))] - let full : List SignType := left ++ middle ++ right - let reduced : List SignType := - left ++ middle.eraseIdx k ++ right - have hfull : - Fin.nodalPerturbationCoreSigns x y = full := by - rfl - have hreduced : - Fin.nodalPerturbationCoreSigns x' y' = reduced := by - simp only [Fin.nodalPerturbationCoreSigns, reduced, left, middle, - right, hxzero, hyzero, hxlast, hylast, hinterior] - have hmul : - x k.castSucc.castSucc * x k.succ.succ < 0 := - hnodal k hk - have hxleft : x k.castSucc.castSucc ≠ 0 := - left_ne_zero_of_mul (ne_of_lt hmul) - have hxright : x k.succ.succ ≠ 0 := - right_ne_zero_of_mul (ne_of_lt hmul) - have hopposite : - SignType.sign (y k.castSucc.castSucc) ≠ 0 ∧ - SignType.sign (y k.succ.succ) ≠ 0 ∧ - SignType.sign (y k.castSucc.castSucc) ≠ - SignType.sign (y k.succ.succ) := by - simpa only [hsign k.castSucc.castSucc hxleft, - hsign k.succ.succ hxright] using - SignType.sign_ne_zero_and_ne_of_mul_neg hmul - have hstep : List.NodalInsertion reduced full := by - by_cases hn : n = 0 - · subst n - have hkzero : k = 0 := Fin.eq_zero k - subst k - have hleftIndex : - (0 : Fin 1).castSucc.castSucc = (0 : Fin 3) := by - exact Fin.ext rfl - have hcenterIndex : - (0 : Fin 1).succ.castSucc = (1 : Fin 3) := by - exact Fin.ext rfl - have hrightIndex : - (0 : Fin 1).succ.succ = (2 : Fin 3) := by - exact Fin.ext rfl - have hlastIndex : - (Fin.last 2 : Fin 3) = (2 : Fin 3) := by - exact Fin.ext rfl - have hxzero' : x (0 : Fin 3) ≠ 0 := by - simpa only [hleftIndex] using hxleft - have hxtwo : x (2 : Fin 3) ≠ 0 := by - simpa only [hrightIndex] using hxright - have hsignLeft : SignType.sign (y 0) ≠ 0 := by - simpa only [hleftIndex] using hopposite.1 - have hsignTwo : SignType.sign (y 2) ≠ 0 := by - simpa only [hrightIndex] using hopposite.2.1 - have hsignNe : - SignType.sign (y 0) ≠ SignType.sign (y 2) := by - simpa only [hleftIndex, hrightIndex] using hopposite.2.2 - simpa [full, reduced, left, middle, right, hxzero', hxtwo, - hcenterIndex, hlastIndex] using - List.NodalInsertion.insert [] [] - (SignType.sign (y 0)) - (SignType.sign (y 2)) - (SignType.sign (y 1)) - hsignLeft hsignTwo hsignNe - · have hnpos : 0 < n := Nat.pos_of_ne_zero hn - by_cases hkzero : (k : ℕ) = 0 - · have hkeq : k = 0 := Fin.ext hkzero - subst k - have hxzero' : x 0 ≠ 0 := by - simpa using hxleft - have hmiddleLength : 1 < middle.length := by - simp [middle] - lia - simpa [full, reduced, left, hxzero'] using - List.NodalInsertion.eraseIdx_first_middle - (SignType.sign (y 0)) middle right - hmiddleLength - (by simpa using hopposite.1) - (by simpa [middle] using hopposite.2.1) - (by simpa [middle] using hopposite.2.2) - · by_cases hklast : (k : ℕ) = n - · have hkeq : k = Fin.last n := Fin.ext hklast - subst k - have hxlast' : x (Fin.last (n + 2)) ≠ 0 := by - simpa using hxright - have hpred : n - 1 + 1 = n := by lia - have hlen : middle.length = n - 1 + 2 := by - simp [middle] - lia - have hleftBound : n - 1 < middle.length := by - rw [hlen] - lia - have hleftMiddle : - middle[n - 1]'hleftBound = - SignType.sign - (y (Fin.last n).castSucc.castSucc) := by - rw [List.getElem_ofFn hleftBound] - congr 2 - apply Fin.ext - simp only [Fin.val_castSucc, Fin.val_succ, Fin.val_last] - lia - have hrightEndpoint : - SignType.sign (y (Fin.last (n + 2))) = - SignType.sign (y (Fin.last n).succ.succ) := by - congr 2 - have haMiddle : middle[n - 1] ≠ 0 := by - rw [hleftMiddle] - exact hopposite.1 - have hbEndpoint : - SignType.sign (y (Fin.last (n + 2))) ≠ 0 := by - rw [hrightEndpoint] - exact hopposite.2.1 - have habMiddle : - middle[n - 1] ≠ - SignType.sign (y (Fin.last (n + 2))) := by - rw [hleftMiddle, hrightEndpoint] - exact hopposite.2.2 - simpa [full, reduced, right, hxlast', hpred] using - List.NodalInsertion.eraseIdx_last_append_singleton - left middle - (SignType.sign (y (Fin.last (n + 2)))) - (n - 1) hlen haMiddle hbEndpoint habMiddle - · have hkpos : 0 < (k : ℕ) := Nat.pos_of_ne_zero hkzero - have hklt : (k : ℕ) < n := by lia - have hpred : - (k : ℕ) - 1 + 1 = (k : ℕ) := by lia - have hsucc : - (k : ℕ) - 1 + 2 = (k : ℕ) + 1 := by lia - have hleftBound : - (k : ℕ) - 1 < middle.length := by - simp [middle] - have hrightBound : - (k : ℕ) - 1 + 2 < middle.length := by - simp [middle] - lia - have hleftMiddle : - middle[(k : ℕ) - 1]'hleftBound = - SignType.sign (y k.castSucc.castSucc) := by - rw [List.getElem_ofFn hleftBound] - congr 2 - apply Fin.ext - simp only [Fin.val_castSucc, Fin.val_succ] - lia - have hrightMiddle : - middle[(k : ℕ) - 1 + 2]'hrightBound = - SignType.sign (y k.succ.succ) := by - rw [List.getElem_ofFn hrightBound] - congr 2 - apply Fin.ext - simp only [Fin.val_castSucc, Fin.val_succ] - lia - have haMiddle : middle[(k : ℕ) - 1] ≠ 0 := by - rw [hleftMiddle] - exact hopposite.1 - have hbMiddle : middle[(k : ℕ) - 1 + 2] ≠ 0 := by - rw [hrightMiddle] - exact hopposite.2.1 - have habMiddle : - middle[(k : ℕ) - 1] ≠ - middle[(k : ℕ) - 1 + 2] := by - rw [hleftMiddle, hrightMiddle] - exact hopposite.2.2 - have hlocal : - List.NodalInsertion - ((left ++ middle ++ right).eraseIdx - (left.length + ((k : ℕ) - 1 + 1))) - (left ++ middle ++ right) := - List.NodalInsertion.eraseIdx_append_middle - left middle right ((k : ℕ) - 1) - hrightBound haMiddle hbMiddle habMiddle - have hkMiddle : (k : ℕ) < middle.length := by - simpa [middle] using k.isLt - rw [hpred] at hlocal - rw [List.eraseIdx_append_middle - left middle right (k : ℕ) hkMiddle] at hlocal - simpa [full, reduced] using hlocal - have hstepCore : - List.NodalInsertion - (Fin.nodalPerturbationCoreSigns x' y') - (Fin.nodalPerturbationCoreSigns x y) := by - rw [hreduced, hfull] - exact hstep - have hsourceComp : - (List.ofFn (SignType.sign ∘ x')).filter (· ≠ 0) = - (List.ofFn (SignType.sign ∘ x)).filter (· ≠ 0) := by - calc - _ = (List.ofFn (fun i => SignType.sign (x' i))).filter - (· ≠ 0) := by - congr 2 - _ = (List.ofFn (fun i => SignType.sign (x i))).filter - (· ≠ 0) := hsource - _ = _ := by - congr 2 - rw [hsourceComp] at hrec - exact hrec.tail hstepCore From 7a46738ef3413bd88a7d6455f6e9763ab60b068c Mon Sep 17 00:00:00 2001 From: Per Alexandersson Date: Mon, 3 Aug 2026 06:07:33 +0000 Subject: [PATCH 116/141] complete finite nodal insertion chain --- .../LinearAlgebra/Matrix/SignVariation.lean | 90 +++++++++++++++++++ 1 file changed, 90 insertions(+) diff --git a/RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariation.lean b/RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariation.lean index 2ab29f14..14cf5646 100644 --- a/RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariation.lean +++ b/RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariation.lean @@ -975,6 +975,96 @@ theorem nodalInsertions_coreSigns_remove rw [hsourceComp] at hrec exact hrec.tail hstepCore +/-- If there is no interior zero, filtering the original signs already gives +the perturbed core signs. -/ +theorem nodalPerturbationCoreSigns_eq_of_no_interior_zero + {n : ℕ} {x y : Fin (n + 2) → ℝ} + (hsign : ∀ i, x i ≠ 0 → + SignType.sign (y i) = SignType.sign (x i)) + (hinterior : ∀ i : Fin n, x i.succ.castSucc ≠ 0) : + (List.ofFn (SignType.sign ∘ x)).filter (· ≠ 0) = + Fin.nodalPerturbationCoreSigns x y := by + have sign_ne_zero_of_ne_zero : + ∀ a : ℝ, a ≠ 0 → SignType.sign a ≠ 0 := by + intro a ha + rcases lt_trichotomy a 0 with hneg | hzero | hpos + · simp [SignType.sign, hneg, not_lt_of_ge hneg.le] + · exact (ha hzero).elim + · simp [SignType.sign, hpos] + let sourceMiddle : List SignType := + List.ofFn (fun i : Fin n => + SignType.sign (x i.succ.castSucc)) + let targetMiddle : List SignType := + List.ofFn (fun i : Fin n => + SignType.sign (y i.succ.castSucc)) + have hsourceMiddle : + sourceMiddle.filter (· ≠ 0) = sourceMiddle := by + apply List.filter_eq_self.mpr + intro s hs + simp only [sourceMiddle, List.mem_ofFn] at hs + obtain ⟨i, rfl⟩ := hs + exact decide_eq_true + (sign_ne_zero_of_ne_zero _ (hinterior i)) + have hmiddle : sourceMiddle = targetMiddle := by + simp only [sourceMiddle, targetMiddle] + congr 1 + funext i + exact (hsign i.succ.castSucc (hinterior i)).symm + have hfilterEndpoint (i : Fin (n + 2)) : + [SignType.sign (x i)].filter (· ≠ 0) = + if x i = 0 then [] else [SignType.sign (y i)] := by + by_cases hi : x i = 0 + · simp [hi, SignType.sign] + · rw [if_neg hi, List.filter_singleton] + have hs := sign_ne_zero_of_ne_zero _ hi + have hp : decide (SignType.sign (x i) ≠ 0) = true := + decide_eq_true hs + rw [hp] + change [SignType.sign (x i)] = [SignType.sign (y i)] + rw [hsign i hi] + rw [List.ofFn_two_endpoints] + change + (([SignType.sign (x 0)] ++ sourceMiddle ++ + [SignType.sign (x (Fin.last (n + 1)))]).filter (· ≠ 0)) = + (if x 0 = 0 then [] else [SignType.sign (y 0)]) ++ + targetMiddle ++ + (if x (Fin.last (n + 1)) = 0 then [] + else [SignType.sign (y (Fin.last (n + 1)))]) + rw [List.filter_append, List.filter_append, hfilterEndpoint, + hsourceMiddle, hmiddle, hfilterEndpoint] + +/-- Nodal insertions transform the filtered original sign list into the +perturbed core sign list. -/ +theorem nodalInsertions_coreSigns + {n : ℕ} {x y : Fin (n + 2) → ℝ} + (hsign : ∀ i, x i ≠ 0 → + SignType.sign (y i) = SignType.sign (x i)) + (hnodal : ∀ i : Fin n, x i.succ.castSucc = 0 → + x i.castSucc.castSucc * x i.succ.succ < 0) : + Relation.ReflTransGen List.NodalInsertion + ((List.ofFn (SignType.sign ∘ x)).filter (· ≠ 0)) + (Fin.nodalPerturbationCoreSigns x y) := by + induction n with + | zero => + have hinterior : + ∀ i : Fin 0, x i.succ.castSucc ≠ 0 := by + exact fun i => Fin.elim0 i + rw [Fin.nodalPerturbationCoreSigns_eq_of_no_interior_zero + hsign hinterior] + | succ n ih => + by_cases hzero : + ∃ k : Fin (n + 1), x k.succ.castSucc = 0 + · obtain ⟨k, hk⟩ := hzero + exact Fin.nodalInsertions_coreSigns_remove + (fun hsign' hnodal' => ih hsign' hnodal') + hsign hnodal k hk + · have hinterior : + ∀ i : Fin (n + 1), x i.succ.castSucc ≠ 0 := by + intro i hi + exact hzero ⟨i, hi⟩ + rw [Fin.nodalPerturbationCoreSigns_eq_of_no_interior_zero + hsign hinterior] + /-- The number of sign changes in a finite vector, in index order and ignoring zero entries. -/ def signVariations {R : Type*} [Zero R] [LinearOrder R] {n : ℕ} From d07d256fa2b6b20dc387bead4973cc35c9918232 Mon Sep 17 00:00:00 2001 From: Per Alexandersson Date: Mon, 3 Aug 2026 06:13:53 +0000 Subject: [PATCH 117/141] clean nodal induction and repair inference --- RealRooted/Mathlib/Data/Fin/Basic.lean | 7 +++++-- .../Matrix/SignRegularStrictification.lean | 7 +++++-- .../LinearAlgebra/Matrix/SignVariation.lean | 17 ++++------------- 3 files changed, 14 insertions(+), 17 deletions(-) diff --git a/RealRooted/Mathlib/Data/Fin/Basic.lean b/RealRooted/Mathlib/Data/Fin/Basic.lean index c8c391a4..ab405638 100644 --- a/RealRooted/Mathlib/Data/Fin/Basic.lean +++ b/RealRooted/Mathlib/Data/Fin/Basic.lean @@ -54,7 +54,7 @@ neighbor. -/ theorem succAbove_center_eq_right (i : Fin n) : (i.succ.castSucc.castSucc).succAbove i.succ.castSucc = i.succ.succ.castSucc := by - rw [Fin.succAbove_of_le_castSucc _ _ le_rfl] + rw [Fin.succAbove_of_le_castSucc _ _ (by rfl)] congr /-- Omitting the old right neighbor sends the corresponding new center to its old value. -/ @@ -78,7 +78,10 @@ theorem succAbove_succ_castSucc rw [Fin.succAbove_of_castSucc_lt _ _ h', Fin.succAbove_of_castSucc_lt _ _ h] exact Fin.ext rfl - · have hki : k ≤ i.castSucc := le_of_not_gt h + · have hki : k ≤ i.castSucc := by + change (k : ℕ) ≤ (i : ℕ) + change ¬(i : ℕ) < (k : ℕ) at h + exact Nat.le_of_not_gt h have h' : k.succ.castSucc ≤ i.succ.castSucc.castSucc := by change (k : ℕ) + 1 ≤ (i : ℕ) + 1 exact Nat.succ_le_succ hki diff --git a/RealRooted/Mathlib/LinearAlgebra/Matrix/SignRegularStrictification.lean b/RealRooted/Mathlib/LinearAlgebra/Matrix/SignRegularStrictification.lean index 5b0d9dc0..9367984e 100644 --- a/RealRooted/Mathlib/LinearAlgebra/Matrix/SignRegularStrictification.lean +++ b/RealRooted/Mathlib/LinearAlgebra/Matrix/SignRegularStrictification.lean @@ -93,7 +93,10 @@ theorem tendsto_gaussianMatrix_mul_atTop {n m : ℕ} Tendsto (fun a => gaussianMatrix n a * A) atTop (𝓝 A) := by have hmul : Continuous (fun M : Matrix (Fin n) (Fin n) ℝ => M * A) := continuous_id.matrix_mul continuous_const - simpa only [Matrix.one_mul] using - hmul.continuousAt.tendsto.comp (tendsto_gaussianMatrix_atTop n) + convert hmul.continuousAt.tendsto.comp + (tendsto_gaussianMatrix_atTop n) using 1 + · funext a + rfl + · simp end Matrix diff --git a/RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariation.lean b/RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariation.lean index 14cf5646..2530c8e7 100644 --- a/RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariation.lean +++ b/RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariation.lean @@ -984,13 +984,6 @@ theorem nodalPerturbationCoreSigns_eq_of_no_interior_zero (hinterior : ∀ i : Fin n, x i.succ.castSucc ≠ 0) : (List.ofFn (SignType.sign ∘ x)).filter (· ≠ 0) = Fin.nodalPerturbationCoreSigns x y := by - have sign_ne_zero_of_ne_zero : - ∀ a : ℝ, a ≠ 0 → SignType.sign a ≠ 0 := by - intro a ha - rcases lt_trichotomy a 0 with hneg | hzero | hpos - · simp [SignType.sign, hneg, not_lt_of_ge hneg.le] - · exact (ha hzero).elim - · simp [SignType.sign, hpos] let sourceMiddle : List SignType := List.ofFn (fun i : Fin n => SignType.sign (x i.succ.castSucc)) @@ -999,12 +992,11 @@ theorem nodalPerturbationCoreSigns_eq_of_no_interior_zero SignType.sign (y i.succ.castSucc)) have hsourceMiddle : sourceMiddle.filter (· ≠ 0) = sourceMiddle := by - apply List.filter_eq_self.mpr + apply List.filter_eq_self.2 intro s hs simp only [sourceMiddle, List.mem_ofFn] at hs obtain ⟨i, rfl⟩ := hs - exact decide_eq_true - (sign_ne_zero_of_ne_zero _ (hinterior i)) + exact decide_eq_true (sign_ne_zero.mpr (hinterior i)) have hmiddle : sourceMiddle = targetMiddle := by simp only [sourceMiddle, targetMiddle] congr 1 @@ -1016,7 +1008,7 @@ theorem nodalPerturbationCoreSigns_eq_of_no_interior_zero by_cases hi : x i = 0 · simp [hi, SignType.sign] · rw [if_neg hi, List.filter_singleton] - have hs := sign_ne_zero_of_ne_zero _ hi + have hs := sign_ne_zero.mpr hi have hp : decide (SignType.sign (x i) ≠ 0) = true := decide_eq_true hs rw [hp] @@ -1056,8 +1048,7 @@ theorem nodalInsertions_coreSigns ∃ k : Fin (n + 1), x k.succ.castSucc = 0 · obtain ⟨k, hk⟩ := hzero exact Fin.nodalInsertions_coreSigns_remove - (fun hsign' hnodal' => ih hsign' hnodal') - hsign hnodal k hk + ih hsign hnodal k hk · have hinterior : ∀ i : Fin (n + 1), x i.succ.castSucc ≠ 0 := by intro i hi From 9a0bc9567612b8de165d7c511475832a47a3dfea Mon Sep 17 00:00:00 2001 From: Per Alexandersson Date: Mon, 3 Aug 2026 06:19:23 +0000 Subject: [PATCH 118/141] prove finite nodal perturbation bound --- .../LinearAlgebra/Matrix/SignVariation.lean | 85 +++++++++++++++++++ 1 file changed, 85 insertions(+) diff --git a/RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariation.lean b/RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariation.lean index 2530c8e7..4aab836a 100644 --- a/RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariation.lean +++ b/RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariation.lean @@ -1062,6 +1062,91 @@ def signVariations {R : Type*} [Zero R] [LinearOrder R] {n : ℕ} (x : Fin n → R) : ℕ := (List.ofFn x).signVariations +/-- A sign-preserving perturbation increases variations by at most two when +all interior zeros are nodal. + +This is Karlin's finite endpoint-loss estimate: interior nodal insertions cost +nothing, while the two endpoints cost at most one variation each. +-/ +theorem signVariations_le_add_two_of_sign_eq_on_nonzero_of_interior_nodal + {n : ℕ} {x y : Fin (n + 2) → ℝ} + (hsign : ∀ i, x i ≠ 0 → + SignType.sign (y i) = SignType.sign (x i)) + (hnodal : ∀ i : Fin n, x i.succ.castSucc = 0 → + x i.castSucc.castSucc * x i.succ.succ < 0) : + Fin.signVariations y ≤ Fin.signVariations x + 2 := by + let middle : List SignType := + List.ofFn (fun i : Fin n => + SignType.sign (y i.succ.castSucc)) + let core := Fin.nodalPerturbationCoreSigns x y + have hchain : + Relation.ReflTransGen List.NodalInsertion + ((List.ofFn (SignType.sign ∘ x)).filter (· ≠ 0)) core := by + exact Fin.nodalInsertions_coreSigns hsign hnodal + have hcore : core.signVariations = Fin.signVariations x := by + rw [List.signVariations_eq_of_nodalInsertions hchain] + exact Fin.filtered_signList_signVariations x + have hsplit : + List.ofFn (SignType.sign ∘ y) = + SignType.sign (y 0) :: + (middle ++ [SignType.sign (y (Fin.last (n + 1)))]) := by + simpa only [middle, Function.comp_apply] using + List.ofFn_two_endpoints (SignType.sign ∘ y) + rw [Fin.signVariations_eq_signList, hsplit] + by_cases hzero : x 0 = 0 + · by_cases hlast : x (Fin.last (n + 1)) = 0 + · have hfull : + SignType.sign (y 0) :: + (middle ++ [SignType.sign (y (Fin.last (n + 1)))]) = + (SignType.sign (y 0) :: core) ++ + [SignType.sign (y (Fin.last (n + 1)))] := by + simp [core, middle, Fin.nodalPerturbationCoreSigns, + hzero, hlast] + rw [hfull] + calc + ((SignType.sign (y 0) :: core) ++ + [SignType.sign (y (Fin.last (n + 1)))]).signVariations ≤ + (SignType.sign (y 0) :: core).signVariations + 1 := + List.signVariations_append_singleton_signType_le_succ _ _ + _ ≤ (core.signVariations + 1) + 1 := + Nat.add_le_add_right + (List.signVariations_cons_le_succ (SignType.sign (y 0)) core) 1 + _ = Fin.signVariations x + 2 := by rw [hcore] + · have hfull : + SignType.sign (y 0) :: + (middle ++ [SignType.sign (y (Fin.last (n + 1)))]) = + SignType.sign (y 0) :: core := by + simp [core, middle, Fin.nodalPerturbationCoreSigns, + hzero, hlast] + rw [hfull] + calc + (SignType.sign (y 0) :: core).signVariations ≤ + core.signVariations + 1 := + List.signVariations_cons_le_succ _ _ + _ ≤ Fin.signVariations x + 2 := by rw [hcore]; lia + · by_cases hlast : x (Fin.last (n + 1)) = 0 + · have hfull : + SignType.sign (y 0) :: + (middle ++ [SignType.sign (y (Fin.last (n + 1)))]) = + core ++ [SignType.sign (y (Fin.last (n + 1)))] := by + simp [core, middle, Fin.nodalPerturbationCoreSigns, + hzero, hlast] + rw [hfull] + calc + (core ++ + [SignType.sign (y (Fin.last (n + 1)))]).signVariations ≤ + core.signVariations + 1 := + List.signVariations_append_singleton_signType_le_succ _ _ + _ ≤ Fin.signVariations x + 2 := by rw [hcore]; lia + · have hfull : + SignType.sign (y 0) :: + (middle ++ [SignType.sign (y (Fin.last (n + 1)))]) = + core := by + simp [core, middle, Fin.nodalPerturbationCoreSigns, + hzero, hlast] + rw [hfull, hcore] + lia + /-- A finite vector has at most one fewer sign variation than its length. -/ lemma signVariations_le_card_sub_one {R : Type*} [Zero R] [LinearOrder R] {n : ℕ} (x : Fin n → R) : signVariations x ≤ n - 1 := by From 6acb1955c086f84cbc53c5cfad413e86eb524f6f Mon Sep 17 00:00:00 2001 From: Per Alexandersson Date: Mon, 3 Aug 2026 06:21:16 +0000 Subject: [PATCH 119/141] add nodal perturbation topology wrapper --- .../Matrix/SignVariationTopology.lean | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariationTopology.lean b/RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariationTopology.lean index dda4e187..ea57ce1e 100644 --- a/RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariationTopology.lean +++ b/RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariationTopology.lean @@ -99,4 +99,20 @@ theorem signVariations_le_of_tendsto obtain ⟨a, hxa, har⟩ := (hmono.and hr).exists exact hxa.trans har +/-- A convergent perturbation of a vector with nodal interior zeros eventually +has at most two additional sign variations. -/ +theorem eventually_signVariations_le_add_two_of_tendsto_of_interior_nodal + {α : Type*} {l : Filter α} [l.NeBot] + {n : ℕ} {f : α → Fin (n + 2) → ℝ} + {x : Fin (n + 2) → ℝ} + (hf : Tendsto f l (𝓝 x)) + (hnodal : ∀ i : Fin n, x i.succ.castSucc = 0 → + x i.castSucc.castSucc * x i.succ.succ < 0) : + ∀ᶠ a in l, + Fin.signVariations (f a) ≤ Fin.signVariations x + 2 := by + filter_upwards [Fin.eventually_sign_eq_of_tendsto hf] with a ha + exact + Fin.signVariations_le_add_two_of_sign_eq_on_nonzero_of_interior_nodal + ha hnodal + end Fin From 9815323da82695ad0bb40ffd855383af93a0c73f Mon Sep 17 00:00:00 2001 From: Per Alexandersson Date: Mon, 3 Aug 2026 06:25:07 +0000 Subject: [PATCH 120/141] golf nodal perturbation topology wrapper --- .../LinearAlgebra/Matrix/SignVariationTopology.lean | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariationTopology.lean b/RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariationTopology.lean index ea57ce1e..48f7fb56 100644 --- a/RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariationTopology.lean +++ b/RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariationTopology.lean @@ -109,10 +109,8 @@ theorem eventually_signVariations_le_add_two_of_tendsto_of_interior_nodal (hnodal : ∀ i : Fin n, x i.succ.castSucc = 0 → x i.castSucc.castSucc * x i.succ.succ < 0) : ∀ᶠ a in l, - Fin.signVariations (f a) ≤ Fin.signVariations x + 2 := by - filter_upwards [Fin.eventually_sign_eq_of_tendsto hf] with a ha - exact - Fin.signVariations_le_add_two_of_sign_eq_on_nonzero_of_interior_nodal - ha hnodal + signVariations (f a) ≤ signVariations x + 2 := by + filter_upwards [eventually_sign_eq_of_tendsto hf] with a ha + exact signVariations_le_add_two_of_sign_eq_on_nonzero_of_interior_nodal ha hnodal end Fin From 68305d4942f7ab4f2645a53cf8e877171e484ec6 Mon Sep 17 00:00:00 2001 From: Per Alexandersson Date: Mon, 3 Aug 2026 06:31:59 +0000 Subject: [PATCH 121/141] add surjective TN nodal kernel bound --- .../Matrix/SignRegularRankDeficient.lean | 53 +++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/RealRooted/Mathlib/LinearAlgebra/Matrix/SignRegularRankDeficient.lean b/RealRooted/Mathlib/LinearAlgebra/Matrix/SignRegularRankDeficient.lean index 9b602123..7cd1c97d 100644 --- a/RealRooted/Mathlib/LinearAlgebra/Matrix/SignRegularRankDeficient.lean +++ b/RealRooted/Mathlib/LinearAlgebra/Matrix/SignRegularRankDeficient.lean @@ -549,6 +549,8 @@ theorem Matrix.IsSignConsistentOrder.signVariations_mulVec_le_rank_sub_one_of_in of columns. Rank zero gives the zero linear map, full column rank uses the injective variation bound, and positive deficient rank uses the rank-preserving deletion step. -/ + +open Filter Topology theorem Matrix.IsSignConsistentOrder.signVariations_mulVec_le_rank_sub_one {n m r : ℕ} {A : Matrix (Fin n) (Fin m) ℝ} @@ -851,3 +853,54 @@ theorem Matrix.IsTotallyNonnegRect.signVariations_mulVec_le (hA : A.IsTotallyNonnegRect) (c : Fin n → ℝ) : Fin.signVariations (A.mulVec c) ≤ Fin.signVariations c := hA.isSignRegular.signVariations_mulVec_le c + +/-- A surjective totally nonnegative matrix forces a nodal kernel vector to +have at least the row count minus one sign variations, up to the two endpoint +variations lost under perturbation. -/ +theorem Matrix.IsTotallyNonnegRect.card_sub_one_le_signVariations_add_two_of_surjective_of_nodal + {rows n : ℕ} {A : Matrix (Fin rows) (Fin (n + 2)) ℝ} + (hA : A.IsTotallyNonnegRect) + (hsurj : Function.Surjective A.mulVec) + {x : Fin (n + 2) → ℝ} + (hker : A.mulVec x = 0) + (hnodal : ∀ i : Fin n, x i.succ.castSucc = 0 → + x i.castSucc.castSucc * x i.succ.succ < 0) : + rows - 1 ≤ Fin.signVariations x + 2 := by + cases rows with + | zero => simp + | succ k => + let alt : Fin (k + 1) → ℝ := fun i => (-1 : ℝ) ^ (i : ℕ) + obtain ⟨z, hz⟩ := hsurj alt + let f : ℝ → Fin (n + 2) → ℝ := fun t => x + t • z + have hf : + Tendsto f (nhdsWithin (0 : ℝ) (Set.Ioi 0)) (𝓝 x) := by + have hfull : + Tendsto f (𝓝 (0 : ℝ)) (𝓝 (x + (0 : ℝ) • z)) := + tendsto_const_nhds.add (tendsto_id.smul_const z) + have hle : + nhdsWithin (0 : ℝ) (Set.Ioi 0) ≤ 𝓝 (0 : ℝ) := by + simp only [nhdsWithin] + exact inf_le_left + have hrestricted := hfull.mono_left hle + simpa only [zero_smul, add_zero] using hrestricted + have hbound := + Fin.eventually_signVariations_le_add_two_of_tendsto_of_interior_nodal + hf hnodal + have hpos : + ∀ᶠ t in nhdsWithin (0 : ℝ) (Set.Ioi 0), 0 < t := + self_mem_nhdsWithin + obtain ⟨t, htbound, ht⟩ := (hbound.and hpos).exists + have himage : + A.mulVec (f t) = + fun i : Fin (k + 1) => (-1 : ℝ) ^ (i : ℕ) * t := by + rw [show f t = x + t • z by rfl, Matrix.mulVec_add, + Matrix.mulVec_smul, hker, hz] + funext i + simp only [Pi.smul_apply, zero_add, alt, smul_eq_mul, mul_comm] + have halt : Fin.StrictlyAlternates (A.mulVec (f t)) := by + rw [himage] + exact Fin.strictlyAlternates_alternating k ht + have hlower : k ≤ Fin.signVariations (A.mulVec (f t)) := + halt.le_signVariations_of_strictMono strictMono_id + simpa using + hlower.trans ((hA.signVariations_mulVec_le (f t)).trans htbound) From 1aa62a71c276967ef3f5e1b785e138711404b14e Mon Sep 17 00:00:00 2001 From: Per Alexandersson Date: Mon, 3 Aug 2026 06:42:23 +0000 Subject: [PATCH 122/141] add repeated ASW sine variation lower bound --- RealRooted/ASWKarlinKernel.lean | 46 +++++++++++++ RealRooted/ASWKarlinVectors.lean | 66 +++++++++++++++++++ .../Matrix/SignRegularRankDeficient.lean | 18 +++++ 3 files changed, 130 insertions(+) diff --git a/RealRooted/ASWKarlinKernel.lean b/RealRooted/ASWKarlinKernel.lean index 6b19a581..c327cbfc 100644 --- a/RealRooted/ASWKarlinKernel.lean +++ b/RealRooted/ASWKarlinKernel.lean @@ -2,6 +2,7 @@ import RealRooted.ASWKarlinMatrix import RealRooted.ASWKarlinThreshold import RealRooted.ASWKarlinVariation import RealRooted.ASWKarlinVectors +import RealRooted.Mathlib.LinearAlgebra.Matrix.SignRegularRankDeficient import Mathlib.Analysis.Complex.Basic import Mathlib.Analysis.SpecialFunctions.Complex.Arg @@ -122,6 +123,51 @@ lemma complex_root_ne_zero_of_coeff_zero_pos {p : ℝ[X]} {z : ℂ} /-- A real complex root of a positive-constant PF polynomial lies on the negative real ray. -/ +/-- Karlin's repeated matrices force a linear lower bound on the sampled sine +vector's sign variations, up to the fixed two-endpoint perturbation loss. -/ +theorem aswKarlinRepeatedSineVariationLowerBound + {p : ℝ[X]} {z : ℂ} (hz : z ∈ (p.map (algebraMap ℝ ℂ)).roots) + (hdegree : 0 < p.natDegree) (hconst : 0 < p.coeff 0) + (hpf : IsPolyaFreqSeq p.coeff) {order blocks : ℕ} + (horder : 0 < order) (hblocks : 0 < blocks) (him : z.im ≠ 0) : + blocks * order - 1 ≤ + Fin.signVariations + (aswKarlinSineVector z.arg p.natDegree order blocks) + 2 := by + have hspan : 0 < p.natDegree + order - 1 := by + lia + have hwidth : 0 < blocks * (p.natDegree + order - 1) := + Nat.mul_pos hblocks hspan + have hcols : + blocks * (p.natDegree + order - 1) + 1 = + (blocks * (p.natDegree + order - 1) - 1) + 2 := by + lia + have hnodal : + ∀ i : Fin (blocks * (p.natDegree + order - 1) - 1), + aswKarlinRootVector z p.natDegree order blocks + (Fin.cast hcols.symm i.succ.castSucc) = 0 → + aswKarlinRootVector z p.natDegree order blocks + (Fin.cast hcols.symm i.castSucc.castSucc) * + aswKarlinRootVector z p.natDegree order blocks + (Fin.cast hcols.symm i.succ.succ) < 0 := by + intro i hi + change (z ^ ((i : ℕ) + 1)).im = 0 at hi + change (z ^ (i : ℕ)).im * (z ^ ((i : ℕ) + 2)).im < 0 + exact + im_pow_mul_im_pow_add_two_neg_of_im_pow_add_one_eq_zero him i hi + have hbound := + Matrix.IsTotallyNonnegRect.card_sub_one_le_signVariations_add_two_of_surjective_of_card_eq + (hpf.aswKarlinMatrix_isTotallyNonnegRect + p.natDegree order blocks) + hcols + (aswKarlinMatrix_mulVec_surjective p.natDegree order blocks + hdegree horder hconst) + (aswKarlinMatrix_mulVec_rootVector hz order blocks hdegree horder) + hnodal + rw [signVariations_aswKarlinRootVector_eq_sine + (complex_root_ne_zero_of_coeff_zero_pos hz hconst) + p.natDegree order blocks] at hbound + exact hbound + lemma arg_eq_pi_of_real_complex_root_of_isPolyaFreqSeq_coeff {p : ℝ[X]} {z : ℂ} (hz : z ∈ (p.map (algebraMap ℝ ℂ)).roots) (hconst : 0 < p.coeff 0) (hpf : IsPolyaFreqSeq p.coeff) diff --git a/RealRooted/ASWKarlinVectors.lean b/RealRooted/ASWKarlinVectors.lean index 64dc177f..e8e10e67 100644 --- a/RealRooted/ASWKarlinVectors.lean +++ b/RealRooted/ASWKarlinVectors.lean @@ -46,6 +46,72 @@ lemma im_pow_eq_norm_pow_mul_sin_arg (z : ℂ) (n : ℕ) : Complex.exp_im] simp +/-- If a nonreal complex geometric progression has an interior zero imaginary +part, the adjacent imaginary parts have opposite strict signs. -/ +lemma im_pow_mul_im_pow_add_two_neg_of_im_pow_add_one_eq_zero + {z : ℂ} (him : z.im ≠ 0) (i : ℕ) + (hzero : (z ^ (i + 1)).im = 0) : + (z ^ i).im * (z ^ (i + 2)).im < 0 := by + let θ := z.arg + have hz : z ≠ 0 := by + intro hz + apply him + simp [hz] + have hnorm : 0 < ‖z‖ := norm_pos_iff.mpr hz + have hsin : Real.sin θ ≠ 0 := by + have hpolar := im_pow_eq_norm_pow_mul_sin_arg z 1 + simp only [pow_one, Nat.cast_one, one_mul] at hpolar + intro hs + apply him + rw [hpolar, hs, mul_zero] + have hzeroSin : Real.sin ((i + 1 : ℕ) * θ) = 0 := by + rw [im_pow_eq_norm_pow_mul_sin_arg] at hzero + exact (mul_eq_zero.mp hzero).resolve_left (pow_ne_zero _ hnorm.ne') + have hcos : Real.cos ((i + 1 : ℕ) * θ) ≠ 0 := by + intro hc + have hsq := Real.sin_sq_add_cos_sq ((i + 1 : ℕ) * θ) + rw [hzeroSin, hc] at hsq + norm_num at hsq + have hprev : + Real.sin (i * θ) = + -Real.cos ((i + 1 : ℕ) * θ) * Real.sin θ := by + have harg : + (i : ℝ) * θ = ((i + 1 : ℕ) : ℝ) * θ - θ := by + push_cast + ring + rw [harg, Real.sin_sub, hzeroSin] + ring + have hnext : + Real.sin ((i + 2 : ℕ) * θ) = + Real.cos ((i + 1 : ℕ) * θ) * Real.sin θ := by + have harg : + ((i + 2 : ℕ) : ℝ) * θ = + ((i + 1 : ℕ) : ℝ) * θ + θ := by + push_cast + ring + rw [harg, Real.sin_add, hzeroSin] + ring + have htrig : + Real.sin (i * θ) * Real.sin ((i + 2 : ℕ) * θ) < 0 := by + rw [hprev, hnext] + calc + (-Real.cos ((i + 1 : ℕ) * θ) * Real.sin θ) * + (Real.cos ((i + 1 : ℕ) * θ) * Real.sin θ) = + -(Real.cos ((i + 1 : ℕ) * θ) ^ 2 * Real.sin θ ^ 2) := by + ring + _ < 0 := neg_lt_zero.mpr + (mul_pos (sq_pos_of_ne_zero hcos) (sq_pos_of_ne_zero hsin)) + rw [im_pow_eq_norm_pow_mul_sin_arg, + im_pow_eq_norm_pow_mul_sin_arg] + calc + (‖z‖ ^ i * Real.sin (i * θ)) * + (‖z‖ ^ (i + 2) * Real.sin ((i + 2 : ℕ) * θ)) = + (‖z‖ ^ i * ‖z‖ ^ (i + 2)) * + (Real.sin (i * θ) * Real.sin ((i + 2 : ℕ) * θ)) := by + ring + _ < 0 := mul_neg_of_pos_of_neg + (mul_pos (pow_pos hnorm _) (pow_pos hnorm _)) htrig + /-- A nonzero complex number's root vector and sampled sine vector have the same coordinate signs. -/ lemma signVariations_aswKarlinRootVector_eq_sine {z : ℂ} (hz : z ≠ 0) diff --git a/RealRooted/Mathlib/LinearAlgebra/Matrix/SignRegularRankDeficient.lean b/RealRooted/Mathlib/LinearAlgebra/Matrix/SignRegularRankDeficient.lean index 7cd1c97d..aa13e237 100644 --- a/RealRooted/Mathlib/LinearAlgebra/Matrix/SignRegularRankDeficient.lean +++ b/RealRooted/Mathlib/LinearAlgebra/Matrix/SignRegularRankDeficient.lean @@ -904,3 +904,21 @@ theorem Matrix.IsTotallyNonnegRect.card_sub_one_le_signVariations_add_two_of_sur halt.le_signVariations_of_strictMono strictMono_id simpa using hlower.trans ((hA.signVariations_mulVec_le (f t)).trans htbound) + +/-- Cardinality-equality wrapper for the surjective totally nonnegative nodal +kernel bound. -/ +theorem Matrix.IsTotallyNonnegRect.card_sub_one_le_signVariations_add_two_of_surjective_of_card_eq + {rows cols n : ℕ} {A : Matrix (Fin rows) (Fin cols) ℝ} + (hA : A.IsTotallyNonnegRect) + (hcols : cols = n + 2) + (hsurj : Function.Surjective A.mulVec) + {x : Fin cols → ℝ} + (hker : A.mulVec x = 0) + (hnodal : ∀ i : Fin n, + x (Fin.cast hcols.symm i.succ.castSucc) = 0 → + x (Fin.cast hcols.symm i.castSucc.castSucc) * + x (Fin.cast hcols.symm i.succ.succ) < 0) : + rows - 1 ≤ Fin.signVariations x + 2 := by + subst cols + exact hA.card_sub_one_le_signVariations_add_two_of_surjective_of_nodal + hsurj hker (by simpa using hnodal) From d699e135aec35ce4fc16b77b95711b871ff65360 Mon Sep 17 00:00:00 2001 From: Per Alexandersson Date: Mon, 3 Aug 2026 06:44:02 +0000 Subject: [PATCH 123/141] clean repeated ASW variation proof --- RealRooted/ASWKarlinKernel.lean | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/RealRooted/ASWKarlinKernel.lean b/RealRooted/ASWKarlinKernel.lean index c327cbfc..551af73b 100644 --- a/RealRooted/ASWKarlinKernel.lean +++ b/RealRooted/ASWKarlinKernel.lean @@ -121,8 +121,6 @@ lemma complex_root_ne_zero_of_coeff_zero_pos {p : ℝ[X]} {z : ℂ} exact heval linarith -/-- A real complex root of a positive-constant PF polynomial lies on the -negative real ray. -/ /-- Karlin's repeated matrices force a linear lower bound on the sampled sine vector's sign variations, up to the fixed two-endpoint perturbation loss. -/ theorem aswKarlinRepeatedSineVariationLowerBound @@ -133,10 +131,8 @@ theorem aswKarlinRepeatedSineVariationLowerBound blocks * order - 1 ≤ Fin.signVariations (aswKarlinSineVector z.arg p.natDegree order blocks) + 2 := by - have hspan : 0 < p.natDegree + order - 1 := by - lia have hwidth : 0 < blocks * (p.natDegree + order - 1) := - Nat.mul_pos hblocks hspan + Nat.mul_pos hblocks (by lia) have hcols : blocks * (p.natDegree + order - 1) + 1 = (blocks * (p.natDegree + order - 1) - 1) + 2 := by @@ -168,6 +164,8 @@ theorem aswKarlinRepeatedSineVariationLowerBound p.natDegree order blocks] at hbound exact hbound +/-- A real complex root of a positive-constant PF polynomial lies on the +negative real ray. -/ lemma arg_eq_pi_of_real_complex_root_of_isPolyaFreqSeq_coeff {p : ℝ[X]} {z : ℂ} (hz : z ∈ (p.map (algebraMap ℝ ℂ)).roots) (hconst : 0 < p.coeff 0) (hpf : IsPolyaFreqSeq p.coeff) From f57293040b271ddb91dba22af8ff29047e0668df Mon Sep 17 00:00:00 2001 From: Per Alexandersson Date: Mon, 3 Aug 2026 06:47:21 +0000 Subject: [PATCH 124/141] add sampled sine floor variation bound --- RealRooted/ASWKarlinSineBounds.lean | 44 +++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/RealRooted/ASWKarlinSineBounds.lean b/RealRooted/ASWKarlinSineBounds.lean index d769962f..4d11953d 100644 --- a/RealRooted/ASWKarlinSineBounds.lean +++ b/RealRooted/ASWKarlinSineBounds.lean @@ -226,6 +226,50 @@ lemma signVariations_sin_mul_lt_of_last_le_nat_mul_pi simp [k] exact lt_trans hlt hk_lt +/-- The sampled sine vector has at most the number of completed half-turns in +its final angle. -/ +lemma signVariations_sin_mul_le_floor_div_pi + {N : ℕ} {θ : ℝ} (hθ0 : 0 ≤ θ) : + Fin.signVariations + (fun j : Fin (N + 1) => Real.sin ((j : ℕ) * θ)) ≤ + ⌊((N : ℝ) * θ) / Real.pi⌋₊ := by + let r : ℕ := ⌊((N : ℝ) * θ) / Real.pi⌋₊ + have hquot : + ((N : ℝ) * θ) / Real.pi < ((r + 1 : ℕ) : ℝ) := by + simpa only [r, Nat.cast_add, Nat.cast_one] using + Nat.lt_floor_add_one (((N : ℝ) * θ) / Real.pi) + have hlast : + (N : ℝ) * θ ≤ ((r + 1 : ℕ) : ℝ) * Real.pi := by + have hmul := mul_lt_mul_of_pos_right hquot Real.pi_pos + rw [div_mul_cancel₀ _ Real.pi_ne_zero] at hmul + exact hmul.le + have hlt := + signVariations_sin_mul_lt_of_last_le_nat_mul_pi + (N := N) (order := r + 1) (by positivity) hθ0 hlast + exact Nat.lt_succ_iff.mp + (by simpa only [Nat.succ_eq_add_one] using hlt) + +/-- Absolute-angle form of the sampled-sine floor bound for Karlin's repeated +vector. -/ +lemma signVariations_aswKarlinSineVector_le_floor_div_pi_abs + (θ : ℝ) (degree order blocks : ℕ) : + Fin.signVariations (aswKarlinSineVector θ degree order blocks) ≤ + ⌊(((blocks * (degree + order - 1) : ℕ) : ℝ) * |θ|) / + Real.pi⌋₊ := by + by_cases hθ : 0 ≤ θ + · rw [abs_of_nonneg hθ] + change Fin.signVariations + (fun j : Fin (blocks * (degree + order - 1) + 1) => + Real.sin ((j : ℕ) * θ)) ≤ _ + exact signVariations_sin_mul_le_floor_div_pi hθ + · have hθneg : θ < 0 := lt_of_not_ge hθ + rw [← signVariations_aswKarlinSineVector_neg θ degree order blocks, + abs_of_neg hθneg] + change Fin.signVariations + (fun j : Fin (blocks * (degree + order - 1) + 1) => + Real.sin ((j : ℕ) * -θ)) ≤ _ + exact signVariations_sin_mul_le_floor_div_pi (neg_nonneg.mpr hθneg.le) + /-- One-block Karlin sine vectors inherit the general sampled-sine sign-variation bound from a last-angle estimate. -/ lemma signVariations_aswKarlinSineVector_lt_of_last_le_order_pi From 2805f67d67f61494872bb748974c0b51500181ba Mon Sep 17 00:00:00 2001 From: Per Alexandersson Date: Mon, 3 Aug 2026 06:51:17 +0000 Subject: [PATCH 125/141] prove ASW sector bound by repeated blocks --- RealRooted/ASWKarlinKernel.lean | 75 +++++++++++++++++++++++++++++++-- 1 file changed, 71 insertions(+), 4 deletions(-) diff --git a/RealRooted/ASWKarlinKernel.lean b/RealRooted/ASWKarlinKernel.lean index 551af73b..4707fe0b 100644 --- a/RealRooted/ASWKarlinKernel.lean +++ b/RealRooted/ASWKarlinKernel.lean @@ -247,10 +247,77 @@ theorem aswKarlinSectorThreshold_le_abs_arg_of_im_ne_zero {p : ℝ[X]} {z : ℂ} (hdegree : 0 < p.natDegree) (hconst : 0 < p.coeff 0) (hpf : IsPolyaFreqSeq p.coeff) {order : ℕ} (horder : 0 < order) (him : z.im ≠ 0) : - aswSectorThreshold p.natDegree order ≤ |z.arg| := - aswKarlinSectorThreshold_le_abs_arg_of_im_ne_zero_of_classicalInput - aswKarlinKernelSignVariationClassicalInput hz hdegree hconst hpf horder - him + aswSectorThreshold p.natDegree order ≤ |z.arg| := by + by_contra hnot + have hθlt : + |z.arg| < aswSectorThreshold p.natDegree order := + lt_of_not_ge hnot + have hden_pos := + aswSectorThreshold_denom_pos p.natDegree order hdegree horder + have hspan_cast : + ((p.natDegree + order - 1 : ℕ) : ℝ) = + (order : ℝ) + p.natDegree - 1 := by + rw [Nat.cast_sub (by lia)] + push_cast + ring + have hmul := mul_lt_mul_of_pos_left hθlt hden_pos + have hnormalize : + ((order : ℝ) + p.natDegree - 1) * + aswSectorThreshold p.natDegree order = + (order : ℝ) * Real.pi := by + rw [aswSectorThreshold] + field_simp [hden_pos.ne'] + rw [hnormalize] at hmul + have hslope_lt : + (((p.natDegree + order - 1 : ℕ) : ℝ) * |z.arg|) / + Real.pi < (order : ℝ) := by + apply (div_lt_iff₀ Real.pi_pos).2 + rw [hspan_cast] + exact hmul + let gap : ℝ := + (order : ℝ) - + (((p.natDegree + order - 1 : ℕ) : ℝ) * |z.arg|) / Real.pi + have hgap : 0 < gap := by + dsimp [gap] + linarith + obtain ⟨blocks, hblocks⟩ := exists_nat_gt (3 / gap) + have hblocks_pos : 0 < blocks := by + have hfrac : (0 : ℝ) < 3 / gap := by positivity + exact_mod_cast hfrac.trans hblocks + have hlarge : (3 : ℝ) < (blocks : ℝ) * gap := by + have hmulGap := mul_lt_mul_of_pos_right hblocks hgap + rw [div_mul_cancel₀ _ hgap.ne'] at hmulGap + exact hmulGap + have hlower := + aswKarlinRepeatedSineVariationLowerBound + hz hdegree hconst hpf horder hblocks_pos him + have hupper := + signVariations_aswKarlinSineVector_le_floor_div_pi_abs + z.arg p.natDegree order blocks + let x : ℝ := + (((blocks * (p.natDegree + order - 1) : ℕ) : ℝ) * |z.arg|) / + Real.pi + have hx : 0 ≤ x := by + dsimp [x] + positivity + have hnat : blocks * order ≤ ⌊x⌋₊ + 3 := by + dsimp [x] + lia + have hcast : ((blocks * order : ℕ) : ℝ) ≤ (⌊x⌋₊ : ℝ) + 3 := by + exact_mod_cast hnat + have hfloor : (⌊x⌋₊ : ℝ) ≤ x := Nat.floor_le hx + have hx_eq : + x = + (blocks : ℝ) * + ((((p.natDegree + order - 1 : ℕ) : ℝ) * |z.arg|) / + Real.pi) := by + dsimp [x] + push_cast + ring + rw [hx_eq] at hcast hfloor + dsimp [gap] at hlarge + push_cast at hcast + linarith /-- Conditional Karlin finite-order sector estimate for one complex root, with the classical sign-variation input supplied explicitly. -/ From 35d1c841b04f4c39015618382ed6d3f76a17abf1 Mon Sep 17 00:00:00 2001 From: Per Alexandersson Date: Mon, 3 Aug 2026 06:53:09 +0000 Subject: [PATCH 126/141] clean repeated block sector proof --- RealRooted/ASWKarlinKernel.lean | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/RealRooted/ASWKarlinKernel.lean b/RealRooted/ASWKarlinKernel.lean index 4707fe0b..e4e6555e 100644 --- a/RealRooted/ASWKarlinKernel.lean +++ b/RealRooted/ASWKarlinKernel.lean @@ -238,10 +238,10 @@ theorem aswKarlinSectorThreshold_le_abs_arg_of_im_ne_zero_of_classicalInput /-- Karlin's finite-order sector estimate for a nonreal complex root of a positive constant-coefficient PF polynomial. -The checked algebraic inputs above provide the repeated totally nonnegative -coefficient-window matrix, its full row rank from the positive constant -coefficient, and the root-supplied kernel vector. The remaining hard ingredient -is the classical variation-diminishing/sign-regular kernel theorem. -/ +The proof uses the repeated totally nonnegative coefficient-window matrices, +their full row rank, the root-supplied kernel vectors, and the sampled-sine +floor bound. Taking sufficiently many blocks absorbs the fixed two-endpoint +perturbation loss. -/ theorem aswKarlinSectorThreshold_le_abs_arg_of_im_ne_zero {p : ℝ[X]} {z : ℂ} (hz : z ∈ (p.map (algebraMap ℝ ℂ)).roots) (hdegree : 0 < p.natDegree) (hconst : 0 < p.coeff 0) @@ -297,15 +297,12 @@ theorem aswKarlinSectorThreshold_le_abs_arg_of_im_ne_zero {p : ℝ[X]} {z : ℂ} let x : ℝ := (((blocks * (p.natDegree + order - 1) : ℕ) : ℝ) * |z.arg|) / Real.pi - have hx : 0 ≤ x := by - dsimp [x] - positivity have hnat : blocks * order ≤ ⌊x⌋₊ + 3 := by dsimp [x] lia have hcast : ((blocks * order : ℕ) : ℝ) ≤ (⌊x⌋₊ : ℝ) + 3 := by exact_mod_cast hnat - have hfloor : (⌊x⌋₊ : ℝ) ≤ x := Nat.floor_le hx + have hfloor : (⌊x⌋₊ : ℝ) ≤ x := Nat.floor_le (by dsimp [x]; positivity) have hx_eq : x = (blocks : ℝ) * From cf2237841bc1c6c4059c97966a8cf7ef0a54355e Mon Sep 17 00:00:00 2001 From: Per Alexandersson Date: Mon, 3 Aug 2026 06:59:00 +0000 Subject: [PATCH 127/141] Remove admitted ASW backend dependency --- RealRooted/ASWKarlinKernel.lean | 10 +++++++--- RealRooted/ASWKarlinVariation.lean | 25 ------------------------- RealRooted/AissenSchoenbergWhitney.lean | 9 ++++++--- 3 files changed, 13 insertions(+), 31 deletions(-) diff --git a/RealRooted/ASWKarlinKernel.lean b/RealRooted/ASWKarlinKernel.lean index e4e6555e..d69ee433 100644 --- a/RealRooted/ASWKarlinKernel.lean +++ b/RealRooted/ASWKarlinKernel.lean @@ -339,8 +339,12 @@ theorem aswKarlinSectorThreshold_le_abs_arg {p : ℝ[X]} {z : ℂ} (hz : z ∈ (p.map (algebraMap ℝ ℂ)).roots) (hdegree : 0 < p.natDegree) (hconst : 0 < p.coeff 0) (hpf : IsPolyaFreqSeq p.coeff) {order : ℕ} (horder : 0 < order) : - aswSectorThreshold p.natDegree order ≤ |z.arg| := - aswKarlinSectorThreshold_le_abs_arg_of_classicalInput - aswKarlinKernelSignVariationClassicalInput hz hdegree hconst hpf horder + aswSectorThreshold p.natDegree order ≤ |z.arg| := by + by_cases him : z.im = 0 + · rw [arg_eq_pi_of_real_complex_root_of_isPolyaFreqSeq_coeff hz hconst hpf him, + abs_of_pos Real.pi_pos] + exact aswSectorThreshold_le_pi p.natDegree order hdegree horder + · exact aswKarlinSectorThreshold_le_abs_arg_of_im_ne_zero + hz hdegree hconst hpf horder him end RealRooted diff --git a/RealRooted/ASWKarlinVariation.lean b/RealRooted/ASWKarlinVariation.lean index bc077c24..6c5c16ea 100644 --- a/RealRooted/ASWKarlinVariation.lean +++ b/RealRooted/ASWKarlinVariation.lean @@ -63,31 +63,6 @@ theorem IsPolyaFreqSeq.aswKarlinKernelSignVariationLowerBound_of_classicalInput exact hclassical hdegree horder hconst hlead hsupport htn hsurj hker hvec_ne -/-- Remaining classical sign-regular kernel lower bound for a full-row-rank -totally nonnegative one-block Karlin coefficient-window matrix. - -This is the only remaining non-elementary input in the current Karlin sector -proof. It should eventually be proved from the specialized sign-regular -variation-diminishing theorem for a full-row-rank totally nonnegative Toeplitz -window matrix. -/ -theorem aswKarlinKernelSignVariationClassicalInput : - AswKarlinKernelSignVariationClassicalInputStatement := by - intro u degree order hdegree horder hconst hlead hsupport htn hsurj v hker hvec_ne - -- Remaining classical step: use `htn`, `hsurj`, the endpoint/support data, - -- `hker`, and `hvec_ne` to prove the kernel sign-variation lower bound. - sorry - -/-- Classical sign-regular kernel lower bound for a PF one-block Karlin -coefficient-window matrix. -/ -theorem IsPolyaFreqSeq.aswKarlinKernelSignVariationLowerBound - {u : ℕ → ℝ} (hpf : IsPolyaFreqSeq u) (degree order : ℕ) - (hdegree : 0 < degree) (horder : 0 < order) (hconst : 0 < u 0) - (hlead : 0 < u degree) (hsupport : ∀ k, degree < k → u k = 0) : - AswKarlinKernelSignVariationLowerBound degree order u := by - exact hpf.aswKarlinKernelSignVariationLowerBound_of_classicalInput - aswKarlinKernelSignVariationClassicalInput degree order hdegree horder - hconst hlead hsupport - /-- The final sector inequality follows once the two sign-variation bounds are available: a lower bound from the full-row-rank TN kernel theorem and an upper bound for the sampled sine vector inside the forbidden sector. -/ diff --git a/RealRooted/AissenSchoenbergWhitney.lean b/RealRooted/AissenSchoenbergWhitney.lean index 6aded912..f4c44b77 100644 --- a/RealRooted/AissenSchoenbergWhitney.lean +++ b/RealRooted/AissenSchoenbergWhitney.lean @@ -301,9 +301,12 @@ theorem aswSectorThreshold_le_abs_arg_of_isPolyaFreqSeq_coeff {p : ℝ[X]} {z : (hdegree : 0 < p.natDegree) (hconst : 0 < p.coeff 0) (hpf : IsPolyaFreqSeq p.coeff) (hz : z ∈ (p.map (algebraMap ℝ ℂ)).roots) (order : ℕ) : - aswSectorThreshold p.natDegree order ≤ |z.arg| := - aswSectorThreshold_le_abs_arg_of_isPolyaFreqSeq_coeff_of_classicalInput - aswKarlinKernelSignVariationClassicalInput hdegree hconst hpf hz order + aswSectorThreshold p.natDegree order ≤ |z.arg| := by + by_cases horder : order = 0 + · simp [aswSectorThreshold, horder] + · exact aswKarlinSectorThreshold_le_abs_arg + (p := p) (z := z) hz hdegree hconst hpf + (horder := Nat.pos_of_ne_zero horder) /-! ### Reduction to positive constant coefficient -/ From e4aa95037088f83dff7d5868e3a61c1ff42012f9 Mon Sep 17 00:00:00 2001 From: Per Alexandersson Date: Mon, 3 Aug 2026 07:04:18 +0000 Subject: [PATCH 128/141] Fix Fin order elaboration on Lean 4.31 --- RealRooted/Mathlib/Data/Fin/Basic.lean | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/RealRooted/Mathlib/Data/Fin/Basic.lean b/RealRooted/Mathlib/Data/Fin/Basic.lean index ab405638..6fa9625d 100644 --- a/RealRooted/Mathlib/Data/Fin/Basic.lean +++ b/RealRooted/Mathlib/Data/Fin/Basic.lean @@ -54,7 +54,9 @@ neighbor. -/ theorem succAbove_center_eq_right (i : Fin n) : (i.succ.castSucc.castSucc).succAbove i.succ.castSucc = i.succ.succ.castSucc := by - rw [Fin.succAbove_of_le_castSucc _ _ (by rfl)] + rw [Fin.succAbove_of_le_castSucc _ _ (by + change (i : ℕ) + 1 ≤ (i : ℕ) + 1 + exact Nat.le_refl _)] congr /-- Omitting the old right neighbor sends the corresponding new center to its old value. -/ From 8ad9e9224083cd6f571777371a33a9b43ea09e86 Mon Sep 17 00:00:00 2001 From: Per Alexandersson Date: Mon, 3 Aug 2026 07:10:17 +0000 Subject: [PATCH 129/141] Define finite sign variations before use --- .../Mathlib/LinearAlgebra/Matrix/SignVariation.lean | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariation.lean b/RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariation.lean index 4aab836a..6c6bc7dd 100644 --- a/RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariation.lean +++ b/RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariation.lean @@ -590,6 +590,12 @@ theorem SignType.sign_ne_zero_and_ne_of_mul_neg namespace Fin +/-- The number of sign changes in a finite vector, in index order and ignoring +zero entries. -/ +def signVariations {R : Type*} [Zero R] [LinearOrder R] {n : ℕ} + (x : Fin n → R) : ℕ := + (List.ofFn x).signVariations + /-- A finite real vector has the same variation count as its explicit sign list. -/ theorem signVariations_eq_signList {n : ℕ} (x : Fin n → ℝ) : Fin.signVariations x = @@ -1056,12 +1062,6 @@ theorem nodalInsertions_coreSigns rw [Fin.nodalPerturbationCoreSigns_eq_of_no_interior_zero hsign hinterior] -/-- The number of sign changes in a finite vector, in index order and ignoring -zero entries. -/ -def signVariations {R : Type*} [Zero R] [LinearOrder R] {n : ℕ} - (x : Fin n → R) : ℕ := - (List.ofFn x).signVariations - /-- A sign-preserving perturbation increases variations by at most two when all interior zeros are nodal. From 1e25bb7108f36a4e1fae16e314a5c2d280fb8b0a Mon Sep 17 00:00:00 2001 From: Per Alexandersson Date: Mon, 3 Aug 2026 07:16:00 +0000 Subject: [PATCH 130/141] Remove unused sign variation simp input --- RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariation.lean | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariation.lean b/RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariation.lean index 6c6bc7dd..0f278dd6 100644 --- a/RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariation.lean +++ b/RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariation.lean @@ -382,7 +382,7 @@ theorem List.signVariations_append_singleton_signType_le_succ ((l.map SignType.sign).filter (· ≠ 0)) (SignType.sign a) by_cases ha : SignType.sign a = 0 · simp [List.signVariations, ha] - · simp [List.signVariations, ha] + · simp [List.signVariations] lia /-- Inserting any sign between opposite nonzero signs does not change sign variations. From 5cccf52b8633647acabfd02ab329c2d7c777e6d2 Mon Sep 17 00:00:00 2001 From: Per Alexandersson Date: Mon, 3 Aug 2026 07:18:17 +0000 Subject: [PATCH 131/141] Add unconditional PF polynomial constructors --- RealRooted/PFPolynomial.lean | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/RealRooted/PFPolynomial.lean b/RealRooted/PFPolynomial.lean index c8683993..0e530ec4 100644 --- a/RealRooted/PFPolynomial.lean +++ b/RealRooted/PFPolynomial.lean @@ -187,6 +187,14 @@ theorem of_sequence let hpnn := hasNonnegCoeffs_of_IsPolyaFreqSeq_coeff hpf ⟨hpnn, hASW hpnn hpf⟩ +/-- Construct a PF polynomial directly from its Pólya-frequency coefficient +sequence using the proved forward ASW theorem. -/ +theorem of_polyaFreqSeq {p : ℝ[X]} + (hpf : IsPolyaFreqSeq (fun n => p.coeff n)) : + IsPFPolynomial p := + let hpnn := hasNonnegCoeffs_of_IsPolyaFreqSeq_coeff hpf + ⟨hpnn, aissenSchoenbergWhitneyForwardOrZero hpnn hpf⟩ + /-- Forward-ASW endpoint closure for positive affine coefficient limits. -/ theorem of_forall_pos_add_C_mul_of_forward (hASW : aissenSchoenbergWhitneyForwardOrZeroStatement) @@ -207,6 +215,25 @@ theorem splits_of_forall_pos_add_C_mul_of_forward p.Splits := (of_forall_pos_add_C_mul_of_forward hASW hpnn hqnn hfamily).ne_zero_and_splits hp0 |>.2 +/-- PF endpoint closure for positive affine coefficient limits, using the +proved forward ASW theorem. -/ +theorem of_forall_pos_add_C_mul + {p q : ℝ[X]} + (hpnn : HasNonnegCoeffs p) (hqnn : HasNonnegCoeffs q) + (hfamily : ∀ {μ : ℝ}, 0 < μ → (p + C μ * q).Splits) : + IsPFPolynomial p := + IsPFPolynomial.of_polyaFreqSeq <| + IsPolyaFreqSeq.of_forall_pos_add_C_mul_splits hpnn hqnn hfamily + +/-- Splitting form of `IsPFPolynomial.of_forall_pos_add_C_mul`. -/ +theorem splits_of_forall_pos_add_C_mul + {p q : ℝ[X]} + (hp0 : p ≠ 0) + (hpnn : HasNonnegCoeffs p) (hqnn : HasNonnegCoeffs q) + (hfamily : ∀ {μ : ℝ}, 0 < μ → (p + C μ * q).Splits) : + p.Splits := + (of_forall_pos_add_C_mul hpnn hqnn hfamily).ne_zero_and_splits hp0 |>.2 + theorem to_sequence {p : ℝ[X]} (hp : IsPFPolynomial p) : From 98e0e60051b20348dc2d399a2e3f41d5440f819b Mon Sep 17 00:00:00 2001 From: Per Alexandersson Date: Mon, 3 Aug 2026 07:20:48 +0000 Subject: [PATCH 132/141] Add argument-free ASW downstream wrappers --- RealRooted/CommonInterleaver/SuccDegreeEndpoint.lean | 12 ++++++++++++ RealRooted/Hadamard.lean | 9 +++++++++ 2 files changed, 21 insertions(+) diff --git a/RealRooted/CommonInterleaver/SuccDegreeEndpoint.lean b/RealRooted/CommonInterleaver/SuccDegreeEndpoint.lean index 6f9c7672..2d6313d6 100644 --- a/RealRooted/CommonInterleaver/SuccDegreeEndpoint.lean +++ b/RealRooted/CommonInterleaver/SuccDegreeEndpoint.lean @@ -251,6 +251,18 @@ theorem PosComboRealRooted.left_splits_of_forward_asw hASW hf_pos.ne_zero hfnn hgnn fun {_} hμ => (hfg.isRealRooted_add_right hμ).2 +/-- The succ-degree left endpoint from the proved forward ASW theorem, with no +backend argument required from the caller. -/ +theorem PosComboRealRooted.left_splits_of_asw + {f g : ℝ[X]} + (hfg : PosComboRealRooted f g) + (hf_pos : HasPosLeadingCoeff f) + (hfnn : HasNonnegCoeffs f) (hgnn : HasNonnegCoeffs g) : + f.Splits := + IsPFPolynomial.splits_of_forall_pos_add_C_mul + hf_pos.ne_zero hfnn hgnn + fun {_} hμ => (hfg.isRealRooted_add_right hμ).2 + /-- Conditional package form of `PosComboRealRooted.left_splits_of_forward_asw` for the milestone-B2 endpoint statement. -/ theorem PosComboSuccDegreeLeftSplitsNonnegStatement_of_forward_asw diff --git a/RealRooted/Hadamard.lean b/RealRooted/Hadamard.lean index 84e61573..2386ff78 100644 --- a/RealRooted/Hadamard.lean +++ b/RealRooted/Hadamard.lean @@ -3526,6 +3526,15 @@ theorem polyaFrequencyHadamardCoeff_of_schurPolyaWagner (hSPW (IsPFPolynomial.of_sequence hASW hp) (IsPFPolynomial.of_sequence hASW hq)).to_sequence +/-- Polynomial-coefficient Pólya-frequency closure from Schur--Pólya--Wagner, +using the proved forward ASW theorem rather than a caller-supplied backend. -/ +theorem polyaFrequencyHadamardCoeff_of_schurPolyaWagner_asw + (hSPW : schurPolyaWagnerHadamardPFStatement) : + polyaFrequencyHadamardCoeffStatement := + fun hp hq => + (hSPW (IsPFPolynomial.of_polyaFreqSeq hp) + (IsPFPolynomial.of_polyaFreqSeq hq)).to_sequence + theorem polyaFrequencyHadamardCoeff_of_garloffWagner_prec0 (hASW : aissenSchoenbergWhitneyForwardOrZeroStatement) (hGW : garloffWagnerHadamardPFPrec0Statement) : From 64787cd67d6aa6a1be4625131945c0a99e600d71 Mon Sep 17 00:00:00 2001 From: Per Alexandersson Date: Mon, 3 Aug 2026 07:22:35 +0000 Subject: [PATCH 133/141] Golf filtered sign variation bridge --- RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariation.lean | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariation.lean b/RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariation.lean index 0f278dd6..711af733 100644 --- a/RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariation.lean +++ b/RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariation.lean @@ -606,8 +606,8 @@ theorem signVariations_eq_signList {n : ℕ} (x : Fin n → ℝ) : theorem filtered_signList_signVariations {n : ℕ} (x : Fin n → ℝ) : ((List.ofFn (SignType.sign ∘ x)).filter (· ≠ 0)).signVariations = Fin.signVariations x := by - rw [List.signVariations_filter_ne_zero] - exact (Fin.signVariations_eq_signList x).symm + simpa only [List.signVariations_filter_ne_zero] using + (Fin.signVariations_eq_signList x).symm /-- Perturbed signs at all interior coordinates and at the original nonzero endpoints. From 377fe065d7cc783b9d9446af3319a482fc3bcc57 Mon Sep 17 00:00:00 2001 From: Per Alexandersson Date: Mon, 3 Aug 2026 07:24:17 +0000 Subject: [PATCH 134/141] Add unconditional ASW pair bridge endpoints --- RealRooted/CommonInterleaver/PairBridge.lean | 16 ++++++++++++++++ .../CommonInterleaver/SuccDegreeEndpoint.lean | 7 +++++++ 2 files changed, 23 insertions(+) diff --git a/RealRooted/CommonInterleaver/PairBridge.lean b/RealRooted/CommonInterleaver/PairBridge.lean index d6a2178e..c19fb499 100644 --- a/RealRooted/CommonInterleaver/PairBridge.lean +++ b/RealRooted/CommonInterleaver/PairBridge.lean @@ -1126,6 +1126,14 @@ theorem posComboNoCommonSuccDegreeSlotData_of_forward_asw_and_rootCrossing posComboNoCommonSuccDegreeSlotData_of_leftSplits_and_rootCrossing (PosComboSuccDegreeLeftSplitsNonnegStatement_of_forward_asw hASW) hcross +/-- Succ-degree slot data from the proved ASW left endpoint and the +descending-root crossing inequalities. -/ +theorem posComboNoCommonSuccDegreeSlotData_of_asw_and_rootCrossing + (hcross : PosComboNoCommonSuccDegreeRootCrossingNonnegStatement) : + PosComboNoCommonSuccDegreeSlotDataNonnegStatement := + posComboNoCommonSuccDegreeSlotData_of_leftSplits_and_rootCrossing + PosComboSuccDegreeLeftSplitsNonnegStatement_of_asw hcross + /-- Succ-degree slot data from the splitting-only ASW target and the root-crossing target. -/ theorem posComboNoCommonSuccDegreeSlotData_of_forward_asw_splits_and_rootCrossing @@ -1145,6 +1153,14 @@ theorem succDegreePairHasCommonInterleaver_nonneg_of_leftSplits_and_rootCrossing (PosComboSuccDegreeLeftSplitsNonnegStatement_of_forward_asw hASW) hcross +/-- Succ-degree pair interleavers from the proved ASW left endpoint and the +descending-root crossing inequalities. -/ +theorem succDegreePairHasCommonInterleaver_nonneg_of_asw_and_rootCrossing + (hcross : PosComboNoCommonSuccDegreeRootCrossingNonnegStatement) : + PosComboNoCommonSuccDegreePairHasCommonInterleaverNonnegStatement := + succDegreePairHasCommonInterleaver_nonneg_of_leftSplits_and_rootCrossing + PosComboSuccDegreeLeftSplitsNonnegStatement_of_asw hcross + /-- Succ-degree pair interleavers from the splitting-only ASW target and the root-crossing target. -/ theorem diff --git a/RealRooted/CommonInterleaver/SuccDegreeEndpoint.lean b/RealRooted/CommonInterleaver/SuccDegreeEndpoint.lean index 2d6313d6..cd1af54c 100644 --- a/RealRooted/CommonInterleaver/SuccDegreeEndpoint.lean +++ b/RealRooted/CommonInterleaver/SuccDegreeEndpoint.lean @@ -263,6 +263,13 @@ theorem PosComboRealRooted.left_splits_of_asw hf_pos.ne_zero hfnn hgnn fun {_} hμ => (hfg.isRealRooted_add_right hμ).2 +/-- Unconditional package form of `PosComboRealRooted.left_splits_of_asw` for +the milestone-B2 endpoint statement. -/ +theorem PosComboSuccDegreeLeftSplitsNonnegStatement_of_asw : + PosComboSuccDegreeLeftSplitsNonnegStatement := by + intro f g hf_pos _ hfnn hgnn hfg _ + exact hfg.left_splits_of_asw hf_pos hfnn hgnn + /-- Conditional package form of `PosComboRealRooted.left_splits_of_forward_asw` for the milestone-B2 endpoint statement. -/ theorem PosComboSuccDegreeLeftSplitsNonnegStatement_of_forward_asw From 32c134b687b4d5f9a6c63bb8f78cced15144209b Mon Sep 17 00:00:00 2001 From: Per Alexandersson Date: Mon, 3 Aug 2026 07:47:15 +0000 Subject: [PATCH 135/141] fix: close sign-variation bounds directly --- RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariation.lean | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariation.lean b/RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariation.lean index 711af733..f0ff4bcc 100644 --- a/RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariation.lean +++ b/RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariation.lean @@ -371,8 +371,7 @@ theorem List.signVariations_cons_le_succ (a : SignType) (l : List SignType) : ((l.map SignType.sign).filter (· ≠ 0)) by_cases ha : SignType.sign a = 0 · simp [List.signVariations, ha] - · simp [List.signVariations, ha] - lia + · simpa [List.signVariations, ha] using h /-- Appending one sign increases the number of sign variations by at most one. -/ theorem List.signVariations_append_singleton_signType_le_succ @@ -382,8 +381,7 @@ theorem List.signVariations_append_singleton_signType_le_succ ((l.map SignType.sign).filter (· ≠ 0)) (SignType.sign a) by_cases ha : SignType.sign a = 0 · simp [List.signVariations, ha] - · simp [List.signVariations] - lia + · simpa [List.signVariations, ha] using h /-- Inserting any sign between opposite nonzero signs does not change sign variations. From 461659d4537ce8965fd7a5896cdaf32fea2c9f35 Mon Sep 17 00:00:00 2001 From: Per Alexandersson Date: Mon, 3 Aug 2026 07:58:28 +0000 Subject: [PATCH 136/141] fix: handle truncated sign-variation counts --- .../Mathlib/LinearAlgebra/Matrix/SignVariation.lean | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariation.lean b/RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariation.lean index f0ff4bcc..cea1ee53 100644 --- a/RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariation.lean +++ b/RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariation.lean @@ -364,6 +364,10 @@ end List theorem SignType.sign_sign (s : SignType) : SignType.sign s = s := by fin_cases s <;> rfl +private lemma add_one_le_pred_add_two (n : ℕ) : + n + 1 ≤ n - 1 + 1 + 1 := by + cases n <;> simp + /-- Prepending one sign increases the number of sign variations by at most one. -/ theorem List.signVariations_cons_le_succ (a : SignType) (l : List SignType) : (a :: l).signVariations ≤ l.signVariations + 1 := by @@ -371,7 +375,8 @@ theorem List.signVariations_cons_le_succ (a : SignType) (l : List SignType) : ((l.map SignType.sign).filter (· ≠ 0)) by_cases ha : SignType.sign a = 0 · simp [List.signVariations, ha] - · simpa [List.signVariations, ha] using h + · simp [List.signVariations, ha] + exact h.trans (add_one_le_pred_add_two _) /-- Appending one sign increases the number of sign variations by at most one. -/ theorem List.signVariations_append_singleton_signType_le_succ @@ -381,7 +386,8 @@ theorem List.signVariations_append_singleton_signType_le_succ ((l.map SignType.sign).filter (· ≠ 0)) (SignType.sign a) by_cases ha : SignType.sign a = 0 · simp [List.signVariations, ha] - · simpa [List.signVariations, ha] using h + · simp [List.signVariations, ha] + exact h.trans (add_one_le_pred_add_two _) /-- Inserting any sign between opposite nonzero signs does not change sign variations. From c25e4545e5eb07c366d674ddc432f22164d4e126 Mon Sep 17 00:00:00 2001 From: Per Alexandersson Date: Mon, 3 Aug 2026 08:08:16 +0000 Subject: [PATCH 137/141] Fix sign variation helper normalization --- .../LinearAlgebra/Matrix/SignVariation.lean | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariation.lean b/RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariation.lean index cea1ee53..df0544fa 100644 --- a/RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariation.lean +++ b/RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariation.lean @@ -371,23 +371,21 @@ private lemma add_one_le_pred_add_two (n : ℕ) : /-- Prepending one sign increases the number of sign variations by at most one. -/ theorem List.signVariations_cons_le_succ (a : SignType) (l : List SignType) : (a :: l).signVariations ≤ l.signVariations + 1 := by - have h := List.length_destutter_cons_ne_le_succ (SignType.sign a) + have h := List.length_destutter_cons_ne_le_succ a ((l.map SignType.sign).filter (· ≠ 0)) - by_cases ha : SignType.sign a = 0 + by_cases ha : a = 0 · simp [List.signVariations, ha] - · simp [List.signVariations, ha] - exact h.trans (add_one_le_pred_add_two _) + · simpa [List.signVariations, ha] using h.trans (add_one_le_pred_add_two _) /-- Appending one sign increases the number of sign variations by at most one. -/ theorem List.signVariations_append_singleton_signType_le_succ (l : List SignType) (a : SignType) : (l ++ [a]).signVariations ≤ l.signVariations + 1 := by have h := List.length_destutter_append_singleton_ne_le_succ - ((l.map SignType.sign).filter (· ≠ 0)) (SignType.sign a) - by_cases ha : SignType.sign a = 0 - · simp [List.signVariations, ha] + ((l.map SignType.sign).filter (· ≠ 0)) a + by_cases ha : a = 0 · simp [List.signVariations, ha] - exact h.trans (add_one_le_pred_add_two _) + · simpa [List.signVariations, ha] using h.trans (add_one_le_pred_add_two _) /-- Inserting any sign between opposite nonzero signs does not change sign variations. From c29af69fcbd398cee4e4377ae2bf6c2bb7c04f53 Mon Sep 17 00:00:00 2001 From: Per Alexandersson Date: Mon, 3 Aug 2026 08:16:36 +0000 Subject: [PATCH 138/141] Fix rank-deficient theorem doc placement --- .../LinearAlgebra/Matrix/SignRegularRankDeficient.lean | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/RealRooted/Mathlib/LinearAlgebra/Matrix/SignRegularRankDeficient.lean b/RealRooted/Mathlib/LinearAlgebra/Matrix/SignRegularRankDeficient.lean index aa13e237..25c3ff92 100644 --- a/RealRooted/Mathlib/LinearAlgebra/Matrix/SignRegularRankDeficient.lean +++ b/RealRooted/Mathlib/LinearAlgebra/Matrix/SignRegularRankDeficient.lean @@ -545,12 +545,12 @@ theorem Matrix.IsSignConsistentOrder.signVariations_mulVec_le_rank_sub_one_of_in have hlower := halt_delete.le_signVariations_of_strictMono hrows lia +open Filter Topology + /-- Karlin's rank-sensitive variation bound, proved by induction on the number of columns. Rank zero gives the zero linear map, full column rank uses the injective variation bound, and positive deficient rank uses the rank-preserving deletion step. -/ - -open Filter Topology theorem Matrix.IsSignConsistentOrder.signVariations_mulVec_le_rank_sub_one {n m r : ℕ} {A : Matrix (Fin n) (Fin m) ℝ} From f3a38a0d6269de9f8d4089e50015bffd0b230f0f Mon Sep 17 00:00:00 2001 From: Per Alexandersson Date: Mon, 3 Aug 2026 08:27:42 +0000 Subject: [PATCH 139/141] Avoid determinant product simp timeout --- .../LinearAlgebra/Matrix/SignRegularRankDeficient.lean | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/RealRooted/Mathlib/LinearAlgebra/Matrix/SignRegularRankDeficient.lean b/RealRooted/Mathlib/LinearAlgebra/Matrix/SignRegularRankDeficient.lean index 25c3ff92..debb5ae4 100644 --- a/RealRooted/Mathlib/LinearAlgebra/Matrix/SignRegularRankDeficient.lean +++ b/RealRooted/Mathlib/LinearAlgebra/Matrix/SignRegularRankDeficient.lean @@ -597,6 +597,13 @@ theorem Matrix.IsSignConsistentOrder.signVariations_mulVec_le_rank_sub_one exact hA.signVariations_mulVec_le_rank_sub_one_of_induction hrank hrlt hrpos ih c +private lemma mul_mul_nonneg_of_cross_mul_nonneg + {a b c d : ℝ} (hac : 0 ≤ a * c) (hbd : 0 ≤ b * d) : + 0 ≤ (a * b) * (c * d) := by + calc + 0 ≤ (a * c) * (b * d) := mul_nonneg hac hbd + _ = (a * b) * (c * d) := by ring + /-- Right multiplication by a totally nonnegative rectangular matrix preserves the common weak sign of ordered minors of a fixed size. Cauchy--Binet expands the product of two output minors as a double sum; sign consistency controls the @@ -634,8 +641,7 @@ theorem Matrix.IsSignConsistentOrder.mul_of_right_isTotallyNonnegRect mul_nonneg (hA es.strictMono hcols) (hA et.strictMono hcols') - simpa only [es, et, mul_assoc, mul_left_comm, mul_comm] using - mul_nonneg hsign hright + exact mul_mul_nonneg_of_cross_mul_nonneg hsign hright /-- The weighted incidence matrix associated with a block map. -/ def Matrix.weightedIncidence From 691e2882dee9860d65ac163807487eb20331f4b8 Mon Sep 17 00:00:00 2001 From: Per Alexandersson Date: Wed, 5 Aug 2026 10:01:27 +0000 Subject: [PATCH 140/141] Replace omega in sign variation proof --- RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariation.lean | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariation.lean b/RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariation.lean index df0544fa..3542ae72 100644 --- a/RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariation.lean +++ b/RealRooted/Mathlib/LinearAlgebra/Matrix/SignVariation.lean @@ -1315,7 +1315,7 @@ lemma exists_strictMono_strictlyAlternates_of_le_signVariations have hh : q ≤ d.length - 1 := by simpa [signVariations, List.signVariations, d, nz, raw, List.map_ofFn] using h - have hlen : q + 1 ≤ d.length := by omega + have hlen : q + 1 ≤ d.length := by lia have hsub : d.Sublist raw := (List.destutter_sublist (R := fun a b : SignType => a ≠ b) nz).trans List.filter_sublist @@ -1332,7 +1332,7 @@ lemma exists_strictMono_strictlyAlternates_of_le_signVariations · intro i let k0 : Fin d.length := Fin.castLE hlen i.castSucc let k1 : Fin d.length := Fin.castLE hlen i.succ - have hklt : (i : ℕ) + 1 < d.length := by omega + have hklt : (i : ℕ) + 1 < d.length := by lia have hchain : d.IsChain (· ≠ ·) := List.isChain_destutter (R := fun a b : SignType => a ≠ b) nz have hkd : d.get k0 ≠ d.get k1 := by From 64426cced4bb034fd3232ba1074319fb88abf8ed Mon Sep 17 00:00:00 2001 From: Per Alexandersson Date: Wed, 5 Aug 2026 10:14:24 +0000 Subject: [PATCH 141/141] Clean Karlin matrix proof warnings --- .../LinearAlgebra/Matrix/Determinant/Basic.lean | 11 +++++++---- .../LinearAlgebra/Matrix/Determinant/Integral.lean | 4 ++-- RealRooted/Mathlib/LinearAlgebra/Matrix/Gaussian.lean | 3 +-- 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/RealRooted/Mathlib/LinearAlgebra/Matrix/Determinant/Basic.lean b/RealRooted/Mathlib/LinearAlgebra/Matrix/Determinant/Basic.lean index 66ef1195..84b4aa3a 100644 --- a/RealRooted/Mathlib/LinearAlgebra/Matrix/Determinant/Basic.lean +++ b/RealRooted/Mathlib/LinearAlgebra/Matrix/Determinant/Basic.lean @@ -56,10 +56,13 @@ theorem det_eq_det_adjacentRowDiff_of_firstColumn_eq_one {n : ℕ} · intro i j simp [B] rw [hdet, det_succ_column_zero, Fin.sum_univ_succ] - simp [B, hA] - apply congrArg det - ext i j - rfl + have hminor : + B.submatrix Fin.succ Fin.succ = + Matrix.of fun (i j : Fin n) => + A i.succ j.succ - A i.castSucc j.succ := by + ext i j + rfl + simpa [B, hA] using congrArg Matrix.det hminor /-- A submatrix with a noninjective column selector has zero determinant. -/ theorem det_submatrix_eq_zero_of_not_injective_right diff --git a/RealRooted/Mathlib/LinearAlgebra/Matrix/Determinant/Integral.lean b/RealRooted/Mathlib/LinearAlgebra/Matrix/Determinant/Integral.lean index 8add6f6d..bafbebc0 100644 --- a/RealRooted/Mathlib/LinearAlgebra/Matrix/Determinant/Integral.lean +++ b/RealRooted/Mathlib/LinearAlgebra/Matrix/Determinant/Integral.lean @@ -54,7 +54,7 @@ theorem integrable_det_rows simp_rw [Units.smul_def, ← Int.cast_smul_eq_zsmul ℝ] rfl rw [hfun] - apply MeasureTheory.integrable_finset_sum Finset.univ + apply MeasureTheory.integrable_finsetSum Finset.univ intro σ _ exact (MeasureTheory.Integrable.fintype_prod fun i => hf i (σ i)).const_mul _ @@ -99,7 +99,7 @@ theorem det_integral_rows_eq_integral_det ((Equiv.Perm.sign σ : ℤ) : ℝ) * ∏ i, f i (x i) (σ i) ∂MeasureTheory.Measure.pi μ := by symm - apply MeasureTheory.integral_finset_sum + apply MeasureTheory.integral_finsetSum intro σ _ exact (hprod σ).const_mul _ _ = ∫ x : n → E, diff --git a/RealRooted/Mathlib/LinearAlgebra/Matrix/Gaussian.lean b/RealRooted/Mathlib/LinearAlgebra/Matrix/Gaussian.lean index 20ccd282..9e30695c 100644 --- a/RealRooted/Mathlib/LinearAlgebra/Matrix/Gaussian.lean +++ b/RealRooted/Mathlib/LinearAlgebra/Matrix/Gaussian.lean @@ -347,8 +347,7 @@ theorem det_adjacentRowDiff_exponentialKernelMatrix_pos {n : ℕ} measure_congr hsupp, Measure.pi_univ] rw [pos_iff_ne_zero, Finset.prod_ne_zero_iff] intro i _ - simp [μ, Real.volume_Ioc] - exact hx i.castSucc_lt_succ + simpa [μ, Real.volume_Ioc] using hx i.castSucc_lt_succ /-- Strictly ordered exponential-kernel minors are positive. -/ theorem det_exponentialKernelMatrix_pos {q : ℕ}