Zulip Chat Archive
Stream: new members
Topic: Exclude zero terms from the sum
jsodd (Aug 14 2024 at 06:46):
I've been trying to solve the problem suggested recently here:
Eric Wieser said:
Here's the statement, in case anyone feels like being sniped:
import Mathlib variable {m n R} [CommRing R] [StarRing R] [PartialOrder R] [StarOrderedRing R] /-- https://en.wikipedia.org/wiki/Sylvester%27s_criterion -/ theorem posDef_fin_iff_det {A : Matrix (Fin n) (Fin n) R} : A.PosDef ↔ ∀ i (h : i ≤ n), 0 < (A.submatrix (Fin.castLE h) (Fin.castLE h)).det := -- this is the interesting mathematical result sorry
It turned out that even the "easy" direction of Sylvester's criterion is hard for me to formalise. I decided to start with a preliminary lemma showing that A.PosDef
implies that the minors of A
are posdef
lemma posDef_minor {A : Matrix (Fin n) (Fin n) R} :
A.PosDef → ∀ i (h : i ≤ n), (A.submatrix (Fin.castLE h) (Fin.castLE h)).PosDef := by
intro h₀ i h₁
constructor
· exact Matrix.IsHermitian.submatrix (h₀.1) (Fin.castLE h₁)
· intro x hx
let y : Fin n → R := λ j ↦ if h : j < i then x ⟨j, h⟩ else 0
-- prove that y A y = x A' x and use y A y > 0
sorry
I have no idea how to approach this. I've tried using Mathlib lemmas, such as this one
theorem submatrix_mulVec_equiv [Fintype n] [Fintype o] [NonUnitalNonAssocSemiring α]
(M : Matrix m n α) (v : o → α) (e₁ : l → m) (e₂ : o ≃ n) :
M.submatrix e₁ e₂ *ᵥ v = (M *ᵥ (v ∘ e₂.symm)) ∘ e₁ :=
funext fun _ => Eq.symm (dotProduct_comp_equiv_symm _ _ _)
but unfortunately the condition (e₂ : o ≃ n)
is too restrictive to handle the "extension by zero" idea. I've also tried expanding the sum, but then I don't know how to throw away all the zeroes from it...
jsodd (Aug 14 2024 at 10:16):
Here's the code up to a point which I cannot prove:
lemma posDef_minor {A : Matrix (Fin n) (Fin n) R} :
A.PosDef → ∀ i (h : i ≤ n), (A.submatrix (Fin.castLE h) (Fin.castLE h)).PosDef := by
intro h₀ i h₁
constructor
· exact Matrix.IsHermitian.submatrix (h₀.1) (Fin.castLE h₁)
· intro x hx
let y : Fin n → R := λ j ↦ if h : j < i then x ⟨j, h⟩ else 0
have h₂ : y ≠ 0 := by
intro hy
apply hx
ext j
have : y (Fin.castLE h₁ j) = x j := dif_pos j.isLt
rw [← this, hy]
simp only [Pi.zero_apply]
calc
0 < Matrix.dotProduct (star y) (A.mulVec y) := h₀.2 y h₂
_ = Matrix.dotProduct (star x)
((A.submatrix (Fin.castLE h₁) (Fin.castLE h₁)).mulVec x) := by
-- ??
sorry
Eric Wieser (Aug 14 2024 at 10:53):
The key lemma is docs#Finset.sum_filter_add_sum_filter_not, I think. This works, but is a mess:
import Mathlib
variable {m n R} [Fintype m] [Fintype n] [CommRing R] [PartialOrder R] [StarRing R]
lemma Matrix.PosDef.submatrix {A : Matrix m m R} (hA : A.PosDef) (f : n ↪ m):
(A.submatrix f f).PosDef := by
constructor
· exact hA.isHermitian.submatrix f
· intro x hx
let y : m → R := Function.extend f x 0
have hyf (i) : y (f i) = x i := by simp [y, (f.injective.factorsThrough _).extend_apply]
have h₂ : y ≠ 0 := by
intro hy
apply hx
ext j
replace h := congr_fun hy (f j)
simpa [y, (f.injective.factorsThrough _).extend_apply] using h
calc
0 < Matrix.dotProduct (star y) (A.mulVec y) := hA.2 y h₂
_ = Matrix.dotProduct (star x) ((A.submatrix f f).mulVec x) := by
-- ??
simp only [Matrix.dotProduct, Matrix.mulVec]
classical
simp only [← Finset.sum_filter_add_sum_filter_not Finset.univ (· ∈ Set.range f), mul_add, Finset.mul_sum,
Finset.sum_add_distrib]
simp only [Set.mem_range, Finset.univ_filter_exists, Pi.star_apply, Finset.mem_univ,
EmbeddingLike.apply_eq_iff_eq, imp_self, implies_true, Finset.sum_image, hyf,
submatrix_apply]
simp (config := {contextual := true}) [Finset.sum_filter, y, -not_exists]
jsodd (Aug 14 2024 at 10:57):
Wow, thanks a lot! How do you even find all these lemmas! I looked through Loogle and Moogle, but couldn't come up with it.
jsodd (Aug 14 2024 at 11:07):
So, Finset.sum_filter_add_sum_filter_not
is what I was looking for. If I understand correctly, it allows to split a sum in two using some decidable proposition-valued function?
Eric Wieser (Aug 14 2024 at 11:20):
Yep, and classical
makes any such function decidable
Eric Wieser (Aug 15 2024 at 20:38):
@jsodd, are you interested in PRing the above to mathlib?
Eric Wieser (Aug 15 2024 at 20:39):
No need to get the full result you were aiming for straight away, that lemma by itself would be a good one to have
jsodd (Aug 15 2024 at 22:58):
Eric Wieser said:
jsodd, are you interested in PRing the above to mathlib?
Thanks, I'll think about it! In the meantime, I've managed to prove one more lemma for Sylvester's criterion. Here it is:
lemma Matrix.PosDef.submatrix.det_pos {A : Matrix n n 𝕜}
(hA : A.PosDef) (e : m ↪ n) :
0 < (A.submatrix e e).det :=
Matrix.PosDef.det_pos (Matrix.PosDef.submatrix hA e)
def Matrix.toBilin_block₁₁
(A : Matrix (n ⊕ o) (l ⊕ m) R) (x : (n ⊕ o) → R) (y : (l ⊕ m) → R) :=
Matrix.dotProduct (x ∘ Sum.inl) (A.toBlocks₁₁.mulVec (y ∘ Sum.inl))
def Matrix.toBilin_block₁₂
(A : Matrix (n ⊕ o) (l ⊕ m) R) (x : (n ⊕ o) → R) (y : (l ⊕ m) → R) :=
Matrix.dotProduct (x ∘ Sum.inl) (A.toBlocks₁₂.mulVec (y ∘ Sum.inr))
def Matrix.toBilin_block₂₁
(A : Matrix (n ⊕ o) (l ⊕ m) R) (x : (n ⊕ o) → R) (y : (l ⊕ m) → R) :=
Matrix.dotProduct (x ∘ Sum.inr) (A.toBlocks₂₁.mulVec (y ∘ Sum.inl))
def Matrix.toBilin_block₂₂
(A : Matrix (n ⊕ o) (l ⊕ m) R) (x : (n ⊕ o) → R) (y : (l ⊕ m) → R) :=
Matrix.dotProduct (x ∘ Sum.inr) (A.toBlocks₂₂.mulVec (y ∘ Sum.inr))
def Matrix.toBilin_block
(A : Matrix (n ⊕ o) (l ⊕ m) R) :=
A.toBilin_block₁₁ + A.toBilin_block₁₂ + A.toBilin_block₂₁ + A.toBilin_block₂₂
def Matrix.toBilin_block'
(A : Matrix (n ⊕ o) (l ⊕ m) R) (x : (n ⊕ o) → R) (y : (l ⊕ m) → R) :=
A.toBilin_block x y
lemma Matrix.toBilin_block_apply
(A : Matrix (n ⊕ o) (l ⊕ m) R) (x : (n ⊕ o) → R) (y : (l ⊕ m) → R) :
Matrix.dotProduct x (A.mulVec y) = A.toBilin_block x y := by
dsimp only [dotProduct, mulVec, toBilin_block, Pi.add_apply, toBilin_block₁₁,
Function.comp_apply, toBlocks₁₁, of_apply, toBilin_block₁₂, toBlocks₁₂, toBilin_block₂₁,
toBlocks₂₁, toBilin_block₂₂, toBlocks₂₂]
simp only [Fintype.sum_sum_type]
simp only [mul_add, Finset.sum_add_distrib, Finset.mul_sum]
ring
It may also be useful...
jsodd (Aug 16 2024 at 21:26):
Here's my attempt at formalising Sylvester's criterion. A bit messy and I still struggle to close some of the goals, but here it is:
import Mathlib
universe u
variable {m n l o R 𝕜 : Type u}
variable [Fintype m] [Fintype n] [DecidableEq m] [DecidableEq n] [Nonempty n]
variable [Fintype l] [Fintype o] [DecidableEq l] [DecidableEq o]
variable [CommRing R] [PartialOrder R] [StarRing R]
variable [RCLike 𝕜]
open Classical ComplexOrder
lemma Matrix.PosDef.submatrix {A : Matrix n n R} (hA : A.PosDef) (f : m ↪ n):
(A.submatrix f f).PosDef := by
constructor
· exact hA.isHermitian.submatrix f
· intro x hx
let y : n → R := Function.extend f x 0
have hyf : ∀ i, y (f i) = x i := by simp [y, (f.injective.factorsThrough _).extend_apply]
have h₂ : y ≠ 0 := by
intro hy
apply hx
ext j
replace h := congr_fun hy (f j)
simpa [y, (f.injective.factorsThrough _).extend_apply] using h
calc
0 < Matrix.dotProduct (star y) (A.mulVec y) := hA.2 y h₂
_ = Matrix.dotProduct (star x) ((A.submatrix f f).mulVec x) := by
simp only [Matrix.dotProduct, Matrix.mulVec]
simp only [← Finset.sum_filter_add_sum_filter_not Finset.univ (· ∈ Set.range f), mul_add, Finset.mul_sum,
Finset.sum_add_distrib]
simp only [Set.mem_range, Finset.univ_filter_exists, Pi.star_apply, Finset.mem_univ,
EmbeddingLike.apply_eq_iff_eq, imp_self, implies_true, Finset.sum_image, hyf,
submatrix_apply]
simp (config := {contextual := true}) [Finset.sum_filter, y, -not_exists]
lemma Matrix.PosDef.submatrix.det_pos {A : Matrix n n 𝕜}
(hA : A.PosDef) (e : m ↪ n) :
0 < (A.submatrix e e).det :=
Matrix.PosDef.det_pos (Matrix.PosDef.submatrix hA e)
variable [MulPosReflectLT 𝕜] [PosMulReflectLT 𝕜]
-- /-- https://en.wikipedia.org/wiki/Sylvester%27s_criterion -/
theorem posDef_fin_iff_det {A : Matrix n n 𝕜} (hA : A.IsHermitian) :
A.PosDef ↔ ∀ (m : Type u) [Fintype m] [DecidableEq m] (e : m ↪ n),
0 < (A.submatrix e e).det := by
constructor
· intro hA m hm e
exact Matrix.PosDef.submatrix.det_pos hA
· intro h_sub
constructor
exact hA
generalize hN : Fintype.card n = N
cases N with
| zero =>
exact absurd hN Fintype.card_ne_zero
| succ N =>
induction N generalizing n with
| zero =>
rw [zero_add] at hN
have h_det : 0 < A.det := by
apply h_sub n ⟨id, Function.injective_id⟩
intro x hx
dsimp only [Matrix.dotProduct, Matrix.mulVec]
obtain ⟨k, hk⟩ := Fintype.card_eq_one_iff.mp hN
rw [Matrix.det_eq_elem_of_card_eq_one hN k] at h_det
simp only [hN, hk, Finset.sum_const, Finset.card_univ, nsmul_eq_mul]
simp only [Nat.cast_one, one_mul, mul_comm, mul_assoc]
apply (mul_pos_iff_of_pos_left h_det).2
apply mul_star_self_pos
apply isRegular_of_ne_zero
intro hk_neg
apply hx
ext j
simp [hk]
exact hk_neg
| succ N ih =>
intro x hx
rename_i _ _ _ hn _ _
let k := Classical.arbitrary n
let s := hn.1 \ {k}
have hs : Disjoint s {k} := Finset.sdiff_disjoint
-- hs₁ follows from card n ≥ 2
have hs₁ : Nonempty s := sorry
let e := fun (a : s) ↦ (a : n)
have he : Function.Injective e := Subtype.val_injective
-- hN₁ follows from s = n \ {k}, where N = card n
have hN₁ : Fintype.card s = N + 1 := sorry
have hn_u : Finset.univ = s ∪ {k} := by
refine Eq.symm (Finset.sdiff_union_of_subset ?h)
simp only [Finset.singleton_subset_iff]
exact Fintype.complete k
let v : s → 𝕜 := fun j ↦ A j k
let x' := x ∘ e
let A' := A.submatrix e e
let y := (x k) • (A'⁻¹.mulVec v) + x'
let C := (star x k) * x k * (A k k - Matrix.dotProduct (star v) (A'⁻¹.mulVec v))
-- Proof by computation
have H : Matrix.dotProduct (star x) (A.mulVec x)
= Matrix.dotProduct (star y) (A'.mulVec y) + C := sorry
rw [H]
have H₁ : 0 ≤ C := by
dsimp only [C]
have H₁₁ : 0 < (A k k - Matrix.dotProduct (star v) (A'⁻¹.mulVec v)) := sorry
have H₁₂ : 0 ≤ star x k * x k := sorry
sorry
have H₂ : 0 < Matrix.dotProduct (star y) (A'.mulVec y) := sorry
exact Right.add_pos_of_pos_of_nonneg H₂ H₁
Eric Wieser (Aug 16 2024 at 22:07):
Do you need to split into 0
, 1
, and n + 2
, or would 0
and n + 1
work as a base case?
Eric Wieser (Aug 16 2024 at 22:11):
Note that this is not quite Sylvester's criterion; you've shown it for all possible principal minors, not the stronger case of just the _leading_ principal ones
jsodd (Aug 16 2024 at 22:43):
Eric Wieser said:
Do you need to split into
0
,1
, andn + 2
, or would0
andn + 1
work as a base case?
Well, if n = 0 it's just not true...
jsodd (Aug 16 2024 at 22:46):
Eric Wieser said:
Note that this is not quite Sylvester's criterion; you've shown it for all possible principal minors, not the stronger case of just the _leading_ principal ones
Yes, I know. I am still shocked by how tedious and hard it turns out to formalize something that obvious as the Sylvester's criterion. I hope it gets better with experience...
Eric Wieser (Apr 10 2025 at 00:23):
@jsodd, do you want to PR your Matrix.PosDef.submatrix
above to mathlib?
Last updated: May 02 2025 at 03:31 UTC