From 56b4840c070e614ccb394816abe3b6d71555343c Mon Sep 17 00:00:00 2001 From: Matteo CIpollina Date: Sun, 1 Mar 2026 22:23:11 +0100 Subject: [PATCH 01/36] Create NegDim.lean --- .../Geometry/Metric/QuadraticForm/NegDim.lean | 959 ++++++++++++++++++ 1 file changed, 959 insertions(+) create mode 100644 PhysLean/Mathematics/Geometry/Metric/QuadraticForm/NegDim.lean diff --git a/PhysLean/Mathematics/Geometry/Metric/QuadraticForm/NegDim.lean b/PhysLean/Mathematics/Geometry/Metric/QuadraticForm/NegDim.lean new file mode 100644 index 000000000..ac5fb0c56 --- /dev/null +++ b/PhysLean/Mathematics/Geometry/Metric/QuadraticForm/NegDim.lean @@ -0,0 +1,959 @@ +/- +Copyright (c) 2025 Matteo Cipollina. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Matteo Cipollina +-/ + +import Mathlib.LinearAlgebra.QuadraticForm.Real +import Mathlib.Algebra.Module.Submodule.Map +import Mathlib.LinearAlgebra.Dimension.Constructions +import Mathlib.LinearAlgebra.FiniteDimensional.Lemmas + +/-! +## Negative index for real quadratic forms + +This file provides a lightweight notion of the **negative index** (`negDim`) of a real quadratic +form on a finite-dimensional real vector space, defined canonically via maximal positive definite +subspaces (the standard “inertia index” definition). + +It is intended as a small reusable API for (pseudo-)Riemannian geometry developments. + +### Rigor note + +The *canonical* invariants in this file are: + +- `posIndex`: the maximum dimension of a subspace on which `Q` is positive definite +- `posDim := posIndex Q` +- `negDim := posIndex (-Q)` +- `zeroDim := finrank - posDim - negDim` +- `signature := ⟨posDim, negDim, zeroDim⟩` + +These are invariant under `QuadraticForm.Equivalent` (isometry equivalence), and require no +diagonalization choices. + +Separately, we also record a **noncomputable choice** of Sylvester diagonalization weights +(`signTypeWeights`) and its induced *chosen* signature (`signatureChoice`) as auxiliary data. This +is useful for bridging to Mathlib's existence theorem +`QuadraticForm.equivalent_signType_weighted_sum_squared` (in +`Mathlib/LinearAlgebra/QuadraticForm/Real.lean`). +-/ + +namespace QuadraticForm + +open Finset Module QuadraticMap SignType +open scoped BigOperators + +/-! ### Signature and indices -/ + +/-- A (finite-dimensional) real quadratic form has a signature `(pos, neg, zero)` given by +Sylvester's law of inertia: the numbers of positive, negative, and zero squares in a diagonal +representation. -/ +structure Signature where + pos : ℕ + neg : ℕ + zero : ℕ + +namespace Signature + +@[simp] +lemma mk_pos (p n z : ℕ) : (Signature.mk p n z).pos = p := rfl + +@[simp] +lemma mk_neg (p n z : ℕ) : (Signature.mk p n z).neg = n := rfl + +@[simp] +lemma mk_zero (p n z : ℕ) : (Signature.mk p n z).zero = z := rfl + +/-- Alias for the `zero` component (common terminology: nullity). -/ +abbrev nullity (s : Signature) : ℕ := s.zero + +@[simp] lemma nullity_eq_zero (s : Signature) : s.nullity = s.zero := rfl + +@[ext] +lemma ext {s t : Signature} (hpos : s.pos = t.pos) (hneg : s.neg = t.neg) + (hzero : s.zero = t.zero) : s = t := by + cases s + cases t + simp_all + +end Signature + +/-- A choice of `SignType`-weights in Sylvester's diagonalization of a quadratic form. + +This is auxiliary data: it exists by `QuadraticForm.equivalent_signType_weighted_sum_squared`, +but is not canonical (it is chosen noncomputably). -/ +noncomputable def signTypeWeights {E : Type*} [AddCommGroup E] [Module ℝ E] [FiniteDimensional ℝ E] + (q : QuadraticForm ℝ E) : Fin (finrank ℝ E) → SignType := + Classical.choose (QuadraticForm.equivalent_signType_weighted_sum_squared q) + +lemma equivalent_weightedSumSquares_signTypeWeights {E : Type*} [AddCommGroup E] [Module ℝ E] + [FiniteDimensional ℝ E] (q : QuadraticForm ℝ E) : + QuadraticMap.Equivalent q + (QuadraticMap.weightedSumSquares ℝ fun i => ((signTypeWeights q i : SignType) : ℝ)) := + Classical.choose_spec (QuadraticForm.equivalent_signType_weighted_sum_squared q) + +/-- A *chosen* signature `(pos, neg, zero)` of a real quadratic form, defined using +`signTypeWeights`. + +This is **not** asserted to be canonical/invariant in this file; it is primarily a bridge to +Mathlib's existence result (Sylvester diagonalization). -/ +noncomputable def signatureChoice {E : Type*} [AddCommGroup E] [Module ℝ E] [FiniteDimensional ℝ E] + (q : QuadraticForm ℝ E) : Signature := by + let w := signTypeWeights q + refine ⟨ + (univ.filter fun i => w i = SignType.pos).card, + (univ.filter fun i => w i = SignType.neg).card, + (univ.filter fun i => w i = 0).card⟩ + +/-! +We will define canonical indices (`posDim`, `negDim`, `zeroDim`, `signature`) below using `posIndex`, +and we keep `signatureChoice` only as auxiliary data. +-/ + +lemma signatureChoice_le_finrank {E : Type*} [AddCommGroup E] [Module ℝ E] [FiniteDimensional ℝ E] + (q : QuadraticForm ℝ E) : + (signatureChoice q).neg ≤ finrank ℝ E ∧ (signatureChoice q).pos ≤ finrank ℝ E ∧ + (signatureChoice q).zero ≤ finrank ℝ E := by + constructor + · simp [signatureChoice, signTypeWeights] + simpa using (Finset.card_filter_le (s := (Finset.univ : Finset (Fin (finrank ℝ E)))) + (p := fun i => signTypeWeights q i = SignType.neg)) + constructor + · simp [signatureChoice, signTypeWeights] + simpa using (Finset.card_filter_le (s := (Finset.univ : Finset (Fin (finrank ℝ E)))) + (p := fun i => signTypeWeights q i = SignType.pos)) + · simp [signatureChoice, signTypeWeights] + simpa using (Finset.card_filter_le (s := (Finset.univ : Finset (Fin (finrank ℝ E)))) + (p := fun i => signTypeWeights q i = 0)) + +/-- For a standard basis vector in a weighted sum of squares, only one term in the sum is nonzero. -/ +lemma QuadraticMap.weightedSumSquares_basis_vector {E : Type*} [AddCommGroup E] [Module ℝ E] + {weights : Fin (finrank ℝ E) → ℝ} {i : Fin (finrank ℝ E)} (v : Fin (finrank ℝ E) → ℝ) + (hv : ∀ j, v j = if j = i then 1 else 0) : + QuadraticMap.weightedSumSquares ℝ weights v = weights i := by + simp only [QuadraticMap.weightedSumSquares_apply] + rw [Finset.sum_eq_single i] + · simp only [hv i, ↓reduceIte, mul_one, smul_eq_mul] + · intro j _ hj + simp only [hv j, if_neg hj, mul_zero, smul_eq_mul] + · simp only [Finset.mem_univ, not_true_eq_false, smul_eq_mul, mul_eq_zero, or_self, + IsEmpty.forall_iff] + +/-- When a quadratic form is equivalent to a weighted sum of squares, negative weights correspond +to vectors where the form takes negative values. -/ +lemma neg_weight_implies_neg_value {E : Type*} [AddCommGroup E] [Module ℝ E] + {q : QuadraticForm ℝ E} {w : Fin (finrank ℝ E) → SignType} + (h_equiv : QuadraticMap.Equivalent q (QuadraticMap.weightedSumSquares ℝ fun i => (w i : ℝ))) + {i : Fin (finrank ℝ E)} (hi : w i = SignType.neg) : + ∃ v : E, v ≠ 0 ∧ q v < 0 := by + let f := Classical.choice h_equiv + let v_std : Fin (finrank ℝ E) → ℝ := fun j => if j = i then 1 else 0 + let v := f.symm v_std + have hv_ne_zero : v ≠ 0 := by + intro h + have : f v = f 0 := by rw [h] + have : f (f.symm v_std) = f 0 := by rw [← this] + have : v_std = 0 := by + rw [← f.apply_symm_apply v_std] + exact Eq.trans this (map_zero f) + have : v_std i = 0 := by rw [this]; rfl + simp only [↓reduceIte, one_ne_zero, v_std] at this + have hq_neg : q v < 0 := by + have heq : q v = QuadraticMap.weightedSumSquares ℝ (fun j => (w j : ℝ)) v_std := + QuadraticMap.IsometryEquiv.map_app f.symm v_std + have hw : QuadraticMap.weightedSumSquares ℝ (fun j => (w j : ℝ)) v_std = (w i : ℝ) := by + apply QuadraticMap.weightedSumSquares_basis_vector v_std + intro j; simp only [v_std] + rw [heq, hw] + have : (w i : ℝ) = -1 := by + simp only [hi, SignType.neg_eq_neg_one, SignType.coe_neg, SignType.coe_one] + rw [this] + exact neg_one_lt_zero + exact ⟨v, hv_ne_zero, hq_neg⟩ + +/-- A positive definite quadratic form cannot have any negative weights in its diagonalization. -/ +lemma posDef_no_neg_weights {E : Type*} [AddCommGroup E] [Module ℝ E] + {q : QuadraticForm ℝ E} (hq : q.PosDef) {w : Fin (finrank ℝ E) → SignType} + (h_equiv : QuadraticMap.Equivalent q (QuadraticMap.weightedSumSquares ℝ fun i => (w i : ℝ))) : + ∀ i, w i ≠ SignType.neg := by + intro i hi + obtain ⟨v, hv_ne_zero, hq_neg⟩ := QuadraticForm.neg_weight_implies_neg_value h_equiv hi + have hq_pos : 0 < q v := hq v hv_ne_zero + exact lt_asymm hq_neg hq_pos + +/-! +### Inertia uniqueness (canonical indices) + +We want the signature counts to be invariants (independent +of the choice of diagonalization). We provide the key intermediate notion of +the **positive index**, defined as the maximum dimension of a subspace on which the form is +positive definite. + +In this file we prove invariance of this index under `QuadraticForm.Equivalent`. In a subsequent +step, one computes this index for diagonal `weightedSumSquares` forms, and deduces uniqueness of +the inertia counts. +-/ + +section InertiaUniqueness + +variable {V : Type*} [AddCommGroup V] [Module ℝ V] [FiniteDimensional ℝ V] + +def IsPosDefOn (Q : QuadraticForm ℝ V) (W : Submodule ℝ V) : Prop := + (Q.comp W.subtype).PosDef + +/-- The positive index of a real quadratic form: the maximal dimension of a subspace on which the +form is positive definite. -/ +noncomputable def posIndex (Q : QuadraticForm ℝ V) : ℕ := by + classical + let n := finrank ℝ V + let S : Finset ℕ := + (Finset.range (n + 1)).filter (fun k => + ∃ W : Submodule ℝ V, finrank ℝ W = k ∧ IsPosDefOn (V := V) Q W) + have hS : S.Nonempty := by + refine ⟨0, ?_⟩ + refine Finset.mem_filter.2 ?_ + refine ⟨by simp, ?_⟩ + refine ⟨(⊥ : Submodule ℝ V), by simp, ?_⟩ + intro x hx + exfalso + exact hx (Subsingleton.elim x 0) + exact S.max' hS + +omit [FiniteDimensional ℝ V] in +lemma posIndex_spec (Q : QuadraticForm ℝ V) : + ∃ W : Submodule ℝ V, finrank ℝ W = posIndex (V := V) Q ∧ IsPosDefOn (V := V) Q W := by + classical + let n := finrank ℝ V + let S : Finset ℕ := + (Finset.range (n + 1)).filter (fun k => + ∃ W : Submodule ℝ V, finrank ℝ W = k ∧ IsPosDefOn (V := V) Q W) + have hS : S.Nonempty := by + refine ⟨0, ?_⟩ + refine Finset.mem_filter.2 ?_ + refine ⟨by simp, ?_⟩ + refine ⟨(⊥ : Submodule ℝ V), by simp, ?_⟩ + intro x hx + exfalso + exact hx (Subsingleton.elim x 0) + have hmem : posIndex (V := V) Q ∈ S := by + simpa [posIndex, S] using (Finset.max'_mem S hS) + rcases (Finset.mem_filter.1 hmem).2 with ⟨W, hW, hWpos⟩ + exact ⟨W, hW, hWpos⟩ + +lemma le_posIndex_of_exists {Q : QuadraticForm ℝ V} {k : ℕ} + (hk : ∃ W : Submodule ℝ V, finrank ℝ W = k ∧ IsPosDefOn (V := V) Q W) : + k ≤ posIndex (V := V) Q := by + classical + let n := finrank ℝ V + let S : Finset ℕ := + (Finset.range (n + 1)).filter (fun k => + ∃ W : Submodule ℝ V, finrank ℝ W = k ∧ IsPosDefOn (V := V) Q W) + have hS : S.Nonempty := by + refine ⟨0, ?_⟩ + refine Finset.mem_filter.2 ?_ + refine ⟨by simp, ?_⟩ + refine ⟨(⊥ : Submodule ℝ V), by simp, ?_⟩ + intro x hx + exfalso + exact hx (Subsingleton.elim x 0) + have hk_mem : k ∈ S := by + refine Finset.mem_filter.2 ?_ + refine ⟨?_, hk⟩ + rcases hk with ⟨W, hW, -⟩ + have hk_le : k ≤ n := by + simpa [hW, n] using (Submodule.finrank_le (R := ℝ) (M := V) W) + exact Finset.mem_range.2 (Nat.lt_succ_of_le hk_le) + simpa [posIndex, S] using (Finset.le_max' S k hk_mem) + +/-- `posIndex` is invariant under equivalence of quadratic forms. -/ +theorem posIndex_eq_of_equivalent {V₂ : Type*} [AddCommGroup V₂] [Module ℝ V₂] + [FiniteDimensional ℝ V₂] {Q : QuadraticForm ℝ V} {Q₂ : QuadraticForm ℝ V₂} + (h : Q.Equivalent Q₂) : + posIndex (V := V) Q = posIndex (V := V₂) Q₂ := by + classical + rcases h with ⟨e⟩ + have hle : posIndex (V := V) Q ≤ posIndex (V := V₂) Q₂ := by + rcases posIndex_spec (V := V) Q with ⟨W, hWfin, hWpos⟩ + let f : V →ₗ[ℝ] V₂ := (e.toLinearEquiv : V ≃ₗ[ℝ] V₂).toLinearMap + have hf_inj : Function.Injective f := e.toLinearEquiv.injective + let W₂ : Submodule ℝ V₂ := W.map f + have hfinrank : finrank ℝ W₂ = finrank ℝ W := by + simpa [W₂] using (LinearEquiv.finrank_eq (Submodule.equivMapOfInjective f hf_inj W)).symm + have hW₂pos : IsPosDefOn (V := V₂) Q₂ W₂ := by + intro x hx + rcases x with ⟨xv, hxv⟩ + have hx0 : (⟨xv, by simpa [W₂] using hxv⟩ : W.map f) ≠ 0 := by + intro h0; apply hx; ext; simpa using congrArg Subtype.val h0 + let eqv := Submodule.equivMapOfInjective f hf_inj W + set x' : W.map f := ⟨xv, by simpa [W₂] using hxv⟩ + have hx' : (eqv.symm x') ≠ 0 := by + intro h0 + apply hx0 + have := congrArg eqv h0 + simpa using this + have hpos' : 0 < (Q.comp W.subtype) (eqv.symm x') := hWpos _ hx' + have hxmap : f ((eqv.symm x' : W) : V) = (x' : V₂) := + Submodule.map_equivMapOfInjective_symm_apply f hf_inj W x' + have heq : Q₂ (x' : V₂) = Q ((eqv.symm x' : W) : V) := by + have : Q₂ (f ((eqv.symm x' : W) : V)) = Q ((eqv.symm x' : W) : V) := by + simp [f] + simpa [hxmap] using this + simpa [IsPosDefOn, QuadraticMap.comp_apply, heq, x'] using hpos' + have hk : + ∃ U : Submodule ℝ V₂, finrank ℝ U = posIndex (V := V) Q ∧ IsPosDefOn (V := V₂) Q₂ U := + ⟨W₂, by simp [hWfin, hfinrank], hW₂pos⟩ + exact le_posIndex_of_exists (V := V₂) hk + have hge : posIndex (V := V₂) Q₂ ≤ posIndex (V := V) Q := by + have h' : Q₂.Equivalent Q := ⟨e.symm⟩ + rcases posIndex_spec (V := V₂) Q₂ with ⟨W, hWfin, hWpos⟩ + let f : V₂ →ₗ[ℝ] V := (e.symm.toLinearEquiv : V₂ ≃ₗ[ℝ] V).toLinearMap + have hf_inj : Function.Injective f := e.symm.toLinearEquiv.injective + let W₂ : Submodule ℝ V := W.map f + have hfinrank : finrank ℝ W₂ = finrank ℝ W := by + simpa [W₂] using (LinearEquiv.finrank_eq (Submodule.equivMapOfInjective f hf_inj W)).symm + have hW₂pos : IsPosDefOn (V := V) Q W₂ := by + intro x hx + rcases x with ⟨xv, hxv⟩ + have hx0 : (⟨xv, by simpa [W₂] using hxv⟩ : W.map f) ≠ 0 := by + intro h0; apply hx; ext; simpa using congrArg Subtype.val h0 + let eqv := Submodule.equivMapOfInjective f hf_inj W + set x' : W.map f := ⟨xv, by simpa [W₂] using hxv⟩ + have hx' : (eqv.symm x') ≠ 0 := by + intro h0 + apply hx0 + have := congrArg eqv h0 + simpa using this + have hpos' : 0 < (Q₂.comp W.subtype) (eqv.symm x') := hWpos _ hx' + have hxmap : f ((eqv.symm x' : W) : V₂) = (x' : V) := + Submodule.map_equivMapOfInjective_symm_apply f hf_inj W x' + have heq : Q (x' : V) = Q₂ ((eqv.symm x' : W) : V₂) := by + have : Q (f ((eqv.symm x' : W) : V₂)) = Q₂ ((eqv.symm x' : W) : V₂) := by + simp [f] + simpa [hxmap] using this + simpa [IsPosDefOn, QuadraticMap.comp_apply, heq, x'] using hpos' + have hk : + ∃ U : Submodule ℝ V, finrank ℝ U = posIndex (V := V₂) Q₂ ∧ IsPosDefOn (V := V) Q U := + ⟨W₂, by simp [hWfin, hfinrank], hW₂pos⟩ + exact le_posIndex_of_exists (V := V) hk + exact le_antisymm hle hge + +end InertiaUniqueness + +/-! ### Canonical signature and indices -/ + +section Canonical + +variable {E : Type*} [AddCommGroup E] [Module ℝ E] [FiniteDimensional ℝ E] + +lemma posIndex_le_finrank (Q : QuadraticForm ℝ E) : + posIndex (V := E) Q ≤ finrank ℝ E := by + rcases posIndex_spec (V := E) Q with ⟨W, hW, -⟩ + have : finrank ℝ W ≤ finrank ℝ E := Submodule.finrank_le (R := ℝ) (M := E) W + simpa [hW] using this + +/-- The positive index of inertia of a real quadratic form (canonical). -/ +noncomputable def posDim (Q : QuadraticForm ℝ E) : ℕ := + posIndex (V := E) Q + +/-- The negative index of inertia of a real quadratic form (canonical). -/ +noncomputable def negDim (Q : QuadraticForm ℝ E) : ℕ := + posIndex (V := E) (-Q) + +omit [FiniteDimensional ℝ E] in +@[simp] +lemma posDim_def (Q : QuadraticForm ℝ E) : Q.posDim = posIndex (V := E) Q := rfl + +omit [FiniteDimensional ℝ E] in +@[simp] +lemma negDim_def (Q : QuadraticForm ℝ E) : Q.negDim = posIndex (V := E) (-Q) := rfl + +lemma posDim_le_finrank (Q : QuadraticForm ℝ E) : Q.posDim ≤ finrank ℝ E := + posIndex_le_finrank (E := E) Q + +lemma negDim_le_finrank (Q : QuadraticForm ℝ E) : Q.negDim ≤ finrank ℝ E := + posIndex_le_finrank (E := E) (-Q) + +lemma posDim_add_negDim_le_finrank (Q : QuadraticForm ℝ E) : + Q.posDim + Q.negDim ≤ finrank ℝ E := by + rcases posIndex_spec (V := E) Q with ⟨Wpos, hWpos, hpos⟩ + rcases posIndex_spec (V := E) (-Q) with ⟨Wneg, hWneg, hneg⟩ + have hdisj : Disjoint Wpos Wneg := by + rw [Submodule.disjoint_def] + intro x hxpos hxneg + by_contra hx0 + have hxpos' : 0 < Q x := by + have : 0 < (Q.comp Wpos.subtype) ⟨x, hxpos⟩ := hpos ⟨x, hxpos⟩ (by simpa using hx0) + simpa [IsPosDefOn, QuadraticMap.comp_apply] using this + have hxneg' : Q x < 0 := by + have : 0 < ((-Q).comp Wneg.subtype) ⟨x, hxneg⟩ := hneg ⟨x, hxneg⟩ (by simpa using hx0) + have : 0 < (-Q) x := by simpa [IsPosDefOn, QuadraticMap.comp_apply] using this + simpa using (neg_pos.mp this) + exact (not_lt_of_gt hxpos') hxneg' + have hdim : finrank ℝ Wpos + finrank ℝ Wneg ≤ finrank ℝ E := + Submodule.finrank_add_finrank_le_of_disjoint hdisj + simpa [posDim, negDim, hWpos, hWneg] using hdim + +/-- The nullity of a real quadratic form, defined so that `pos + neg + zero = finrank`. -/ +noncomputable def zeroDim (Q : QuadraticForm ℝ E) : ℕ := + finrank ℝ E - Q.posDim - Q.negDim + +omit [FiniteDimensional ℝ E] in +@[simp] +lemma zeroDim_def (Q : QuadraticForm ℝ E) : + Q.zeroDim = finrank ℝ E - Q.posDim - Q.negDim := rfl + +/-- The signature `(pos, neg, zero)` of a real quadratic form (canonical). -/ +noncomputable def signature (Q : QuadraticForm ℝ E) : Signature := + ⟨Q.posDim, Q.negDim, Q.zeroDim⟩ + +omit [FiniteDimensional ℝ E] in +@[simp] +lemma signature_pos (Q : QuadraticForm ℝ E) : Q.signature.pos = Q.posDim := rfl + +omit [FiniteDimensional ℝ E] in +@[simp] +lemma signature_neg (Q : QuadraticForm ℝ E) : Q.signature.neg = Q.negDim := rfl + +omit [FiniteDimensional ℝ E] in +@[simp] +lemma signature_zero (Q : QuadraticForm ℝ E) : Q.signature.zero = Q.zeroDim := rfl + +omit [FiniteDimensional ℝ E] in +@[simp] +lemma signature_def (Q : QuadraticForm ℝ E) : + Q.signature = ⟨Q.posDim, Q.negDim, Q.zeroDim⟩ := rfl + +lemma posDim_add_negDim_add_zeroDim (Q : QuadraticForm ℝ E) : + Q.posDim + Q.negDim + Q.zeroDim = finrank ℝ E := by + set n : ℕ := finrank ℝ E + set p : ℕ := Q.posDim + set m : ℕ := Q.negDim + have hp : p ≤ n := by + simpa [p, n] using (posDim_le_finrank (E := E) Q) + have hpm : p + m ≤ n := by + simpa [p, m, n] using (posDim_add_negDim_le_finrank (E := E) Q) + have hm : m ≤ n - p := by + exact (Nat.le_sub_iff_add_le hp).2 (by simpa [Nat.add_comm, p, m, n] using hpm) + calc + Q.posDim + Q.negDim + Q.zeroDim + = p + m + (n - p - m) := by simp [zeroDim, n, p, m] + _ = p + (m + ((n - p) - m)) := by + simp [Nat.add_assoc] + _ = p + (n - p) := by + simp [Nat.add_sub_of_le hm] + _ = n := by + simp [Nat.add_sub_of_le hp] + +lemma signature_sum (Q : QuadraticForm ℝ E) : + Q.signature.pos + Q.signature.neg + Q.signature.zero = finrank ℝ E := by + simpa using (posDim_add_negDim_add_zeroDim (E := E) Q) + +/-- For a positive definite quadratic form, `posDim = finrank`. -/ +theorem posDim_posDef {Q : QuadraticForm ℝ E} (hQ : Q.PosDef) : + Q.posDim = finrank ℝ E := by + apply le_antisymm (posDim_le_finrank (E := E) Q) + have hk : + ∃ W : Submodule ℝ E, finrank ℝ W = finrank ℝ E ∧ IsPosDefOn (V := E) Q W := by + refine ⟨(⊤ : Submodule ℝ E), by simp, ?_⟩ + intro x hx + have hx' : (x : E) ≠ 0 := by + intro h0 + apply hx + ext + simp [h0] + simpa [IsPosDefOn, QuadraticMap.comp_apply] using (hQ (x : E) hx') + have : finrank ℝ E ≤ posIndex (V := E) Q := + le_posIndex_of_exists (V := E) (k := finrank ℝ E) hk + simpa [posDim] using this + +/-- For a positive definite quadratic form, `negDim = 0`. -/ +theorem negDim_posDef {Q : QuadraticForm ℝ E} (hQ : Q.PosDef) : Q.negDim = 0 := by + rcases posIndex_spec (V := E) (-Q) with ⟨W, hW, hWpos⟩ + have hWbot : W = ⊥ := by + ext x + constructor + · intro hx + by_contra hx0 + have hpos' : 0 < ((-Q).comp W.subtype) ⟨x, hx⟩ := hWpos ⟨x, hx⟩ (by + intro h0; apply hx0; simpa using congrArg Subtype.val h0) + have hneg' : Q x < 0 := by + have : 0 < (-Q) x := by simpa [QuadraticMap.comp_apply] using hpos' + simpa using (neg_pos.mp this) + have hposQ : 0 < Q x := hQ x hx0 + exact (not_lt_of_gt hposQ) hneg' + · intro hx + have hx0 : x = 0 := by + simpa [Submodule.mem_bot] using hx + simp [hx0] + have hfin0 : finrank ℝ W = 0 := by simp [hWbot] + have : posIndex (V := E) (-Q) = 0 := by simpa [hW] using hfin0 + simp [negDim, this] + +/-- For a positive definite quadratic form, `zeroDim = 0`. -/ +theorem zeroDim_posDef {Q : QuadraticForm ℝ E} (hQ : Q.PosDef) : + Q.zeroDim = 0 := by + have hpos : Q.posDim = finrank ℝ E := posDim_posDef (E := E) hQ + have hneg : Q.negDim = 0 := negDim_posDef (E := E) hQ + have hpos' : posIndex (V := E) Q = finrank ℝ E := by simpa [posDim] using hpos + have hneg' : posIndex (V := E) (-Q) = 0 := by simpa [negDim] using hneg + simp [zeroDim, posDim, negDim, hpos', hneg'] + +omit [FiniteDimensional ℝ E] in +lemma Equivalent.neg {E₂ : Type*} [AddCommGroup E₂] [Module ℝ E₂] [FiniteDimensional ℝ E₂] + {Q : QuadraticForm ℝ E} {Q₂ : QuadraticForm ℝ E₂} (h : Q.Equivalent Q₂) : + (-Q).Equivalent (-Q₂) := by + rcases h with ⟨e⟩ + refine ⟨?_⟩ + refine + { toLinearEquiv := e.toLinearEquiv + map_app' := fun x => ?_ } + simp + +theorem posDim_eq_of_equivalent {E₂ : Type*} [AddCommGroup E₂] [Module ℝ E₂] [FiniteDimensional ℝ E₂] + {Q : QuadraticForm ℝ E} {Q₂ : QuadraticForm ℝ E₂} (h : Q.Equivalent Q₂) : + Q.posDim = Q₂.posDim := by + simp [posDim, posIndex_eq_of_equivalent (V := E) (V₂ := E₂) h] + +theorem negDim_eq_of_equivalent {E₂ : Type*} [AddCommGroup E₂] [Module ℝ E₂] [FiniteDimensional ℝ E₂] + {Q : QuadraticForm ℝ E} {Q₂ : QuadraticForm ℝ E₂} (h : Q.Equivalent Q₂) : + Q.negDim = Q₂.negDim := by + have h' : (-Q).Equivalent (-Q₂) := Equivalent.neg (E := E) (E₂ := E₂) h + simp [negDim, posIndex_eq_of_equivalent (V := E) (V₂ := E₂) h'] + +theorem zeroDim_eq_of_equivalent {E₂ : Type*} [AddCommGroup E₂] [Module ℝ E₂] [FiniteDimensional ℝ E₂] + {Q : QuadraticForm ℝ E} {Q₂ : QuadraticForm ℝ E₂} (h : Q.Equivalent Q₂) : + Q.zeroDim = Q₂.zeroDim := by + rcases h with ⟨e⟩ + have hfin : finrank ℝ E = finrank ℝ E₂ := LinearEquiv.finrank_eq e.toLinearEquiv + have hposI : posIndex (V := E) Q = posIndex (V := E₂) Q₂ := + posIndex_eq_of_equivalent (V := E) (V₂ := E₂) ⟨e⟩ + have hnegI : posIndex (V := E) (-Q) = posIndex (V := E₂) (-Q₂) := + posIndex_eq_of_equivalent (V := E) (V₂ := E₂) (Equivalent.neg (E := E) (E₂ := E₂) ⟨e⟩) + simp [zeroDim, posDim, negDim, hfin, hposI, hnegI] + +theorem signature_eq_of_equivalent {E₂ : Type*} [AddCommGroup E₂] [Module ℝ E₂] [FiniteDimensional ℝ E₂] + {Q : QuadraticForm ℝ E} {Q₂ : QuadraticForm ℝ E₂} (h : Q.Equivalent Q₂) : + Q.signature = Q₂.signature := by + rcases h with ⟨e⟩ + have hfin : finrank ℝ E = finrank ℝ E₂ := LinearEquiv.finrank_eq e.toLinearEquiv + have hposI : posIndex (V := E) Q = posIndex (V := E₂) Q₂ := + posIndex_eq_of_equivalent (V := E) (V₂ := E₂) ⟨e⟩ + have hnegI : posIndex (V := E) (-Q) = posIndex (V := E₂) (-Q₂) := + posIndex_eq_of_equivalent (V := E) (V₂ := E₂) (Equivalent.neg (E := E) (E₂ := E₂) ⟨e⟩) + ext <;> simp [signature, posDim, negDim, zeroDim, hfin, hposI, hnegI] + +end Canonical + +/-! +### Diagonal computation + +We compute the canonical indices for the diagonal form `weightedSumSquares` with `SignType` weights. +This is the key bridge between the canonical `posIndex`-based definition and Mathlib's Sylvester +diagonalization existence theorem. +-/ + +section Diagonal + +open scoped BigOperators + +noncomputable section + +variable {n : ℕ} + +private abbrev DiagForm (w : Fin n → SignType) : QuadraticForm ℝ (Fin n → ℝ) := + QuadraticMap.weightedSumSquares ℝ fun i => (w i : ℝ) + +private def posSet (w : Fin n → SignType) : Finset (Fin n) := + (Finset.univ.filter fun i => w i = SignType.pos) + +private lemma mem_posSet_iff {w : Fin n → SignType} {i : Fin n} : + i ∈ posSet (n := n) w ↔ w i = SignType.pos := by + simp [posSet] + +private def supportedOnPos (w : Fin n → SignType) : Submodule ℝ (Fin n → ℝ) where + carrier := {v | ∀ i, w i ≠ SignType.pos → v i = 0} + zero_mem' := by intro i hi; simp + add_mem' := by + intro v₁ v₂ hv₁ hv₂ i hi + simp [Pi.add_apply, hv₁ i hi, hv₂ i hi] + smul_mem' := by + intro a v hv i hi + simp [Pi.smul_apply, hv i hi] + +private lemma supportedOnPos_mem {w : Fin n → SignType} {v : Fin n → ℝ} : + v ∈ supportedOnPos (n := n) w ↔ ∀ i, w i ≠ SignType.pos → v i = 0 := Iff.rfl + +private def restrictPos (w : Fin n → SignType) : + (Fin n → ℝ) →ₗ[ℝ] ({i // i ∈ posSet (n := n) w} → ℝ) where + toFun v i := v i.1 + map_add' := by intro v₁ v₂; ext i; rfl + map_smul' := by intro a v; ext i; rfl + +private lemma restrictPos_apply {w : Fin n → SignType} (v : Fin n → ℝ) + (i : {i // i ∈ posSet (n := n) w}) : + restrictPos (n := n) w v i = v i.1 := rfl + +private lemma diag_nonpos_of_no_pos {w : Fin n → SignType} {v : Fin n → ℝ} + (hv : ∀ i, w i = SignType.pos → v i = 0) : + DiagForm (n := n) w v ≤ 0 := by + simp only [DiagForm, QuadraticMap.weightedSumSquares_apply] + refine Finset.sum_nonpos ?_ + intro i _ + cases hwi : w i <;> simp [hwi, hv i, mul_self_nonneg] + +private lemma supportedOnPos_posDef (w : Fin n → SignType) : + IsPosDefOn (V := Fin n → ℝ) (DiagForm (n := n) w) (supportedOnPos (n := n) w) := by + classical + intro v hv0 + -- pick a coordinate where `v` is nonzero + have hv' : ∃ i, (v : Fin n → ℝ) i ≠ 0 := by + by_contra h + apply hv0 + ext i + have : (v : Fin n → ℝ) i = 0 := by + by_contra hi + exact h ⟨i, hi⟩ + simpa using this + rcases hv' with ⟨i, hi⟩ + have hwpos : w i = SignType.pos := by + by_contra hne + have : (v : Fin n → ℝ) i = 0 := by + exact (supportedOnPos_mem (n := n) (w := w) |>.1 v.property) i hne + exact hi this + have hterm_pos : 0 < (v : Fin n → ℝ) i ^ 2 := by + simpa [pow_two] using sq_pos_of_ne_zero hi + have hle : + (v : Fin n → ℝ) i ^ 2 ≤ DiagForm (n := n) w (v : Fin n → ℝ) := by + simp only [DiagForm, QuadraticMap.weightedSumSquares_apply] + have hnonneg : + ∀ j : Fin n, 0 ≤ ((w j : ℝ) • ((v : Fin n → ℝ) j * (v : Fin n → ℝ) j)) := by + intro j + by_cases hj : w j = SignType.pos + · simp [hj, mul_self_nonneg] + · have : (v : Fin n → ℝ) j = 0 := (supportedOnPos_mem (n := n) (w := w)).1 v.property j hj + simp [this] + have : ((w i : ℝ) • ((v : Fin n → ℝ) i * (v : Fin n → ℝ) i)) + ≤ ∑ j : Fin n, (w j : ℝ) • ((v : Fin n → ℝ) j * (v : Fin n → ℝ) j) := by + refine Finset.single_le_sum (fun j _ => hnonneg j) ?_ + simp + simpa [hwpos, pow_two] using this + exact lt_of_lt_of_le hterm_pos hle + +theorem posIndex_diag_signType (w : Fin n → SignType) : + posIndex (V := Fin n → ℝ) (DiagForm (n := n) w) = (posSet (n := n) w).card := by + -- lower bound: exhibit the supported-on-positive submodule + have h_lower : + (posSet (n := n) w).card ≤ posIndex (V := Fin n → ℝ) (DiagForm (n := n) w) := by + -- `supportedOnPos` is isomorphic to functions on the positive index set + let W := supportedOnPos (n := n) w + have hfin : + finrank ℝ W = (posSet (n := n) w).card := by + -- linear equivalence with functions on the subtype `{i // i ∈ posSet}` + let e : + W ≃ₗ[ℝ] ({i // i ∈ posSet (n := n) w} → ℝ) := + { toLinearMap := + { toFun := fun v i => (v : Fin n → ℝ) i.1 + map_add' := by intro v₁ v₂; ext i; rfl + map_smul' := by intro a v; ext i; rfl } + invFun := fun u => + ⟨fun i => if h : i ∈ posSet (n := n) w then u ⟨i, h⟩ else 0, by + intro i hi + by_cases h : i ∈ posSet (n := n) w + · exfalso + exact hi (mem_posSet_iff (n := n) (w := w) |>.1 h) + · simp [h]⟩ + left_inv := by + intro v + ext i + by_cases h : i ∈ posSet (n := n) w + · have : w i = SignType.pos := (mem_posSet_iff (n := n) (w := w)).1 h + simp [h] + · have : w i ≠ SignType.pos := by + intro hpos; exact h ((mem_posSet_iff (n := n) (w := w)).2 hpos) + have : (v : Fin n → ℝ) i = 0 := (supportedOnPos_mem (n := n) (w := w)).1 v.property i this + simp [h, this] + right_inv := by + intro u + ext i + simp } + simpa using (LinearEquiv.finrank_eq e) + have hk : + ∃ W' : Submodule ℝ (Fin n → ℝ), + finrank ℝ W' = (posSet (n := n) w).card ∧ + IsPosDefOn (V := Fin n → ℝ) (DiagForm (n := n) w) W' := by + refine ⟨W, hfin, supportedOnPos_posDef (n := n) w⟩ + exact le_posIndex_of_exists (V := Fin n → ℝ) (Q := DiagForm (n := n) w) hk + -- upper bound: any positive definite subspace injects into the positive coordinates + have h_upper : + posIndex (V := Fin n → ℝ) (DiagForm (n := n) w) ≤ (posSet (n := n) w).card := by + rcases posIndex_spec (V := Fin n → ℝ) (DiagForm (n := n) w) with ⟨W, hW, hWpos⟩ + let f := restrictPos (n := n) w + let g : W →ₗ[ℝ] ({i // i ∈ posSet (n := n) w} → ℝ) := f.comp W.subtype + have hg_inj : Function.Injective g := by + intro x y hxy + have : g (x - y) = 0 := by + simpa [g, map_sub] using congrArg (fun z => z - g y) hxy + have hvanish : ∀ i, w i = SignType.pos → ((x - y : W) : (Fin n → ℝ)) i = 0 := by + intro i hi + have : (g (x - y)) ⟨i, (mem_posSet_iff (n := n) (w := w)).2 hi⟩ = 0 := by + simp [this] + simpa [g, f, restrictPos_apply] using this + have hnonpos : + DiagForm (n := n) w ((x - y : W) : (Fin n → ℝ)) ≤ 0 := + diag_nonpos_of_no_pos (n := n) (w := w) hvanish + by_cases hxy0 : x - y = 0 + · have : x = y := sub_eq_zero.mp hxy0 + exact this + · have hpos : 0 < (DiagForm (n := n) w).comp W.subtype (x - y) := hWpos _ hxy0 + have : ¬ (0 < (DiagForm (n := n) w).comp W.subtype (x - y)) := + not_lt_of_ge (by simpa [QuadraticMap.comp_apply] using hnonpos) + exact (this hpos).elim + have hfin_le : + finrank ℝ W ≤ (posSet (n := n) w).card := by + let eW : W ≃ₗ[ℝ] LinearMap.range g := LinearEquiv.ofInjective g hg_inj + have hWrange : finrank ℝ W = finrank ℝ (LinearMap.range g) := LinearEquiv.finrank_eq eW + have hrange_le : + finrank ℝ (LinearMap.range g) ≤ finrank ℝ ({i // i ∈ posSet (n := n) w} → ℝ) := + Submodule.finrank_le (R := ℝ) (M := ({i // i ∈ posSet (n := n) w} → ℝ)) (LinearMap.range g) + have htarget : + finrank ℝ ({i // i ∈ posSet (n := n) w} → ℝ) = (posSet (n := n) w).card := by + simp + simpa [hWrange, htarget] using hrange_le + simpa [hW] using hfin_le + exact le_antisymm h_upper h_lower + +theorem posIndex_weightedSumSquares_signType (w : Fin n → SignType) : + posIndex (V := Fin n → ℝ) + (QuadraticMap.weightedSumSquares ℝ fun i : Fin n => (w i : ℝ)) = + (Finset.univ.filter fun i : Fin n => w i = SignType.pos).card := by + simpa [DiagForm, posSet] using (posIndex_diag_signType (n := n) w) + +theorem posDim_weightedSumSquares_signType (w : Fin n → SignType) : + QuadraticForm.posDim (E := Fin n → ℝ) + ((QuadraticMap.weightedSumSquares ℝ fun i : Fin n => (w i : ℝ)) : + QuadraticForm ℝ (Fin n → ℝ)) = + (Finset.univ.filter fun i : Fin n => w i = SignType.pos).card := by + simp [QuadraticForm.posDim, posIndex_weightedSumSquares_signType (n := n) w] + +theorem negDim_weightedSumSquares_signType (w : Fin n → SignType) : + QuadraticForm.negDim (E := Fin n → ℝ) + ((QuadraticMap.weightedSumSquares ℝ fun i : Fin n => (w i : ℝ)) : + QuadraticForm ℝ (Fin n → ℝ)) = + (Finset.univ.filter fun i : Fin n => w i = SignType.neg).card := by + set Q : QuadraticForm ℝ (Fin n → ℝ) := + (QuadraticMap.weightedSumSquares ℝ fun i : Fin n => (w i : ℝ)) + have hneg : + (-Q) = + (QuadraticMap.weightedSumSquares ℝ fun i : Fin n => ((-w i : SignType) : ℝ)) := by + ext v + simp [Q, QuadraticMap.weightedSumSquares_apply] + have hposIndex : + posIndex (V := Fin n → ℝ) + (QuadraticMap.weightedSumSquares ℝ fun i : Fin n => ((-w i : SignType) : ℝ)) = + (Finset.univ.filter fun i : Fin n => (-w i) = SignType.pos).card := by + simpa using (posIndex_weightedSumSquares_signType (n := n) fun i => -w i) + have hcard : + (Finset.univ.filter fun i : Fin n => (-w i) = SignType.pos).card = + (Finset.univ.filter fun i : Fin n => w i = SignType.neg).card := by + have hset : + (Finset.univ.filter fun i : Fin n => (-w i) = SignType.pos) = + (Finset.univ.filter fun i : Fin n => w i = SignType.neg) := by + ext i + cases hi : w i <;> simp [hi] + simpa using congrArg Finset.card hset + have hmain : + posIndex (V := Fin n → ℝ) (-Q) = + (Finset.univ.filter fun i : Fin n => w i = SignType.neg).card := by + calc + posIndex (V := Fin n → ℝ) (-Q) + = posIndex (V := Fin n → ℝ) + (QuadraticMap.weightedSumSquares ℝ fun i : Fin n => ((-w i : SignType) : ℝ)) := by + simp [hneg] + _ = (Finset.univ.filter fun i : Fin n => (-w i) = SignType.pos).card := hposIndex + _ = (Finset.univ.filter fun i : Fin n => w i = SignType.neg).card := hcard + simpa [QuadraticForm.negDim, Q] using hmain + +private lemma card_filters_pos_neg_zero (w : Fin n → SignType) : + (Finset.univ.filter fun i : Fin n => w i = SignType.pos).card + + (Finset.univ.filter fun i : Fin n => w i = SignType.neg).card + + (Finset.univ.filter fun i : Fin n => w i = 0).card = + Fintype.card (Fin n) := by + let A : Finset (Fin n) := Finset.univ.filter fun i => w i = SignType.pos + let B : Finset (Fin n) := Finset.univ.filter fun i => w i = SignType.neg + let C : Finset (Fin n) := Finset.univ.filter fun i => w i = 0 + have hAB : Disjoint A B := by + refine Finset.disjoint_left.2 ?_ + intro i hiA hiB + have hiPos : w i = SignType.pos := (Finset.mem_filter.1 hiA).2 + have hiNeg : w i = SignType.neg := (Finset.mem_filter.1 hiB).2 + have : (SignType.pos : SignType) = SignType.neg := by simp [hiPos] at hiNeg + cases this + have hABC : Disjoint (A ∪ B) C := by + refine Finset.disjoint_left.2 ?_ + intro i hiAB hiC + have hiC' : w i = 0 := (Finset.mem_filter.1 hiC).2 + rcases Finset.mem_union.1 hiAB with hiA | hiB + · have : w i = SignType.pos := (Finset.mem_filter.1 hiA).2 + have : (SignType.pos : SignType) = 0 := by simp [this] at hiC' + cases this + · have : w i = SignType.neg := (Finset.mem_filter.1 hiB).2 + have : (SignType.neg : SignType) = 0 := by simp [this] at hiC' + cases this + have hunion : (A ∪ B) ∪ C = Finset.univ := by + ext i + cases hi : w i <;> simp [A, B, C, hi] + have hcardAB : (A ∪ B).card = A.card + B.card := + Finset.card_union_of_disjoint hAB + have hcardABC : ((A ∪ B) ∪ C).card = (A ∪ B).card + C.card := + Finset.card_union_of_disjoint hABC + have hcard : ((A ∪ B) ∪ C).card = A.card + B.card + C.card := by + calc + ((A ∪ B) ∪ C).card = (A ∪ B).card + C.card := hcardABC + _ = (A.card + B.card) + C.card := by simp [hcardAB, Nat.add_assoc] + _ = A.card + B.card + C.card := by simp [Nat.add_assoc] + have huniv : ((A ∪ B) ∪ C).card = Fintype.card (Fin n) := by + have : ((A ∪ B) ∪ C).card = (Finset.univ : Finset (Fin n)).card := + congrArg Finset.card hunion + simpa using this + have : A.card + B.card + C.card = Fintype.card (Fin n) := by + calc + A.card + B.card + C.card = ((A ∪ B) ∪ C).card := by simpa [hcard] using hcard.symm + _ = Fintype.card (Fin n) := huniv + simpa [A, B, C, Nat.add_assoc, Nat.add_left_comm, Nat.add_comm] using this + +theorem zeroDim_weightedSumSquares_signType (w : Fin n → SignType) : + QuadraticForm.zeroDim (E := Fin n → ℝ) + ((QuadraticMap.weightedSumSquares ℝ fun i : Fin n => (w i : ℝ)) : + QuadraticForm ℝ (Fin n → ℝ)) = + (Finset.univ.filter fun i : Fin n => w i = 0).card := by + have hfin : finrank ℝ (Fin n → ℝ) = Fintype.card (Fin n) := by simp + have hpos := posDim_weightedSumSquares_signType (n := n) w + have hneg := negDim_weightedSumSquares_signType (n := n) w + have hsum := card_filters_pos_neg_zero (n := n) w + let A : ℕ := (Finset.univ.filter fun i : Fin n => w i = SignType.pos).card + let B : ℕ := (Finset.univ.filter fun i : Fin n => w i = SignType.neg).card + let C : ℕ := (Finset.univ.filter fun i : Fin n => w i = 0).card + have hsum' : A + B + C = Fintype.card (Fin n) := by simpa [A, B, C] using hsum + have hz0 : Fintype.card (Fin n) - A - B = C := by + have hn : Fintype.card (Fin n) = A + (B + C) := by + simpa [Nat.add_assoc] using hsum'.symm + have h1 : Fintype.card (Fin n) - A = B + C := by + simp [hn] + calc + Fintype.card (Fin n) - A - B = (Fintype.card (Fin n) - A) - B := rfl + _ = (B + C) - B := by + simpa using congrArg (fun t => t - B) h1 + _ = C := by simp [Nat.add_sub_cancel_left] + set Q : QuadraticForm ℝ (Fin n → ℝ) := + (QuadraticMap.weightedSumSquares ℝ fun i : Fin n => (w i : ℝ)) + have hposI : posIndex (V := Fin n → ℝ) Q = A := by + simpa [Q, A] using (posIndex_weightedSumSquares_signType (n := n) w) + have hnegI : posIndex (V := Fin n → ℝ) (-Q) = B := by + have h0 : + posIndex (V := Fin n → ℝ) + (QuadraticMap.weightedSumSquares ℝ fun i : Fin n => ((-w i : SignType) : ℝ)) = + (Finset.univ.filter fun i : Fin n => (-w i) = SignType.pos).card := by + simpa using (posIndex_weightedSumSquares_signType (n := n) fun i => -w i) + have hneg' : (-Q) = + (QuadraticMap.weightedSumSquares ℝ fun i : Fin n => ((-w i : SignType) : ℝ)) := by + ext v + simp [Q, QuadraticMap.weightedSumSquares_apply] + have hcard : + (Finset.univ.filter fun i : Fin n => (-w i) = SignType.pos).card = B := by + have hset : + (Finset.univ.filter fun i : Fin n => (-w i) = SignType.pos) = + (Finset.univ.filter fun i : Fin n => w i = SignType.neg) := by + ext i + cases hi : w i <;> simp [hi] + simpa [B] using congrArg Finset.card hset + calc + posIndex (V := Fin n → ℝ) (-Q) + = posIndex (V := Fin n → ℝ) + (QuadraticMap.weightedSumSquares ℝ fun i : Fin n => ((-w i : SignType) : ℝ)) := by + simp [hneg'] + _ = (Finset.univ.filter fun i : Fin n => (-w i) = SignType.pos).card := h0 + _ = B := hcard + have : QuadraticForm.zeroDim (E := Fin n → ℝ) Q = C := by + dsimp [QuadraticForm.zeroDim, QuadraticForm.posDim, QuadraticForm.negDim] + rw [hfin, hposI, hnegI] + simpa using hz0 + simpa [C] using this + +end + +end Diagonal + +/-! +### Canonical signature equals Sylvester choice + +Using the diagonal computation for `weightedSumSquares` and the Sylvester diagonalization existence +theorem, we show that the canonical signature defined via `posIndex` agrees with the previously +defined `signatureChoice`. In particular, `signatureChoice` becomes an invariant of the quadratic +form (independent of the chosen diagonalization witness). +-/ + +section SylvesterBridge + +variable {E : Type*} [AddCommGroup E] [Module ℝ E] [FiniteDimensional ℝ E] + +theorem posDim_eq_signatureChoice_pos (q : QuadraticForm ℝ E) : + q.posDim = (signatureChoice q).pos := by + let w : Fin (finrank ℝ E) → SignType := signTypeWeights q + let Qd : QuadraticForm ℝ (Fin (finrank ℝ E) → ℝ) := + (QuadraticMap.weightedSumSquares ℝ fun i : Fin (finrank ℝ E) => (w i : ℝ)) + have heq : q.Equivalent Qd := by + simpa [Qd, w, signTypeWeights] using (equivalent_weightedSumSquares_signTypeWeights q) + have hpos : q.posDim = Qd.posDim := + posDim_eq_of_equivalent (E := E) (E₂ := Fin (finrank ℝ E) → ℝ) heq + have hposd : + Qd.posDim = (Finset.univ.filter fun i : Fin (finrank ℝ E) => w i = SignType.pos).card := by + simpa [Qd] using (posDim_weightedSumSquares_signType (n := finrank ℝ E) w) + simpa [signatureChoice, w, signTypeWeights] using hpos.trans hposd + +theorem negDim_eq_signatureChoice_neg (q : QuadraticForm ℝ E) : + q.negDim = (signatureChoice q).neg := by + let w : Fin (finrank ℝ E) → SignType := signTypeWeights q + let Qd : QuadraticForm ℝ (Fin (finrank ℝ E) → ℝ) := + (QuadraticMap.weightedSumSquares ℝ fun i : Fin (finrank ℝ E) => (w i : ℝ)) + have heq : q.Equivalent Qd := by + simpa [Qd, w, signTypeWeights] using (equivalent_weightedSumSquares_signTypeWeights q) + have hneg : q.negDim = Qd.negDim := + negDim_eq_of_equivalent (E := E) (E₂ := Fin (finrank ℝ E) → ℝ) heq + have hnegd : + Qd.negDim = (Finset.univ.filter fun i : Fin (finrank ℝ E) => w i = SignType.neg).card := by + simpa [Qd] using (negDim_weightedSumSquares_signType (n := finrank ℝ E) w) + simpa [signatureChoice, w, signTypeWeights] using hneg.trans hnegd + +theorem zeroDim_eq_signatureChoice_zero (q : QuadraticForm ℝ E) : + q.zeroDim = (signatureChoice q).zero := by + let w : Fin (finrank ℝ E) → SignType := signTypeWeights q + let Qd : QuadraticForm ℝ (Fin (finrank ℝ E) → ℝ) := + (QuadraticMap.weightedSumSquares ℝ fun i : Fin (finrank ℝ E) => (w i : ℝ)) + have heq : q.Equivalent Qd := by + simpa [Qd, w, signTypeWeights] using (equivalent_weightedSumSquares_signTypeWeights q) + have hzero : q.zeroDim = Qd.zeroDim := + zeroDim_eq_of_equivalent (E := E) (E₂ := Fin (finrank ℝ E) → ℝ) heq + have hzerod : + Qd.zeroDim = (Finset.univ.filter fun i : Fin (finrank ℝ E) => w i = 0).card := by + simpa [Qd] using (zeroDim_weightedSumSquares_signType (n := finrank ℝ E) w) + simpa [signatureChoice, w, signTypeWeights] using hzero.trans hzerod + +theorem signature_eq_signatureChoice (q : QuadraticForm ℝ E) : + q.signature = signatureChoice q := by + ext + · simpa using posDim_eq_signatureChoice_pos (E := E) q + · simpa using negDim_eq_signatureChoice_neg (E := E) q + · simpa using zeroDim_eq_signatureChoice_zero (E := E) q + +theorem signatureChoice_eq_of_equivalent {E₂ : Type*} [AddCommGroup E₂] [Module ℝ E₂] + [FiniteDimensional ℝ E₂] {Q : QuadraticForm ℝ E} {Q₂ : QuadraticForm ℝ E₂} + (h : Q.Equivalent Q₂) : + signatureChoice Q = signatureChoice Q₂ := by + calc + signatureChoice Q = Q.signature := (signature_eq_signatureChoice (E := E) Q).symm + _ = Q₂.signature := signature_eq_of_equivalent (E := E) (E₂ := E₂) h + _ = signatureChoice Q₂ := signature_eq_signatureChoice (E := E₂) Q₂ + +end SylvesterBridge + +end QuadraticForm From a624ac0ff1941a096bcdd6b635676013a25ade61 Mon Sep 17 00:00:00 2001 From: Matteo CIpollina Date: Sun, 1 Mar 2026 22:23:15 +0100 Subject: [PATCH 02/36] Update Defs.lean --- .../Metric/PseudoRiemannian/Defs.lean | 264 ++++++++---------- 1 file changed, 118 insertions(+), 146 deletions(-) diff --git a/PhysLean/Mathematics/Geometry/Metric/PseudoRiemannian/Defs.lean b/PhysLean/Mathematics/Geometry/Metric/PseudoRiemannian/Defs.lean index c5a120a0d..bb171a97a 100644 --- a/PhysLean/Mathematics/Geometry/Metric/PseudoRiemannian/Defs.lean +++ b/PhysLean/Mathematics/Geometry/Metric/PseudoRiemannian/Defs.lean @@ -5,11 +5,12 @@ Authors: Matteo Cipollina -/ import Mathlib.Analysis.InnerProductSpace.Basic -import Mathlib.Analysis.RCLike.Lemmas import Mathlib.Geometry.Manifold.MFDeriv.Defs +import Mathlib.Geometry.Manifold.VectorBundle.Hom +import Mathlib.Geometry.Manifold.VectorBundle.Tangent import Mathlib.LinearAlgebra.BilinearForm.Properties -import Mathlib.LinearAlgebra.QuadraticForm.Real import Mathlib.Topology.LocallyConstant.Basic +import PhysLean.Mathematics.Geometry.Metric.QuadraticForm.NegDim /-! # Pseudo-Riemannian Metrics on Smooth Manifolds @@ -39,10 +40,9 @@ of a maximal negative definite subspace. * `PseudoRiemannianMetric.toQuadraticForm g x`: The quadratic form `v ↦ gₓ(v, v)` associated with the metric at point `x`. -This formalization adopts a direct approach, defining the metric as a family of bilinear forms -on tangent spaces, varying smoothly over the manifold. This pragmatic choice allows for foundational -development while acknowledging that a more abstract ideal would involve defining metrics as -sections of a tensor bundle (e.g., `Hom(TM ⊗ TM, ℝ)` or `TM →L[ℝ] TM →L[ℝ] ℝ`. +This formalization packages smoothness in the same style as the modern Mathlib Riemannian API: +the metric is a section of the (vector-bundle) bundle of bilinear forms, and the smoothness +assumption is a `ContMDiff` statement for this section (instead of being phrased chartwise). ## Reference @@ -58,122 +58,9 @@ noncomputable section open Bundle Set Finset Function Filter Module Topology ContinuousLinearMap open scoped Manifold Bundle LinearMap Dual -namespace QuadraticForm - -variable {K : Type*} [Field K] - -/-! ## Negative Index -/ - -/-- The negative dimension (often called the index or negative index of inertia) of a -quadratic form `q` on a finite-dimensional real vector space. - -This value is defined by diagonalizing the quadratic form into an equivalent -`QuadraticMap.weightedSumSquares ℝ s`, where `s : Fin (finrank ℝ E) → SignType` -assigns `1`, `0`, or `-1` to each component. The `negDim` is the count of -components `i` for which `s i = SignType.neg`. - -By Sylvester's Law of Inertia, this count is an invariant of the quadratic form. -Geometrically, `negDim q` represents the dimension of any maximal vector subspace -on which `q` is negative definite. This corresponds to O'Neill's Definition 18 (p. 47) -of the index `ν` of a symmetric bilinear form `b` on `V`, which is "the largest integer -that is the dimension of a subspace `W ⊂ V` on which `b|W` is negative -definite." -/ -noncomputable def negDim {E : Type*} [AddCommGroup E] - [Module ℝ E] [FiniteDimensional ℝ E] - (q : QuadraticForm ℝ E) : ℕ := by classical - let P : (Fin (finrank ℝ E) → SignType) → Prop := fun w => - QuadraticMap.Equivalent q (QuadraticMap.weightedSumSquares ℝ fun i => (w i : ℝ)) - let h_exists : ∃ w, P w := QuadraticForm.equivalent_signType_weighted_sum_squared q - let w := Classical.choose h_exists - exact Finset.card (Finset.filter (fun i => w i = SignType.neg) Finset.univ) - -/-- For a standard basis vector in a weighted sum of squares, only one term in the sum - is nonzero. -/ -lemma QuadraticMap.weightedSumSquares_basis_vector {E : Type*} [AddCommGroup E] - [Module ℝ E] {weights : Fin (finrank ℝ E) → ℝ} - {i : Fin (finrank ℝ E)} (v : Fin (finrank ℝ E) → ℝ) - (hv : ∀ j, v j = if j = i then 1 else 0) : - QuadraticMap.weightedSumSquares ℝ weights v = weights i := by - simp only [QuadraticMap.weightedSumSquares_apply] - rw [Finset.sum_eq_single i] - · simp only [hv i, ↓reduceIte, mul_one, smul_eq_mul] - · intro j _ hj - simp only [hv j, if_neg hj, mul_zero, smul_eq_mul] - · simp only [Finset.mem_univ, not_true_eq_false, smul_eq_mul, mul_eq_zero, or_self, - IsEmpty.forall_iff] - -/-- When a quadratic form is equivalent to a weighted sum of squares, - negative weights correspond to vectors where the form takes negative values. - This is a concrete realization of a 1-dimensional negative definite subspace, - contributing to O'Neill's index `ν` (Definition 18, p. 47). -/ -lemma neg_weight_implies_neg_value {E : Type*} [AddCommGroup E] [Module ℝ E] - {q : QuadraticForm ℝ E} {w : Fin (finrank ℝ E) → SignType} - (h_equiv : QuadraticMap.Equivalent q (QuadraticMap.weightedSumSquares ℝ fun i => (w i : ℝ))) - {i : Fin (finrank ℝ E)} (hi : w i = SignType.neg) : - ∃ v : E, v ≠ 0 ∧ q v < 0 := by - let f := Classical.choice h_equiv - let v_std : Fin (finrank ℝ E) → ℝ := fun j => if j = i then 1 else 0 - let v := f.symm v_std - have hv_ne_zero : v ≠ 0 := by - intro h - have : f v = f 0 := by rw [h] - have : f (f.symm v_std) = f 0 := by rw [← this] - have : v_std = 0 := by - rw [← f.apply_symm_apply v_std] - exact Eq.trans this (map_zero f) - have : v_std i = 0 := by rw [this]; rfl - simp only [↓reduceIte, one_ne_zero, v_std] at this - have hq_neg : q v < 0 := by - have heq : q v = QuadraticMap.weightedSumSquares ℝ (fun j => (w j : ℝ)) v_std := - QuadraticMap.IsometryEquiv.map_app f.symm v_std - have hw : QuadraticMap.weightedSumSquares ℝ (fun j => (w j : ℝ)) v_std = (w i : ℝ) := by - apply QuadraticMap.weightedSumSquares_basis_vector v_std - intro j; simp only [v_std] - rw [heq, hw] - have : (w i : ℝ) = -1 := by simp only [hi, SignType.neg_eq_neg_one, SignType.coe_neg, - SignType.coe_one] - rw [this] - exact neg_one_lt_zero - exact ⟨v, hv_ne_zero, hq_neg⟩ - -/-- A positive definite quadratic form cannot have any negative weights - in its diagonal representation. A quadratic form `q` derived from a bilinear form `b` - is positive definite if `b(v,v) > 0` for `v ≠ 0` (O'Neill, Definition 17 (1), p. 46). - The existence of a negative weight would imply `q(v) < 0` for some `v ≠ 0`, a contradiction. -/ -lemma posDef_no_neg_weights {E : Type*} [AddCommGroup E] [Module ℝ E] - {q : QuadraticForm ℝ E} (hq : q.PosDef) - {w : Fin (finrank ℝ E) → SignType} - (h_equiv : QuadraticMap.Equivalent q (QuadraticMap.weightedSumSquares ℝ fun i => (w i : ℝ))) : - ∀ i, w i ≠ SignType.neg := by - intro i hi - obtain ⟨v, hv_ne_zero, hq_neg⟩ := QuadraticForm.neg_weight_implies_neg_value h_equiv hi - have hq_pos : 0 < q v := hq v hv_ne_zero - exact lt_asymm hq_neg hq_pos - -/-- For a positive definite quadratic form, the negative dimension (index) is zero. - O'Neill states (p. 47) that "ν = 0 if and only if b is positive semidefinite." - Since positive definite implies positive semidefinite (Definitions 17 (1) and (2), p. 46), - a positive definite form must have index `ν = 0`. -/ -theorem rankNeg_eq_zero {E : Type*} [AddCommGroup E] - [Module ℝ E] [FiniteDimensional ℝ E] {q : QuadraticForm ℝ E} (hq : q.PosDef) : - q.negDim = 0 := by - haveI : Invertible (2 : ℝ) := inferInstance - unfold QuadraticForm.negDim - have h_exists := equivalent_signType_weighted_sum_squared q - let w := Classical.choose h_exists - have h_equiv : QuadraticMap.Equivalent q - (QuadraticMap.weightedSumSquares ℝ fun i => (w i : ℝ)) := - Classical.choose_spec h_exists - have h_no_neg : ∀ i, w i ≠ SignType.neg := - QuadraticForm.posDef_no_neg_weights hq h_equiv - simp [Finset.card_eq_zero, Finset.filter_eq_empty_iff] - exact fun ⦃x⦄ => h_no_neg x - -end QuadraticForm - /-! ## Pseudo-Riemannian Metric -/ -/-- +/-! Constructs a `QuadraticForm` on the tangent space `TₓM` at a point `x` from the value of a pseudo-Riemannian metric at that point. (O'Neill, p. 47, "The function q: V → R given by q(v) = b(v,v) is the associated quadratic @@ -184,7 +71,16 @@ The quadratic form `Qₓ` at `x` is defined as `Qₓ(v) = gₓ(v,v)`. The associated symmetric bilinear form required by `QuadraticForm.exists_companion'` is `Bₓ(v,w) = gₓ(v,w) + gₓ(w,v)`. Given the symmetry `symm`, this is `2 * gₓ(v,w)`. -/ -private def pseudoRiemannianMetricValToQuadraticForm +namespace PseudoRiemannianMetric + +/-- +Turn a (curried) bilinear form `val` on each tangent space into the associated quadratic form +`v ↦ val x v v`. + +This helper is intentionally public: it is the bridge between a bundled description of a metric +(`val` + symmetry) and quadratic-form invariants such as `QuadraticForm.negDim`. +-/ +def valToQuadraticForm {E : Type v} [NormedAddCommGroup E] [NormedSpace ℝ E] {H : Type w} [TopologicalSpace H] {M : Type w} [TopologicalSpace M] [ChartedSpace H M] @@ -207,6 +103,43 @@ private def pseudoRiemannianMetricValToQuadraticForm ContinuousLinearMap.add_apply, symm x] ring⟩ +end PseudoRiemannianMetric + +/-- A general (pseudo-)metric tensor of smoothness class `C^n` on a manifold `M`. + +This is the common core shared by Riemannian and pseudo-Riemannian metrics: +a smoothly varying symmetric, nondegenerate bilinear form on each tangent space. + +The pseudo-Riemannian notion will extend this with an index/signature constancy condition. -/ +@[ext] +structure MetricTensor + (E : Type v) (H : Type w) (M : Type w) (n : WithTop ℕ∞) + [inst_norm_grp_E : NormedAddCommGroup E] + [inst_norm_sp_E : NormedSpace ℝ E] + [inst_top_H : TopologicalSpace H] + [inst_top_M : TopologicalSpace M] + [inst_chart_M : ChartedSpace H M] + (I : ModelWithCorners ℝ E H) + [inst_mani : IsManifold I (n + 1) M] + [inst_mani1 : IsManifold I 1 M] + [inst_tangent_findim : ∀ (x : M), FiniteDimensional ℝ (TangentSpace I x)] : + Type (max u v w) where + /-- The metric tensor at each point `x : M`, represented as a continuous linear map + `TₓM →L[ℝ] (TₓM →L[ℝ] ℝ)`. Applying it twice, `(val x v) w`, yields `gₓ(v, w)`. -/ + val : ∀ (x : M), TangentSpace I x →L[ℝ] (TangentSpace I x →L[ℝ] ℝ) + /-- Symmetry: `gₓ(v, w) = gₓ(w, v)`. -/ + symm : ∀ (x : M) (v w : TangentSpace I x), (val x v) w = (val x w) v + /-- Non-degeneracy: if `gₓ(v, w) = 0` for all `w`, then `v = 0`. -/ + nondegenerate : ∀ (x : M) (v : TangentSpace I x), (∀ w : TangentSpace I x, + (val x v) w = 0) → v = 0 + /-- Smoothness of the metric tensor as a smooth section of the bundle of bilinear forms. + + This packaging follows the same pattern as Mathlib's Riemannian metric API, using `TotalSpace.mk'` + for the bundled map. -/ + contMDiff : ContMDiff I (I.prod 𝓘(ℝ, E →L[ℝ] E →L[ℝ] ℝ)) n + (fun x ↦ + TotalSpace.mk' (E →L[ℝ] E →L[ℝ] ℝ) x (val x)) + /-- A pseudo-Riemannian metric of smoothness class `C^n` on a manifold `M` modelled on `(E, H)` with model `I`. This structure defines a smoothly varying, non-degenerate, symmetric, continuous bilinear form `gₓ` of constant negative dimension on the tangent space `TₓM` @@ -215,49 +148,32 @@ This structure formalizes O'Neill's Definition 3.1 (p. 54) of a metric tensor `g as a "symmetric non-degenerate (0,2) tensor field on M of constant index." Each `gₓ` is a scalar product (O'Neill, Definition 20, p. 47) on `TₓM`. -/ @[ext] -structure PseudoRiemannianMetric +structure PseudoRiemannianMetric extends MetricTensor E H M n I (E : Type v) (H : Type w) (M : Type w) (n : WithTop ℕ∞) [inst_norm_grp_E : NormedAddCommGroup E] [inst_norm_sp_E : NormedSpace ℝ E] [inst_top_H : TopologicalSpace H] [inst_top_M : TopologicalSpace M] [inst_chart_M : ChartedSpace H M] - [inst_chart_E : ChartedSpace H E] (I : ModelWithCorners ℝ E H) [inst_mani : IsManifold I (n + 1) M] + [inst_mani1 : IsManifold I 1 M] [inst_tangent_findim : ∀ (x : M), FiniteDimensional ℝ (TangentSpace I x)] : Type (max u v w) where - /-- The metric tensor at each point `x : M`, represented as a continuous linear map - `TₓM →L[ℝ] (TₓM →L[ℝ] ℝ)`. Applying it twice, `(val x v) w`, yields `gₓ(v, w)`. -/ - val : ∀ (x : M), TangentSpace I x →L[ℝ] (TangentSpace I x →L[ℝ] ℝ) - /-- The metric is symmetric: `gₓ(v, w) = gₓ(w, v)`. -/ - symm : ∀ (x : M) (v w : TangentSpace I x), (val x v) w = (val x w) v - /-- The metric is non-degenerate: if `gₓ(v, w) = 0` for all `w`, then `v = 0`. -/ - nondegenerate : ∀ (x : M) (v : TangentSpace I x), (∀ w : TangentSpace I x, - (val x v) w = 0) → v = 0 - /-- The metric varies smoothly: Expressed in local coordinates via the chart - `e := extChartAt I x₀`, the function - `y ↦ g_{e.symm y}(mfderiv I I e.symm y v, mfderiv I I e.symm y w)` is `C^n` smooth on the - chart's target `e.target` for any constant vectors `v, w` in the model space `E`. -/ - smooth_in_charts' : ∀ (x₀ : M) (v w : E), - let e := extChartAt I x₀ - ContDiffWithinAt ℝ n - (fun y => val (e.symm y) (mfderiv I I e.symm y v) (mfderiv I I e.symm y w)) - (e.target) (e x₀) /-- The negative dimension (`QuadraticForm.negDim`) of the metric's quadratic form is locally constant. On a connected manifold, this implies it is constant globally. -/ negDim_isLocallyConstant : IsLocallyConstant (fun x : M => have : FiniteDimensional ℝ (TangentSpace I x) := inferInstance - (pseudoRiemannianMetricValToQuadraticForm val symm x).negDim) + (PseudoRiemannianMetric.valToQuadraticForm val symm x).negDim) namespace PseudoRiemannianMetric variable {E : Type v} {H : Type w} {M : Type w} {n : WithTop ℕ∞} variable [NormedAddCommGroup E] [NormedSpace ℝ E] -variable [TopologicalSpace H] [TopologicalSpace M] [ChartedSpace H M] [ChartedSpace H E] +variable [TopologicalSpace H] [TopologicalSpace M] [ChartedSpace H M] variable {I : ModelWithCorners ℝ E H} -variable [IsManifold I (n + 1) M] +variable [IsManifold I (n + 1) M] [IsManifold I 1 M] variable [inst_tangent_findim : ∀ (x : M), FiniteDimensional ℝ (TangentSpace I x)] variable {g : PseudoRiemannianMetric E H M n I} @@ -283,7 +199,7 @@ def toBilinForm (g : PseudoRiemannianMetric E H M n I) (x : M) : /-- Convert a pseudo-Riemannian metric at a point `x` to a quadratic form `v ↦ gₓ(v, v)`. -/ abbrev toQuadraticForm (g : PseudoRiemannianMetric E H M n I) (x : M) : QuadraticForm ℝ (TangentSpace I x) := - pseudoRiemannianMetricValToQuadraticForm g.val g.symm x + PseudoRiemannianMetric.valToQuadraticForm g.val g.symm x /-- Coercion from PseudoRiemannianMetric to its function representation. -/ instance coeFunInst : CoeFun (PseudoRiemannianMetric E H M n I) @@ -300,6 +216,38 @@ lemma toQuadraticForm_apply (g : PseudoRiemannianMetric E H M n I) (x : M) (v : TangentSpace I x) : toQuadraticForm g x v = g.val x v v := rfl +/-! ## Index (negative inertia) -/ + +/-- The (negative) index of a pseudo-Riemannian metric at a point, defined as the negative index of +the associated quadratic form `v ↦ gₓ(v,v)`. -/ +def index (g : PseudoRiemannianMetric E H M n I) (x : M) : ℕ := + (g.toQuadraticForm x).negDim + +@[simp] lemma index_def (g : PseudoRiemannianMetric E H M n I) (x : M) : + g.index x = (g.toQuadraticForm x).negDim := rfl + +lemma index_isLocallyConstant (g : PseudoRiemannianMetric E H M n I) : + IsLocallyConstant (fun x : M => g.index x) := by + -- this is exactly the structure field, rewritten through `toQuadraticForm` + simpa [index, toQuadraticForm] using g.negDim_isLocallyConstant + +lemma index_eq_of_isPreconnected (g : PseudoRiemannianMetric E H M n I) {s : Set M} + (hs : IsPreconnected s) {x y : M} (hx : x ∈ s) (hy : y ∈ s) : + g.index x = g.index y := + (index_isLocallyConstant (g := g)).apply_eq_of_isPreconnected hs hx hy + +lemma index_eq_of_preconnectedSpace [PreconnectedSpace M] (g : PseudoRiemannianMetric E H M n I) + (x y : M) : + g.index x = g.index y := + (index_isLocallyConstant (g := g)).apply_eq_of_preconnectedSpace x y + +lemma index_eq_of_mem_connectedComponent (g : PseudoRiemannianMetric E H M n I) (x y : M) + (hy : y ∈ connectedComponent x) : + g.index y = g.index x := + (index_isLocallyConstant (g := g)).apply_eq_of_isPreconnected + (isConnected_connectedComponent.isPreconnected) + hy (mem_connectedComponent : x ∈ connectedComponent x) + @[simp] lemma toBilinForm_isSymm (g : PseudoRiemannianMetric E H M n I) (x : M) : (toBilinForm g x).IsSymm := by @@ -501,9 +449,9 @@ section Cotangent variable {E : Type v} {H : Type w} {M : Type w} {n : WithTop ℕ∞} variable [NormedAddCommGroup E] [NormedSpace ℝ E] -variable [TopologicalSpace H] [TopologicalSpace M] [ChartedSpace H M] [ChartedSpace H E] +variable [TopologicalSpace H] [TopologicalSpace M] [ChartedSpace H M] variable {I : ModelWithCorners ℝ E H} -variable [IsManifold I (n + 1) M] +variable [IsManifold I (n + 1) M] [IsManifold I 1 M] variable [inst_tangent_findim : ∀ (x : M), FiniteDimensional ℝ (TangentSpace I x)] /-- The value of the induced metric on the cotangent space at point `x`. -/ @@ -629,6 +577,30 @@ lemma cotangentToBilinForm_nondegenerate (g : PseudoRiemannianMetric E H M n I) intro y; rw [LinearMap.BilinForm.isSymm_def.mp (cotangentToBilinForm_isSymm g x)]; simp [hω] exact hv v +/-! ## Cotangent signature -/ + +/-- The cotangent quadratic form is equivalent to the tangent quadratic form via `sharp`. -/ +theorem cotangentToQuadraticForm_equivalent_toQuadraticForm + (g : PseudoRiemannianMetric E H M n I) (x : M) : + (g.cotangentToQuadraticForm x).Equivalent (g.toQuadraticForm x) := by + classical + refine ⟨?_⟩ + refine + { toLinearEquiv := (g.sharpEquiv x).toLinearEquiv + map_app' := fun ω => ?_ } + -- unfold through the simp lemmas for both quadratic forms + simp [cotangentToQuadraticForm_apply, cotangentMetricVal, toQuadraticForm, toQuadraticForm_apply, + sharpEquiv, sharpL, coe_sharpEquiv] + +theorem cotangent_signature_eq (g : PseudoRiemannianMetric E H M n I) (x : M) : + (g.cotangentToQuadraticForm x).signature = (g.toQuadraticForm x).signature := + QuadraticForm.signature_eq_of_equivalent (E := (TangentSpace I x →L[ℝ] ℝ)) + (E₂ := TangentSpace I x) (cotangentToQuadraticForm_equivalent_toQuadraticForm (g := g) x) + +theorem cotangent_negDim_eq (g : PseudoRiemannianMetric E H M n I) (x : M) : + (g.cotangentToQuadraticForm x).negDim = (g.toQuadraticForm x).negDim := + congrArg QuadraticForm.Signature.neg (cotangent_signature_eq (g := g) x) + end Cotangent end PseudoRiemannianMetric From 4a5619747eb5a98c1a37b7244fc5f045cb09b8b6 Mon Sep 17 00:00:00 2001 From: Matteo CIpollina Date: Sun, 1 Mar 2026 22:23:17 +0100 Subject: [PATCH 03/36] Update Defs.lean --- .../Geometry/Metric/Riemannian/Defs.lean | 266 +++++------------- 1 file changed, 73 insertions(+), 193 deletions(-) diff --git a/PhysLean/Mathematics/Geometry/Metric/Riemannian/Defs.lean b/PhysLean/Mathematics/Geometry/Metric/Riemannian/Defs.lean index 3caa55842..7f7aa2047 100644 --- a/PhysLean/Mathematics/Geometry/Metric/Riemannian/Defs.lean +++ b/PhysLean/Mathematics/Geometry/Metric/Riemannian/Defs.lean @@ -4,214 +4,94 @@ Released under Apache 2.0 license as described in the file LICENSE. Authors: Matteo Cipollina -/ import PhysLean.Mathematics.Geometry.Metric.PseudoRiemannian.Defs -import Mathlib.Algebra.Lie.OfAssociative -import Mathlib.MeasureTheory.Integral.IntervalIntegral.Basic +import Mathlib.Geometry.Manifold.VectorBundle.Riemannian +import Mathlib.Geometry.Manifold.VectorBundle.Tangent /-! # Riemannian Metric Definitions -This module defines the Riemannian metric, building on pseudo-Riemannian metrics. +This file is a PhysLean-facing wrapper around the modern Mathlib Riemannian metric API. + +Concretely, a `C^n` Riemannian metric on a manifold is a smooth section of the bundle of bilinear +forms on the tangent bundle, packaged as `Bundle.ContMDiffRiemannianMetric`. + +We provide: +- an abbreviation `RiemannianMetric` for the tangent-bundle specialization, and +- a coercion to the PhysLean `PseudoRiemannianMetric` (by forgetting positivity and remembering + nondegeneracy + constant index `0`). -/ namespace PseudoRiemannianMetric -section RiemannianMetric -open Bundle Set Finset Function Filter Module Topology ContinuousLinearMap -open scoped Manifold Bundle LinearMap Dual -open PseudoRiemannianMetric InnerProductSpace +open Bundle ContinuousLinearMap +open scoped Manifold Bundle noncomputable section +section + variable {E : Type v} [NormedAddCommGroup E] [NormedSpace ℝ E] variable {H : Type w} [TopologicalSpace H] -variable {M : Type w} [TopologicalSpace M] [ChartedSpace H M] [ChartedSpace H E] -variable {I : ModelWithCorners ℝ E H} {n : ℕ∞} - -/-- A `RiemannianMetric` on a manifold `M` modeled on `H` with corners `I` (over the model space `E` -, typically `ℝ^m`) is a family of inner products on the tangent spaces `TangentSpace I x` -for each `x : M`. This family is required to vary smoothly with `x`, specifically with smoothness -`C^n`. - -This structure `extends` `PseudoRiemannianMetric`, inheriting the core properties of a -pseudo-Riemannian metric, such as being a symmetric, non-degenerate, `C^n`-smooth tensor field -of type `(0,2)`. The key distinguishing feature of a Riemannian metric is its positive-definiteness. - -The `pos_def'` field ensures that for any point `x` on the manifold and any non-zero tangent -vector `v` at `x`, the inner product `gₓ(v, v)` (denoted `val x v v`) is strictly positive. -This condition makes each `val x` (the metric at point `x`) a positive-definite symmetric -bilinear form, effectively an inner product, on the tangent space `TangentSpace I x`. - -Parameters: -- `I`: The `ModelWithCorners` for the manifold `M`. This defines the model space `E` (e.g., `ℝ^d`) - and the model space for the boundary `H`. -- `n`: The smoothness class of the metric, an `ℕ∞` value. The metric tensor components are `C^n` - functions. -- `M`: The type of the manifold. -- `[TopologicalSpace M]`: Assumes `M` has a topological structure. -- `[ChartedSpace H M]`: Assumes `M` is equipped with an atlas of charts to `H`. -- `[IsManifold I (n + 1) M]`: Assumes `M` is a manifold of smoothness `C^(n+1)`. - The manifold needs to be slightly smoother than the metric itself for certain constructions. -- `[inst_tangent_findim : ∀ (x : M), FiniteDimensional ℝ (TangentSpace I x)]`: - Ensures that each tangent space is a finite-dimensional real vector space. - -Fields: -- `toPseudoRiemannianMetric`: The underlying pseudo-Riemannian metric. This provides the - smooth family of symmetric bilinear forms `val : M → SymBilinForm ℝ (TangentSpace I ·)`. -- `pos_def'`: The positive-definiteness condition: `∀ x v, v ≠ 0 → val x v v > 0`. This asserts - that for any point `x` and any non-zero tangent vector `v` at `x`, the metric evaluated - on `(v, v)` is strictly positive. -/ -@[ext] -structure RiemannianMetric - (I : ModelWithCorners ℝ E H) (n : ℕ∞) (M : Type w) - [TopologicalSpace M] [ChartedSpace H M] [IsManifold I (n + 1) M] - [inst_tangent_findim : ∀ (x : M), FiniteDimensional ℝ (TangentSpace I x)] - extends PseudoRiemannianMetric E H M n I where - /-- `gₓ(v, v) > 0` for all nonzero `v`. `val` is inherited from `PseudoRiemannianMetric`. -/ - pos_def' : ∀ x v, v ≠ 0 → val x v v > 0 +variable {M : Type w} [TopologicalSpace M] [ChartedSpace H M] +variable {I : ModelWithCorners ℝ E H} {n : WithTop ℕ∞} +variable [IsManifold I 1 M] [IsManifold I (n + 1) M] + +/-- A `C^n` Riemannian metric on `M`, packaged using Mathlib's modern bundle API. -/ +abbrev RiemannianMetric + (I : ModelWithCorners ℝ E H) (n : WithTop ℕ∞) (M : Type w) + [TopologicalSpace M] [ChartedSpace H M] [IsManifold I 1 M] [IsManifold I (n + 1) M] := + Bundle.ContMDiffRiemannianMetric (IB := I) (n := n) (F := E) (E := fun x : M ↦ TangentSpace I x) + namespace RiemannianMetric -variable {I : ModelWithCorners ℝ E H} {n : ℕ∞} {M : Type w} -variable [TopologicalSpace M] [ChartedSpace H M] [IsManifold I (n + 1) M] -variable [inst_tangent_findim : ∀ (x : M), FiniteDimensional ℝ (TangentSpace I x)] - -/-- Coercion from RiemannianMetric to its underlying PseudoRiemannianMetric. -/ -instance : Coe (RiemannianMetric I n M) (PseudoRiemannianMetric E H M (n) I) where - coe g := g.toPseudoRiemannianMetric - -@[simp] -lemma pos_def (g : RiemannianMetric I n M) (x : M) (v : TangentSpace I x) - (hv : v ≠ 0) : - (g.toPseudoRiemannianMetric.val x v) v > 0 := g.pos_def' x v hv - -/-- The quadratic form associated with a Riemannian metric is positive definite. -/ -@[simp] -lemma toQuadraticForm_posDef (g : RiemannianMetric I n M) (x : M) : - (g.toQuadraticForm x).PosDef := - λ v hv => g.pos_def x v hv - -lemma riemannian_metric_negDim_zero (g : RiemannianMetric I n M) (x : M) : - (g.toQuadraticForm x).negDim = 0 := by - apply QuadraticForm.rankNeg_eq_zero - exact g.toQuadraticForm_posDef x - -/-! ## InnerProductSpace structure from RiemannianMetric -/ - -section InnerProductSpace - -variable (g : RiemannianMetric I n M) (x : M) - -/-- The `InnerProductSpace.Core` structure for `TₓM` induced by a Riemannian metric `g`. - This provides the properties of an inner product: symmetry, - non-negativity, linearity, and definiteness. - Each `gₓ` is an inner product on `TₓM` (O'Neill, p. 55). -/ -noncomputable def tangentInnerCore (g : RiemannianMetric I n M) (x : M) : - InnerProductSpace.Core ℝ (TangentSpace I x) where - inner := λ v w => g.inner x v w - conj_inner_symm := λ v w => by - simp only [inner_apply, conj_trivial] - exact g.toPseudoRiemannianMetric.symm x w v - re_inner_nonneg := λ v => by - simp only [inner_apply, RCLike.re_to_real] - by_cases hv : v = 0 - · simp [hv, map_zero] - · exact le_of_lt (g.pos_def x v hv) - add_left := λ u v w => by - simp only [inner_apply, map_add, ContinuousLinearMap.add_apply] - smul_left := λ r u v => by - simp only [inner_apply, map_smul, conj_trivial] - rfl - definite := fun v (h_inner_zero : g.inner x v v = 0) => by - by_contra h_v_ne_zero - have h_pos : g.inner x v v > 0 := g.pos_def x v h_v_ne_zero - linarith [h_inner_zero, h_pos] - -/-! ### Local `NormedAddCommGroup` and `InnerProductSpace` Instances - -These instances are defined locally to be used when a specific Riemannian metric `g` -and point `x` are in context. They are not global instances to avoid typeclass conflicts -and to respect the fact that a manifold might not have a canonical Riemannian metric, -or might be studied with an indefinite (pseudo-Riemannian) metric where these -standard norm structures are not appropriate. -/ - -/-- Creates a `NormedAddCommGroup` structure on `TₓM` from a Riemannian metric `g`. -/ -noncomputable def TangentSpace.metricNormedAddCommGroup (g : RiemannianMetric I n M) (x : M) : - NormedAddCommGroup (TangentSpace I x) := - @InnerProductSpace.Core.toNormedAddCommGroup ℝ (TangentSpace I x) _ _ _ (tangentInnerCore g x) - -/-- Creates an `InnerProductSpace` structure on `TₓM` from a Riemannian metric `g`. - Alternative implementation using `letI`. -/ -noncomputable def TangentSpace.metricInnerProductSpace' (g : RiemannianMetric I n M) (x : M) : - letI := TangentSpace.metricNormedAddCommGroup g x - InnerProductSpace ℝ (TangentSpace I x) := - InnerProductSpace.ofCore (tangentInnerCore g x).toCore - -/-- Creates an `InnerProductSpace` structure on `TₓM` from a Riemannian metric `g`. -/ -noncomputable def TangentSpace.metricInnerProductSpace (g : RiemannianMetric I n M) (x : M) : - let _ := TangentSpace.metricNormedAddCommGroup g x - InnerProductSpace ℝ (TangentSpace I x) := - let inner_core := tangentInnerCore g x - let _ := TangentSpace.metricNormedAddCommGroup g x - InnerProductSpace.ofCore inner_core.toCore - -/-- The norm on a tangent space induced by a Riemannian metric, defined as the square root - of the inner product of a vector with itself. -/ -noncomputable def norm (g : RiemannianMetric I n M) (x : M) (v : TangentSpace I x) : ℝ := - Real.sqrt (g.inner x v v) - --- Example using the norm function -example (g : RiemannianMetric I n M) (x : M) (v : TangentSpace I x) : - norm g x v ≥ 0 := Real.sqrt_nonneg _ - --- Example showing how to use the metric inner product space -example (g : RiemannianMetric I n M) (x : M) (v w : TangentSpace I x) : - (TangentSpace.metricInnerProductSpace g x).inner v w = g.inner x v w := by - letI := TangentSpace.metricInnerProductSpace g x - rfl - -/-- Helper function to compute the norm on a tangent space from a Riemannian metric, - using the underlying `NormedAddCommGroup` structure. -/ -noncomputable def norm' (g : RiemannianMetric I n M) (x : M) (v : TangentSpace I x) : ℝ := - let normed_group := TangentSpace.metricNormedAddCommGroup g x - @Norm.norm (TangentSpace I x) (@NormedAddCommGroup.toNorm (TangentSpace I x) normed_group) v - --- Example: Using a custom norm function instead of the notation -example (g : RiemannianMetric I n M) (x : M) (v : TangentSpace I x) : - norm g x v ≥ 0 := by - unfold norm - apply Real.sqrt_nonneg - -example (g : RiemannianMetric I n M) (x : M) (v : TangentSpace I x) : ℝ := - letI := TangentSpace.metricNormedAddCommGroup g x - ‖v‖ - -example (g : RiemannianMetric I n M) (x : M) (v : TangentSpace I x) : ℝ := - let normed_group := TangentSpace.metricNormedAddCommGroup g x - @Norm.norm (TangentSpace I x) (@NormedAddCommGroup.toNorm (TangentSpace I x) normed_group) v - -lemma norm_eq_norm_of_metricNormedAddCommGroup (g : RiemannianMetric I n M) (x : M) - (v : TangentSpace I x) : norm g x v = @Norm.norm (TangentSpace I x) - (@NormedAddCommGroup.toNorm _ (TangentSpace.metricNormedAddCommGroup g x)) v := by - unfold norm - let normed_group := TangentSpace.metricNormedAddCommGroup g x - unfold TangentSpace.metricNormedAddCommGroup - simp only [inner_apply] - rfl - -end InnerProductSpace - -/-! ## Curve -/ - -section Curve - -/-- Calculates the length of a curve `γ` between parameters `t₀` and `t₁` -using the Riemannian metric `g`. This is defined as the integral of the norm of -the tangent vector along the curve. -/ -def curveLength (g : RiemannianMetric I n M) (γ : ℝ → M) (t₀ t₁ : ℝ) - {IDE : ModelWithCorners ℝ ℝ ℝ}[ChartedSpace ℝ ℝ] : ℝ := - ∫ t in t₀..t₁, norm g (γ t) ((mfderiv IDE I γ t) ((1 : ℝ) : TangentSpace IDE t)) - -end Curve +variable (g : RiemannianMetric (I := I) (n := n) M) +variable [∀ x : M, FiniteDimensional ℝ (TangentSpace I x)] + +/-- Forget the positivity to get a pseudo-Riemannian metric. The index is (locally constantly) `0`. +This is the bridge that makes pseudo-Riemannian API (musical isomorphisms, etc.) usable for a +Riemannian metric. -/ +def toPseudoRiemannianMetric (g : RiemannianMetric (I := I) (n := n) M) : + PseudoRiemannianMetric E H M n I where + val := g.inner + symm := g.symm + nondegenerate := by + intro x v hv + by_cases h : v = 0 + · simp [h] + · have hp : 0 < g.inner x v v := g.pos x v h + have h0 : g.inner x v v = 0 := hv v + exact (ne_of_gt hp h0).elim + contMDiff := g.contMDiff + negDim_isLocallyConstant := by + -- On a Riemannian metric, the associated quadratic form is positive definite, hence `negDim = 0`. + refine IsLocallyConstant.of_constant _ (fun x y => ?_) + -- Both sides are `0`. + have hx : + (PseudoRiemannianMetric.valToQuadraticForm g.inner g.symm x).negDim = 0 := by + apply QuadraticForm.negDim_posDef + intro v hv + simpa [PseudoRiemannianMetric.valToQuadraticForm] using g.pos x v hv + have hy : + (PseudoRiemannianMetric.valToQuadraticForm g.inner g.symm y).negDim = 0 := by + apply QuadraticForm.negDim_posDef + intro v hv + simpa [PseudoRiemannianMetric.valToQuadraticForm] using g.pos y v hv + have hx' : + (-PseudoRiemannianMetric.valToQuadraticForm g.inner g.symm x).posIndex = 0 := by + simpa [QuadraticForm.negDim] using hx + have hy' : + (-PseudoRiemannianMetric.valToQuadraticForm g.inner g.symm y).posIndex = 0 := by + simpa [QuadraticForm.negDim] using hy + simp [hx', hy'] + +instance : + Coe (RiemannianMetric (I := I) (n := n) M) + (PseudoRiemannianMetric E H M n I) := + ⟨fun g => toPseudoRiemannianMetric (I := I) (n := n) (M := M) g⟩ end RiemannianMetric + end -end RiemannianMetric + +end + end PseudoRiemannianMetric From 843e24f1bb48cfe98d96cd8b8c4853c653e9d457 Mon Sep 17 00:00:00 2001 From: Matteo CIpollina Date: Sun, 1 Mar 2026 22:23:20 +0100 Subject: [PATCH 04/36] Create Defs.lean --- .../Geometry/Metric/Lorentzian/Defs.lean | 58 +++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 PhysLean/Mathematics/Geometry/Metric/Lorentzian/Defs.lean diff --git a/PhysLean/Mathematics/Geometry/Metric/Lorentzian/Defs.lean b/PhysLean/Mathematics/Geometry/Metric/Lorentzian/Defs.lean new file mode 100644 index 000000000..da99c6e56 --- /dev/null +++ b/PhysLean/Mathematics/Geometry/Metric/Lorentzian/Defs.lean @@ -0,0 +1,58 @@ +/- +Copyright (c) 2026. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +-/ + +import PhysLean.Mathematics.Geometry.Metric.PseudoRiemannian.Defs + +/-! +# Lorentzian metrics (PhysLean) + +This file defines Lorentzian metrics as pseudo-Riemannian metrics of index `1` (negative dimension +`1`), in the sense of Sylvester's law of inertia (`QuadraticForm.negDim`). + +This is intentionally lightweight: it provides a reusable definition that composes with the +existing pseudo-Riemannian API (musical isomorphisms, induced bilinear forms, etc.). +-/ + +namespace PseudoRiemannianMetric + +noncomputable section + +open scoped Manifold + +section + +variable {E : Type v} [NormedAddCommGroup E] [NormedSpace ℝ E] +variable {H : Type w} [TopologicalSpace H] +variable {M : Type w} [TopologicalSpace M] [ChartedSpace H M] +variable {I : ModelWithCorners ℝ E H} {n : WithTop ℕ∞} +variable [IsManifold I 1 M] [IsManifold I (n + 1) M] +variable [∀ x : M, FiniteDimensional ℝ (TangentSpace I x)] + +/-- A Lorentzian metric is a pseudo-Riemannian metric of (constant) index `1`, i.e. the associated +quadratic form has `negDim = 1` at every point. -/ +@[ext] +structure LorentzianMetric extends PseudoRiemannianMetric E H M n I where + negDim_eq_one : ∀ x : M, (toQuadraticForm toPseudoRiemannianMetric x).negDim = 1 + +namespace LorentzianMetric + +variable (g : LorentzianMetric (E := E) (H := H) (M := M) (I := I) (n := n)) + +@[simp] lemma negDim_eq_one' (x : M) : + (g.toPseudoRiemannianMetric.toQuadraticForm x).negDim = 1 := + g.negDim_eq_one x + +instance : Coe (LorentzianMetric (E := E) (H := H) (M := M) (I := I) (n := n)) + (PseudoRiemannianMetric E H M n I) := + ⟨fun g => g.toPseudoRiemannianMetric⟩ + +end LorentzianMetric + +end + +end + +end PseudoRiemannianMetric + From 5461e143ef10c07c1df533ca5fd4a534ea71ec36 Mon Sep 17 00:00:00 2001 From: Matteo CIpollina Date: Sun, 1 Mar 2026 22:48:13 +0100 Subject: [PATCH 05/36] add MetricTensor --- .../Metric/PseudoRiemannian/Defs.lean | 359 ++++++++++++++++-- 1 file changed, 331 insertions(+), 28 deletions(-) diff --git a/PhysLean/Mathematics/Geometry/Metric/PseudoRiemannian/Defs.lean b/PhysLean/Mathematics/Geometry/Metric/PseudoRiemannian/Defs.lean index bb171a97a..d7d61060c 100644 --- a/PhysLean/Mathematics/Geometry/Metric/PseudoRiemannian/Defs.lean +++ b/PhysLean/Mathematics/Geometry/Metric/PseudoRiemannian/Defs.lean @@ -123,7 +123,7 @@ structure MetricTensor [inst_mani : IsManifold I (n + 1) M] [inst_mani1 : IsManifold I 1 M] [inst_tangent_findim : ∀ (x : M), FiniteDimensional ℝ (TangentSpace I x)] : - Type (max u v w) where + Type _ where /-- The metric tensor at each point `x : M`, represented as a continuous linear map `TₓM →L[ℝ] (TₓM →L[ℝ] ℝ)`. Applying it twice, `(val x v) w`, yields `gₓ(v, w)`. -/ val : ∀ (x : M), TangentSpace I x →L[ℝ] (TangentSpace I x →L[ℝ] ℝ) @@ -140,6 +140,324 @@ structure MetricTensor (fun x ↦ TotalSpace.mk' (E →L[ℝ] E →L[ℝ] ℝ) x (val x)) +namespace MetricTensor + +variable {E : Type v} {H : Type w} {M : Type w} {n : WithTop ℕ∞} +variable [NormedAddCommGroup E] [NormedSpace ℝ E] +variable [TopologicalSpace H] [TopologicalSpace M] [ChartedSpace H M] +variable {I : ModelWithCorners ℝ E H} +variable [IsManifold I (n + 1) M] [IsManifold I 1 M] +variable [inst_tangent_findim : ∀ (x : M), FiniteDimensional ℝ (TangentSpace I x)] + +/-- Coercion from `MetricTensor` to its `val` function. -/ +instance coeFunInst : CoeFun (MetricTensor E H M n I) + (fun _ => ∀ x : M, TangentSpace I x →L[ℝ] (TangentSpace I x →L[ℝ] ℝ)) where + coe g := g.val + +/-- The bilinear form on `TₓM` associated to a metric tensor. -/ +def toBilinForm (g : MetricTensor E H M n I) (x : M) : + LinearMap.BilinForm ℝ (TangentSpace I x) where + toFun := fun v => + { toFun := fun w => g.val x v w + map_add' := fun w₁ w₂ => by simp only [ContinuousLinearMap.map_add] + map_smul' := fun c w => by simp only [map_smul, smul_eq_mul, RingHom.id_apply] } + map_add' := fun v₁ v₂ => by + ext w + simp only [map_add, add_apply, LinearMap.coe_mk, AddHom.coe_mk, LinearMap.add_apply] + map_smul' := fun c v => by + ext w + simp only [map_smul, coe_smul', Pi.smul_apply, smul_eq_mul, LinearMap.coe_mk, AddHom.coe_mk, + RingHom.id_apply, LinearMap.smul_apply] + +/-- The quadratic form `v ↦ gₓ(v,v)` associated to a metric tensor. -/ +abbrev toQuadraticForm (g : MetricTensor E H M n I) (x : M) : + QuadraticForm ℝ (TangentSpace I x) := + PseudoRiemannianMetric.valToQuadraticForm g.val g.symm x + +@[simp] +lemma toBilinForm_apply (g : MetricTensor E H M n I) (x : M) (v w : TangentSpace I x) : + toBilinForm g x v w = g.val x v w := rfl + +@[simp] +lemma toQuadraticForm_apply (g : MetricTensor E H M n I) (x : M) (v : TangentSpace I x) : + toQuadraticForm g x v = g.val x v v := rfl + +@[simp] +lemma toBilinForm_isSymm (g : MetricTensor E H M n I) (x : M) : + (toBilinForm g x).IsSymm := by + refine { eq := ?_ } + intro v w + simp [toBilinForm_apply, g.symm x v w] + +@[simp] +lemma toBilinForm_nondegenerate (g : MetricTensor E H M n I) (x : M) : + (toBilinForm g x).Nondegenerate := by + unfold LinearMap.BilinForm.Nondegenerate LinearMap.Nondegenerate + LinearMap.SeparatingLeft LinearMap.SeparatingRight + constructor + · intro v hv + simp_rw [toBilinForm_apply] at hv + exact g.nondegenerate x v hv + · intro v hv + simp_rw [toBilinForm_apply] at hv + have hw : ∀ w : TangentSpace I x, (g.val x v) w = 0 := by + intro w + -- symmetry gives `g(v,w)=g(w,v)=0` + simpa [g.symm x v w] using hv w + exact g.nondegenerate x v hw + +/-- The value `gₓ(v,w)` of a metric tensor. -/ +def inner (g : MetricTensor E H M n I) (x : M) (v w : TangentSpace I x) : ℝ := + g.val x v w + +@[simp] +lemma inner_apply (g : MetricTensor E H M n I) (x : M) (v w : TangentSpace I x) : + inner g x v w = g.val x v w := rfl + +/-! ## Flat / sharp (musical isomorphisms) -/ + +/-- Index lowering map `v ↦ gₓ(v, -)` as a linear map. -/ +def flat (g : MetricTensor E H M n I) (x : M) : + TangentSpace I x →ₗ[ℝ] (TangentSpace I x →L[ℝ] ℝ) := + { toFun := fun v => g.val x v + map_add' := fun v w => by simp [ContinuousLinearMap.map_add] + map_smul' := fun a v => by simp} + +@[simp] +lemma flat_apply (g : MetricTensor E H M n I) (x : M) (v w : TangentSpace I x) : + (flat g x v) w = g.val x v w := rfl + +/-- Index lowering map as a continuous linear map. -/ +def flatL (g : MetricTensor E H M n I) (x : M) : + TangentSpace I x →L[ℝ] (TangentSpace I x →L[ℝ] ℝ) where + toFun := fun v => g.val x v + map_add' := fun v w => by simp [ContinuousLinearMap.map_add] + map_smul' := fun a v => by simp + cont := ContinuousLinearMap.continuous (g.val x) + +@[simp] +lemma flatL_apply (g : MetricTensor E H M n I) (x : M) (v w : TangentSpace I x) : + (flatL g x v) w = g.val x v w := rfl + +lemma flat_inj (g : MetricTensor E H M n I) (x : M) : Function.Injective (flat g x) := by + rw [← LinearMap.ker_eq_bot] + apply LinearMap.ker_eq_bot'.mpr + intro v hv + apply g.nondegenerate x v + intro w + exact DFunLike.congr_fun hv w + +lemma flatL_inj (g : MetricTensor E H M n I) (x : M) : Function.Injective (flatL g x) := + flat_inj g x + +lemma flatL_surj (g : MetricTensor E H M n I) (x : M) : Function.Surjective (g.flatL x) := by + haveI : FiniteDimensional ℝ (TangentSpace I x) := inst_tangent_findim x + have h_finrank_eq : + finrank ℝ (TangentSpace I x) = finrank ℝ (TangentSpace I x →L[ℝ] ℝ) := by + have h_dual_eq : finrank ℝ (TangentSpace I x →L[ℝ] ℝ) = + finrank ℝ (Module.Dual ℝ (TangentSpace I x)) := by + let to_dual : (TangentSpace I x →L[ℝ] ℝ) → Module.Dual ℝ (TangentSpace I x) := + fun f => f.toLinearMap + let from_dual : Module.Dual ℝ (TangentSpace I x) → (TangentSpace I x →L[ℝ] ℝ) := + fun f => ContinuousLinearMap.mk f (by + apply LinearMap.continuous_of_finiteDimensional) + let equiv : (TangentSpace I x →L[ℝ] ℝ) ≃ₗ[ℝ] Module.Dual ℝ (TangentSpace I x) := + { toFun := to_dual + invFun := from_dual + map_add' := fun f g => by ext v; rfl + map_smul' := fun c f => by ext v; rfl + left_inv := fun f => by ext v; rfl + right_inv := fun f => by ext v; rfl } + exact LinearEquiv.finrank_eq equiv + rw [h_dual_eq, ← Subspace.dual_finrank_eq] + exact + (LinearMap.injective_iff_surjective_of_finrank_eq_finrank h_finrank_eq).mp (flatL_inj g x) + +/-- `flatEquiv` as a continuous linear equivalence. -/ +def flatEquiv (g : MetricTensor E H M n I) (x : M) : + TangentSpace I x ≃L[ℝ] (TangentSpace I x →L[ℝ] ℝ) := + LinearEquiv.toContinuousLinearEquiv <| + LinearEquiv.ofBijective (g.flatL x).toLinearMap ⟨g.flatL_inj x, g.flatL_surj x⟩ + +@[simp] lemma flatEquiv_apply (g : MetricTensor E H M n I) (x : M) (v w : TangentSpace I x) : + (g.flatEquiv x v) w = g.val x v w := rfl + +/-- Index raising equivalence as the inverse of `flatEquiv`. -/ +def sharpEquiv (g : MetricTensor E H M n I) (x : M) : + (TangentSpace I x →L[ℝ] ℝ) ≃L[ℝ] TangentSpace I x := + (g.flatEquiv x).symm + +def sharpL (g : MetricTensor E H M n I) (x : M) : + (TangentSpace I x →L[ℝ] ℝ) →L[ℝ] TangentSpace I x := + (g.sharpEquiv x).toContinuousLinearMap + +/-- Index raising map `sharp` as a linear map. -/ +noncomputable def sharp (g : MetricTensor E H M n I) (x : M) : + (TangentSpace I x →L[ℝ] ℝ) →ₗ[ℝ] TangentSpace I x := + (g.sharpEquiv x).toLinearEquiv.toLinearMap + +@[simp] +lemma sharpL_apply_flatL (g : MetricTensor E H M n I) (x : M) (v : TangentSpace I x) : + g.sharpL x (g.flatL x v) = v := + (g.flatEquiv x).left_inv v + +@[simp] +lemma flatL_apply_sharpL (g : MetricTensor E H M n I) (x : M) + (ω : TangentSpace I x →L[ℝ] ℝ) : + g.flatL x (g.sharpL x ω) = ω := + (g.flatEquiv x).right_inv ω + +@[simp] +lemma flat_sharp_apply (g : MetricTensor E H M n I) (x : M) + (ω : TangentSpace I x →L[ℝ] ℝ) : + g.flat x (g.sharp x ω) = ω := by + ext v + have h := congrArg (fun f : TangentSpace I x →L[ℝ] ℝ => f v) (flatL_apply_sharpL (g := g) x ω) + simpa [flat, flatL, sharp, sharpL] using h + +@[simp] +lemma sharp_flat_apply (g : MetricTensor E H M n I) (x : M) (v : TangentSpace I x) : + g.sharp x (g.flat x v) = v := by + have h := sharpL_apply_flatL (g := g) x v + simpa [sharp, sharpL, flat, flatL] using h + +/-- Metric evaluated at `sharp ω₁` and `sharp ω₂`. -/ +@[simp] +lemma apply_sharp_sharp (g : MetricTensor E H M n I) (x : M) + (ω₁ ω₂ : TangentSpace I x →L[ℝ] ℝ) : + g.val x (g.sharpL x ω₁) (g.sharpL x ω₂) = ω₁ (g.sharpL x ω₂) := by + rw [← flatL_apply (g := g) x (g.sharpL x ω₁)] + rw [flatL_apply_sharpL (g := g) x ω₁] + +/-- Metric evaluated at `v` and `sharp ω`. -/ +lemma apply_vec_sharp (g : MetricTensor E H M n I) (x : M) (v : TangentSpace I x) + (ω : TangentSpace I x →L[ℝ] ℝ) : + g.val x v (g.sharpL x ω) = ω v := by + rw [g.symm x v (g.sharpL x ω)] + rw [← flatL_apply (g := g) x (g.sharpL x ω)] + rw [flatL_apply_sharpL (g := g) x ω] + +/-! ## Cotangent metric induced by `g` -/ + +/-- The induced metric value on the cotangent space at `x`. -/ +noncomputable def cotangentMetricVal (g : MetricTensor E H M n I) (x : M) + (ω₁ ω₂ : TangentSpace I x →L[ℝ] ℝ) : ℝ := + g.val x (g.sharpL x ω₁) (g.sharpL x ω₂) + +@[simp] lemma cotangentMetricVal_eq_apply_sharp (g : MetricTensor E H M n I) (x : M) + (ω₁ ω₂ : TangentSpace I x →L[ℝ] ℝ) : + cotangentMetricVal g x ω₁ ω₂ = ω₁ (g.sharpL x ω₂) := by + simp [cotangentMetricVal, apply_sharp_sharp] + +lemma cotangentMetricVal_symm (g : MetricTensor E H M n I) (x : M) + (ω₁ ω₂ : TangentSpace I x →L[ℝ] ℝ) : + cotangentMetricVal g x ω₁ ω₂ = cotangentMetricVal g x ω₂ ω₁ := by + unfold cotangentMetricVal + rw [g.symm x (g.sharpL x ω₁) (g.sharpL x ω₂)] + +/-- The induced cotangent metric as a bilinear form. -/ +noncomputable def cotangentToBilinForm (g : MetricTensor E H M n I) (x : M) : + LinearMap.BilinForm ℝ (TangentSpace I x →L[ℝ] ℝ) where + toFun ω₁ := + { toFun := fun ω₂ => cotangentMetricVal g x ω₁ ω₂ + map_add' := fun ω₂ ω₃ => by simp [cotangentMetricVal, ContinuousLinearMap.map_add] + map_smul' := fun c ω₂ => by simp [cotangentMetricVal, map_smul, smul_eq_mul, RingHom.id_apply] } + map_add' := fun ω₁ ω₂ => by + ext ω₃ + simp [cotangentMetricVal, ContinuousLinearMap.map_add, ContinuousLinearMap.add_apply, + LinearMap.coe_mk, AddHom.coe_mk, LinearMap.add_apply] + map_smul' := fun c ω₁ => by + ext ω₂ + simp [cotangentMetricVal, ContinuousLinearMap.smul_apply, + LinearMap.coe_mk, AddHom.coe_mk, RingHom.id_apply, LinearMap.smul_apply] + +/-- The induced cotangent metric as a quadratic form. -/ +noncomputable def cotangentToQuadraticForm (g : MetricTensor E H M n I) (x : M) : + QuadraticForm ℝ (TangentSpace I x →L[ℝ] ℝ) where + toFun ω := cotangentMetricVal g x ω ω + toFun_smul a ω := by + simp [cotangentMetricVal, ContinuousLinearMap.map_smul, ContinuousLinearMap.smul_apply, smul_smul] + ring + exists_companion' := + ⟨LinearMap.mk₂ ℝ (fun ω₁ ω₂ => + cotangentMetricVal g x ω₁ ω₂ + cotangentMetricVal g x ω₂ ω₁) + (fun ω₁ ω₂ ω₃ => by simp [cotangentMetricVal, map_add, add_apply]; ring) + (fun a ω₁ ω₂ => by + simp [cotangentMetricVal, map_smul, smul_apply] + ring_nf) + (fun ω₁ ω₂ ω₃ => by simp [cotangentMetricVal, map_add, add_apply]; ring) + (fun a ω₁ ω₂ => by + simp [cotangentMetricVal, map_smul, smul_apply] + ring_nf), + by + intro ω₁ ω₂ + simp [LinearMap.mk₂_apply, cotangentMetricVal, ContinuousLinearMap.map_add, + ContinuousLinearMap.add_apply] + ring⟩ + +@[simp] lemma cotangentToBilinForm_apply (g : MetricTensor E H M n I) (x : M) + (ω₁ ω₂ : TangentSpace I x →L[ℝ] ℝ) : + cotangentToBilinForm g x ω₁ ω₂ = cotangentMetricVal g x ω₁ ω₂ := rfl + +@[simp] lemma cotangentToQuadraticForm_apply (g : MetricTensor E H M n I) (x : M) + (ω : TangentSpace I x →L[ℝ] ℝ) : + cotangentToQuadraticForm g x ω = cotangentMetricVal g x ω ω := rfl + +@[simp] lemma cotangentToBilinForm_isSymm (g : MetricTensor E H M n I) (x : M) : + (cotangentToBilinForm g x).IsSymm := by + refine { eq := ?_ } + intro ω₁ ω₂ + -- avoid rewriting `cotangentMetricVal` via its simp-lemma; use symmetry of `g` directly + simp [cotangentToBilinForm_apply, cotangentMetricVal, g.symm] + +/-- Nondegeneracy of the cotangent metric. -/ +lemma cotangentMetricVal_nondegenerate (g : MetricTensor E H M n I) (x : M) + (ω : TangentSpace I x →L[ℝ] ℝ) + (h : ∀ v : TangentSpace I x →L[ℝ] ℝ, cotangentMetricVal g x ω v = 0) : + ω = 0 := by + apply ContinuousLinearMap.ext + intro v + have h_forall : ∀ w : TangentSpace I x, ω w = 0 := by + intro w + let ω' : TangentSpace I x →L[ℝ] ℝ := g.flatL x w + have this : g.sharpL x ω' = w := by + simp [ω'] + have h_apply : cotangentMetricVal g x ω ω' = 0 := h ω' + simp [cotangentMetricVal_eq_apply_sharp] at h_apply + simpa [this] using h_apply + exact h_forall v + +@[simp] lemma cotangentToBilinForm_nondegenerate (g : MetricTensor E H M n I) (x : M) : + (cotangentToBilinForm g x).Nondegenerate := by + unfold LinearMap.BilinForm.Nondegenerate LinearMap.Nondegenerate + LinearMap.SeparatingLeft LinearMap.SeparatingRight + constructor + · intro ω hω + apply cotangentMetricVal_nondegenerate (g := g) x ω + intro v + exact hω v + · intro ω hω + apply cotangentMetricVal_nondegenerate (g := g) x ω + intro v + have hv : ∀ y : TangentSpace I x →L[ℝ] ℝ, ((g.cotangentToBilinForm x) ω) y = 0 := by + intro y + rw [LinearMap.BilinForm.isSymm_def.mp (cotangentToBilinForm_isSymm (g := g) x)] + simp [hω] + exact hv v + +/-- The cotangent quadratic form is equivalent to the tangent quadratic form via `sharp`. -/ +theorem cotangentToQuadraticForm_equivalent_toQuadraticForm (g : MetricTensor E H M n I) (x : M) : + (g.cotangentToQuadraticForm x).Equivalent (g.toQuadraticForm x) := by + classical + refine ⟨?_⟩ + refine + { toLinearEquiv := (g.sharpEquiv x).toLinearEquiv + map_app' := fun ω => ?_ } + simp [cotangentToQuadraticForm_apply, cotangentMetricVal, toQuadraticForm, toQuadraticForm_apply, + sharpEquiv, sharpL] + +end MetricTensor + /-- A pseudo-Riemannian metric of smoothness class `C^n` on a manifold `M` modelled on `(E, H)` with model `I`. This structure defines a smoothly varying, non-degenerate, symmetric, continuous bilinear form `gₓ` of constant negative dimension on the tangent space `TₓM` @@ -148,7 +466,7 @@ This structure formalizes O'Neill's Definition 3.1 (p. 54) of a metric tensor `g as a "symmetric non-degenerate (0,2) tensor field on M of constant index." Each `gₓ` is a scalar product (O'Neill, Definition 20, p. 47) on `TₓM`. -/ @[ext] -structure PseudoRiemannianMetric extends MetricTensor E H M n I +structure PseudoRiemannianMetric (E : Type v) (H : Type w) (M : Type w) (n : WithTop ℕ∞) [inst_norm_grp_E : NormedAddCommGroup E] [inst_norm_sp_E : NormedSpace ℝ E] @@ -159,7 +477,7 @@ structure PseudoRiemannianMetric extends MetricTensor E H M n I [inst_mani : IsManifold I (n + 1) M] [inst_mani1 : IsManifold I 1 M] [inst_tangent_findim : ∀ (x : M), FiniteDimensional ℝ (TangentSpace I x)] : - Type (max u v w) where + Type _ extends toMetricTensor : MetricTensor E H M n I where /-- The negative dimension (`QuadraticForm.negDim`) of the metric's quadratic form is locally constant. On a connected manifold, this implies it is constant globally. -/ negDim_isLocallyConstant : @@ -181,25 +499,14 @@ variable {g : PseudoRiemannianMetric E H M n I} this function constructs a bilinear form on the tangent space at `x`. For tangent vectors `u v : T_x M`, the bilinear form is given by: `g_x(u, v) = g(x)(u, v)` -/ -def toBilinForm (g : PseudoRiemannianMetric E H M n I) (x : M) : - LinearMap.BilinForm ℝ (TangentSpace I x) where - toFun := λ v => { toFun := λ w => g.val x v w, - map_add' := λ w₁ w₂ => by - simp only [ContinuousLinearMap.map_add], - map_smul' := λ c w => by - simp only [map_smul, smul_eq_mul, RingHom.id_apply] } - map_add' := λ v₁ v₂ => by - ext w - simp only [map_add, add_apply, LinearMap.coe_mk, AddHom.coe_mk, LinearMap.add_apply] - map_smul' := λ c v => by - ext w - simp only [map_smul, coe_smul', Pi.smul_apply, smul_eq_mul, LinearMap.coe_mk, AddHom.coe_mk, - RingHom.id_apply, LinearMap.smul_apply] +abbrev toBilinForm (g : PseudoRiemannianMetric E H M n I) (x : M) : + LinearMap.BilinForm ℝ (TangentSpace I x) := + MetricTensor.toBilinForm (g := g.toMetricTensor) x /-- Convert a pseudo-Riemannian metric at a point `x` to a quadratic form `v ↦ gₓ(v, v)`. -/ abbrev toQuadraticForm (g : PseudoRiemannianMetric E H M n I) (x : M) : QuadraticForm ℝ (TangentSpace I x) := - PseudoRiemannianMetric.valToQuadraticForm g.val g.symm x + MetricTensor.toQuadraticForm (g := g.toMetricTensor) x /-- Coercion from PseudoRiemannianMetric to its function representation. -/ instance coeFunInst : CoeFun (PseudoRiemannianMetric E H M n I) @@ -263,7 +570,8 @@ lemma toBilinForm_nondegenerate (g : PseudoRiemannianMetric E H M n I) (x : M) : · intro v hv; simp_rw [toBilinForm_apply] at hv; exact g.nondegenerate x v hv · intro v hv; simp_rw [toBilinForm_apply] at hv; have hw : ∀ (w : TangentSpace I x), ((g.val x) v) w = 0 := by - intro w; rw [symm]; simp [hv] + intro w + simpa [g.symm x v w] using hv w exact g.nondegenerate x v hw /-- The inner product (or scalar product) on the tangent space at point `x` @@ -285,21 +593,16 @@ is invertible (O'Neill, Lemma 19, p. 47), and that this map is an isomorphism fr to its dual. -/ def flat (g : PseudoRiemannianMetric E H M n I) (x : M) : TangentSpace I x →ₗ[ℝ] (TangentSpace I x →L[ℝ] ℝ) := - { toFun := λ v => g.val x v, - map_add' := λ v w => by simp only [ContinuousLinearMap.map_add], - map_smul' := λ a v => by simp only [ContinuousLinearMap.map_smul]; rfl } + MetricTensor.flat (g := g.toMetricTensor) x @[simp] lemma flat_apply (g : PseudoRiemannianMetric E H M n I) (x : M) (v w : TangentSpace I x) : (flat g x v) w = g.val x v w := by rfl /-- The musical isomorphism as a continuous linear map. -/ -def flatL (g : PseudoRiemannianMetric E H M n I) (x : M) : - TangentSpace I x →L[ℝ] (TangentSpace I x →L[ℝ] ℝ) where - toFun := λ v => g.val x v - map_add' := λ v w => by simp only [ContinuousLinearMap.map_add] - map_smul' := λ a v => by simp only [ContinuousLinearMap.map_smul]; rfl - cont := ContinuousLinearMap.continuous (g.val x) +abbrev flatL (g : PseudoRiemannianMetric E H M n I) (x : M) : + TangentSpace I x →L[ℝ] (TangentSpace I x →L[ℝ] ℝ) := + MetricTensor.flatL (g := g.toMetricTensor) x @[simp] lemma flatL_apply (g : PseudoRiemannianMetric E H M n I) (x : M) (v w : TangentSpace I x) : From 6b93d731771d38ab0b1cc03c2aa78bd10aa3d56f Mon Sep 17 00:00:00 2001 From: Matteo CIpollina Date: Sun, 1 Mar 2026 23:49:40 +0100 Subject: [PATCH 06/36] Update NegDim.lean --- PhysLean/Mathematics/Geometry/Metric/QuadraticForm/NegDim.lean | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PhysLean/Mathematics/Geometry/Metric/QuadraticForm/NegDim.lean b/PhysLean/Mathematics/Geometry/Metric/QuadraticForm/NegDim.lean index ac5fb0c56..f94c26778 100644 --- a/PhysLean/Mathematics/Geometry/Metric/QuadraticForm/NegDim.lean +++ b/PhysLean/Mathematics/Geometry/Metric/QuadraticForm/NegDim.lean @@ -843,7 +843,7 @@ theorem zeroDim_weightedSumSquares_signType (w : Fin n → SignType) : Fintype.card (Fin n) - A - B = (Fintype.card (Fin n) - A) - B := rfl _ = (B + C) - B := by simpa using congrArg (fun t => t - B) h1 - _ = C := by simp [Nat.add_sub_cancel_left] + _ = C := by simp set Q : QuadraticForm ℝ (Fin n → ℝ) := (QuadraticMap.weightedSumSquares ℝ fun i : Fin n => (w i : ℝ)) have hposI : posIndex (V := Fin n → ℝ) Q = A := by From 10e216644c521210158111a24717a9cc07bcc116 Mon Sep 17 00:00:00 2001 From: Matteo CIpollina Date: Sun, 1 Mar 2026 23:49:42 +0100 Subject: [PATCH 07/36] Update Defs.lean --- .../Metric/PseudoRiemannian/Defs.lean | 199 ++++++++++++------ 1 file changed, 136 insertions(+), 63 deletions(-) diff --git a/PhysLean/Mathematics/Geometry/Metric/PseudoRiemannian/Defs.lean b/PhysLean/Mathematics/Geometry/Metric/PseudoRiemannian/Defs.lean index d7d61060c..fb4ccf642 100644 --- a/PhysLean/Mathematics/Geometry/Metric/PseudoRiemannian/Defs.lean +++ b/PhysLean/Mathematics/Geometry/Metric/PseudoRiemannian/Defs.lean @@ -58,6 +58,82 @@ noncomputable section open Bundle Set Finset Function Filter Module Topology ContinuousLinearMap open scoped Manifold Bundle LinearMap Dual +/-! ## Bundle-level infrastructure (Mathlib-style) -/ + +namespace Bundle + +/-! ### Fiberwise pseudo-Riemannian structures -/ + +section PseudoRiemannianBundle + +variable + {B : Type*} [TopologicalSpace B] + {E : B → Type*} [∀ x, NormedAddCommGroup (E x)] [∀ x, NormedSpace ℝ (E x)] + +/-- A pseudo-Riemannian structure on a family of fibers `E x`: a symmetric, nondegenerate bilinear +form on each fiber, expressed as a continuous bilinear map. -/ +class PseudoRiemannianBundle : Type _ where + metric : ∀ x : B, E x →L[ℝ] E x →L[ℝ] ℝ + symm : ∀ (x : B) (v w : E x), metric x v w = metric x w v + nondegenerate : ∀ (x : B) (v : E x), (∀ w : E x, metric x v w = 0) → v = 0 + +variable [PseudoRiemannianBundle (B := B) (E := E)] + +/-- The fiberwise pseudo-inner-product \(g_x(v,w)\). -/ +abbrev pseudoInner (x : B) (v w : E x) : ℝ := + (PseudoRiemannianBundle.metric (B := B) (E := E) x) v w + +end PseudoRiemannianBundle + +/-! ### Smoothness of pseudo-Riemannian structures on vector bundles -/ + +section ContMDiff + +open scoped ENat + +variable + {EB : Type*} [NormedAddCommGroup EB] [NormedSpace ℝ EB] + {HB : Type*} [TopologicalSpace HB] {IB : ModelWithCorners ℝ EB HB} {n n' : WithTop ℕ∞} + {B : Type*} [TopologicalSpace B] [ChartedSpace HB B] + {F : Type*} [NormedAddCommGroup F] [NormedSpace ℝ F] + {E : B → Type*} [TopologicalSpace (TotalSpace F E)] [∀ x, NormedAddCommGroup (E x)] + [∀ x, NormedSpace ℝ (E x)] + [FiberBundle F E] [VectorBundle ℝ F E] + [PseudoRiemannianBundle (B := B) (E := E)] + +variable (IB n F E) in +/-- A pseudo-Riemannian bundle is `C^n` if its fiberwise bilinear form depends `C^n`-smoothly on the +base point, when seen as a section of the bundle of bilinear forms. -/ +class IsContMDiffPseudoRiemannianBundle : Prop where + exists_contMDiff : + ∃ g : Π x : B, E x →L[ℝ] E x →L[ℝ] ℝ, + ContMDiff IB (IB.prod 𝓘(ℝ, F →L[ℝ] F →L[ℝ] ℝ)) n + (fun b ↦ TotalSpace.mk' (F →L[ℝ] F →L[ℝ] ℝ) b (g b)) + ∧ ∀ (x : B) (v w : E x), pseudoInner (B := B) (E := E) x v w = g x v w + +lemma IsContMDiffPseudoRiemannianBundle.of_le + [h : IsContMDiffPseudoRiemannianBundle (IB := IB) (n := n) (F := F) (E := E)] (h' : n' ≤ n) : + IsContMDiffPseudoRiemannianBundle (IB := IB) (n := n') (F := F) (E := E) := by + rcases h.exists_contMDiff with ⟨g, g_smooth, hg⟩ + exact ⟨g, g_smooth.of_le h', hg⟩ + +instance [IsContMDiffPseudoRiemannianBundle (IB := IB) (n := (1 : WithTop ℕ∞)) (F := F) (E := E)] : + IsContMDiffPseudoRiemannianBundle (IB := IB) (n := (0 : WithTop ℕ∞)) (F := F) (E := E) := + IsContMDiffPseudoRiemannianBundle.of_le (IB := IB) (F := F) (E := E) zero_le_one + +instance [IsContMDiffPseudoRiemannianBundle (IB := IB) (n := (2 : WithTop ℕ∞)) (F := F) (E := E)] : + IsContMDiffPseudoRiemannianBundle (IB := IB) (n := (1 : WithTop ℕ∞)) (F := F) (E := E) := + IsContMDiffPseudoRiemannianBundle.of_le (IB := IB) (F := F) (E := E) one_le_two + +instance [IsContMDiffPseudoRiemannianBundle (IB := IB) (n := (3 : WithTop ℕ∞)) (F := F) (E := E)] : + IsContMDiffPseudoRiemannianBundle (IB := IB) (n := (2 : WithTop ℕ∞)) (F := F) (E := E) := + IsContMDiffPseudoRiemannianBundle.of_le (IB := IB) (n := (3 : WithTop ℕ∞)) (F := F) (E := E) + (by norm_cast) + +end ContMDiff + +end Bundle + /-! ## Pseudo-Riemannian Metric -/ /-! @@ -340,7 +416,7 @@ lemma apply_vec_sharp (g : MetricTensor E H M n I) (x : M) (v : TangentSpace I x /-! ## Cotangent metric induced by `g` -/ /-- The induced metric value on the cotangent space at `x`. -/ -noncomputable def cotangentMetricVal (g : MetricTensor E H M n I) (x : M) +def cotangentMetricVal (g : MetricTensor E H M n I) (x : M) (ω₁ ω₂ : TangentSpace I x →L[ℝ] ℝ) : ℝ := g.val x (g.sharpL x ω₁) (g.sharpL x ω₂) @@ -356,7 +432,7 @@ lemma cotangentMetricVal_symm (g : MetricTensor E H M n I) (x : M) rw [g.symm x (g.sharpL x ω₁) (g.sharpL x ω₂)] /-- The induced cotangent metric as a bilinear form. -/ -noncomputable def cotangentToBilinForm (g : MetricTensor E H M n I) (x : M) : +def cotangentToBilinForm (g : MetricTensor E H M n I) (x : M) : LinearMap.BilinForm ℝ (TangentSpace I x →L[ℝ] ℝ) where toFun ω₁ := { toFun := fun ω₂ => cotangentMetricVal g x ω₁ ω₂ @@ -372,11 +448,11 @@ noncomputable def cotangentToBilinForm (g : MetricTensor E H M n I) (x : M) : LinearMap.coe_mk, AddHom.coe_mk, RingHom.id_apply, LinearMap.smul_apply] /-- The induced cotangent metric as a quadratic form. -/ -noncomputable def cotangentToQuadraticForm (g : MetricTensor E H M n I) (x : M) : +def cotangentToQuadraticForm (g : MetricTensor E H M n I) (x : M) : QuadraticForm ℝ (TangentSpace I x →L[ℝ] ℝ) where toFun ω := cotangentMetricVal g x ω ω toFun_smul a ω := by - simp [cotangentMetricVal, ContinuousLinearMap.map_smul, ContinuousLinearMap.smul_apply, smul_smul] + simp [cotangentMetricVal, ContinuousLinearMap.smul_apply] ring exists_companion' := ⟨LinearMap.mk₂ ℝ (fun ω₁ ω₂ => @@ -395,19 +471,21 @@ noncomputable def cotangentToQuadraticForm (g : MetricTensor E H M n I) (x : M) ContinuousLinearMap.add_apply] ring⟩ -@[simp] lemma cotangentToBilinForm_apply (g : MetricTensor E H M n I) (x : M) +@[simp] +lemma cotangentToBilinForm_apply (g : MetricTensor E H M n I) (x : M) (ω₁ ω₂ : TangentSpace I x →L[ℝ] ℝ) : cotangentToBilinForm g x ω₁ ω₂ = cotangentMetricVal g x ω₁ ω₂ := rfl -@[simp] lemma cotangentToQuadraticForm_apply (g : MetricTensor E H M n I) (x : M) +@[simp] +lemma cotangentToQuadraticForm_apply (g : MetricTensor E H M n I) (x : M) (ω : TangentSpace I x →L[ℝ] ℝ) : cotangentToQuadraticForm g x ω = cotangentMetricVal g x ω ω := rfl -@[simp] lemma cotangentToBilinForm_isSymm (g : MetricTensor E H M n I) (x : M) : +@[simp] +lemma cotangentToBilinForm_isSymm (g : MetricTensor E H M n I) (x : M) : (cotangentToBilinForm g x).IsSymm := by refine { eq := ?_ } intro ω₁ ω₂ - -- avoid rewriting `cotangentMetricVal` via its simp-lemma; use symmetry of `g` directly simp [cotangentToBilinForm_apply, cotangentMetricVal, g.symm] /-- Nondegeneracy of the cotangent metric. -/ @@ -453,8 +531,7 @@ theorem cotangentToQuadraticForm_equivalent_toQuadraticForm (g : MetricTensor E refine { toLinearEquiv := (g.sharpEquiv x).toLinearEquiv map_app' := fun ω => ?_ } - simp [cotangentToQuadraticForm_apply, cotangentMetricVal, toQuadraticForm, toQuadraticForm_apply, - sharpEquiv, sharpL] + simp [cotangentToQuadraticForm_apply, cotangentMetricVal, toQuadraticForm, sharpEquiv, sharpL] end MetricTensor @@ -493,7 +570,23 @@ variable [TopologicalSpace H] [TopologicalSpace M] [ChartedSpace H M] variable {I : ModelWithCorners ℝ E H} variable [IsManifold I (n + 1) M] [IsManifold I 1 M] variable [inst_tangent_findim : ∀ (x : M), FiniteDimensional ℝ (TangentSpace I x)] -variable {g : PseudoRiemannianMetric E H M n I} + +/-! ## Predicate typeclass: pseudo-Riemannian manifolds -/ + +/-- Prop-valued typeclass recording that the manifold carries *some* `C^n` pseudo-Riemannian metric. + +This mirrors the way one often uses `IsRiemannianManifold` as a Prop-valued predicate, but here +we do not derive any distance structure (pseudo-metrics are indefinite). -/ +class IsPseudoRiemannianManifold : Prop where + exists_metric : ∃ _ : PseudoRiemannianMetric E H M n I, True + +noncomputable def pseudoRiemannianMetric [h : IsPseudoRiemannianManifold (E := E) (H := H) (M := M) (n := n) (I := I)] : + PseudoRiemannianMetric E H M n I := + Classical.choose h.exists_metric + +instance (g : PseudoRiemannianMetric E H M n I) : + IsPseudoRiemannianManifold (E := E) (H := H) (M := M) (n := n) (I := I) := + ⟨⟨g, trivial⟩⟩ /-- Given a pseudo-Riemannian metric `g` on manifold `M` and a point `x : M`, this function constructs a bilinear form on the tangent space at `x`. @@ -530,12 +623,12 @@ the associated quadratic form `v ↦ gₓ(v,v)`. -/ def index (g : PseudoRiemannianMetric E H M n I) (x : M) : ℕ := (g.toQuadraticForm x).negDim -@[simp] lemma index_def (g : PseudoRiemannianMetric E H M n I) (x : M) : - g.index x = (g.toQuadraticForm x).negDim := rfl +@[simp] +lemma index_def (g : PseudoRiemannianMetric E H M n I) (x : M) : + g.index x = (g.toQuadraticForm x).negDim := rfl lemma index_isLocallyConstant (g : PseudoRiemannianMetric E H M n I) : IsLocallyConstant (fun x : M => g.index x) := by - -- this is exactly the structure field, rewritten through `toQuadraticForm` simpa [index, toQuadraticForm] using g.negDim_isLocallyConstant lemma index_eq_of_isPreconnected (g : PseudoRiemannianMetric E H M n I) {s : Set M} @@ -623,29 +716,8 @@ lemma flatL_inj (g : PseudoRiemannianMetric E H M n I) (x : M) : lemma flatL_surj (g : PseudoRiemannianMetric E H M n I) (x : M) : Function.Surjective (g.flatL x) := by - haveI : FiniteDimensional ℝ (TangentSpace I x) := inst_tangent_findim x - have h_finrank_eq : finrank ℝ (TangentSpace I x) = finrank ℝ (TangentSpace I x →L[ℝ] ℝ) := by - have h_dual_eq : finrank ℝ (TangentSpace I x →L[ℝ] ℝ) = finrank ℝ (Module.Dual ℝ - (TangentSpace I x)) := by - let to_dual : (TangentSpace I x →L[ℝ] ℝ) → Module.Dual ℝ (TangentSpace I x) := - fun f => f.toLinearMap - let from_dual : Module.Dual ℝ (TangentSpace I x) → (TangentSpace I x →L[ℝ] ℝ) := fun f => - ContinuousLinearMap.mk f (by - apply LinearMap.continuous_of_finiteDimensional) - let equiv : (TangentSpace I x →L[ℝ] ℝ) ≃ₗ[ℝ] Module.Dual ℝ (TangentSpace I x) := - { toFun := to_dual, - invFun := from_dual, - map_add' := fun f g => by - ext v; unfold to_dual; simp only [LinearMap.add_apply]; rfl, - map_smul' := fun c f => by - ext v; unfold to_dual; simp only [LinearMap.smul_apply]; rfl, - left_inv := fun f => by - ext v; unfold to_dual from_dual; simp, - right_inv := fun f => by - ext v; unfold to_dual from_dual; simp } - exact LinearEquiv.finrank_eq equiv - rw [h_dual_eq, ← Subspace.dual_finrank_eq] - exact (LinearMap.injective_iff_surjective_of_finrank_eq_finrank h_finrank_eq).mp (flatL_inj g x) + simpa [PseudoRiemannianMetric.flatL] using + MetricTensor.flatL_surj (g := g.toMetricTensor) x /-- The "musical" isomorphism (index lowering) from `TₓM` to its dual, as a continuous linear equivalence. This equivalence is a direct result of `gₓ` being @@ -654,10 +726,7 @@ def flatEquiv (g : PseudoRiemannianMetric E H M n I) (x : M) : TangentSpace I x ≃L[ℝ] (TangentSpace I x →L[ℝ] ℝ) := - LinearEquiv.toContinuousLinearEquiv - (LinearEquiv.ofBijective - ((g.flatL x).toLinearMap) - ⟨g.flatL_inj x, g.flatL_surj x⟩) + MetricTensor.flatEquiv (g := g.toMetricTensor) x lemma coe_flatEquiv (g : PseudoRiemannianMetric E H M n I) (x : M) : @@ -680,12 +749,13 @@ guaranteed by the non-degeneracy of `gₓ` (O'Neill, Lemma 19, p. 47). -/ def sharpEquiv (g : PseudoRiemannianMetric E H M n I) (x : M) : (TangentSpace I x →L[ℝ] ℝ) ≃L[ℝ] TangentSpace I x := - (g.flatEquiv x).symm + MetricTensor.sharpEquiv (g := g.toMetricTensor) x /-- The index raising map `sharp` as a continuous linear map. -/ def sharpL (g : PseudoRiemannianMetric E H M n I) (x : M) : - (TangentSpace I x →L[ℝ] ℝ) →L[ℝ] TangentSpace I x := (g.sharpEquiv x).toContinuousLinearMap + (TangentSpace I x →L[ℝ] ℝ) →L[ℝ] TangentSpace I x := + MetricTensor.sharpL (g := g.toMetricTensor) x lemma sharpL_eq_toContinuousLinearMap (g : PseudoRiemannianMetric E H M n I) (x : M) : @@ -698,52 +768,54 @@ lemma coe_sharpEquiv /-- The index raising map `sharp` as a linear map. -/ noncomputable def sharp (g : PseudoRiemannianMetric E H M n I) (x : M) : - (TangentSpace I x →L[ℝ] ℝ) →ₗ[ℝ] TangentSpace I x := (g.sharpEquiv x).toLinearEquiv.toLinearMap + (TangentSpace I x →L[ℝ] ℝ) →ₗ[ℝ] TangentSpace I x := + MetricTensor.sharp (g := g.toMetricTensor) x @[simp] lemma sharpL_apply_flatL (g : PseudoRiemannianMetric E H M n I) (x : M) (v : TangentSpace I x) : g.sharpL x (g.flatL x v) = v := - (g.flatEquiv x).left_inv v + by + simp [PseudoRiemannianMetric.sharpL, PseudoRiemannianMetric.flatL, + MetricTensor.sharpL_apply_flatL (g := g.toMetricTensor) x v] @[simp] lemma flatL_apply_sharpL (g : PseudoRiemannianMetric E H M n I) (x : M) (ω : TangentSpace I x →L[ℝ] ℝ) : - g.flatL x (g.sharpL x ω) = ω := (g.flatEquiv x).right_inv ω + g.flatL x (g.sharpL x ω) = ω := by + simp [PseudoRiemannianMetric.sharpL, PseudoRiemannianMetric.flatL, + MetricTensor.flatL_apply_sharpL (g := g.toMetricTensor) x ω] /-- Applying `sharp` then `flat` recovers the original covector. -/ @[simp] lemma flat_sharp_apply (g : PseudoRiemannianMetric E H M n I) (x : M) (ω : TangentSpace I x →L[ℝ] ℝ) : g.flat x (g.sharp x ω) = ω := by - have := flatL_apply_sharpL g x ω - simp only [flat, sharp]; simp only [LinearEquiv.coe_coe] at this ⊢ - exact this + simp [PseudoRiemannianMetric.flat, PseudoRiemannianMetric.sharp, + MetricTensor.flat_sharp_apply (g := g.toMetricTensor) x ω] @[simp] lemma sharp_flat_apply (g : PseudoRiemannianMetric E H M n I) (x : M) (v : TangentSpace I x) : g.sharp x (g.flat x v) = v := by - have := sharpL_apply_flatL g x v - simp only [sharp, flat]; simp only [LinearEquiv.coe_coe] at this ⊢ - exact this + simp [PseudoRiemannianMetric.flat, PseudoRiemannianMetric.sharp, + MetricTensor.sharp_flat_apply (g := g.toMetricTensor) x v] /-- The metric evaluated at `sharp ω₁` and `sharp ω₂`. -/ @[simp] lemma apply_sharp_sharp (g : PseudoRiemannianMetric E H M n I) (x : M) (ω₁ ω₂ : TangentSpace I x →L[ℝ] ℝ) : g.val x (g.sharpL x ω₁) (g.sharpL x ω₂) = ω₁ (g.sharpL x ω₂) := by - rw [← flatL_apply g x (g.sharpL x ω₁)] - rw [flatL_apply_sharpL g x ω₁] + simp [PseudoRiemannianMetric.sharpL, + MetricTensor.apply_sharp_sharp (g := g.toMetricTensor) x ω₁ ω₂] /-- The metric evaluated at `v` and `sharp ω`. -/ lemma apply_vec_sharp (g : PseudoRiemannianMetric E H M n I) (x : M) (v : TangentSpace I x) (ω : TangentSpace I x →L[ℝ] ℝ) : g.val x v (g.sharpL x ω) = ω v := by - rw [g.symm x v (g.sharpL x ω)] - rw [← flatL_apply g x (g.sharpL x ω)] - rw [flatL_apply_sharpL g x ω] + simp [PseudoRiemannianMetric.sharpL, + MetricTensor.apply_vec_sharp (g := g.toMetricTensor) x v ω] end Sharp @@ -758,7 +830,7 @@ variable [IsManifold I (n + 1) M] [IsManifold I 1 M] variable [inst_tangent_findim : ∀ (x : M), FiniteDimensional ℝ (TangentSpace I x)] /-- The value of the induced metric on the cotangent space at point `x`. -/ -noncomputable def cotangentMetricVal (g : PseudoRiemannianMetric E H M n I) (x : M) +def cotangentMetricVal (g : PseudoRiemannianMetric E H M n I) (x : M) (ω₁ ω₂ : TangentSpace I x →L[ℝ] ℝ) : ℝ := g.val x (g.sharpL x ω₁) (g.sharpL x ω₂) @@ -777,7 +849,7 @@ lemma cotangentMetricVal_symm (g : PseudoRiemannianMetric E H M n I) (x : M) /-- The induced metric on the cotangent space at point `x` as a bilinear form. For covectors `ω₁` and `ω₂`, this gives `g(ω₁^#, ω₂^#)`, where `ω^#` is the "sharp" musical isomorphism raising indices. -/ -noncomputable def cotangentToBilinForm (g : PseudoRiemannianMetric E H M n I) (x : M) : +def cotangentToBilinForm (g : PseudoRiemannianMetric E H M n I) (x : M) : LinearMap.BilinForm ℝ (TangentSpace I x →L[ℝ] ℝ) where toFun ω₁ := { toFun := λ ω₂ => cotangentMetricVal g x ω₁ ω₂, map_add' := λ ω₂ ω₃ => by @@ -802,7 +874,7 @@ noncomputable def cotangentToBilinForm (g : PseudoRiemannianMetric E H M n I) (x /-- The cometric on the cotangent space T_x*M at `x`, expressed as a quadratic form. It is induced by the pseudo-Riemannian metric on the tangent space T_xM. -/ -noncomputable def cotangentToQuadraticForm (g : PseudoRiemannianMetric E H M n I) (x : M) : +def cotangentToQuadraticForm (g : PseudoRiemannianMetric E H M n I) (x : M) : QuadraticForm ℝ (TangentSpace I x →L[ℝ] ℝ) where toFun ω := cotangentMetricVal g x ω ω toFun_smul a ω := by @@ -891,9 +963,10 @@ theorem cotangentToQuadraticForm_equivalent_toQuadraticForm refine { toLinearEquiv := (g.sharpEquiv x).toLinearEquiv map_app' := fun ω => ?_ } - -- unfold through the simp lemmas for both quadratic forms - simp [cotangentToQuadraticForm_apply, cotangentMetricVal, toQuadraticForm, toQuadraticForm_apply, - sharpEquiv, sharpL, coe_sharpEquiv] + have hsh : g.sharpL x ω = g.sharpEquiv x ω := rfl + -- unfold through the simp lemmas for both quadratic forms, but keep `cotangentMetricVal` as `g.val _ _ _` + -- (avoids rewriting to `ω (sharpL ω)`). + simp [cotangentToQuadraticForm_apply, cotangentMetricVal, toQuadraticForm, hsh] theorem cotangent_signature_eq (g : PseudoRiemannianMetric E H M n I) (x : M) : (g.cotangentToQuadraticForm x).signature = (g.toQuadraticForm x).signature := From 9b037f90adaa0f86d97ca525ac6c1e2216edb56f Mon Sep 17 00:00:00 2001 From: Matteo CIpollina Date: Mon, 2 Mar 2026 01:02:08 +0100 Subject: [PATCH 08/36] Update Defs.lean --- .../Metric/PseudoRiemannian/Defs.lean | 98 +++++++++++++++---- 1 file changed, 80 insertions(+), 18 deletions(-) diff --git a/PhysLean/Mathematics/Geometry/Metric/PseudoRiemannian/Defs.lean b/PhysLean/Mathematics/Geometry/Metric/PseudoRiemannian/Defs.lean index fb4ccf642..ef796ced8 100644 --- a/PhysLean/Mathematics/Geometry/Metric/PseudoRiemannian/Defs.lean +++ b/PhysLean/Mathematics/Geometry/Metric/PseudoRiemannian/Defs.lean @@ -79,10 +79,29 @@ class PseudoRiemannianBundle : Type _ where variable [PseudoRiemannianBundle (B := B) (E := E)] +/-- The metric as a family of continuous bilinear maps. -/ +abbrev metric (x : B) : E x →L[ℝ] E x →L[ℝ] ℝ := + PseudoRiemannianBundle.metric (B := B) (E := E) x + /-- The fiberwise pseudo-inner-product \(g_x(v,w)\). -/ abbrev pseudoInner (x : B) (v w : E x) : ℝ := (PseudoRiemannianBundle.metric (B := B) (E := E) x) v w +omit [TopologicalSpace B] in +@[simp] lemma pseudoInner_def (x : B) (v w : E x) : + pseudoInner (B := B) (E := E) x v w = metric (B := B) (E := E) x v w := rfl + +omit [TopologicalSpace B] in +lemma pseudoInner_symm (x : B) (v w : E x) : + pseudoInner (B := B) (E := E) x v w = pseudoInner (B := B) (E := E) x w v := by + simpa [pseudoInner] using (PseudoRiemannianBundle.symm (B := B) (E := E) x v w) + +omit [TopologicalSpace B] in +lemma pseudoInner_nondegenerate (x : B) (v : E x) (hv : ∀ w : E x, + pseudoInner (B := B) (E := E) x v w = 0) : + v = 0 := + PseudoRiemannianBundle.nondegenerate (B := B) (E := E) x v hv + end PseudoRiemannianBundle /-! ### Smoothness of pseudo-Riemannian structures on vector bundles -/ @@ -130,6 +149,54 @@ instance [IsContMDiffPseudoRiemannianBundle (IB := IB) (n := (3 : WithTop ℕ∞ IsContMDiffPseudoRiemannianBundle.of_le (IB := IB) (n := (3 : WithTop ℕ∞)) (F := F) (E := E) (by norm_cast) +section ContMDiffPairing + +variable + {EM : Type*} [NormedAddCommGroup EM] [NormedSpace ℝ EM] + {HM : Type*} [TopologicalSpace HM] {IM : ModelWithCorners ℝ EM HM} + {M : Type*} [TopologicalSpace M] [ChartedSpace HM M] + [h : IsContMDiffPseudoRiemannianBundle (IB := IB) (n := n) (F := F) (E := E)] + {b : M → B} {v w : ∀ x, E (b x)} {s : Set M} {x : M} + +/-- Given two smooth maps into the same fibers of a pseudo-Riemannian bundle, their pairing is smooth. -/ +lemma ContMDiffWithinAt.pseudoInner_bundle + (hv : ContMDiffWithinAt IM (IB.prod 𝓘(ℝ, F)) n (fun m ↦ (v m : TotalSpace F E)) s x) + (hw : ContMDiffWithinAt IM (IB.prod 𝓘(ℝ, F)) n (fun m ↦ (w m : TotalSpace F E)) s x) : + ContMDiffWithinAt IM 𝓘(ℝ) n (fun m ↦ pseudoInner (B := B) (E := E) (b m) (v m) (w m)) s x := by + rcases h.exists_contMDiff with ⟨g, g_smooth, hg⟩ + have hb : ContMDiffWithinAt IM IB n b s x := by + simp only [contMDiffWithinAt_totalSpace] at hv + exact hv.1 + simp only [hg] + have : ContMDiffWithinAt IM (IB.prod 𝓘(ℝ)) n + (fun m ↦ TotalSpace.mk' ℝ (E := Bundle.Trivial B ℝ) (b m) (g (b m) (v m) (w m))) s x := by + apply ContMDiffWithinAt.clm_bundle_apply₂ (F₁ := F) (F₂ := F) + · exact ContMDiffAt.comp_contMDiffWithinAt x g_smooth.contMDiffAt hb + · exact hv + · exact hw + simp only [contMDiffWithinAt_totalSpace] at this + exact this.2 + +lemma ContMDiffAt.pseudoInner_bundle + (hv : ContMDiffAt IM (IB.prod 𝓘(ℝ, F)) n (fun m ↦ (v m : TotalSpace F E)) x) + (hw : ContMDiffAt IM (IB.prod 𝓘(ℝ, F)) n (fun m ↦ (w m : TotalSpace F E)) x) : + ContMDiffAt IM 𝓘(ℝ) n (fun m ↦ pseudoInner (B := B) (E := E) (b m) (v m) (w m)) x := + ContMDiffWithinAt.pseudoInner_bundle (IB := IB) (n := n) (F := F) (E := E) hv hw + +lemma ContMDiffOn.pseudoInner_bundle + (hv : ContMDiffOn IM (IB.prod 𝓘(ℝ, F)) n (fun m ↦ (v m : TotalSpace F E)) s) + (hw : ContMDiffOn IM (IB.prod 𝓘(ℝ, F)) n (fun m ↦ (w m : TotalSpace F E)) s) : + ContMDiffOn IM 𝓘(ℝ) n (fun m ↦ pseudoInner (B := B) (E := E) (b m) (v m) (w m)) s := + fun x hx ↦ (hv x hx).pseudoInner_bundle (IB := IB) (n := n) (F := F) (E := E) (hw x hx) + +lemma ContMDiff.pseudoInner_bundle + (hv : ContMDiff IM (IB.prod 𝓘(ℝ, F)) n (fun m ↦ (v m : TotalSpace F E))) + (hw : ContMDiff IM (IB.prod 𝓘(ℝ, F)) n (fun m ↦ (w m : TotalSpace F E))) : + ContMDiff IM 𝓘(ℝ) n (fun m ↦ pseudoInner (B := B) (E := E) (b m) (v m) (w m)) := + fun x ↦ (hv x).pseudoInner_bundle (IB := IB) (n := n) (F := F) (E := E) (hw x) + +end ContMDiffPairing + end ContMDiff end Bundle @@ -210,7 +277,7 @@ structure MetricTensor (val x v) w = 0) → v = 0 /-- Smoothness of the metric tensor as a smooth section of the bundle of bilinear forms. - This packaging follows the same pattern as Mathlib's Riemannian metric API, using `TotalSpace.mk'` + We follow the same pattern as Mathlib's Riemannian metric API, using `TotalSpace.mk'` for the bundled map. -/ contMDiff : ContMDiff I (I.prod 𝓘(ℝ, E →L[ℝ] E →L[ℝ] ℝ)) n (fun x ↦ @@ -471,17 +538,17 @@ def cotangentToQuadraticForm (g : MetricTensor E H M n I) (x : M) : ContinuousLinearMap.add_apply] ring⟩ -@[simp] +@[simp] lemma cotangentToBilinForm_apply (g : MetricTensor E H M n I) (x : M) (ω₁ ω₂ : TangentSpace I x →L[ℝ] ℝ) : cotangentToBilinForm g x ω₁ ω₂ = cotangentMetricVal g x ω₁ ω₂ := rfl -@[simp] +@[simp] lemma cotangentToQuadraticForm_apply (g : MetricTensor E H M n I) (x : M) (ω : TangentSpace I x →L[ℝ] ℝ) : cotangentToQuadraticForm g x ω = cotangentMetricVal g x ω ω := rfl -@[simp] +@[simp] lemma cotangentToBilinForm_isSymm (g : MetricTensor E H M n I) (x : M) : (cotangentToBilinForm g x).IsSymm := by refine { eq := ?_ } @@ -573,20 +640,17 @@ variable [inst_tangent_findim : ∀ (x : M), FiniteDimensional ℝ (TangentSpace /-! ## Predicate typeclass: pseudo-Riemannian manifolds -/ -/-- Prop-valued typeclass recording that the manifold carries *some* `C^n` pseudo-Riemannian metric. - -This mirrors the way one often uses `IsRiemannianManifold` as a Prop-valued predicate, but here -we do not derive any distance structure (pseudo-metrics are indefinite). -/ -class IsPseudoRiemannianManifold : Prop where - exists_metric : ∃ _ : PseudoRiemannianMetric E H M n I, True +/-- Prop-valued predicate recording existence of a `C^n` pseudo-Riemannian metric. -noncomputable def pseudoRiemannianMetric [h : IsPseudoRiemannianManifold (E := E) (H := H) (M := M) (n := n) (I := I)] : - PseudoRiemannianMetric E H M n I := - Classical.choose h.exists_metric +This is the “there exists a metric” statement, *without* making any noncanonical choice. For +bundle-first development, the primary data is still an explicit `PseudoRiemannianMetric` (or a +`Bundle.PseudoRiemannianBundle` + smoothness instance). -/ +class HasPseudoRiemannianMetric : Prop where + out : Nonempty (PseudoRiemannianMetric E H M n I) instance (g : PseudoRiemannianMetric E H M n I) : - IsPseudoRiemannianManifold (E := E) (H := H) (M := M) (n := n) (I := I) := - ⟨⟨g, trivial⟩⟩ + HasPseudoRiemannianMetric (E := E) (H := H) (M := M) (n := n) (I := I) := + ⟨⟨g⟩⟩ /-- Given a pseudo-Riemannian metric `g` on manifold `M` and a point `x : M`, this function constructs a bilinear form on the tangent space at `x`. @@ -623,7 +687,7 @@ the associated quadratic form `v ↦ gₓ(v,v)`. -/ def index (g : PseudoRiemannianMetric E H M n I) (x : M) : ℕ := (g.toQuadraticForm x).negDim -@[simp] +@[simp] lemma index_def (g : PseudoRiemannianMetric E H M n I) (x : M) : g.index x = (g.toQuadraticForm x).negDim := rfl @@ -964,8 +1028,6 @@ theorem cotangentToQuadraticForm_equivalent_toQuadraticForm { toLinearEquiv := (g.sharpEquiv x).toLinearEquiv map_app' := fun ω => ?_ } have hsh : g.sharpL x ω = g.sharpEquiv x ω := rfl - -- unfold through the simp lemmas for both quadratic forms, but keep `cotangentMetricVal` as `g.val _ _ _` - -- (avoids rewriting to `ω (sharpL ω)`). simp [cotangentToQuadraticForm_apply, cotangentMetricVal, toQuadraticForm, hsh] theorem cotangent_signature_eq (g : PseudoRiemannianMetric E H M n I) (x : M) : From bb54203f9772fb69d452ba87633ca9290948aa2c Mon Sep 17 00:00:00 2001 From: Matteo CIpollina Date: Mon, 2 Mar 2026 01:02:16 +0100 Subject: [PATCH 09/36] Update Defs.lean --- .../Mathematics/Geometry/Metric/Riemannian/Defs.lean | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/PhysLean/Mathematics/Geometry/Metric/Riemannian/Defs.lean b/PhysLean/Mathematics/Geometry/Metric/Riemannian/Defs.lean index 7f7aa2047..04d8cc8f6 100644 --- a/PhysLean/Mathematics/Geometry/Metric/Riemannian/Defs.lean +++ b/PhysLean/Mathematics/Geometry/Metric/Riemannian/Defs.lean @@ -83,6 +83,15 @@ def toPseudoRiemannianMetric (g : RiemannianMetric (I := I) (n := n) M) : simpa [QuadraticForm.negDim] using hy simp [hx', hy'] +@[simp] lemma toPseudoRiemannianMetric_index (g : RiemannianMetric (I := I) (n := n) M) (x : M) : + (toPseudoRiemannianMetric (I := I) (n := n) (M := M) g).index x = 0 := by + have hx : (PseudoRiemannianMetric.valToQuadraticForm g.inner g.symm x).negDim = 0 := by + apply QuadraticForm.negDim_posDef + intro v hv + simpa [PseudoRiemannianMetric.valToQuadraticForm] using g.pos x v hv + simpa [PseudoRiemannianMetric.index, PseudoRiemannianMetric.toQuadraticForm, + toPseudoRiemannianMetric] using hx + instance : Coe (RiemannianMetric (I := I) (n := n) M) (PseudoRiemannianMetric E H M n I) := From dc5d0c4f0c6b49cdee69c1903fb8846078e95d61 Mon Sep 17 00:00:00 2001 From: Matteo CIpollina Date: Mon, 2 Mar 2026 09:21:53 +0100 Subject: [PATCH 10/36] refactor existence predicate to PSR --- .../Metric/PseudoRiemannian/Defs.lean | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/PhysLean/Mathematics/Geometry/Metric/PseudoRiemannian/Defs.lean b/PhysLean/Mathematics/Geometry/Metric/PseudoRiemannian/Defs.lean index ef796ced8..c1824b991 100644 --- a/PhysLean/Mathematics/Geometry/Metric/PseudoRiemannian/Defs.lean +++ b/PhysLean/Mathematics/Geometry/Metric/PseudoRiemannian/Defs.lean @@ -652,6 +652,35 @@ instance (g : PseudoRiemannianMetric E H M n I) : HasPseudoRiemannianMetric (E := E) (H := H) (M := M) (n := n) (I := I) := ⟨⟨g⟩⟩ +theorem hasPseudoRiemannianMetric_iff : + HasPseudoRiemannianMetric (E := E) (H := H) (M := M) (n := n) (I := I) ↔ + Nonempty (PseudoRiemannianMetric E H M n I) := + ⟨fun h => h.out, fun h => ⟨h⟩⟩ + +theorem hasPseudoRiemannianMetric_iff_exists : + HasPseudoRiemannianMetric (E := E) (H := H) (M := M) (n := n) (I := I) ↔ + ∃ _ : PseudoRiemannianMetric E H M n I, True := + ⟨fun h => by + rcases h.out with ⟨g⟩ + exact ⟨g, trivial⟩, + fun h => by + rcases h with ⟨g, -⟩ + exact ⟨⟨g⟩⟩⟩ + +/-- Typeclass carrying a *chosen* pseudo-Riemannian metric (as opposed to the mere existence +predicate `HasPseudoRiemannianMetric`). This is the convenient form for downstream constructions. -/ +class PseudoRiemannianManifold : Type _ where + metric : PseudoRiemannianMetric E H M n I + +/-- The chosen pseudo-Riemannian metric on a manifold typeclass. -/ +abbrev pseudoRiemannianMetric [PseudoRiemannianManifold (E := E) (H := H) (M := M) (n := n) (I := I)] : + PseudoRiemannianMetric E H M n I := + (PseudoRiemannianManifold.metric (E := E) (H := H) (M := M) (n := n) (I := I)) + +instance [PseudoRiemannianManifold (E := E) (H := H) (M := M) (n := n) (I := I)] : + HasPseudoRiemannianMetric (E := E) (H := H) (M := M) (n := n) (I := I) := + ⟨⟨pseudoRiemannianMetric (E := E) (H := H) (M := M) (n := n) (I := I)⟩⟩ + /-- Given a pseudo-Riemannian metric `g` on manifold `M` and a point `x : M`, this function constructs a bilinear form on the tangent space at `x`. For tangent vectors `u v : T_x M`, the bilinear form is given by: From 415f4729972a22b8dfcbf2de6133300d233b18bc Mon Sep 17 00:00:00 2001 From: Matteo CIpollina Date: Mon, 2 Mar 2026 09:25:36 +0100 Subject: [PATCH 11/36] remove noncomputable section --- .../Metric/PseudoRiemannian/Defs.lean | 35 ++++++++++--------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/PhysLean/Mathematics/Geometry/Metric/PseudoRiemannian/Defs.lean b/PhysLean/Mathematics/Geometry/Metric/PseudoRiemannian/Defs.lean index c1824b991..d648a9b52 100644 --- a/PhysLean/Mathematics/Geometry/Metric/PseudoRiemannian/Defs.lean +++ b/PhysLean/Mathematics/Geometry/Metric/PseudoRiemannian/Defs.lean @@ -53,7 +53,7 @@ leanprover.zulipchat.com/#narrow/channel/113488-general/topic/.28Pseudo.29.20Rie section PseudoRiemannianMetric -noncomputable section +--noncomputable section open Bundle Set Finset Function Filter Module Topology ContinuousLinearMap open scoped Manifold Bundle LinearMap Dual @@ -417,20 +417,21 @@ lemma flatL_surj (g : MetricTensor E H M n I) (x : M) : Function.Surjective (g.f (LinearMap.injective_iff_surjective_of_finrank_eq_finrank h_finrank_eq).mp (flatL_inj g x) /-- `flatEquiv` as a continuous linear equivalence. -/ -def flatEquiv (g : MetricTensor E H M n I) (x : M) : +noncomputable def flatEquiv (g : MetricTensor E H M n I) (x : M) : TangentSpace I x ≃L[ℝ] (TangentSpace I x →L[ℝ] ℝ) := LinearEquiv.toContinuousLinearEquiv <| LinearEquiv.ofBijective (g.flatL x).toLinearMap ⟨g.flatL_inj x, g.flatL_surj x⟩ -@[simp] lemma flatEquiv_apply (g : MetricTensor E H M n I) (x : M) (v w : TangentSpace I x) : +@[simp] +lemma flatEquiv_apply (g : MetricTensor E H M n I) (x : M) (v w : TangentSpace I x) : (g.flatEquiv x v) w = g.val x v w := rfl /-- Index raising equivalence as the inverse of `flatEquiv`. -/ -def sharpEquiv (g : MetricTensor E H M n I) (x : M) : +noncomputable def sharpEquiv (g : MetricTensor E H M n I) (x : M) : (TangentSpace I x →L[ℝ] ℝ) ≃L[ℝ] TangentSpace I x := (g.flatEquiv x).symm -def sharpL (g : MetricTensor E H M n I) (x : M) : +noncomputable def sharpL (g : MetricTensor E H M n I) (x : M) : (TangentSpace I x →L[ℝ] ℝ) →L[ℝ] TangentSpace I x := (g.sharpEquiv x).toContinuousLinearMap @@ -483,7 +484,7 @@ lemma apply_vec_sharp (g : MetricTensor E H M n I) (x : M) (v : TangentSpace I x /-! ## Cotangent metric induced by `g` -/ /-- The induced metric value on the cotangent space at `x`. -/ -def cotangentMetricVal (g : MetricTensor E H M n I) (x : M) +noncomputable def cotangentMetricVal (g : MetricTensor E H M n I) (x : M) (ω₁ ω₂ : TangentSpace I x →L[ℝ] ℝ) : ℝ := g.val x (g.sharpL x ω₁) (g.sharpL x ω₂) @@ -499,7 +500,7 @@ lemma cotangentMetricVal_symm (g : MetricTensor E H M n I) (x : M) rw [g.symm x (g.sharpL x ω₁) (g.sharpL x ω₂)] /-- The induced cotangent metric as a bilinear form. -/ -def cotangentToBilinForm (g : MetricTensor E H M n I) (x : M) : +noncomputable def cotangentToBilinForm (g : MetricTensor E H M n I) (x : M) : LinearMap.BilinForm ℝ (TangentSpace I x →L[ℝ] ℝ) where toFun ω₁ := { toFun := fun ω₂ => cotangentMetricVal g x ω₁ ω₂ @@ -515,7 +516,7 @@ def cotangentToBilinForm (g : MetricTensor E H M n I) (x : M) : LinearMap.coe_mk, AddHom.coe_mk, RingHom.id_apply, LinearMap.smul_apply] /-- The induced cotangent metric as a quadratic form. -/ -def cotangentToQuadraticForm (g : MetricTensor E H M n I) (x : M) : +noncomputable def cotangentToQuadraticForm (g : MetricTensor E H M n I) (x : M) : QuadraticForm ℝ (TangentSpace I x →L[ℝ] ℝ) where toFun ω := cotangentMetricVal g x ω ω toFun_smul a ω := by @@ -572,7 +573,8 @@ lemma cotangentMetricVal_nondegenerate (g : MetricTensor E H M n I) (x : M) simpa [this] using h_apply exact h_forall v -@[simp] lemma cotangentToBilinForm_nondegenerate (g : MetricTensor E H M n I) (x : M) : +@[simp] +lemma cotangentToBilinForm_nondegenerate (g : MetricTensor E H M n I) (x : M) : (cotangentToBilinForm g x).Nondegenerate := by unfold LinearMap.BilinForm.Nondegenerate LinearMap.Nondegenerate LinearMap.SeparatingLeft LinearMap.SeparatingRight @@ -713,7 +715,7 @@ lemma toQuadraticForm_apply (g : PseudoRiemannianMetric E H M n I) (x : M) /-- The (negative) index of a pseudo-Riemannian metric at a point, defined as the negative index of the associated quadratic form `v ↦ gₓ(v,v)`. -/ -def index (g : PseudoRiemannianMetric E H M n I) (x : M) : ℕ := +noncomputable def index (g : PseudoRiemannianMetric E H M n I) (x : M) : ℕ := (g.toQuadraticForm x).negDim @[simp] @@ -815,7 +817,7 @@ lemma flatL_surj /-- The "musical" isomorphism (index lowering) from `TₓM` to its dual, as a continuous linear equivalence. This equivalence is a direct result of `gₓ` being a non-degenerate bilinear form (O'Neill, Def 17(3), p. 46; Lemma 19, p. 47). -/ -def flatEquiv +noncomputable def flatEquiv (g : PseudoRiemannianMetric E H M n I) (x : M) : TangentSpace I x ≃L[ℝ] (TangentSpace I x →L[ℝ] ℝ) := @@ -839,13 +841,13 @@ section Sharp /-- The "musical" isomorphism (index raising) from the dual of `TₓM` to `TₓM`. This is the inverse of `flatEquiv g x`, and its existence as an isomorphism is guaranteed by the non-degeneracy of `gₓ` (O'Neill, Lemma 19, p. 47). -/ -def sharpEquiv +noncomputable def sharpEquiv (g : PseudoRiemannianMetric E H M n I) (x : M) : (TangentSpace I x →L[ℝ] ℝ) ≃L[ℝ] TangentSpace I x := MetricTensor.sharpEquiv (g := g.toMetricTensor) x /-- The index raising map `sharp` as a continuous linear map. -/ -def sharpL +noncomputable def sharpL (g : PseudoRiemannianMetric E H M n I) (x : M) : (TangentSpace I x →L[ℝ] ℝ) →L[ℝ] TangentSpace I x := MetricTensor.sharpL (g := g.toMetricTensor) x @@ -923,7 +925,7 @@ variable [IsManifold I (n + 1) M] [IsManifold I 1 M] variable [inst_tangent_findim : ∀ (x : M), FiniteDimensional ℝ (TangentSpace I x)] /-- The value of the induced metric on the cotangent space at point `x`. -/ -def cotangentMetricVal (g : PseudoRiemannianMetric E H M n I) (x : M) +noncomputable def cotangentMetricVal (g : PseudoRiemannianMetric E H M n I) (x : M) (ω₁ ω₂ : TangentSpace I x →L[ℝ] ℝ) : ℝ := g.val x (g.sharpL x ω₁) (g.sharpL x ω₂) @@ -942,7 +944,7 @@ lemma cotangentMetricVal_symm (g : PseudoRiemannianMetric E H M n I) (x : M) /-- The induced metric on the cotangent space at point `x` as a bilinear form. For covectors `ω₁` and `ω₂`, this gives `g(ω₁^#, ω₂^#)`, where `ω^#` is the "sharp" musical isomorphism raising indices. -/ -def cotangentToBilinForm (g : PseudoRiemannianMetric E H M n I) (x : M) : +noncomputable def cotangentToBilinForm (g : PseudoRiemannianMetric E H M n I) (x : M) : LinearMap.BilinForm ℝ (TangentSpace I x →L[ℝ] ℝ) where toFun ω₁ := { toFun := λ ω₂ => cotangentMetricVal g x ω₁ ω₂, map_add' := λ ω₂ ω₃ => by @@ -967,7 +969,7 @@ def cotangentToBilinForm (g : PseudoRiemannianMetric E H M n I) (x : M) : /-- The cometric on the cotangent space T_x*M at `x`, expressed as a quadratic form. It is induced by the pseudo-Riemannian metric on the tangent space T_xM. -/ -def cotangentToQuadraticForm (g : PseudoRiemannianMetric E H M n I) (x : M) : +noncomputable def cotangentToQuadraticForm (g : PseudoRiemannianMetric E H M n I) (x : M) : QuadraticForm ℝ (TangentSpace I x →L[ℝ] ℝ) where toFun ω := cotangentMetricVal g x ω ω toFun_smul a ω := by @@ -1071,5 +1073,4 @@ theorem cotangent_negDim_eq (g : PseudoRiemannianMetric E H M n I) (x : M) : end Cotangent end PseudoRiemannianMetric -end end PseudoRiemannianMetric From a2692e1641c5ffa9bcde90481811664ea415d120 Mon Sep 17 00:00:00 2001 From: Matteo CIpollina Date: Mon, 2 Mar 2026 17:03:44 +0100 Subject: [PATCH 12/36] Update Defs.lean --- .../Metric/PseudoRiemannian/Defs.lean | 24 ++++++++----------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/PhysLean/Mathematics/Geometry/Metric/PseudoRiemannian/Defs.lean b/PhysLean/Mathematics/Geometry/Metric/PseudoRiemannian/Defs.lean index d648a9b52..d9c2ad7b5 100644 --- a/PhysLean/Mathematics/Geometry/Metric/PseudoRiemannian/Defs.lean +++ b/PhysLean/Mathematics/Geometry/Metric/PseudoRiemannian/Defs.lean @@ -40,7 +40,7 @@ of a maximal negative definite subspace. * `PseudoRiemannianMetric.toQuadraticForm g x`: The quadratic form `v ↦ gₓ(v, v)` associated with the metric at point `x`. -This formalization packages smoothness in the same style as the modern Mathlib Riemannian API: +We show smoothness with same pattern as mathlib Riemannian API: the metric is a section of the (vector-bundle) bundle of bilinear forms, and the smoothness assumption is a `ContMDiff` statement for this section (instead of being phrased chartwise). @@ -53,8 +53,6 @@ leanprover.zulipchat.com/#narrow/channel/113488-general/topic/.28Pseudo.29.20Rie section PseudoRiemannianMetric ---noncomputable section - open Bundle Set Finset Function Filter Module Topology ContinuousLinearMap open scoped Manifold Bundle LinearMap Dual @@ -769,7 +767,7 @@ def inner (g : PseudoRiemannianMetric E H M n I) (x : M) (v w : TangentSpace I x @[simp] lemma inner_apply (g : PseudoRiemannianMetric E H M n I) (x : M) (v w : TangentSpace I x) : - inner g x v w = g.val x v w := rfl + inner g x v w = g.val x v w := rfl /-! ## Flat -/ @@ -785,7 +783,7 @@ def flat (g : PseudoRiemannianMetric E H M n I) (x : M) : @[simp] lemma flat_apply (g : PseudoRiemannianMetric E H M n I) (x : M) (v w : TangentSpace I x) : - (flat g x v) w = g.val x v w := by rfl + (flat g x v) w = g.val x v w := by rfl /-- The musical isomorphism as a continuous linear map. -/ abbrev flatL (g : PseudoRiemannianMetric E H M n I) (x : M) : @@ -824,13 +822,13 @@ noncomputable def flatEquiv MetricTensor.flatEquiv (g := g.toMetricTensor) x lemma coe_flatEquiv - (g : PseudoRiemannianMetric E H M n I) (x : M) : - (g.flatEquiv x : TangentSpace I x →ₗ[ℝ] (TangentSpace I x →L[ℝ] ℝ)) = g.flatL x := rfl + (g : PseudoRiemannianMetric E H M n I) (x : M) : + (g.flatEquiv x : TangentSpace I x →ₗ[ℝ] (TangentSpace I x →L[ℝ] ℝ)) = g.flatL x := rfl @[simp] lemma flatEquiv_apply - (g : PseudoRiemannianMetric E H M n I) (x : M) (v w : TangentSpace I x) : - (g.flatEquiv x v) w = g.val x v w := rfl + (g : PseudoRiemannianMetric E H M n I) (x : M) (v w : TangentSpace I x) : + (g.flatEquiv x v) w = g.val x v w := rfl end Flat @@ -869,10 +867,9 @@ noncomputable def sharp @[simp] lemma sharpL_apply_flatL (g : PseudoRiemannianMetric E H M n I) (x : M) (v : TangentSpace I x) : - g.sharpL x (g.flatL x v) = v := - by - simp [PseudoRiemannianMetric.sharpL, PseudoRiemannianMetric.flatL, - MetricTensor.sharpL_apply_flatL (g := g.toMetricTensor) x v] + g.sharpL x (g.flatL x v) = v := by + simp [PseudoRiemannianMetric.sharpL, PseudoRiemannianMetric.flatL, + MetricTensor.sharpL_apply_flatL (g := g.toMetricTensor) x v] @[simp] lemma flatL_apply_sharpL @@ -1053,7 +1050,6 @@ lemma cotangentToBilinForm_nondegenerate (g : PseudoRiemannianMetric E H M n I) theorem cotangentToQuadraticForm_equivalent_toQuadraticForm (g : PseudoRiemannianMetric E H M n I) (x : M) : (g.cotangentToQuadraticForm x).Equivalent (g.toQuadraticForm x) := by - classical refine ⟨?_⟩ refine { toLinearEquiv := (g.sharpEquiv x).toLinearEquiv From 3d4041b7dd87bf657ba260233a5fcca2d652c4f5 Mon Sep 17 00:00:00 2001 From: Matteo CIpollina Date: Mon, 2 Mar 2026 17:03:47 +0100 Subject: [PATCH 13/36] Update Defs.lean --- .../Geometry/Metric/Riemannian/Defs.lean | 29 +++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/PhysLean/Mathematics/Geometry/Metric/Riemannian/Defs.lean b/PhysLean/Mathematics/Geometry/Metric/Riemannian/Defs.lean index 04d8cc8f6..0982b6849 100644 --- a/PhysLean/Mathematics/Geometry/Metric/Riemannian/Defs.lean +++ b/PhysLean/Mathematics/Geometry/Metric/Riemannian/Defs.lean @@ -64,7 +64,6 @@ def toPseudoRiemannianMetric (g : RiemannianMetric (I := I) (n := n) M) : negDim_isLocallyConstant := by -- On a Riemannian metric, the associated quadratic form is positive definite, hence `negDim = 0`. refine IsLocallyConstant.of_constant _ (fun x y => ?_) - -- Both sides are `0`. have hx : (PseudoRiemannianMetric.valToQuadraticForm g.inner g.symm x).negDim = 0 := by apply QuadraticForm.negDim_posDef @@ -83,7 +82,8 @@ def toPseudoRiemannianMetric (g : RiemannianMetric (I := I) (n := n) M) : simpa [QuadraticForm.negDim] using hy simp [hx', hy'] -@[simp] lemma toPseudoRiemannianMetric_index (g : RiemannianMetric (I := I) (n := n) M) (x : M) : +@[simp] +lemma toPseudoRiemannianMetric_index (g : RiemannianMetric (I := I) (n := n) M) (x : M) : (toPseudoRiemannianMetric (I := I) (n := n) (M := M) g).index x = 0 := by have hx : (PseudoRiemannianMetric.valToQuadraticForm g.inner g.symm x).negDim = 0 := by apply QuadraticForm.negDim_posDef @@ -99,6 +99,31 @@ instance : end RiemannianMetric +/-! ## Existence predicates (non-choosing) -/ + +/-- Prop-valued predicate recording existence of a `C^n` Riemannian metric (bundle-first), without +making any noncanonical choice. -/ +class HasRiemannianMetric : Prop where + out : Nonempty (RiemannianMetric (I := I) (n := n) M) + +instance (g : RiemannianMetric (I := I) (n := n) M) : + HasRiemannianMetric (I := I) (n := n) (M := M) := + ⟨⟨g⟩⟩ + +theorem hasRiemannianMetric_iff : + HasRiemannianMetric (I := I) (n := n) (M := M) ↔ + Nonempty (RiemannianMetric (I := I) (n := n) M) := + ⟨fun h => h.out, fun h => ⟨h⟩⟩ + +/-- Any Riemannian metric gives a pseudo-Riemannian metric (of index `0`), hence existence of a +Riemannian metric implies existence of a pseudo-Riemannian metric. -/ +instance [h : HasRiemannianMetric (I := I) (n := n) (M := M)] + [∀ x : M, FiniteDimensional ℝ (TangentSpace I x)] : + PseudoRiemannianMetric.HasPseudoRiemannianMetric (E := E) (H := H) (M := M) (n := n) (I := I) := + ⟨by + rcases h.out with ⟨g⟩ + exact ⟨RiemannianMetric.toPseudoRiemannianMetric (I := I) (n := n) (M := M) g⟩⟩ + end end From c2b50df2f0daa26cd0f387923e00dfe58095a964 Mon Sep 17 00:00:00 2001 From: Matteo CIpollina Date: Mon, 2 Mar 2026 18:50:03 +0100 Subject: [PATCH 14/36] refactor --- .../Geometry/Metric/Lorentzian/Defs.lean | 35 +++++++----- .../Metric/PseudoRiemannian/Defs.lean | 56 +++++++++++-------- .../Geometry/Metric/QuadraticForm/NegDim.lean | 2 +- 3 files changed, 57 insertions(+), 36 deletions(-) diff --git a/PhysLean/Mathematics/Geometry/Metric/Lorentzian/Defs.lean b/PhysLean/Mathematics/Geometry/Metric/Lorentzian/Defs.lean index da99c6e56..f87345186 100644 --- a/PhysLean/Mathematics/Geometry/Metric/Lorentzian/Defs.lean +++ b/PhysLean/Mathematics/Geometry/Metric/Lorentzian/Defs.lean @@ -1,6 +1,7 @@ /- -Copyright (c) 2026. All rights reserved. +Copyright (c) 2026 Matteo Cipollina. All rights reserved. Released under Apache 2.0 license as described in the file LICENSE. +Authors: Matteo Cipollina -/ import PhysLean.Mathematics.Geometry.Metric.PseudoRiemannian.Defs @@ -30,23 +31,32 @@ variable {I : ModelWithCorners ℝ E H} {n : WithTop ℕ∞} variable [IsManifold I 1 M] [IsManifold I (n + 1) M] variable [∀ x : M, FiniteDimensional ℝ (TangentSpace I x)] -/-- A Lorentzian metric is a pseudo-Riemannian metric of (constant) index `1`, i.e. the associated -quadratic form has `negDim = 1` at every point. -/ -@[ext] -structure LorentzianMetric extends PseudoRiemannianMetric E H M n I where - negDim_eq_one : ∀ x : M, (toQuadraticForm toPseudoRiemannianMetric x).negDim = 1 +/-- A Lorentzian metric is a pseudo-Riemannian metric whose associated quadratic form has +`negDim = 1` at every point. -/ +abbrev LorentzianMetric (E : Type v) (H : Type w) (M : Type w) (n : WithTop ℕ∞) + [NormedAddCommGroup E] [NormedSpace ℝ E] + [TopologicalSpace H] [TopologicalSpace M] [ChartedSpace H M] + (I : ModelWithCorners ℝ E H) [IsManifold I (n + 1) M] [IsManifold I 1 M] + [∀ x : M, FiniteDimensional ℝ (TangentSpace I x)] := + PseudoRiemannianMetric E H M n I + +/-- Typeclass asserting that a pseudo-Riemannian metric is Lorentzian (index `1`). -/ +class IsLorentzianMetric (g : LorentzianMetric (E := E) (H := H) (M := M) (I := I) (n := n)) : + Prop where + negDim_eq_one : ∀ x : M, (g.toQuadraticForm x).negDim = 1 namespace LorentzianMetric variable (g : LorentzianMetric (E := E) (H := H) (M := M) (I := I) (n := n)) -@[simp] lemma negDim_eq_one' (x : M) : - (g.toPseudoRiemannianMetric.toQuadraticForm x).negDim = 1 := - g.negDim_eq_one x +@[simp] lemma negDim_eq_one (x : M) [IsLorentzianMetric (g := g)] : + (g.toQuadraticForm x).negDim = 1 := + IsLorentzianMetric.negDim_eq_one (g := g) x -instance : Coe (LorentzianMetric (E := E) (H := H) (M := M) (I := I) (n := n)) - (PseudoRiemannianMetric E H M n I) := - ⟨fun g => g.toPseudoRiemannianMetric⟩ +@[simp] lemma index_eq_one (x : M) [IsLorentzianMetric (g := g)] : + g.index x = 1 := by + simpa [PseudoRiemannianMetric.index] using + (IsLorentzianMetric.negDim_eq_one (g := g) x) end LorentzianMetric @@ -55,4 +65,3 @@ end end end PseudoRiemannianMetric - diff --git a/PhysLean/Mathematics/Geometry/Metric/PseudoRiemannian/Defs.lean b/PhysLean/Mathematics/Geometry/Metric/PseudoRiemannian/Defs.lean index d9c2ad7b5..e2b0aa833 100644 --- a/PhysLean/Mathematics/Geometry/Metric/PseudoRiemannian/Defs.lean +++ b/PhysLean/Mathematics/Geometry/Metric/PseudoRiemannian/Defs.lean @@ -10,7 +10,7 @@ import Mathlib.Geometry.Manifold.VectorBundle.Hom import Mathlib.Geometry.Manifold.VectorBundle.Tangent import Mathlib.LinearAlgebra.BilinearForm.Properties import Mathlib.Topology.LocallyConstant.Basic -import PhysLean.Mathematics.Geometry.Metric.QuadraticForm.NegDim +import PhysLean.Mathematics.Geometry.Metric.QuadraticForm.Index /-! # Pseudo-Riemannian Metrics on Smooth Manifolds @@ -262,8 +262,7 @@ structure MetricTensor [inst_chart_M : ChartedSpace H M] (I : ModelWithCorners ℝ E H) [inst_mani : IsManifold I (n + 1) M] - [inst_mani1 : IsManifold I 1 M] - [inst_tangent_findim : ∀ (x : M), FiniteDimensional ℝ (TangentSpace I x)] : + [inst_mani1 : IsManifold I 1 M] : Type _ where /-- The metric tensor at each point `x : M`, represented as a continuous linear map `TₓM →L[ℝ] (TₓM →L[ℝ] ℝ)`. Applying it twice, `(val x v) w`, yields `gₓ(v, w)`. -/ @@ -288,7 +287,6 @@ variable [NormedAddCommGroup E] [NormedSpace ℝ E] variable [TopologicalSpace H] [TopologicalSpace M] [ChartedSpace H M] variable {I : ModelWithCorners ℝ E H} variable [IsManifold I (n + 1) M] [IsManifold I 1 M] -variable [inst_tangent_findim : ∀ (x : M), FiniteDimensional ℝ (TangentSpace I x)] /-- Coercion from `MetricTensor` to its `val` function. -/ instance coeFunInst : CoeFun (MetricTensor E H M n I) @@ -357,25 +355,20 @@ lemma inner_apply (g : MetricTensor E H M n I) (x : M) (v w : TangentSpace I x) /-! ## Flat / sharp (musical isomorphisms) -/ +/-- Index lowering map `v ↦ gₓ(v, -)` as a continuous linear map. -/ +abbrev flatL (g : MetricTensor E H M n I) (x : M) : + TangentSpace I x →L[ℝ] (TangentSpace I x →L[ℝ] ℝ) := + g.val x + /-- Index lowering map `v ↦ gₓ(v, -)` as a linear map. -/ -def flat (g : MetricTensor E H M n I) (x : M) : +abbrev flat (g : MetricTensor E H M n I) (x : M) : TangentSpace I x →ₗ[ℝ] (TangentSpace I x →L[ℝ] ℝ) := - { toFun := fun v => g.val x v - map_add' := fun v w => by simp [ContinuousLinearMap.map_add] - map_smul' := fun a v => by simp} + (g.flatL x).toLinearMap @[simp] lemma flat_apply (g : MetricTensor E H M n I) (x : M) (v w : TangentSpace I x) : (flat g x v) w = g.val x v w := rfl -/-- Index lowering map as a continuous linear map. -/ -def flatL (g : MetricTensor E H M n I) (x : M) : - TangentSpace I x →L[ℝ] (TangentSpace I x →L[ℝ] ℝ) where - toFun := fun v => g.val x v - map_add' := fun v w => by simp [ContinuousLinearMap.map_add] - map_smul' := fun a v => by simp - cont := ContinuousLinearMap.continuous (g.val x) - @[simp] lemma flatL_apply (g : MetricTensor E H M n I) (x : M) (v w : TangentSpace I x) : (flatL g x v) w = g.val x v w := rfl @@ -391,6 +384,10 @@ lemma flat_inj (g : MetricTensor E H M n I) (x : M) : Function.Injective (flat g lemma flatL_inj (g : MetricTensor E H M n I) (x : M) : Function.Injective (flatL g x) := flat_inj g x +section FiniteDimensional + +variable [inst_tangent_findim : ∀ (x : M), FiniteDimensional ℝ (TangentSpace I x)] + lemma flatL_surj (g : MetricTensor E H M n I) (x : M) : Function.Surjective (g.flatL x) := by haveI : FiniteDimensional ℝ (TangentSpace I x) := inst_tangent_findim x have h_finrank_eq : @@ -489,7 +486,7 @@ noncomputable def cotangentMetricVal (g : MetricTensor E H M n I) (x : M) @[simp] lemma cotangentMetricVal_eq_apply_sharp (g : MetricTensor E H M n I) (x : M) (ω₁ ω₂ : TangentSpace I x →L[ℝ] ℝ) : cotangentMetricVal g x ω₁ ω₂ = ω₁ (g.sharpL x ω₂) := by - simp [cotangentMetricVal, apply_sharp_sharp] + simp [cotangentMetricVal] lemma cotangentMetricVal_symm (g : MetricTensor E H M n I) (x : M) (ω₁ ω₂ : TangentSpace I x →L[ℝ] ℝ) : @@ -552,7 +549,7 @@ lemma cotangentToBilinForm_isSymm (g : MetricTensor E H M n I) (x : M) : (cotangentToBilinForm g x).IsSymm := by refine { eq := ?_ } intro ω₁ ω₂ - simp [cotangentToBilinForm_apply, cotangentMetricVal, g.symm] + simpa [cotangentToBilinForm_apply] using (cotangentMetricVal_symm (g := g) x ω₁ ω₂) /-- Nondegeneracy of the cotangent metric. -/ lemma cotangentMetricVal_nondegenerate (g : MetricTensor E H M n I) (x : M) @@ -600,6 +597,21 @@ theorem cotangentToQuadraticForm_equivalent_toQuadraticForm (g : MetricTensor E map_app' := fun ω => ?_ } simp [cotangentToQuadraticForm_apply, cotangentMetricVal, toQuadraticForm, sharpEquiv, sharpL] +/-! ## Pointwise index (finite-dimensional) -/ + +/-- The (negative) index of a metric tensor at a point, defined as the negative index of the +associated quadratic form `v ↦ gₓ(v,v)`. + +This is a pointwise invariant; it need not be locally constant. -/ +noncomputable def index (g : MetricTensor E H M n I) (x : M) : ℕ := + (g.toQuadraticForm x).negDim + +omit inst_tangent_findim in +@[simp] lemma index_def (g : MetricTensor E H M n I) (x : M) : + g.index x = (g.toQuadraticForm x).negDim := rfl + +end FiniteDimensional + end MetricTensor /-- A pseudo-Riemannian metric of smoothness class `C^n` on a manifold `M` modelled on `(E, H)` @@ -890,16 +902,16 @@ lemma flat_sharp_apply lemma sharp_flat_apply (g : PseudoRiemannianMetric E H M n I) (x : M) (v : TangentSpace I x) : g.sharp x (g.flat x v) = v := by - simp [PseudoRiemannianMetric.flat, PseudoRiemannianMetric.sharp, - MetricTensor.sharp_flat_apply (g := g.toMetricTensor) x v] + have h : g.sharpL x (g.flatL x v) = v := + PseudoRiemannianMetric.sharpL_apply_flatL (g := g) x v + convert h using 1 /-- The metric evaluated at `sharp ω₁` and `sharp ω₂`. -/ @[simp] lemma apply_sharp_sharp (g : PseudoRiemannianMetric E H M n I) (x : M) (ω₁ ω₂ : TangentSpace I x →L[ℝ] ℝ) : g.val x (g.sharpL x ω₁) (g.sharpL x ω₂) = ω₁ (g.sharpL x ω₂) := by - simp [PseudoRiemannianMetric.sharpL, - MetricTensor.apply_sharp_sharp (g := g.toMetricTensor) x ω₁ ω₂] + simp [PseudoRiemannianMetric.sharpL] /-- The metric evaluated at `v` and `sharp ω`. -/ lemma apply_vec_sharp diff --git a/PhysLean/Mathematics/Geometry/Metric/QuadraticForm/NegDim.lean b/PhysLean/Mathematics/Geometry/Metric/QuadraticForm/NegDim.lean index f94c26778..cb6b89012 100644 --- a/PhysLean/Mathematics/Geometry/Metric/QuadraticForm/NegDim.lean +++ b/PhysLean/Mathematics/Geometry/Metric/QuadraticForm/NegDim.lean @@ -1,5 +1,5 @@ /- -Copyright (c) 2025 Matteo Cipollina. All rights reserved. +Copyright (c) 2026 Matteo Cipollina. All rights reserved. Released under Apache 2.0 license as described in the file LICENSE. Authors: Matteo Cipollina -/ From 175594a16b536d88ddf73d3d269db44af6ac79db Mon Sep 17 00:00:00 2001 From: Matteo Cipollina Date: Mon, 2 Mar 2026 19:05:56 +0100 Subject: [PATCH 15/36] Change import from QuadraticForm.Index to NegDim --- PhysLean/Mathematics/Geometry/Metric/PseudoRiemannian/Defs.lean | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PhysLean/Mathematics/Geometry/Metric/PseudoRiemannian/Defs.lean b/PhysLean/Mathematics/Geometry/Metric/PseudoRiemannian/Defs.lean index e2b0aa833..045c4ee04 100644 --- a/PhysLean/Mathematics/Geometry/Metric/PseudoRiemannian/Defs.lean +++ b/PhysLean/Mathematics/Geometry/Metric/PseudoRiemannian/Defs.lean @@ -10,7 +10,7 @@ import Mathlib.Geometry.Manifold.VectorBundle.Hom import Mathlib.Geometry.Manifold.VectorBundle.Tangent import Mathlib.LinearAlgebra.BilinearForm.Properties import Mathlib.Topology.LocallyConstant.Basic -import PhysLean.Mathematics.Geometry.Metric.QuadraticForm.Index +import PhysLean.Mathematics.Geometry.Metric.QuadraticForm.NegDim /-! # Pseudo-Riemannian Metrics on Smooth Manifolds From a62f40f26a9585b0dd5cb187428118e270ae17b1 Mon Sep 17 00:00:00 2001 From: Matteo Cipollina Date: Mon, 2 Mar 2026 19:06:42 +0100 Subject: [PATCH 16/36] misc --- .../Metric/PseudoRiemannian/Defs.lean | 1171 ++--------------- 1 file changed, 109 insertions(+), 1062 deletions(-) diff --git a/PhysLean/Mathematics/Geometry/Metric/PseudoRiemannian/Defs.lean b/PhysLean/Mathematics/Geometry/Metric/PseudoRiemannian/Defs.lean index 045c4ee04..0b4c6c82a 100644 --- a/PhysLean/Mathematics/Geometry/Metric/PseudoRiemannian/Defs.lean +++ b/PhysLean/Mathematics/Geometry/Metric/PseudoRiemannian/Defs.lean @@ -3,1082 +3,129 @@ Copyright (c) 2025 Matteo Cipollina. All rights reserved. Released under Apache 2.0 license as described in the file LICENSE. Authors: Matteo Cipollina -/ - -import Mathlib.Analysis.InnerProductSpace.Basic -import Mathlib.Geometry.Manifold.MFDeriv.Defs -import Mathlib.Geometry.Manifold.VectorBundle.Hom +import PhysLean.Mathematics.Geometry.Metric.PseudoRiemannian.Defs +import Mathlib.Geometry.Manifold.VectorBundle.Riemannian import Mathlib.Geometry.Manifold.VectorBundle.Tangent -import Mathlib.LinearAlgebra.BilinearForm.Properties -import Mathlib.Topology.LocallyConstant.Basic -import PhysLean.Mathematics.Geometry.Metric.QuadraticForm.NegDim - /-! -# Pseudo-Riemannian Metrics on Smooth Manifolds - -This file formalizes pseudo-Riemannian metrics on smooth manifolds and establishes their basic -properties. - -A pseudo-Riemannian metric equips a manifold with a smoothly varying, non-degenerate, symmetric -bilinear form of *constant index* on each tangent space, generalizing the concept of an inner -product space to curved spaces. The index here refers to `QuadraticForm.negDim`, the dimension -of a maximal negative definite subspace. - -## Main Definitions - -* `PseudoRiemannianMetric E H M n I`: A structure representing a `C^n` pseudo-Riemannian metric - on a manifold `M` modelled on `(E, H)` with model with corners `I`. It consists of a family - of non-degenerate, symmetric, continuous bilinear forms `gₓ` on each tangent space `TₓM`, - varying `C^n`-smoothly with `x` and having a locally constant negative dimension (`negDim`). - The model space `E` must be finite-dimensional, and the manifold `M` must be `C^{n+1}` smooth. - -* `PseudoRiemannianMetric.flatEquiv g x`: The "musical isomorphism" from the tangent space at `x` - to its dual space, representing the canonical isomorphism induced by the metric. +# Riemannian Metric Definitions -* `PseudoRiemannianMetric.sharpEquiv g x`: The inverse of the flat isomorphism, mapping from - the dual space back to the tangent space. +This file builds around the modern Mathlib Riemannian metric API. -* `PseudoRiemannianMetric.toQuadraticForm g x`: The quadratic form `v ↦ gₓ(v, v)` associated - with the metric at point `x`. +Concretely, a `C^n` Riemannian metric on a manifold is a smooth section of the bundle of bilinear +forms on the tangent bundle, packaged as `Bundle.ContMDiffRiemannianMetric`. -We show smoothness with same pattern as mathlib Riemannian API: -the metric is a section of the (vector-bundle) bundle of bilinear forms, and the smoothness -assumption is a `ContMDiff` statement for this section (instead of being phrased chartwise). - -## Reference - -* Barrett O'Neill, "Semi-Riemannian Geometry With Applications to Relativity" (Academic Press, 1983) -* [Discussion on Zulip about (Pseudo) Riemannian metrics] https. -leanprover.zulipchat.com/#narrow/channel/113488-general/topic/.28Pseudo.29.20Riemannian.20metric +We provide: +- an abbreviation `RiemannianMetric` for the tangent-bundle specialization, and +- a coercion to the PhysLean `PseudoRiemannianMetric` (by forgetting positivity and remembering + nondegeneracy + constant index `0`). -/ -section PseudoRiemannianMetric - -open Bundle Set Finset Function Filter Module Topology ContinuousLinearMap -open scoped Manifold Bundle LinearMap Dual - -/-! ## Bundle-level infrastructure (Mathlib-style) -/ - -namespace Bundle - -/-! ### Fiberwise pseudo-Riemannian structures -/ - -section PseudoRiemannianBundle - -variable - {B : Type*} [TopologicalSpace B] - {E : B → Type*} [∀ x, NormedAddCommGroup (E x)] [∀ x, NormedSpace ℝ (E x)] - -/-- A pseudo-Riemannian structure on a family of fibers `E x`: a symmetric, nondegenerate bilinear -form on each fiber, expressed as a continuous bilinear map. -/ -class PseudoRiemannianBundle : Type _ where - metric : ∀ x : B, E x →L[ℝ] E x →L[ℝ] ℝ - symm : ∀ (x : B) (v w : E x), metric x v w = metric x w v - nondegenerate : ∀ (x : B) (v : E x), (∀ w : E x, metric x v w = 0) → v = 0 - -variable [PseudoRiemannianBundle (B := B) (E := E)] - -/-- The metric as a family of continuous bilinear maps. -/ -abbrev metric (x : B) : E x →L[ℝ] E x →L[ℝ] ℝ := - PseudoRiemannianBundle.metric (B := B) (E := E) x - -/-- The fiberwise pseudo-inner-product \(g_x(v,w)\). -/ -abbrev pseudoInner (x : B) (v w : E x) : ℝ := - (PseudoRiemannianBundle.metric (B := B) (E := E) x) v w - -omit [TopologicalSpace B] in -@[simp] lemma pseudoInner_def (x : B) (v w : E x) : - pseudoInner (B := B) (E := E) x v w = metric (B := B) (E := E) x v w := rfl - -omit [TopologicalSpace B] in -lemma pseudoInner_symm (x : B) (v w : E x) : - pseudoInner (B := B) (E := E) x v w = pseudoInner (B := B) (E := E) x w v := by - simpa [pseudoInner] using (PseudoRiemannianBundle.symm (B := B) (E := E) x v w) - -omit [TopologicalSpace B] in -lemma pseudoInner_nondegenerate (x : B) (v : E x) (hv : ∀ w : E x, - pseudoInner (B := B) (E := E) x v w = 0) : - v = 0 := - PseudoRiemannianBundle.nondegenerate (B := B) (E := E) x v hv - -end PseudoRiemannianBundle - -/-! ### Smoothness of pseudo-Riemannian structures on vector bundles -/ - -section ContMDiff - -open scoped ENat - -variable - {EB : Type*} [NormedAddCommGroup EB] [NormedSpace ℝ EB] - {HB : Type*} [TopologicalSpace HB] {IB : ModelWithCorners ℝ EB HB} {n n' : WithTop ℕ∞} - {B : Type*} [TopologicalSpace B] [ChartedSpace HB B] - {F : Type*} [NormedAddCommGroup F] [NormedSpace ℝ F] - {E : B → Type*} [TopologicalSpace (TotalSpace F E)] [∀ x, NormedAddCommGroup (E x)] - [∀ x, NormedSpace ℝ (E x)] - [FiberBundle F E] [VectorBundle ℝ F E] - [PseudoRiemannianBundle (B := B) (E := E)] - -variable (IB n F E) in -/-- A pseudo-Riemannian bundle is `C^n` if its fiberwise bilinear form depends `C^n`-smoothly on the -base point, when seen as a section of the bundle of bilinear forms. -/ -class IsContMDiffPseudoRiemannianBundle : Prop where - exists_contMDiff : - ∃ g : Π x : B, E x →L[ℝ] E x →L[ℝ] ℝ, - ContMDiff IB (IB.prod 𝓘(ℝ, F →L[ℝ] F →L[ℝ] ℝ)) n - (fun b ↦ TotalSpace.mk' (F →L[ℝ] F →L[ℝ] ℝ) b (g b)) - ∧ ∀ (x : B) (v w : E x), pseudoInner (B := B) (E := E) x v w = g x v w - -lemma IsContMDiffPseudoRiemannianBundle.of_le - [h : IsContMDiffPseudoRiemannianBundle (IB := IB) (n := n) (F := F) (E := E)] (h' : n' ≤ n) : - IsContMDiffPseudoRiemannianBundle (IB := IB) (n := n') (F := F) (E := E) := by - rcases h.exists_contMDiff with ⟨g, g_smooth, hg⟩ - exact ⟨g, g_smooth.of_le h', hg⟩ - -instance [IsContMDiffPseudoRiemannianBundle (IB := IB) (n := (1 : WithTop ℕ∞)) (F := F) (E := E)] : - IsContMDiffPseudoRiemannianBundle (IB := IB) (n := (0 : WithTop ℕ∞)) (F := F) (E := E) := - IsContMDiffPseudoRiemannianBundle.of_le (IB := IB) (F := F) (E := E) zero_le_one - -instance [IsContMDiffPseudoRiemannianBundle (IB := IB) (n := (2 : WithTop ℕ∞)) (F := F) (E := E)] : - IsContMDiffPseudoRiemannianBundle (IB := IB) (n := (1 : WithTop ℕ∞)) (F := F) (E := E) := - IsContMDiffPseudoRiemannianBundle.of_le (IB := IB) (F := F) (E := E) one_le_two - -instance [IsContMDiffPseudoRiemannianBundle (IB := IB) (n := (3 : WithTop ℕ∞)) (F := F) (E := E)] : - IsContMDiffPseudoRiemannianBundle (IB := IB) (n := (2 : WithTop ℕ∞)) (F := F) (E := E) := - IsContMDiffPseudoRiemannianBundle.of_le (IB := IB) (n := (3 : WithTop ℕ∞)) (F := F) (E := E) - (by norm_cast) - -section ContMDiffPairing - -variable - {EM : Type*} [NormedAddCommGroup EM] [NormedSpace ℝ EM] - {HM : Type*} [TopologicalSpace HM] {IM : ModelWithCorners ℝ EM HM} - {M : Type*} [TopologicalSpace M] [ChartedSpace HM M] - [h : IsContMDiffPseudoRiemannianBundle (IB := IB) (n := n) (F := F) (E := E)] - {b : M → B} {v w : ∀ x, E (b x)} {s : Set M} {x : M} - -/-- Given two smooth maps into the same fibers of a pseudo-Riemannian bundle, their pairing is smooth. -/ -lemma ContMDiffWithinAt.pseudoInner_bundle - (hv : ContMDiffWithinAt IM (IB.prod 𝓘(ℝ, F)) n (fun m ↦ (v m : TotalSpace F E)) s x) - (hw : ContMDiffWithinAt IM (IB.prod 𝓘(ℝ, F)) n (fun m ↦ (w m : TotalSpace F E)) s x) : - ContMDiffWithinAt IM 𝓘(ℝ) n (fun m ↦ pseudoInner (B := B) (E := E) (b m) (v m) (w m)) s x := by - rcases h.exists_contMDiff with ⟨g, g_smooth, hg⟩ - have hb : ContMDiffWithinAt IM IB n b s x := by - simp only [contMDiffWithinAt_totalSpace] at hv - exact hv.1 - simp only [hg] - have : ContMDiffWithinAt IM (IB.prod 𝓘(ℝ)) n - (fun m ↦ TotalSpace.mk' ℝ (E := Bundle.Trivial B ℝ) (b m) (g (b m) (v m) (w m))) s x := by - apply ContMDiffWithinAt.clm_bundle_apply₂ (F₁ := F) (F₂ := F) - · exact ContMDiffAt.comp_contMDiffWithinAt x g_smooth.contMDiffAt hb - · exact hv - · exact hw - simp only [contMDiffWithinAt_totalSpace] at this - exact this.2 - -lemma ContMDiffAt.pseudoInner_bundle - (hv : ContMDiffAt IM (IB.prod 𝓘(ℝ, F)) n (fun m ↦ (v m : TotalSpace F E)) x) - (hw : ContMDiffAt IM (IB.prod 𝓘(ℝ, F)) n (fun m ↦ (w m : TotalSpace F E)) x) : - ContMDiffAt IM 𝓘(ℝ) n (fun m ↦ pseudoInner (B := B) (E := E) (b m) (v m) (w m)) x := - ContMDiffWithinAt.pseudoInner_bundle (IB := IB) (n := n) (F := F) (E := E) hv hw - -lemma ContMDiffOn.pseudoInner_bundle - (hv : ContMDiffOn IM (IB.prod 𝓘(ℝ, F)) n (fun m ↦ (v m : TotalSpace F E)) s) - (hw : ContMDiffOn IM (IB.prod 𝓘(ℝ, F)) n (fun m ↦ (w m : TotalSpace F E)) s) : - ContMDiffOn IM 𝓘(ℝ) n (fun m ↦ pseudoInner (B := B) (E := E) (b m) (v m) (w m)) s := - fun x hx ↦ (hv x hx).pseudoInner_bundle (IB := IB) (n := n) (F := F) (E := E) (hw x hx) - -lemma ContMDiff.pseudoInner_bundle - (hv : ContMDiff IM (IB.prod 𝓘(ℝ, F)) n (fun m ↦ (v m : TotalSpace F E))) - (hw : ContMDiff IM (IB.prod 𝓘(ℝ, F)) n (fun m ↦ (w m : TotalSpace F E))) : - ContMDiff IM 𝓘(ℝ) n (fun m ↦ pseudoInner (B := B) (E := E) (b m) (v m) (w m)) := - fun x ↦ (hv x).pseudoInner_bundle (IB := IB) (n := n) (F := F) (E := E) (hw x) - -end ContMDiffPairing - -end ContMDiff - -end Bundle - -/-! ## Pseudo-Riemannian Metric -/ - -/-! -Constructs a `QuadraticForm` on the tangent space `TₓM` at a point `x` from the -value of a pseudo-Riemannian metric at that point. -(O'Neill, p. 47, "The function q: V → R given by q(v) = b(v,v) is the associated quadratic -form of b.") -The pseudo-Riemannian metric is given by `val`, a family of continuous bilinear forms -`gₓ: TₓM × TₓM → ℝ` for each `x : M`. -The quadratic form `Qₓ` at `x` is defined as `Qₓ(v) = gₓ(v,v)`. -The associated symmetric bilinear form required by `QuadraticForm.exists_companion'` -is `Bₓ(v,w) = gₓ(v,w) + gₓ(w,v)`. Given the symmetry `symm`, this is `2 * gₓ(v,w)`. --/ -namespace PseudoRiemannianMetric - -/-- -Turn a (curried) bilinear form `val` on each tangent space into the associated quadratic form -`v ↦ val x v v`. - -This helper is intentionally public: it is the bridge between a bundled description of a metric -(`val` + symmetry) and quadratic-form invariants such as `QuadraticForm.negDim`. --/ -def valToQuadraticForm - {E : Type v} [NormedAddCommGroup E] [NormedSpace ℝ E] - {H : Type w} [TopologicalSpace H] - {M : Type w} [TopologicalSpace M] [ChartedSpace H M] - {I : ModelWithCorners ℝ E H} - (val : ∀ (x : M), TangentSpace I x →L[ℝ] (TangentSpace I x →L[ℝ] ℝ)) - (symm : ∀ (x : M) (v w : TangentSpace I x), (val x v) w = (val x w) v) - (x : M) : QuadraticForm ℝ (TangentSpace I x) where - toFun v := val x v v - toFun_smul a v := by - simp only [ContinuousLinearMap.map_smul, ContinuousLinearMap.smul_apply, smul_smul] - exists_companion' := - ⟨LinearMap.mk₂ ℝ (fun v y => val x v y + val x y v) - (fun v₁ v₂ y => by simp only [map_add, add_apply]; ring) - (fun a v y => by simp only [map_smul, smul_apply]; ring_nf; exact Eq.symm (smul_add ..)) - (fun v y₁ y₂ => by simp only [map_add, add_apply]; ring) - (fun a v y => by simp only [map_smul, smul_apply]; ring_nf; exact Eq.symm (smul_add ..)), - by - intro v y - simp only [LinearMap.mk₂_apply, ContinuousLinearMap.map_add, - ContinuousLinearMap.add_apply, symm x] - ring⟩ - -end PseudoRiemannianMetric - -/-- A general (pseudo-)metric tensor of smoothness class `C^n` on a manifold `M`. - -This is the common core shared by Riemannian and pseudo-Riemannian metrics: -a smoothly varying symmetric, nondegenerate bilinear form on each tangent space. - -The pseudo-Riemannian notion will extend this with an index/signature constancy condition. -/ -@[ext] -structure MetricTensor - (E : Type v) (H : Type w) (M : Type w) (n : WithTop ℕ∞) - [inst_norm_grp_E : NormedAddCommGroup E] - [inst_norm_sp_E : NormedSpace ℝ E] - [inst_top_H : TopologicalSpace H] - [inst_top_M : TopologicalSpace M] - [inst_chart_M : ChartedSpace H M] - (I : ModelWithCorners ℝ E H) - [inst_mani : IsManifold I (n + 1) M] - [inst_mani1 : IsManifold I 1 M] : - Type _ where - /-- The metric tensor at each point `x : M`, represented as a continuous linear map - `TₓM →L[ℝ] (TₓM →L[ℝ] ℝ)`. Applying it twice, `(val x v) w`, yields `gₓ(v, w)`. -/ - val : ∀ (x : M), TangentSpace I x →L[ℝ] (TangentSpace I x →L[ℝ] ℝ) - /-- Symmetry: `gₓ(v, w) = gₓ(w, v)`. -/ - symm : ∀ (x : M) (v w : TangentSpace I x), (val x v) w = (val x w) v - /-- Non-degeneracy: if `gₓ(v, w) = 0` for all `w`, then `v = 0`. -/ - nondegenerate : ∀ (x : M) (v : TangentSpace I x), (∀ w : TangentSpace I x, - (val x v) w = 0) → v = 0 - /-- Smoothness of the metric tensor as a smooth section of the bundle of bilinear forms. - - We follow the same pattern as Mathlib's Riemannian metric API, using `TotalSpace.mk'` - for the bundled map. -/ - contMDiff : ContMDiff I (I.prod 𝓘(ℝ, E →L[ℝ] E →L[ℝ] ℝ)) n - (fun x ↦ - TotalSpace.mk' (E →L[ℝ] E →L[ℝ] ℝ) x (val x)) - -namespace MetricTensor - -variable {E : Type v} {H : Type w} {M : Type w} {n : WithTop ℕ∞} -variable [NormedAddCommGroup E] [NormedSpace ℝ E] -variable [TopologicalSpace H] [TopologicalSpace M] [ChartedSpace H M] -variable {I : ModelWithCorners ℝ E H} -variable [IsManifold I (n + 1) M] [IsManifold I 1 M] - -/-- Coercion from `MetricTensor` to its `val` function. -/ -instance coeFunInst : CoeFun (MetricTensor E H M n I) - (fun _ => ∀ x : M, TangentSpace I x →L[ℝ] (TangentSpace I x →L[ℝ] ℝ)) where - coe g := g.val - -/-- The bilinear form on `TₓM` associated to a metric tensor. -/ -def toBilinForm (g : MetricTensor E H M n I) (x : M) : - LinearMap.BilinForm ℝ (TangentSpace I x) where - toFun := fun v => - { toFun := fun w => g.val x v w - map_add' := fun w₁ w₂ => by simp only [ContinuousLinearMap.map_add] - map_smul' := fun c w => by simp only [map_smul, smul_eq_mul, RingHom.id_apply] } - map_add' := fun v₁ v₂ => by - ext w - simp only [map_add, add_apply, LinearMap.coe_mk, AddHom.coe_mk, LinearMap.add_apply] - map_smul' := fun c v => by - ext w - simp only [map_smul, coe_smul', Pi.smul_apply, smul_eq_mul, LinearMap.coe_mk, AddHom.coe_mk, - RingHom.id_apply, LinearMap.smul_apply] - -/-- The quadratic form `v ↦ gₓ(v,v)` associated to a metric tensor. -/ -abbrev toQuadraticForm (g : MetricTensor E H M n I) (x : M) : - QuadraticForm ℝ (TangentSpace I x) := - PseudoRiemannianMetric.valToQuadraticForm g.val g.symm x - -@[simp] -lemma toBilinForm_apply (g : MetricTensor E H M n I) (x : M) (v w : TangentSpace I x) : - toBilinForm g x v w = g.val x v w := rfl - -@[simp] -lemma toQuadraticForm_apply (g : MetricTensor E H M n I) (x : M) (v : TangentSpace I x) : - toQuadraticForm g x v = g.val x v v := rfl - -@[simp] -lemma toBilinForm_isSymm (g : MetricTensor E H M n I) (x : M) : - (toBilinForm g x).IsSymm := by - refine { eq := ?_ } - intro v w - simp [toBilinForm_apply, g.symm x v w] - -@[simp] -lemma toBilinForm_nondegenerate (g : MetricTensor E H M n I) (x : M) : - (toBilinForm g x).Nondegenerate := by - unfold LinearMap.BilinForm.Nondegenerate LinearMap.Nondegenerate - LinearMap.SeparatingLeft LinearMap.SeparatingRight - constructor - · intro v hv - simp_rw [toBilinForm_apply] at hv - exact g.nondegenerate x v hv - · intro v hv - simp_rw [toBilinForm_apply] at hv - have hw : ∀ w : TangentSpace I x, (g.val x v) w = 0 := by - intro w - -- symmetry gives `g(v,w)=g(w,v)=0` - simpa [g.symm x v w] using hv w - exact g.nondegenerate x v hw - -/-- The value `gₓ(v,w)` of a metric tensor. -/ -def inner (g : MetricTensor E H M n I) (x : M) (v w : TangentSpace I x) : ℝ := - g.val x v w - -@[simp] -lemma inner_apply (g : MetricTensor E H M n I) (x : M) (v w : TangentSpace I x) : - inner g x v w = g.val x v w := rfl - -/-! ## Flat / sharp (musical isomorphisms) -/ - -/-- Index lowering map `v ↦ gₓ(v, -)` as a continuous linear map. -/ -abbrev flatL (g : MetricTensor E H M n I) (x : M) : - TangentSpace I x →L[ℝ] (TangentSpace I x →L[ℝ] ℝ) := - g.val x - -/-- Index lowering map `v ↦ gₓ(v, -)` as a linear map. -/ -abbrev flat (g : MetricTensor E H M n I) (x : M) : - TangentSpace I x →ₗ[ℝ] (TangentSpace I x →L[ℝ] ℝ) := - (g.flatL x).toLinearMap - -@[simp] -lemma flat_apply (g : MetricTensor E H M n I) (x : M) (v w : TangentSpace I x) : - (flat g x v) w = g.val x v w := rfl - -@[simp] -lemma flatL_apply (g : MetricTensor E H M n I) (x : M) (v w : TangentSpace I x) : - (flatL g x v) w = g.val x v w := rfl - -lemma flat_inj (g : MetricTensor E H M n I) (x : M) : Function.Injective (flat g x) := by - rw [← LinearMap.ker_eq_bot] - apply LinearMap.ker_eq_bot'.mpr - intro v hv - apply g.nondegenerate x v - intro w - exact DFunLike.congr_fun hv w - -lemma flatL_inj (g : MetricTensor E H M n I) (x : M) : Function.Injective (flatL g x) := - flat_inj g x - -section FiniteDimensional - -variable [inst_tangent_findim : ∀ (x : M), FiniteDimensional ℝ (TangentSpace I x)] - -lemma flatL_surj (g : MetricTensor E H M n I) (x : M) : Function.Surjective (g.flatL x) := by - haveI : FiniteDimensional ℝ (TangentSpace I x) := inst_tangent_findim x - have h_finrank_eq : - finrank ℝ (TangentSpace I x) = finrank ℝ (TangentSpace I x →L[ℝ] ℝ) := by - have h_dual_eq : finrank ℝ (TangentSpace I x →L[ℝ] ℝ) = - finrank ℝ (Module.Dual ℝ (TangentSpace I x)) := by - let to_dual : (TangentSpace I x →L[ℝ] ℝ) → Module.Dual ℝ (TangentSpace I x) := - fun f => f.toLinearMap - let from_dual : Module.Dual ℝ (TangentSpace I x) → (TangentSpace I x →L[ℝ] ℝ) := - fun f => ContinuousLinearMap.mk f (by - apply LinearMap.continuous_of_finiteDimensional) - let equiv : (TangentSpace I x →L[ℝ] ℝ) ≃ₗ[ℝ] Module.Dual ℝ (TangentSpace I x) := - { toFun := to_dual - invFun := from_dual - map_add' := fun f g => by ext v; rfl - map_smul' := fun c f => by ext v; rfl - left_inv := fun f => by ext v; rfl - right_inv := fun f => by ext v; rfl } - exact LinearEquiv.finrank_eq equiv - rw [h_dual_eq, ← Subspace.dual_finrank_eq] - exact - (LinearMap.injective_iff_surjective_of_finrank_eq_finrank h_finrank_eq).mp (flatL_inj g x) - -/-- `flatEquiv` as a continuous linear equivalence. -/ -noncomputable def flatEquiv (g : MetricTensor E H M n I) (x : M) : - TangentSpace I x ≃L[ℝ] (TangentSpace I x →L[ℝ] ℝ) := - LinearEquiv.toContinuousLinearEquiv <| - LinearEquiv.ofBijective (g.flatL x).toLinearMap ⟨g.flatL_inj x, g.flatL_surj x⟩ - -@[simp] -lemma flatEquiv_apply (g : MetricTensor E H M n I) (x : M) (v w : TangentSpace I x) : - (g.flatEquiv x v) w = g.val x v w := rfl - -/-- Index raising equivalence as the inverse of `flatEquiv`. -/ -noncomputable def sharpEquiv (g : MetricTensor E H M n I) (x : M) : - (TangentSpace I x →L[ℝ] ℝ) ≃L[ℝ] TangentSpace I x := - (g.flatEquiv x).symm - -noncomputable def sharpL (g : MetricTensor E H M n I) (x : M) : - (TangentSpace I x →L[ℝ] ℝ) →L[ℝ] TangentSpace I x := - (g.sharpEquiv x).toContinuousLinearMap - -/-- Index raising map `sharp` as a linear map. -/ -noncomputable def sharp (g : MetricTensor E H M n I) (x : M) : - (TangentSpace I x →L[ℝ] ℝ) →ₗ[ℝ] TangentSpace I x := - (g.sharpEquiv x).toLinearEquiv.toLinearMap - -@[simp] -lemma sharpL_apply_flatL (g : MetricTensor E H M n I) (x : M) (v : TangentSpace I x) : - g.sharpL x (g.flatL x v) = v := - (g.flatEquiv x).left_inv v - -@[simp] -lemma flatL_apply_sharpL (g : MetricTensor E H M n I) (x : M) - (ω : TangentSpace I x →L[ℝ] ℝ) : - g.flatL x (g.sharpL x ω) = ω := - (g.flatEquiv x).right_inv ω - -@[simp] -lemma flat_sharp_apply (g : MetricTensor E H M n I) (x : M) - (ω : TangentSpace I x →L[ℝ] ℝ) : - g.flat x (g.sharp x ω) = ω := by - ext v - have h := congrArg (fun f : TangentSpace I x →L[ℝ] ℝ => f v) (flatL_apply_sharpL (g := g) x ω) - simpa [flat, flatL, sharp, sharpL] using h - -@[simp] -lemma sharp_flat_apply (g : MetricTensor E H M n I) (x : M) (v : TangentSpace I x) : - g.sharp x (g.flat x v) = v := by - have h := sharpL_apply_flatL (g := g) x v - simpa [sharp, sharpL, flat, flatL] using h - -/-- Metric evaluated at `sharp ω₁` and `sharp ω₂`. -/ -@[simp] -lemma apply_sharp_sharp (g : MetricTensor E H M n I) (x : M) - (ω₁ ω₂ : TangentSpace I x →L[ℝ] ℝ) : - g.val x (g.sharpL x ω₁) (g.sharpL x ω₂) = ω₁ (g.sharpL x ω₂) := by - rw [← flatL_apply (g := g) x (g.sharpL x ω₁)] - rw [flatL_apply_sharpL (g := g) x ω₁] - -/-- Metric evaluated at `v` and `sharp ω`. -/ -lemma apply_vec_sharp (g : MetricTensor E H M n I) (x : M) (v : TangentSpace I x) - (ω : TangentSpace I x →L[ℝ] ℝ) : - g.val x v (g.sharpL x ω) = ω v := by - rw [g.symm x v (g.sharpL x ω)] - rw [← flatL_apply (g := g) x (g.sharpL x ω)] - rw [flatL_apply_sharpL (g := g) x ω] - -/-! ## Cotangent metric induced by `g` -/ - -/-- The induced metric value on the cotangent space at `x`. -/ -noncomputable def cotangentMetricVal (g : MetricTensor E H M n I) (x : M) - (ω₁ ω₂ : TangentSpace I x →L[ℝ] ℝ) : ℝ := - g.val x (g.sharpL x ω₁) (g.sharpL x ω₂) - -@[simp] lemma cotangentMetricVal_eq_apply_sharp (g : MetricTensor E H M n I) (x : M) - (ω₁ ω₂ : TangentSpace I x →L[ℝ] ℝ) : - cotangentMetricVal g x ω₁ ω₂ = ω₁ (g.sharpL x ω₂) := by - simp [cotangentMetricVal] - -lemma cotangentMetricVal_symm (g : MetricTensor E H M n I) (x : M) - (ω₁ ω₂ : TangentSpace I x →L[ℝ] ℝ) : - cotangentMetricVal g x ω₁ ω₂ = cotangentMetricVal g x ω₂ ω₁ := by - unfold cotangentMetricVal - rw [g.symm x (g.sharpL x ω₁) (g.sharpL x ω₂)] - -/-- The induced cotangent metric as a bilinear form. -/ -noncomputable def cotangentToBilinForm (g : MetricTensor E H M n I) (x : M) : - LinearMap.BilinForm ℝ (TangentSpace I x →L[ℝ] ℝ) where - toFun ω₁ := - { toFun := fun ω₂ => cotangentMetricVal g x ω₁ ω₂ - map_add' := fun ω₂ ω₃ => by simp [cotangentMetricVal, ContinuousLinearMap.map_add] - map_smul' := fun c ω₂ => by simp [cotangentMetricVal, map_smul, smul_eq_mul, RingHom.id_apply] } - map_add' := fun ω₁ ω₂ => by - ext ω₃ - simp [cotangentMetricVal, ContinuousLinearMap.map_add, ContinuousLinearMap.add_apply, - LinearMap.coe_mk, AddHom.coe_mk, LinearMap.add_apply] - map_smul' := fun c ω₁ => by - ext ω₂ - simp [cotangentMetricVal, ContinuousLinearMap.smul_apply, - LinearMap.coe_mk, AddHom.coe_mk, RingHom.id_apply, LinearMap.smul_apply] - -/-- The induced cotangent metric as a quadratic form. -/ -noncomputable def cotangentToQuadraticForm (g : MetricTensor E H M n I) (x : M) : - QuadraticForm ℝ (TangentSpace I x →L[ℝ] ℝ) where - toFun ω := cotangentMetricVal g x ω ω - toFun_smul a ω := by - simp [cotangentMetricVal, ContinuousLinearMap.smul_apply] - ring - exists_companion' := - ⟨LinearMap.mk₂ ℝ (fun ω₁ ω₂ => - cotangentMetricVal g x ω₁ ω₂ + cotangentMetricVal g x ω₂ ω₁) - (fun ω₁ ω₂ ω₃ => by simp [cotangentMetricVal, map_add, add_apply]; ring) - (fun a ω₁ ω₂ => by - simp [cotangentMetricVal, map_smul, smul_apply] - ring_nf) - (fun ω₁ ω₂ ω₃ => by simp [cotangentMetricVal, map_add, add_apply]; ring) - (fun a ω₁ ω₂ => by - simp [cotangentMetricVal, map_smul, smul_apply] - ring_nf), - by - intro ω₁ ω₂ - simp [LinearMap.mk₂_apply, cotangentMetricVal, ContinuousLinearMap.map_add, - ContinuousLinearMap.add_apply] - ring⟩ - -@[simp] -lemma cotangentToBilinForm_apply (g : MetricTensor E H M n I) (x : M) - (ω₁ ω₂ : TangentSpace I x →L[ℝ] ℝ) : - cotangentToBilinForm g x ω₁ ω₂ = cotangentMetricVal g x ω₁ ω₂ := rfl - -@[simp] -lemma cotangentToQuadraticForm_apply (g : MetricTensor E H M n I) (x : M) - (ω : TangentSpace I x →L[ℝ] ℝ) : - cotangentToQuadraticForm g x ω = cotangentMetricVal g x ω ω := rfl - -@[simp] -lemma cotangentToBilinForm_isSymm (g : MetricTensor E H M n I) (x : M) : - (cotangentToBilinForm g x).IsSymm := by - refine { eq := ?_ } - intro ω₁ ω₂ - simpa [cotangentToBilinForm_apply] using (cotangentMetricVal_symm (g := g) x ω₁ ω₂) - -/-- Nondegeneracy of the cotangent metric. -/ -lemma cotangentMetricVal_nondegenerate (g : MetricTensor E H M n I) (x : M) - (ω : TangentSpace I x →L[ℝ] ℝ) - (h : ∀ v : TangentSpace I x →L[ℝ] ℝ, cotangentMetricVal g x ω v = 0) : - ω = 0 := by - apply ContinuousLinearMap.ext - intro v - have h_forall : ∀ w : TangentSpace I x, ω w = 0 := by - intro w - let ω' : TangentSpace I x →L[ℝ] ℝ := g.flatL x w - have this : g.sharpL x ω' = w := by - simp [ω'] - have h_apply : cotangentMetricVal g x ω ω' = 0 := h ω' - simp [cotangentMetricVal_eq_apply_sharp] at h_apply - simpa [this] using h_apply - exact h_forall v - -@[simp] -lemma cotangentToBilinForm_nondegenerate (g : MetricTensor E H M n I) (x : M) : - (cotangentToBilinForm g x).Nondegenerate := by - unfold LinearMap.BilinForm.Nondegenerate LinearMap.Nondegenerate - LinearMap.SeparatingLeft LinearMap.SeparatingRight - constructor - · intro ω hω - apply cotangentMetricVal_nondegenerate (g := g) x ω - intro v - exact hω v - · intro ω hω - apply cotangentMetricVal_nondegenerate (g := g) x ω - intro v - have hv : ∀ y : TangentSpace I x →L[ℝ] ℝ, ((g.cotangentToBilinForm x) ω) y = 0 := by - intro y - rw [LinearMap.BilinForm.isSymm_def.mp (cotangentToBilinForm_isSymm (g := g) x)] - simp [hω] - exact hv v - -/-- The cotangent quadratic form is equivalent to the tangent quadratic form via `sharp`. -/ -theorem cotangentToQuadraticForm_equivalent_toQuadraticForm (g : MetricTensor E H M n I) (x : M) : - (g.cotangentToQuadraticForm x).Equivalent (g.toQuadraticForm x) := by - classical - refine ⟨?_⟩ - refine - { toLinearEquiv := (g.sharpEquiv x).toLinearEquiv - map_app' := fun ω => ?_ } - simp [cotangentToQuadraticForm_apply, cotangentMetricVal, toQuadraticForm, sharpEquiv, sharpL] - -/-! ## Pointwise index (finite-dimensional) -/ - -/-- The (negative) index of a metric tensor at a point, defined as the negative index of the -associated quadratic form `v ↦ gₓ(v,v)`. - -This is a pointwise invariant; it need not be locally constant. -/ -noncomputable def index (g : MetricTensor E H M n I) (x : M) : ℕ := - (g.toQuadraticForm x).negDim - -omit inst_tangent_findim in -@[simp] lemma index_def (g : MetricTensor E H M n I) (x : M) : - g.index x = (g.toQuadraticForm x).negDim := rfl - -end FiniteDimensional - -end MetricTensor - -/-- A pseudo-Riemannian metric of smoothness class `C^n` on a manifold `M` modelled on `(E, H)` -with model `I`. This structure defines a smoothly varying, non-degenerate, symmetric, -continuous bilinear form `gₓ` of constant negative dimension on the tangent space `TₓM` -at each point `x`. Requires `M` to be `C^{n+1}` smooth. -This structure formalizes O'Neill's Definition 3.1 (p. 54) of a metric tensor `g` on `M` -as a "symmetric non-degenerate (0,2) tensor field on M of constant index." -Each `gₓ` is a scalar product (O'Neill, Definition 20, p. 47) on `TₓM`. -/ -@[ext] -structure PseudoRiemannianMetric - (E : Type v) (H : Type w) (M : Type w) (n : WithTop ℕ∞) - [inst_norm_grp_E : NormedAddCommGroup E] - [inst_norm_sp_E : NormedSpace ℝ E] - [inst_top_H : TopologicalSpace H] - [inst_top_M : TopologicalSpace M] - [inst_chart_M : ChartedSpace H M] - (I : ModelWithCorners ℝ E H) - [inst_mani : IsManifold I (n + 1) M] - [inst_mani1 : IsManifold I 1 M] - [inst_tangent_findim : ∀ (x : M), FiniteDimensional ℝ (TangentSpace I x)] : - Type _ extends toMetricTensor : MetricTensor E H M n I where - /-- The negative dimension (`QuadraticForm.negDim`) of the metric's quadratic form is - locally constant. On a connected manifold, this implies it is constant globally. -/ - negDim_isLocallyConstant : - IsLocallyConstant (fun x : M => - have : FiniteDimensional ℝ (TangentSpace I x) := inferInstance - (PseudoRiemannianMetric.valToQuadraticForm val symm x).negDim) - namespace PseudoRiemannianMetric -variable {E : Type v} {H : Type w} {M : Type w} {n : WithTop ℕ∞} -variable [NormedAddCommGroup E] [NormedSpace ℝ E] -variable [TopologicalSpace H] [TopologicalSpace M] [ChartedSpace H M] -variable {I : ModelWithCorners ℝ E H} -variable [IsManifold I (n + 1) M] [IsManifold I 1 M] -variable [inst_tangent_findim : ∀ (x : M), FiniteDimensional ℝ (TangentSpace I x)] - -/-! ## Predicate typeclass: pseudo-Riemannian manifolds -/ - -/-- Prop-valued predicate recording existence of a `C^n` pseudo-Riemannian metric. - -This is the “there exists a metric” statement, *without* making any noncanonical choice. For -bundle-first development, the primary data is still an explicit `PseudoRiemannianMetric` (or a -`Bundle.PseudoRiemannianBundle` + smoothness instance). -/ -class HasPseudoRiemannianMetric : Prop where - out : Nonempty (PseudoRiemannianMetric E H M n I) - -instance (g : PseudoRiemannianMetric E H M n I) : - HasPseudoRiemannianMetric (E := E) (H := H) (M := M) (n := n) (I := I) := +open Bundle ContinuousLinearMap +open scoped Manifold Bundle + +noncomputable section + +section + +variable {E : Type v} [NormedAddCommGroup E] [NormedSpace ℝ E] +variable {H : Type w} [TopologicalSpace H] +variable {M : Type w} [TopologicalSpace M] [ChartedSpace H M] +variable {I : ModelWithCorners ℝ E H} {n : WithTop ℕ∞} +variable [IsManifold I 1 M] [IsManifold I (n + 1) M] + +/-- A `C^n` Riemannian metric on `M`, packaged using Mathlib's modern bundle API. -/ +abbrev RiemannianMetric + (I : ModelWithCorners ℝ E H) (n : WithTop ℕ∞) (M : Type w) + [TopologicalSpace M] [ChartedSpace H M] [IsManifold I 1 M] [IsManifold I (n + 1) M] := + Bundle.ContMDiffRiemannianMetric (IB := I) (n := n) (F := E) (E := fun x : M ↦ TangentSpace I x) + +namespace RiemannianMetric + +variable (g : RiemannianMetric (I := I) (n := n) M) +variable [∀ x : M, FiniteDimensional ℝ (TangentSpace I x)] + +/-- Forget the positivity to get a pseudo-Riemannian metric. The index is (locally constantly) `0`. +This is the bridge that makes pseudo-Riemannian API (musical isomorphisms, etc.) usable for a +Riemannian metric. -/ +def toPseudoRiemannianMetric (g : RiemannianMetric (I := I) (n := n) M) : + PseudoRiemannianMetric E H M n I where + val := g.inner + symm := g.symm + nondegenerate := by + intro x v hv + by_cases h : v = 0 + · simp [h] + · have hp : 0 < g.inner x v v := g.pos x v h + have h0 : g.inner x v v = 0 := hv v + exact (ne_of_gt hp h0).elim + contMDiff := g.contMDiff + negDim_isLocallyConstant := by + -- On a Riemannian metric, the associated quadratic form is positive definite, hence `negDim = 0`. + refine IsLocallyConstant.of_constant _ (fun x y => ?_) + have hx : + (PseudoRiemannianMetric.valToQuadraticForm g.inner g.symm x).negDim = 0 := by + apply QuadraticForm.negDim_posDef + intro v hv + simpa [PseudoRiemannianMetric.valToQuadraticForm] using g.pos x v hv + have hy : + (PseudoRiemannianMetric.valToQuadraticForm g.inner g.symm y).negDim = 0 := by + apply QuadraticForm.negDim_posDef + intro v hv + simpa [PseudoRiemannianMetric.valToQuadraticForm] using g.pos y v hv + have hx' : + (-PseudoRiemannianMetric.valToQuadraticForm g.inner g.symm x).posIndex = 0 := by + simpa [QuadraticForm.negDim] using hx + have hy' : + (-PseudoRiemannianMetric.valToQuadraticForm g.inner g.symm y).posIndex = 0 := by + simpa [QuadraticForm.negDim] using hy + simp [hx', hy'] + +@[simp] +lemma toPseudoRiemannianMetric_index (g : RiemannianMetric (I := I) (n := n) M) (x : M) : + (toPseudoRiemannianMetric (I := I) (n := n) (M := M) g).index x = 0 := by + have hx : (PseudoRiemannianMetric.valToQuadraticForm g.inner g.symm x).negDim = 0 := by + apply QuadraticForm.negDim_posDef + intro v hv + simpa [PseudoRiemannianMetric.valToQuadraticForm] using g.pos x v hv + simpa [PseudoRiemannianMetric.index, PseudoRiemannianMetric.toQuadraticForm, + toPseudoRiemannianMetric] using hx + +instance : + Coe (RiemannianMetric (I := I) (n := n) M) + (PseudoRiemannianMetric E H M n I) := + ⟨fun g => toPseudoRiemannianMetric (I := I) (n := n) (M := M) g⟩ + +end RiemannianMetric + +/-! ## Existence predicates (non-choosing) -/ + +/-- Prop-valued predicate recording existence of a `C^n` Riemannian metric (bundle-first), without +making any noncanonical choice. -/ +class HasRiemannianMetric : Prop where + out : Nonempty (RiemannianMetric (I := I) (n := n) M) + +instance (g : RiemannianMetric (I := I) (n := n) M) : + HasRiemannianMetric (I := I) (n := n) (M := M) := ⟨⟨g⟩⟩ -theorem hasPseudoRiemannianMetric_iff : - HasPseudoRiemannianMetric (E := E) (H := H) (M := M) (n := n) (I := I) ↔ - Nonempty (PseudoRiemannianMetric E H M n I) := +theorem hasRiemannianMetric_iff : + HasRiemannianMetric (I := I) (n := n) (M := M) ↔ + Nonempty (RiemannianMetric (I := I) (n := n) M) := ⟨fun h => h.out, fun h => ⟨h⟩⟩ -theorem hasPseudoRiemannianMetric_iff_exists : - HasPseudoRiemannianMetric (E := E) (H := H) (M := M) (n := n) (I := I) ↔ - ∃ _ : PseudoRiemannianMetric E H M n I, True := - ⟨fun h => by - rcases h.out with ⟨g⟩ - exact ⟨g, trivial⟩, - fun h => by - rcases h with ⟨g, -⟩ - exact ⟨⟨g⟩⟩⟩ - -/-- Typeclass carrying a *chosen* pseudo-Riemannian metric (as opposed to the mere existence -predicate `HasPseudoRiemannianMetric`). This is the convenient form for downstream constructions. -/ -class PseudoRiemannianManifold : Type _ where - metric : PseudoRiemannianMetric E H M n I - -/-- The chosen pseudo-Riemannian metric on a manifold typeclass. -/ -abbrev pseudoRiemannianMetric [PseudoRiemannianManifold (E := E) (H := H) (M := M) (n := n) (I := I)] : - PseudoRiemannianMetric E H M n I := - (PseudoRiemannianManifold.metric (E := E) (H := H) (M := M) (n := n) (I := I)) - -instance [PseudoRiemannianManifold (E := E) (H := H) (M := M) (n := n) (I := I)] : - HasPseudoRiemannianMetric (E := E) (H := H) (M := M) (n := n) (I := I) := - ⟨⟨pseudoRiemannianMetric (E := E) (H := H) (M := M) (n := n) (I := I)⟩⟩ - -/-- Given a pseudo-Riemannian metric `g` on manifold `M` and a point `x : M`, -this function constructs a bilinear form on the tangent space at `x`. -For tangent vectors `u v : T_x M`, the bilinear form is given by: -`g_x(u, v) = g(x)(u, v)` -/ -abbrev toBilinForm (g : PseudoRiemannianMetric E H M n I) (x : M) : - LinearMap.BilinForm ℝ (TangentSpace I x) := - MetricTensor.toBilinForm (g := g.toMetricTensor) x - -/-- Convert a pseudo-Riemannian metric at a point `x` to a quadratic form `v ↦ gₓ(v, v)`. -/ -abbrev toQuadraticForm (g : PseudoRiemannianMetric E H M n I) (x : M) : - QuadraticForm ℝ (TangentSpace I x) := - MetricTensor.toQuadraticForm (g := g.toMetricTensor) x - -/-- Coercion from PseudoRiemannianMetric to its function representation. -/ -instance coeFunInst : CoeFun (PseudoRiemannianMetric E H M n I) - (fun _ => ∀ x : M, TangentSpace I x →L[ℝ] (TangentSpace I x →L[ℝ] ℝ)) where - coe g := g.val - -@[simp] -lemma toBilinForm_apply (g : PseudoRiemannianMetric E H M n I) (x : M) - (v w : TangentSpace I x) : - toBilinForm g x v w = g.val x v w := rfl - -@[simp] -lemma toQuadraticForm_apply (g : PseudoRiemannianMetric E H M n I) (x : M) - (v : TangentSpace I x) : - toQuadraticForm g x v = g.val x v v := rfl - -/-! ## Index (negative inertia) -/ - -/-- The (negative) index of a pseudo-Riemannian metric at a point, defined as the negative index of -the associated quadratic form `v ↦ gₓ(v,v)`. -/ -noncomputable def index (g : PseudoRiemannianMetric E H M n I) (x : M) : ℕ := - (g.toQuadraticForm x).negDim - -@[simp] -lemma index_def (g : PseudoRiemannianMetric E H M n I) (x : M) : - g.index x = (g.toQuadraticForm x).negDim := rfl - -lemma index_isLocallyConstant (g : PseudoRiemannianMetric E H M n I) : - IsLocallyConstant (fun x : M => g.index x) := by - simpa [index, toQuadraticForm] using g.negDim_isLocallyConstant - -lemma index_eq_of_isPreconnected (g : PseudoRiemannianMetric E H M n I) {s : Set M} - (hs : IsPreconnected s) {x y : M} (hx : x ∈ s) (hy : y ∈ s) : - g.index x = g.index y := - (index_isLocallyConstant (g := g)).apply_eq_of_isPreconnected hs hx hy - -lemma index_eq_of_preconnectedSpace [PreconnectedSpace M] (g : PseudoRiemannianMetric E H M n I) - (x y : M) : - g.index x = g.index y := - (index_isLocallyConstant (g := g)).apply_eq_of_preconnectedSpace x y - -lemma index_eq_of_mem_connectedComponent (g : PseudoRiemannianMetric E H M n I) (x y : M) - (hy : y ∈ connectedComponent x) : - g.index y = g.index x := - (index_isLocallyConstant (g := g)).apply_eq_of_isPreconnected - (isConnected_connectedComponent.isPreconnected) - hy (mem_connectedComponent : x ∈ connectedComponent x) - -@[simp] -lemma toBilinForm_isSymm (g : PseudoRiemannianMetric E H M n I) (x : M) : - (toBilinForm g x).IsSymm := by - refine { eq := ?_ } - intro v w; simp only [toBilinForm_apply]; exact g.symm x v w - -@[simp] -lemma toBilinForm_nondegenerate (g : PseudoRiemannianMetric E H M n I) (x : M) : - (toBilinForm g x).Nondegenerate := by - unfold LinearMap.BilinForm.Nondegenerate LinearMap.Nondegenerate - LinearMap.SeparatingLeft LinearMap.SeparatingRight - constructor - · intro v hv; simp_rw [toBilinForm_apply] at hv; exact g.nondegenerate x v hv - · intro v hv; simp_rw [toBilinForm_apply] at hv; - have hw : ∀ (w : TangentSpace I x), ((g.val x) v) w = 0 := by - intro w - simpa [g.symm x v w] using hv w - exact g.nondegenerate x v hw - -/-- The inner product (or scalar product) on the tangent space at point `x` - induced by the pseudo-Riemannian metric `g`. This is `gₓ(v, w)`. -/ -def inner (g : PseudoRiemannianMetric E H M n I) (x : M) (v w : TangentSpace I x) : ℝ := - g.val x v w - -@[simp] -lemma inner_apply (g : PseudoRiemannianMetric E H M n I) (x : M) (v w : TangentSpace I x) : - inner g x v w = g.val x v w := rfl - -/-! ## Flat -/ - -section Flat - -/-- The "musical" isomorphism (index lowering) `v ↦ gₓ(v, -)`. -The non-degeneracy of `gₓ` (O'Neill, Def 17 (3), p. 46) means its matrix representation -is invertible (O'Neill, Lemma 19, p. 47), and that this map is an isomorphism from `TₓM` -to its dual. -/ -def flat (g : PseudoRiemannianMetric E H M n I) (x : M) : - TangentSpace I x →ₗ[ℝ] (TangentSpace I x →L[ℝ] ℝ) := - MetricTensor.flat (g := g.toMetricTensor) x - -@[simp] -lemma flat_apply (g : PseudoRiemannianMetric E H M n I) (x : M) (v w : TangentSpace I x) : - (flat g x v) w = g.val x v w := by rfl - -/-- The musical isomorphism as a continuous linear map. -/ -abbrev flatL (g : PseudoRiemannianMetric E H M n I) (x : M) : - TangentSpace I x →L[ℝ] (TangentSpace I x →L[ℝ] ℝ) := - MetricTensor.flatL (g := g.toMetricTensor) x +/-- Any Riemannian metric gives a pseudo-Riemannian metric (of index `0`), hence existence of a +Riemannian metric implies existence of a pseudo-Riemannian metric. -/ +instance [h : HasRiemannianMetric (I := I) (n := n) (M := M)] + [∀ x : M, FiniteDimensional ℝ (TangentSpace I x)] : + PseudoRiemannianMetric.HasPseudoRiemannianMetric (E := E) (H := H) (M := M) (n := n) (I := I) := + ⟨by + rcases h.out with ⟨g⟩ + exact ⟨RiemannianMetric.toPseudoRiemannianMetric (I := I) (n := n) (M := M) g⟩⟩ -@[simp] -lemma flatL_apply (g : PseudoRiemannianMetric E H M n I) (x : M) (v w : TangentSpace I x) : - (flatL g x v) w = g.val x v w := rfl - -@[simp] -lemma flat_inj (g : PseudoRiemannianMetric E H M n I) (x : M) : - Function.Injective (flat g x) := by - rw [← LinearMap.ker_eq_bot]; apply LinearMap.ker_eq_bot'.mpr - intro v hv; apply g.nondegenerate x v; intro w; exact DFunLike.congr_fun hv w - -@[simp] -lemma flatL_inj (g : PseudoRiemannianMetric E H M n I) (x : M) : - Function.Injective (flatL g x) := - flat_inj g x - -@[simp] -lemma flatL_surj - (g : PseudoRiemannianMetric E H M n I) (x : M) : - Function.Surjective (g.flatL x) := by - simpa [PseudoRiemannianMetric.flatL] using - MetricTensor.flatL_surj (g := g.toMetricTensor) x - -/-- The "musical" isomorphism (index lowering) from `TₓM` to its dual, -as a continuous linear equivalence. This equivalence is a direct result of `gₓ` being -a non-degenerate bilinear form (O'Neill, Def 17(3), p. 46; Lemma 19, p. 47). -/ -noncomputable def flatEquiv - (g : PseudoRiemannianMetric E H M n I) - (x : M) : - TangentSpace I x ≃L[ℝ] (TangentSpace I x →L[ℝ] ℝ) := - MetricTensor.flatEquiv (g := g.toMetricTensor) x - -lemma coe_flatEquiv - (g : PseudoRiemannianMetric E H M n I) (x : M) : - (g.flatEquiv x : TangentSpace I x →ₗ[ℝ] (TangentSpace I x →L[ℝ] ℝ)) = g.flatL x := rfl - -@[simp] -lemma flatEquiv_apply - (g : PseudoRiemannianMetric E H M n I) (x : M) (v w : TangentSpace I x) : - (g.flatEquiv x v) w = g.val x v w := rfl - -end Flat - -/-! ## Sharp -/ - -section Sharp +end -/-- The "musical" isomorphism (index raising) from the dual of `TₓM` to `TₓM`. -This is the inverse of `flatEquiv g x`, and its existence as an isomorphism is -guaranteed by the non-degeneracy of `gₓ` (O'Neill, Lemma 19, p. 47). -/ -noncomputable def sharpEquiv - (g : PseudoRiemannianMetric E H M n I) (x : M) : - (TangentSpace I x →L[ℝ] ℝ) ≃L[ℝ] TangentSpace I x := - MetricTensor.sharpEquiv (g := g.toMetricTensor) x +end -/-- The index raising map `sharp` as a continuous linear map. -/ -noncomputable def sharpL - (g : PseudoRiemannianMetric E H M n I) (x : M) : - (TangentSpace I x →L[ℝ] ℝ) →L[ℝ] TangentSpace I x := - MetricTensor.sharpL (g := g.toMetricTensor) x - -lemma sharpL_eq_toContinuousLinearMap - (g : PseudoRiemannianMetric E H M n I) (x : M) : - g.sharpL x = (g.sharpEquiv x).toContinuousLinearMap := rfl - -lemma coe_sharpEquiv - (g : PseudoRiemannianMetric E H M n I) (x : M) : - (g.sharpEquiv x : (TangentSpace I x →L[ℝ] ℝ) →L[ℝ] TangentSpace I x) = g.sharpL x := rfl - -/-- The index raising map `sharp` as a linear map. -/ -noncomputable def sharp - (g : PseudoRiemannianMetric E H M n I) (x : M) : - (TangentSpace I x →L[ℝ] ℝ) →ₗ[ℝ] TangentSpace I x := - MetricTensor.sharp (g := g.toMetricTensor) x - -@[simp] -lemma sharpL_apply_flatL - (g : PseudoRiemannianMetric E H M n I) (x : M) (v : TangentSpace I x) : - g.sharpL x (g.flatL x v) = v := by - simp [PseudoRiemannianMetric.sharpL, PseudoRiemannianMetric.flatL, - MetricTensor.sharpL_apply_flatL (g := g.toMetricTensor) x v] - -@[simp] -lemma flatL_apply_sharpL - (g : PseudoRiemannianMetric E H M n I) (x : M) (ω : TangentSpace I x →L[ℝ] ℝ) : - g.flatL x (g.sharpL x ω) = ω := by - simp [PseudoRiemannianMetric.sharpL, PseudoRiemannianMetric.flatL, - MetricTensor.flatL_apply_sharpL (g := g.toMetricTensor) x ω] - -/-- Applying `sharp` then `flat` recovers the original covector. -/ -@[simp] -lemma flat_sharp_apply - (g : PseudoRiemannianMetric E H M n I) (x : M) (ω : TangentSpace I x →L[ℝ] ℝ) : - g.flat x (g.sharp x ω) = ω := by - simp [PseudoRiemannianMetric.flat, PseudoRiemannianMetric.sharp, - MetricTensor.flat_sharp_apply (g := g.toMetricTensor) x ω] - -@[simp] -lemma sharp_flat_apply - (g : PseudoRiemannianMetric E H M n I) (x : M) (v : TangentSpace I x) : - g.sharp x (g.flat x v) = v := by - have h : g.sharpL x (g.flatL x v) = v := - PseudoRiemannianMetric.sharpL_apply_flatL (g := g) x v - convert h using 1 - -/-- The metric evaluated at `sharp ω₁` and `sharp ω₂`. -/ -@[simp] -lemma apply_sharp_sharp - (g : PseudoRiemannianMetric E H M n I) (x : M) (ω₁ ω₂ : TangentSpace I x →L[ℝ] ℝ) : - g.val x (g.sharpL x ω₁) (g.sharpL x ω₂) = ω₁ (g.sharpL x ω₂) := by - simp [PseudoRiemannianMetric.sharpL] - -/-- The metric evaluated at `v` and `sharp ω`. -/ -lemma apply_vec_sharp - (g : PseudoRiemannianMetric E H M n I) (x : M) (v : TangentSpace I x) - (ω : TangentSpace I x →L[ℝ] ℝ) : - g.val x v (g.sharpL x ω) = ω v := by - simp [PseudoRiemannianMetric.sharpL, - MetricTensor.apply_vec_sharp (g := g.toMetricTensor) x v ω] - -end Sharp - -/-! ## Cotangent -/ -section Cotangent - -variable {E : Type v} {H : Type w} {M : Type w} {n : WithTop ℕ∞} -variable [NormedAddCommGroup E] [NormedSpace ℝ E] -variable [TopologicalSpace H] [TopologicalSpace M] [ChartedSpace H M] -variable {I : ModelWithCorners ℝ E H} -variable [IsManifold I (n + 1) M] [IsManifold I 1 M] -variable [inst_tangent_findim : ∀ (x : M), FiniteDimensional ℝ (TangentSpace I x)] - -/-- The value of the induced metric on the cotangent space at point `x`. -/ -noncomputable def cotangentMetricVal (g : PseudoRiemannianMetric E H M n I) (x : M) - (ω₁ ω₂ : TangentSpace I x →L[ℝ] ℝ) : ℝ := - g.val x (g.sharpL x ω₁) (g.sharpL x ω₂) - -@[simp] -lemma cotangentMetricVal_eq_apply_sharp (g : PseudoRiemannianMetric E H M n I) (x : M) - (ω₁ ω₂ : TangentSpace I x →L[ℝ] ℝ) : - cotangentMetricVal g x ω₁ ω₂ = ω₁ (g.sharpL x ω₂) := by - rw [cotangentMetricVal, apply_sharp_sharp] - -lemma cotangentMetricVal_symm (g : PseudoRiemannianMetric E H M n I) (x : M) - (ω₁ ω₂ : TangentSpace I x →L[ℝ] ℝ) : - cotangentMetricVal g x ω₁ ω₂ = cotangentMetricVal g x ω₂ ω₁ := by - unfold cotangentMetricVal - rw [g.symm x (g.sharpL x ω₁) (g.sharpL x ω₂)] - -/-- The induced metric on the cotangent space at point `x` as a bilinear form. -For covectors `ω₁` and `ω₂`, this gives `g(ω₁^#, ω₂^#)`, where `ω^#` is -the "sharp" musical isomorphism raising indices. -/ -noncomputable def cotangentToBilinForm (g : PseudoRiemannianMetric E H M n I) (x : M) : - LinearMap.BilinForm ℝ (TangentSpace I x →L[ℝ] ℝ) where - toFun ω₁ := { toFun := λ ω₂ => cotangentMetricVal g x ω₁ ω₂, - map_add' := λ ω₂ ω₃ => by - simp only [cotangentMetricVal, - ContinuousLinearMap.map_add], - map_smul' := λ c ω₂ => by - simp only [cotangentMetricVal, - map_smul, smul_eq_mul, RingHom.id_apply] } - map_add' := λ ω₁ ω₂ => by - ext ω₃ - simp only [cotangentMetricVal, - ContinuousLinearMap.map_add, - ContinuousLinearMap.add_apply, - LinearMap.coe_mk, AddHom.coe_mk, LinearMap.add_apply] - map_smul' := λ c ω₁ => by - ext ω₂ - simp only [cotangentMetricVal, - ContinuousLinearMap.map_smul, - ContinuousLinearMap.smul_apply, - LinearMap.coe_mk, AddHom.coe_mk, - RingHom.id_apply, LinearMap.smul_apply] - -/-- The cometric on the cotangent space T_x*M at `x`, expressed as a quadratic form. -It is induced by the pseudo-Riemannian metric on the tangent space T_xM. -/ -noncomputable def cotangentToQuadraticForm (g : PseudoRiemannianMetric E H M n I) (x : M) : - QuadraticForm ℝ (TangentSpace I x →L[ℝ] ℝ) where - toFun ω := cotangentMetricVal g x ω ω - toFun_smul a ω := by - simp only [cotangentMetricVal, - ContinuousLinearMap.map_smul, - ContinuousLinearMap.smul_apply, - smul_smul] - exists_companion' := - ⟨LinearMap.mk₂ ℝ (fun ω₁ ω₂ => - cotangentMetricVal g x ω₁ ω₂ + cotangentMetricVal g x ω₂ ω₁) - (fun ω₁ ω₂ ω₃ => by simp only [cotangentMetricVal, map_add, add_apply]; ring) - (fun a ω₁ ω₂ => by - simp only [cotangentMetricVal, map_smul, smul_apply]; - ring_nf; exact Eq.symm (smul_add ..)) - (fun ω₁ ω₂ ω₃ => by - simp only [cotangentMetricVal, map_add, add_apply]; ring) - (fun a ω₁ ω₂ => by - simp only [cotangentMetricVal, map_smul, smul_apply]; ring_nf; - exact Eq.symm (smul_add ..)), - by - intro ω₁ ω₂ - simp only [LinearMap.mk₂_apply, cotangentMetricVal] - simp only [ContinuousLinearMap.map_add, ContinuousLinearMap.add_apply] - ring⟩ - -@[simp] -lemma cotangentToBilinForm_apply (g : PseudoRiemannianMetric E H M n I) (x : M) - (ω₁ ω₂ : TangentSpace I x →L[ℝ] ℝ) : - cotangentToBilinForm g x ω₁ ω₂ = cotangentMetricVal g x ω₁ ω₂ := rfl - -@[simp] -lemma cotangentToQuadraticForm_apply (g : PseudoRiemannianMetric E H M n I) (x : M) - (ω : TangentSpace I x →L[ℝ] ℝ) : - cotangentToQuadraticForm g x ω = cotangentMetricVal g x ω ω := rfl - -@[simp] -lemma cotangentToBilinForm_isSymm (g : PseudoRiemannianMetric E H M n I) (x : M) : - (cotangentToBilinForm g x).IsSymm := by - refine { eq := ?_ } - intro ω₁ ω₂; simp only [cotangentToBilinForm_apply]; exact cotangentMetricVal_symm g x ω₁ ω₂ - -/-- The cotangent metric is non-degenerate: if `cotangentMetricVal g x ω v = 0` for all `v`, - then `ω = 0`. -/ -lemma cotangentMetricVal_nondegenerate (g : PseudoRiemannianMetric E H M n I) (x : M) - (ω : TangentSpace I x →L[ℝ] ℝ) (h : ∀ v : TangentSpace I x →L[ℝ] ℝ, - cotangentMetricVal g x ω v = 0) : - ω = 0 := by - apply ContinuousLinearMap.ext - intro v - have h_forall : ∀ w : TangentSpace I x, ω w = 0 := by - intro w - let ω' : TangentSpace I x →L[ℝ] ℝ := g.flatL x w - have this : g.sharpL x ω' = w := by - simp only [ω', sharpL_apply_flatL] - have h_apply : cotangentMetricVal g x ω ω' = 0 := h ω' - simp only [cotangentMetricVal_eq_apply_sharp] at h_apply - rw [this] at h_apply - exact h_apply - exact h_forall v - -@[simp] -lemma cotangentToBilinForm_nondegenerate (g : PseudoRiemannianMetric E H M n I) (x : M) : - (cotangentToBilinForm g x).Nondegenerate := by - unfold LinearMap.BilinForm.Nondegenerate LinearMap.Nondegenerate - LinearMap.SeparatingLeft LinearMap.SeparatingRight - constructor - · intro ω hω - apply cotangentMetricVal_nondegenerate g x ω - intro v - exact hω v - · intro ω hω - apply cotangentMetricVal_nondegenerate g x ω - intro v - have hv : ∀ (y : TangentSpace I x →L[ℝ] ℝ), ((g.cotangentToBilinForm x) ω) y = 0 := by - intro y; rw [LinearMap.BilinForm.isSymm_def.mp (cotangentToBilinForm_isSymm g x)]; simp [hω] - exact hv v - -/-! ## Cotangent signature -/ - -/-- The cotangent quadratic form is equivalent to the tangent quadratic form via `sharp`. -/ -theorem cotangentToQuadraticForm_equivalent_toQuadraticForm - (g : PseudoRiemannianMetric E H M n I) (x : M) : - (g.cotangentToQuadraticForm x).Equivalent (g.toQuadraticForm x) := by - refine ⟨?_⟩ - refine - { toLinearEquiv := (g.sharpEquiv x).toLinearEquiv - map_app' := fun ω => ?_ } - have hsh : g.sharpL x ω = g.sharpEquiv x ω := rfl - simp [cotangentToQuadraticForm_apply, cotangentMetricVal, toQuadraticForm, hsh] - -theorem cotangent_signature_eq (g : PseudoRiemannianMetric E H M n I) (x : M) : - (g.cotangentToQuadraticForm x).signature = (g.toQuadraticForm x).signature := - QuadraticForm.signature_eq_of_equivalent (E := (TangentSpace I x →L[ℝ] ℝ)) - (E₂ := TangentSpace I x) (cotangentToQuadraticForm_equivalent_toQuadraticForm (g := g) x) - -theorem cotangent_negDim_eq (g : PseudoRiemannianMetric E H M n I) (x : M) : - (g.cotangentToQuadraticForm x).negDim = (g.toQuadraticForm x).negDim := - congrArg QuadraticForm.Signature.neg (cotangent_signature_eq (g := g) x) - -end Cotangent - -end PseudoRiemannianMetric end PseudoRiemannianMetric From 3856a2bb9e10d55b93d5ba8be447573aa0f9156b Mon Sep 17 00:00:00 2001 From: Matteo CIpollina Date: Tue, 3 Mar 2026 07:44:46 +0100 Subject: [PATCH 17/36] refactor --- MathlibDocumentationStyle.md | 186 ++++++ MathlibNamingConventions.md | 367 +++++++++++ MathlibStyle.md | 450 ++++++++++++++ .../Metric/PseudoRiemannian/Defs.lean | 2 +- .../Geometry/Metric/QuadraticForm/Index.lean | 19 + .../Geometry/Metric/QuadraticForm/NegDim.lean | 587 +++++++++--------- .../Geometry/Metric/Riemannian/Defs.lean | 2 +- 7 files changed, 1312 insertions(+), 301 deletions(-) create mode 100644 MathlibDocumentationStyle.md create mode 100644 MathlibNamingConventions.md create mode 100644 MathlibStyle.md create mode 100644 PhysLean/Mathematics/Geometry/Metric/QuadraticForm/Index.lean diff --git a/MathlibDocumentationStyle.md b/MathlibDocumentationStyle.md new file mode 100644 index 000000000..5272d8981 --- /dev/null +++ b/MathlibDocumentationStyle.md @@ -0,0 +1,186 @@ +Documentation style +All pull requests must meet the following documentation standards. See the doc-gen repo for information about the automatically generated doc pages. + +You can preview the markdown processing of a GitHub page or pull request using the Lean doc preview page. + +Header comment +Each mathlib file should start with: + +a header comment with copyright information (see the recommendations in our style guidelines); +the list of imports (one on each line); +a module docstring containing general documentation, written using Markdown and LaTeX. +(See the example below.) + +Headers use atx-style headers (with hash signs, no underlying dash). The open and close delimiters /-! and -/ should appear on their own lines. + +The mandatory title of the file is a first level header. It is followed by a summary of the content of the file. + +The other sections, with second level headers are (in this order): + +Main definitions (optional, can be covered in the summary) +Main statements (optional, can be covered in the summary) +Notation (omitted only if no notation is introduced in this file) +Implementation notes (description of important design decisions or interface features, including use of type classes and simp canonical form for new definitions) +References (references to textbooks or papers, or Wikipedia pages) +Tags (a list of keywords that could be useful when doing text search in mathlib to find where something is covered) +References should refer to bibtex entries in the mathlib citations file. See the Citing other works section below. + +The following code block is an example of a file header. + +/- +Copyright (c) 2018 Robert Y. Lewis. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Robert Y. Lewis +-/ +module + +public import Mathlib.Algebra.Order.AbsoluteValue.Basic +public import Mathlib.NumberTheory.Padics.PadicVal.Basic + +/-! +# p-adic norm + +This file defines the `p`-adic norm on `ℚ`. + +The `p`-adic valuation on `ℚ` is the difference of the multiplicities of `p` in the numerator and +denominator of `q`. This function obeys the standard properties of a valuation, with the appropriate +assumptions on `p`. + +The valuation induces a norm on `ℚ`. This norm is a nonarchimedean absolute value. +It takes values in {0} ∪ {1/p^k | k ∈ ℤ}. + +## Implementation notes + +Much, but not all, of this file assumes that `p` is prime. This assumption is inferred automatically +by taking `[Fact p.Prime]` as a type class argument. + +## References + +* [F. Q. Gouvêa, *p-adic numbers*][gouvea1997] +* [R. Y. Lewis, *A formal proof of Hensel's lemma over the p-adic integers*][lewis2019] +* + +## Tags + +p-adic, p adic, padic, norm, valuation +-/ +Doc strings +Every definition and major theorem is required to have a doc string. (Doc strings on lemmas are also encouraged, particularly if the lemma has any mathematical content or might be useful in another file.) These are introduced using /-- and closed by -/ above the definition, with either newlines or single spaces between the markers and the text. Subsequent lines in a doc-string should not be indented. They can contain Markdown and LaTeX as well: see the next section. If a doc string is a complete sentence, then it should end in a period. Named theorems, such as the mean value theorem should be bold faced (i.e., with two asterisks before and after). + +Doc strings should convey the mathematical meaning of the definition. They are allowed to lie slightly about the actual implementation. The following is a doc string example: + +/-- If `q ≠ 0`, the `p`-adic norm of a rational `q` is `p ^ (-padicValRat p q)`. +If `q = 0`, the `p`-adic norm of `q` is `0`. -/ +def padicNorm (p : ℕ) (q : ℚ) : ℚ := + if q = 0 then 0 else (p : ℚ) ^ (-padicValRat p q) +An example that is slightly lying but still describes the mathematical content would be: + +/-- `padicValRat` defines the valuation of a rational `q` to be the valuation of `q.num` minus the +valuation of `q.den`. If `q = 0` or `p = 1`, then `padicValRat p q` defaults to `0`. -/ +def padicValRat (p : ℕ) (q : ℚ) : ℤ := + padicValInt p q.num - padicValNat p q.den +Tactic documentation +Tactics should have a docstring that matches the Lean documentation style guide. Key points are: Be complete and self-contained but brief. The docstring should start with a full sentence that has the tactic as the subject. (Example: "rewrite [e] uses the expression e as a rewrite rule on the main goal.") All different options and forms of the tactic should appear in a bulleted list. The "Examples:" section, if any, should be a sequence of code blocks. + +Linting +The docBlame linter lists all definitions that do not have doc strings. The docBlameThm linter will lists theorems and lemmas that do not have doc strings. The tacticDocs linter lists all tactics that do not have docstrings. + +To run only the docBlame linter, add the following to the end of your lean file: + +#lint only docBlame +To run only the docBlame and docBlameThm linters, add the following to the end of your lean file: + +#lint only docBlame docBlameThm +To run the all default linters, including docBlame and tacticDocs, add the following to the end of your lean file: + +#lint +To run the all default linters, including docBlame and run docBlameThm, add the following to the end of your lean file: + +#lint docBlameThm +LaTeX and Markdown +We generally put references to Lean declarations or variables in between backticks. Writing the fully-qualified name (e.g. finset.card_pos instead of just card_pos) will turn the name into a link on our online docs. + +Raw URLs should be enclosed in angle brackets <...> to ensure that they will be clickable online. (Some URLs, especially those with parentheses or other special symbols, may not be parsed correctly by the markdown renderer.) + +When talking about mathematical symbols instead, it may be preferable to use LaTeX. LaTeX can be included in doc strings in three ways: + +using single dollar signs $ ... $ to render math inline, +using double dollar signs $$ ... $$ to render math in "display mode", or +using environments \begin{*} ... \end{*} (without dollar signs). +These correspond to the MathJax settings of our online docs. The interaction between the Markdown and LaTeX there is similar to that on https://math.stackexchange.com and https://mathoverflow.net, so you can paste a doc string into an editing sandbox there to preview the final result. See also the math.stackexchange MathJax tutorial. + +Sectioning comments +It is common to structure a file in sections, where each section contains related declarations. By describing the sections with module documentation /-! ... -/ at the beginning, these sections can be seen in the documentation. + +While these sectioning comments will often correspond to section or namespace commands, this is not required. You can use sectioning comments inside of a section or namespace, and you can have multiple sections or namespaces following one sectioning comment. + +Sectioning comments are for display and readability only. They have no semantic meaning. + +Third-level headers ### should be used for titles inside sectioning comments. + +If the comment is more than one line long, the delimiters /-! and -/ should appear on their own lines. + +See Lean/Expr/Basic.lean for an example in practice. + +namespace BinderInfo + +/-! ### Declarations about `BinderInfo` -/ + +/-- The brackets corresponding to a given `BinderInfo`. -/ +def brackets : BinderInfo → String × String + | BinderInfo.implicit => ("{", "}") + | BinderInfo.strictImplicit => ("{{", "}}") + | BinderInfo.instImplicit => ("[", "]") + | _ => ("(", ")") + +end BinderInfo + +namespace Name + +/-! ### Declarations about `name` -/ + +/-- Find the largest prefix `n` of a `Name` such that `f n != none`, then replace this prefix +with the value of `f n`. -/ +def mapPrefix (f : Name → Option Name) (n : Name) : Name := Id.run do + if let some n' := f n then return n' + match n with + | anonymous => anonymous + | str n' s => mkStr (mapPrefix f n') s + | num n' i => mkNum (mapPrefix f n') i +Theories documentation +In addition to documentation living in Lean files, we have theories documentation where we give overviews spanning several Lean files, and more mathematical explanations in cases where formalization requires slightly exotic points of view, see for instance the topology documentation. + +Citing other works +To cite papers and books in doc strings, the references should first be added to the BibTeX file: docs/references.bib. To normalize the file with bibtool, you can run: + +bibtool --preserve.key.case=on --preserve.keys=on --print.use.tab=off --pass.comments=on -s -i docs/references.bib -o docs/references.bib +To ensure that your citations become links in the online docs, you can use either of the following two styles: + +First, you may enclose the citation key used in docs/references.bib in square brackets: + +The proof can be found in [Boole1854]. +In the online docs, this will become something like: + +The proof can be found in [Boo54] + +(The key will change into an alpha style label and become a link to the References page of the docs.) + +Alternatively, you can use custom text for the citation by putting text in square brackets ahead of the citation key: + +See [Grundlagen der Geometrie][hilbert1999] for an alternative axiomatization. +See Grundlagen der Geometrie for an alternative axiomatization. + +Note that you currently cannot use the closing square bracket ] symbol in the link text. So the following will not result in a working link: + +We follow [Euclid's *Elements* [Prop. 1]][heath1956a]. +We follow [Euclid's Elements [Prop. 1]][heath1956a]. + +Language +Documentation should be written in English. Any common spelling (e.g. British, American or Australian English) is acceptable. Pull requests should not be made that only change which of these spellings are used, but it is acceptable to change the spelling as part of a PR that substantially enhances the documentation. Contrast this with the rule for declaration names, which should use American English spelling. + +Examples +The following files are maintained as examples of good documentation style: + +Mathlib.NumberTheory.Padics.PadicNorm +Mathlib.Topology.Basic +Analysis.Calculus.ContDiff.Basic diff --git a/MathlibNamingConventions.md b/MathlibNamingConventions.md new file mode 100644 index 000000000..6292c4b2b --- /dev/null +++ b/MathlibNamingConventions.md @@ -0,0 +1,367 @@ +Mathlib naming conventions +This guide is written for Lean 4. + +File names +.lean files in mathlib should generally be named in UpperCamelCase. A (very rare) exception are files named after some specifically lower-cased object, e.g. lp.lean for a file specifically about the space (and not ). Such exceptions should be discussed on Zulip first. + +General conventions +Capitalization +Unlike Lean 3, in which the convention was that all declarations used snake_case, in mathlib under Lean 4 we use a combination of snake_case, lowerCamelCase and UpperCamelCase according to the following naming scheme. + +Terms of Props (e.g. proofs, theorem names) use snake_case. +Props and Types (or Sort) (inductive types, structures, classes) are in UpperCamelCase. There are some rare exceptions: some fields of structures are currently wrongly lower-cased (see the example for the class LT below). +Functions are named the same way as their return values (e.g. a function of type A → B → C is named as though it is a term of type C). +All other terms of Types (basically anything else) are in lowerCamelCase. +When something named with UpperCamelCase is part of something named with snake_case, it is referenced in lowerCamelCase. +Acronyms like LE are written upper-/lowercase as a group, depending on what the first character would be. +Rules 1-6 apply to fields of a structure or constructors of an inductive type in the same way. +There are some rare exceptions to preserve local naming symmetry: e.g., we use Ne rather than NE to follow the example of Eq; outParam has a Sort output but is not UpperCamelCase. Some other exceptions include intervals (Set.Icc, Set.Iic, etc.), where the I is capitalized despite the fact that it should be lowerCamelCase according to the convention. Any such exceptions should be discussed on Zulip. + +Examples +-- follows rule 2 +structure OneHom (M : Type _) (N : Type _) [One M] [One N] where + toFun : M → N -- follows rule 4 via rule 3 and rule 7 + map_one' : toFun 1 = 1 -- follows rule 1 via rule 7 + +-- follows rule 2 via rule 3 +class CoeIsOneHom [One M] [One N] : Prop where + coe_one : (↑(1 : M) : N) = 1 -- follows rule 1 via rule 6 + +-- follows rule 1 via rule 3 +theorem map_one [OneHomClass F M N] (f : F) : f 1 = 1 := sorry + +-- follows rules 1 and 5 +theorem MonoidHom.toOneHom_injective [MulOneClass M] [MulOneClass N] : + Function.Injective (MonoidHom.toOneHom : (M →* N) → OneHom M N) := sorry +-- manual align is needed due to `lowerCamelCase` with several words inside `snake_case` +#align monoid_hom.to_one_hom_injective MonoidHom.toOneHom_injective + +-- follows rule 2 +class HPow (α : Type u) (β : Type v) (γ : Type w) where + hPow : α → β → γ -- follows rule 3 via rule 6; note that rule 5 does not apply + +-- follows rules 2 and 6 +class LT (α : Type u) where + lt : α → α → Prop -- this is an exception to rule 2 + +-- follows rules 2 (for `Semifield`) and 4 (for `toIsField`) +theorem Semifield.toIsField (R : Type u) [Semifield R] : + IsField R -- follows rule 2 + +-- follows rules 1 and 6 +theorem gt_iff_lt [LT α] {a b : α} : a > b ↔ b < a := sorry + +-- follows rule 2; `Ne` is an exception to rule 6 +class NeZero : Prop := sorry + +-- follows rules 1 and 5 +theorem neZero_iff {R : Type _} [Zero R] {n : R} : NeZero n ↔ n ≠ 0 := sorry +-- manual align is needed due to `lowerCamelCase` with several words inside `snake_case` +#align ne_zero_iff neZero_iff +Spelling +Declaration names use American English spelling. So e.g. we use factorization, Localization and FiberBundle and not factorisation, Localisation or FibreBundle. Contrast this with the rule for documentation, which is allowed to use other common English spellings. + +Names of symbols +When translating the statements of theorems into words, the following dictionary is often used. + +Logic +symbol shortcut name notes +∨ \or or +∧ \and and +→ \r of / imp the conclusion is stated first and the hypotheses are often omitted +↔ \iff iff sometimes omitted along with the right hand side of the iff +¬ \n not +∃ \ex exists / bex bex stands for "bounded exists" +∀ \fo all / forall / ball ball stands for "bounded forall" += eq often omitted +≠ \ne ne +∘ \o comp +ball and bex are still used in Lean core, but should not be used in mathlib. + +Set +symbol shortcut name notes +∈ \in mem +∉ \notin notMem +∪ \cup union +∩ \cap inter +⋃ \bigcup iUnion / biUnion i for "indexed", bi for "bounded indexed" +⋂ \bigcap iInter / biInter i for "indexed", bi for "bounded indexed" +⋃₀ \bigcup\0 sUnion s for "set" +⋂₀ \bigcap\0 sInter s for "set" +\ \\ sdiff +ᶜ \^c compl +{x | p x} setOf +{x} singleton +{x, y} pair +Algebra +symbol shortcut name notes +0 zero ++ add +- neg / sub neg for the unary function, sub for the binary function +1 one +* mul +^ pow +/ div +• \bu smul +⁻¹ \-1 inv +⅟ \frac1 invOf +∣ \| dvd +∑ \sum sum +∏ \prod prod +Lattices +symbol shortcut name notes +< lt / gt +≤ \le le / ge +⊔ \sup sup a binary operator +⊓ \inf inf a binary operator +⨆ \supr iSup / biSup / ciSup c for "conditionally complete" +⨅ \infi iInf / biInf / ciInf c for "conditionally complete" +⊥ \bot bot +⊤ \top top +The symbols ≤ and < have a special naming convention. In mathlib, we almost always use ≤ and < instead of ≥ and >, so we can use both le/lt and ge/gt for naming ≤ and <. There are a few reasons to use ge/gt: + +We use ge/gt if the arguments to ≤ or < appear in different orders. We use le/lt for the first occurrence of ≤/< in the theorem name, and then ge/gt indicates that the arguments are swapped. +We use ge/gt to match the argument order of another relation, such as = or ≠. +We use ge/gt to describe the ≤ or < relation with its arguments swapped. +We use ge/gt if the second argument to ≤ or < is 'more variable'. +-- follows rule 1 +theorem lt_iff_le_not_ge [Preorder α] {a b : α} : a < b ↔ a ≤ b ∧ ¬b ≤ a := sorry +theorem not_le_of_gt [Preorder α] {a b : α} (h : a < b) : ¬b ≤ a := sorry +theorem LT.lt.not_ge [Preorder α] {a b : α} (h : a < b) : ¬b ≤ a := sorry + +-- follows rule 2 +theorem Eq.ge [Preorder α] {a b : α} (h : a = b) : b ≤ a := sorry +theorem ne_of_gt [Preorder α] {a b : α} (h : b < a) : a ≠ b := sorry + +-- follows rule 3 +theorem ge_trans [Preorder α] {a b : α} : b ≤ a → c ≤ b → c ≤ a := sorry + +-- follows rule 4 +theorem le_of_forall_gt [LinearOrder α] {a b : α} (H : ∀ (c : α), a < c → b < c) : b ≤ a := sorry +Dots +Dots are used for namespaces, and also for automatically generated names like recursors, eliminators and structure projections. They can also be introduced manually, for example, where projector notation is useful. Thus they are used in all of the following situations. + +Note: since And is a (binary function into) Prop, it is UpperCamelCased according to the naming conventions, and so its namespace is And.*. This may seem at odds with the dictionary ∧ --> and but because upper camel case types get lower camel cased when they appear in names of theorems, the dictionary is still valid in general. The same applies to Or, Iff, Not, Eq, HEq, Ne, etc. + +Intro, elim, and destruct rules for logical connectives, whether they are automatically generated or not: + +And.intro +And.elim +And.left +And.right +Or.inl +Or.inr +Or.intro_left +Or.intro_right +Iff.intro +Iff.elim +Iff.mp +Iff.mpr +Not.intro +Not.elim +Eq.refl +Eq.rec +Eq.subst +HEq.refl +HEq.rec +HEq.subst +Exists.intro +Exists.elim +True.intro +False.elim +Places where projection notation is useful, for example: + +And.symm +Or.symm +Or.resolve_left +Or.resolve_right +Eq.symm +Eq.trans +HEq.symm +HEq.trans +Iff.symm +Iff.refl +It is useful to use dot notation even for types which are not inductive types. For instance, we use: + +LE.trans +LT.trans_le +LE.trans_lt +Axiomatic descriptions +Some theorems are described using axiomatic names, rather than describing their conclusions. + +def (for unfolding a definition) +refl +irrefl +symm +trans +antisymm +asymm +congr +comm +assoc +left_comm +right_comm +mul_left_cancel +mul_right_cancel +inj (injective) +Variable conventions +u, v, w, ... for universes +α, β, γ, ... for generic types +a, b, c, ... for propositions +x, y, z, ... for elements of a generic type +h, h₁, ... for assumptions +p, q, r, ... for predicates and relations +s, t, ... for lists +s, t, ... for sets +m, n, k, ... for natural numbers +i, j, k, ... for integers +Types with a mathematical content are expressed with the usual mathematical notation, often with an upper case letter (G for a group, R for a ring, K or 𝕜 for a field, E for a vector space, ...). This convention is not followed in older files, where greek letters are used for all types. Pull requests renaming type variables in these files are welcome. + +Identifiers and theorem names +We adopt the following naming guidelines to make it easier for users to guess the name of a theorem or find it using tab completion. Common "axiomatic" properties of an operation like conjunction or disjunction are put in a namespace that begins with the name of the operation: + +import Mathlib.Logic.Basic + +#check And.comm +#check Or.comm +In particular, this includes intro and elim operations for logical connectives, and properties of relations: + +import Mathlib.Logic.Basic + +#check And.intro +#check And.elim +#check Or.intro_left +#check Or.intro_right +#check Or.elim + +#check Eq.refl +#check Eq.symm +#check Eq.trans +Note however we do not do this for axiomatic logical and arithmetic operations. + +import Mathlib.Algebra.Group.Basic + +#check and_assoc +#check mul_comm +#check mul_assoc +#check @mul_left_cancel -- multiplication is left cancelative +For the most part, however, we rely on descriptive names. Often the name of theorem simply describes the conclusion: + +import Mathlib.Algebra.Ring.Basic +open Nat +#check succ_ne_zero +#check mul_zero +#check mul_one +#check @sub_add_eq_add_sub +#check @le_iff_lt_or_eq +If only a prefix of the description is enough to convey the meaning, the name may be made even shorter: + +import Mathlib.Algebra.Ring.Basic + +#check @neg_neg +#check Nat.pred_succ +When an operation is written as infix, the theorem names follow suit. For example, we write neg_mul_neg rather than mul_neg_neg to describe the pattern -a * -b. + +Sometimes, to disambiguate the name of theorem or better convey the intended reference, it is necessary to describe some of the hypotheses. The word "of" is used to separate these hypotheses: + +import Mathlib.Algebra.Order.Monoid.Lemmas + +open Nat + +#check lt_of_succ_le +#check lt_of_not_ge +#check lt_of_le_of_ne +#check add_lt_add_of_lt_of_le +The hypotheses are listed in the order they appear, not reverse order. For example, the theorem A → B → C would be named C_of_A_of_B. + +Sometimes abbreviations or alternative descriptions are easier to work with. For example, we use pos, neg, nonpos, nonneg rather than zero_lt, lt_zero, le_zero, and zero_le. + +import Mathlib.Algebra.Order.Monoid.Lemmas +import Mathlib.Algebra.Order.Ring.Lemmas + +open Nat + +#check mul_pos +#check mul_nonpos_of_nonneg_of_nonpos +#check add_lt_of_lt_of_nonpos +#check add_lt_of_nonpos_of_lt +These conventions are not perfect. They cannot distinguish compound expressions up to associativity, or repeated occurrences in a pattern. For that, we make do as best we can. For example, a + b - b = a could be named either add_sub_self or add_sub_cancel. + +Sometimes the word "left" or "right" is helpful to describe variants of a theorem. + +import Mathlib.Algebra.Order.Monoid.Lemmas +import Mathlib.Algebra.Order.Ring.Lemmas + +open Nat + +#check add_le_add_left +#check add_le_add_right +#check le_of_mul_le_mul_left +#check le_of_mul_le_mul_right +When referring to a namespaced definition in a lemma name not in the same namespace, the definition should have its namespace removed. If the definition name is unambiguous without its namespace, it can be used as is. Else, the namespace is prepended back to it in lowerCamelCase. This is to ensure that _-separated strings in a lemma name correspond to a definition name or connective. + +import Mathlib.Data.Int.Cast.Basic +import Mathlib.Data.Nat.Cast.Basic +import Mathlib.Topology.Constructions + +#check Prod.fst +#check continuous_fst + +#check Nat.cast +#check map_natCast +#check Int.cast_natCast +Naming of structural lemmas +We are trying to standardize certain naming patterns for structural lemmas. + +Extensionality +A lemma of the form (∀ x, f x = g x) → f = g should be named .ext, and labelled with the @[ext] attribute. Often this type of lemma can be generated automatically by putting the @[ext] attribute on a structure. (However an automatically generated lemma will always be written in terms of the structure projections, and often there is a better statement, e.g. using coercions, that should be written by hand then marked with @[ext].) + +A lemma of the form f = g ↔ ∀ x, f x = g x should be named .ext_iff. + +Injectivity +Where possible, injectivity lemmas should be written in terms of an Function.Injective f conclusion which use the full word injective, typically as f_injective. The form injective_f still appears often in mathlib. + +In addition to these, a variant should usually be provided as a bidirectional implication, e.g. as f x = f y ↔ x = y, which can be obtained from Function.Injective.eq_iff. Such lemmas should be named f_inj (although if they are in an appropriate namespace .inj is good too). Bidirectional injectivity lemmas are often good candidates for @[simp]. There are still many unidirectional implications named inj in mathlib, and it is reasonable to update and replace these as you come across them. + +Note however that constructors for inductive types have automatically generated unidirectional implications, named .inj, and there is no intention to change this. When such an automatically generated lemma already exists, and a bidirectional lemma is needed, it may be named .inj_iff. + +An injectivity lemma that uses "left" or "right" should refer to the argument that "changes". For example, a lemma with the statement a - b = a - c ↔ b = c could be called sub_right_inj. + +Induction and recursion principles +Induction/recursion principles are ways to construct data or proofs for all elements of some type T, by providing ways to construct this data or proof in more constrained specific contexts. These principles should be phrased to accept a motive argument, which declares what property we are proving or what data we are constructing for all T. When the motive eliminates into Prop, it is an induction principle, and the name should contain induction. On the other hand, when the motive eliminates into Sort u or Type u, it is a recursive principle, and the name should contain rec instead. + +Additionally, the name should contain on iff in the argument order, the value comes before the constructions. + +The following table summarizes these naming conventions: + +motive eliminates into: Prop Sort u or Type u +value first T.induction_on T.recOn +constructions first T.induction T.rec +Variation on these names are acceptable when necessary (e.g. for disambiguation). + +Predicates as suffixes +Most predicates should be added as prefixes. Eg IsClosed (Icc a b) should be called isClosed_Icc, not Icc_isClosed. + +Some widely used predicates don't follow this rule. Those are the predicates that are analogous to an atom already suffixed by the naming convention. Here is a non-exhaustive list: + +We use _inj for f a = f b ↔ a = b, so we also use _injective for Injective f, _surjective for Surjective f, _bijective for Bijective f... +We use _mono for a ≤ b → f a ≤ f b and _anti for a ≤ b → f b ≤ f a, so we also use _monotone for Monotone f, _antitone for Antitone f, _strictMono for StrictMono f, _strictAnti for StrictAnti f, etc... +Predicates as suffixes can be preceded by either _left or _right to signify that a binary operation is left- or right-monotone. For example, mul_left_monotone : Monotone (· * a) proves left-monotonicity of multiplication and not monotonicity of left-multiplication. + +Prop-valued classes +Mathlib has many Prop-valued classes and other definitions. For example "let be a topological ring" is written variable (R : Type*) [Ring R] [TopologicalSpace R] [IsTopologicalRing R] and "let be a group and let be a normal subgroup" is written variable (G : Type*) [Group G] (H : Subgroup G) [Normal H]. Here IsTopologicalRing R and Normal H are not extra data, but are extra assumptions on data we have already. + +Mathlib currently strives towards the following naming convention for these Prop-valued classes. If the class is a noun then its name should begin with Is. If however is it an adjective then its name does not need to begin with an Is. So for example IsNormal would be acceptable for the "normal subgroup" typeclass, but Normal is also fine; we might say "assume the subgroup H is normal" in informal language. However IsTopologicalRing is preferred for the "topological ring" typeclass, as we do not say "assume the ring R is topological" informally. + +Unexpanded and expanded forms of functions +The multiplication of two functions f and g can be denoted equivalently as f * g or fun x ↦ f x * g x. These expressions are definitionally equal, but not syntactically (and they don't share the same key in indexing trees), which means that tools like rw, fun_prop or apply? will not use a theorem with one form on an expression with the other form. Therefore, it is sometimes convenient to have variants of the statements using the two forms. If one needs to distinguish between them, statements involving the first unexpanded form are written using just mul, while statements using the second expanded form should instead use fun_mul. If there is no need to disambiguate because a lemma is given using only the expanded form, the prefix fun_ is not required. + +For instance, the fact that the multiplication of two continuous functions is continuous is + +theorem Continuous.fun_mul (hf : Continuous f) (hg : Continuous g) : Continuous fun x ↦ f x * g x +and + +theorem Continuous.mul (hf : Continuous f) (hg : Continuous g) : Continuous (f * g) +Both theorems deserve tagging with the fun_prop attribute. + +The same goes for addition, subtraction, negation, powers and compositions of functions. diff --git a/MathlibStyle.md b/MathlibStyle.md new file mode 100644 index 000000000..b5840a5d5 --- /dev/null +++ b/MathlibStyle.md @@ -0,0 +1,450 @@ +Library Style Guidelines +In addition to the naming conventions, files in the Lean library generally adhere to the following guidelines and conventions. Having a uniform style makes it easier to browse the library and read the contents, but these are meant to be guidelines rather than rigid rules. + +Variable conventions +u, v, w, ... for universes +α, β, γ, ... for generic types +a, b, c, ... for propositions +x, y, z, ... for elements of a generic type +h, h₁, ... for assumptions +p, q, r, ... for predicates and relations +s, t, ... for lists +s, t, ... for sets +m, n, k, ... for natural numbers +i, j, k, ... for integers +Types with a mathematical content are expressed with the usual mathematical notation, often with an upper case letter (G for a group, R for a ring, K or 𝕜 for a field, E for a vector space, ...). This convention is not followed in older files, where greek letters are used for all types. Pull requests renaming type variables in these files are welcome. + +Line length +Lines should not be longer than 100 characters. This makes files easier to read, especially on a small screen or in a small window. If you are editing with VS Code, there is a visual marker which will indicate a 100 character limit. + +Header and imports +The file header should contain copyright information, a list of all the authors who have made significant contributions to the file, and a description of the contents. Do all imports right after the header, without a line break, on separate lines. + +/- +Copyright (c) 2024 Joe Cool. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Joe Cool +-/ +import Mathlib.Data.Nat.Basic +import Mathlib.Algebra.Group.Defs +(Tip: If you're editing mathlib in VS Code, you can write copy and then press TAB to generate a skeleton of the copyright header.) + +Regarding the list of authors: use Authors even when there is only a single author. Don't end the line with a period, and use commas (, ) to separate all author names (so don't use and between the penultimate and final author.) We don't have strict rules on what contributions qualify for inclusion there. The general idea is that the people listed there should be the ones we would reach out to if we had questions about the design or development of the Lean code. + +Module docstrings +After the copyright header and the imports, please add a module docstring (delimited with /-! and -/) containing + +a title of the file, +a summary of the contents (the main definitions and theorems, proof techniques, etc…) +notation that has been used in the file (if any) +references to the literature (if any) +In total, the module docstring should look something like this: + +/-! +# Foos and bars + +In this file we introduce `foo` and `bar`, +two main concepts in the theory of xyzzyology. + +## Main results + +- `exists_foo`: the main existence theorem of `foo`s. +- `bar_of_foo_of_baz`: a construction of a `bar`, given a `foo` and a `baz`. + If this doc-string is longer than one line, subsequent lines should be indented by two spaces + (as required by markdown syntax). +- `bar_eq` : the main classification theorem of `bar`s. + +## Notation + + - `|_|` : The barrification operator, see `bar_of_foo`. + +## References + +See [Thales600BC] for the original account on Xyzzyology. +-/ +New bibliography entries should be added to docs/references.bib. + +See our documentation requirements for more suggestions and examples. + +Structuring definitions and theorems +All declarations (e.g., def, lemma, theorem, class, structure, inductive, instance, etc.) and commands (e.g., variable, open, section, namespace, notation, etc.) are considered top-level and these words should appear flush-left in the document. In particular, opening a namespace or section does not result in indenting the contents of that namespace or section. (Note: within VS Code, hovering over any declaration such as def Foo ... will show the fully qualified name, like MyNamespace Foo if Foo is declared while the namespace MyNamespace is open.) + +These guidelines hold for declarations starting with def, lemma and theorem. For "theorem statement", also read "type of a definition" and for "proof" also read "definition body". + +Use spaces on both sides of ":", ":=" or infix operators. Put them before a line break rather than at the beginning of the next line. + +In what follows, "indent" without an explicit indication of the amount means "indent by 2 additional spaces". + +After stating the theorem, we indent the lines in the subsequent proof by 2 spaces. + +open Nat +theorem nat_case {P : Nat → Prop} (n : Nat) (H1 : P 0) (H2 : ∀ m, P (succ m)) : P n := + Nat.recOn n H1 (fun m IH ↦ H2 m) +If the theorem statement requires multiple lines, indent the subsequent lines by 4 spaces. The proof is still indented only 2 spaces (not 6 = 4 + 2). When providing a proof in tactic mode, the by is placed on the line prior to the first tactic; however, by should not be placed on a line by itself. In practice this means you will often see := by at the end of a theorem statement. + +import Mathlib.Data.Nat.Basic + +theorem le_induction {P : Nat → Prop} {m} + (h0 : P m) (h1 : ∀ n, m ≤ n → P n → P (n + 1)) : + ∀ n, m ≤ n → P n := by + apply Nat.le.rec + · exact h0 + · exact h1 _ + +def decreasingInduction {P : ℕ → Sort*} (h : ∀ n, P (n + 1) → P n) {m n : ℕ} (mn : m ≤ n) + (hP : P n) : P m := + Nat.leRecOn mn (fun {k} ih hsk => ih <| h k hsk) (fun h => h) hP +When a proof term takes multiple arguments, it is sometimes clearer, and often necessary, to put some of the arguments on subsequent lines. In that case, indent each argument. This rule, i.e., indent an additional two spaces, applies more generally whenever a term spans multiple lines. + +open Nat +axiom zero_or_succ (n : Nat) : n = zero ∨ n = succ (pred n) +theorem nat_discriminate {B : Prop} {n : Nat} (H1: n = 0 → B) (H2 : ∀ m, n = succ m → B) : B := + Or.elim (zero_or_succ n) + (fun H3 : n = zero ↦ H1 H3) + (fun H3 : n = succ (pred n) ↦ H2 (pred n) H3) +Don't orphan parentheses; keep them with their arguments. + +Here is a longer example. + +import Mathlib.Init.Data.List.Lemmas + +open List +variable {T : Type} + +theorem mem_split {x : T} {l : List T} : x ∈ l → ∃ s t : List T, l = s ++ (x :: t) := + List.recOn l + (fun H : x ∈ [] ↦ False.elim ((mem_nil_iff _).mp H)) + (fun y l ↦ + fun IH : x ∈ l → ∃ s t : List T, l = s ++ (x :: t) ↦ + fun H : x ∈ y :: l ↦ + Or.elim (eq_or_mem_of_mem_cons H) + (fun H1 : x = y ↦ + Exists.intro [] (Exists.intro l (by rw [H1]; rfl))) + (fun H1 : x ∈ l ↦ + let ⟨s, (H2 : ∃ t : List T, l = s ++ (x :: t))⟩ := IH H1 + let ⟨t, (H3 : l = s ++ (x :: t))⟩ := H2 + have H4 : y :: l = (y :: s) ++ (x :: t) := by rw [H3]; rfl + Exists.intro (y :: s) (Exists.intro t H4))) +A short declaration can be written on a single line: + +open Nat +theorem succ_pos : ∀ n : Nat, 0 < succ n := zero_lt_succ + +def square (x : Nat) : Nat := x * x +A have can be put on a single line when the justification is short. + +example (n k : Nat) (h : n < k) : ... := + have h1 : n ≠ k := ne_of_lt h + ... +When the justification is too long, you should put it on the next line, indented by an additional two spaces. + +example (n k : Nat) (h : n < k) : ... := + have h1 : n ≠ k := + ne_of_lt h + ... +When the justification of the have uses tactic mode, the by should be placed on the same line, regardless of whether the justification spans multiple lines. + +example (n k : Nat) (h : n < k) : ... := + have h1 : n ≠ k := by apply ne_of_lt; exact h + ... + +example (n k : Nat) (h : n < k) : ... := + have h1 : n ≠ k := by + apply ne_of_lt + exact h + ... +When the arguments themselves are long enough to require line breaks, use an additional indent for every line after the first, as in the following example: + +import Mathlib.Data.Nat.Basic + +theorem Nat.add_right_inj {n m k : Nat} : n + m = n + k → m = k := + Nat.recOn n + (fun H : 0 + m = 0 + k ↦ calc + m = 0 + m := Eq.symm (zero_add m) + _ = 0 + k := H + _ = k := zero_add _) + (fun (n : Nat) (IH : n + m = n + k → m = k) (H : succ n + m = succ n + k) ↦ + have H2 : succ (n + m) = succ (n + k) := calc + succ (n + m) = succ n + m := Eq.symm (succ_add n m) + _ = succ n + k := H + _ = succ (n + k) := succ_add n k + have H3 : n + m = n + k := succ.inj H2 + IH H3) +In a class or structure definition, fields are indented 2 spaces, and moreover each field should have a docstring, as in: + +structure PrincipalSeg {α β : Type*} (r : α → α → Prop) (s : β → β → Prop) extends r ↪r s where + /-- The supremum of the principal segment -/ + top : β + /-- The image of the order embedding is the set of elements `b` such that `s b top` -/ + down' : ∀ b, s b top ↔ ∃ a, toRelEmbedding a = b + +class Module (R : Type u) (M : Type v) [Semiring R] [AddCommMonoid M] extends + DistribMulAction R M where + /-- Scalar multiplication distributes over addition from the right. -/ + protected add_smul : ∀ (r s : R) (x : M), (r + s) • x = r • x + s • x + /-- Scalar multiplication by zero gives zero. -/ + protected zero_smul : ∀ x : M, (0 : R) • x = 0 +When using a constructor taking several arguments in a definition, arguments line up, as in: + +theorem Ordinal.sub_eq_zero_iff_le {a b : Ordinal} : a - b = 0 ↔ a ≤ b := + ⟨fun h => by simpa only [h, add_zero] using le_add_sub a b, + fun h => by rwa [← Ordinal.le_zero, sub_le, add_zero]⟩ +Instances +When providing terms of structures or instances of classes, the where syntax should be used to avoid the need for enclosing braces, as in: + +instance instOrderBot : OrderBot ℕ where + bot := 0 + bot_le := Nat.zero_le +If there is already an instance instBot, then one can write + +instance instOrderBot : OrderBot ℕ where + __ := instBot + bot_le := Nat.zero_le +Hypotheses Left of Colon +Generally, having arguments to the left of the colon is preferred over having arguments in universal quantifiers or implications, if the proof starts by introducing these variables. For instance: + +example (n : ℝ) (h : 1 < n) : 0 < n := by linarith +is preferred over + +example (n : ℝ) : 1 < n → 0 < n := fun h ↦ by linarith +and + +example (n : ℕ) : 0 ≤ n := Nat.zero_le n +is preferred over + +example : ∀ (n : ℕ), 0 ≤ n := Nat.zero_le +Note that pattern-matching does not count as the proof starting by introducing variables. For example, the following is a valid use case of having a hypothesis right of the column: + +lemma zero_le : ∀ n : ℕ, 0 ≤ n + | 0 => le_rfl + | n + 1 => add_nonneg (zero_le n) zero_le_one +Binders +Use a space after binders: + +example : ∀ α : Type, ∀ x : α, ∃ y, y = x := + fun (α : Type) (x : α) ↦ Exists.intro x rfl +Anonymous functions +Lean has several nice syntax options for declaring anonymous functions. For very simple functions, one can use the centered dot as the function argument, as in (· ^ 2) to represent the squaring function. However, sometimes it is necessary to refer to the arguments by name (e.g., if they appear in multiple places in the function body). The Lean default for this is fun x => x * x, but the ↦ arrow (inserted with \mapsto) is also valid. In mathlib the pretty printer displays ↦, and we slightly prefer this in the source as well. The lambda notation λ x ↦ x * x, while syntactically valid, is disallowed in mathlib in favor of the fun keyword. + +Calculations +There is some flexibility in how you write calculational proofs, although there are some rules enforced by the syntax requirements of calc itself. However, there are some general guidelines. + +As with by, the calc keyword should be placed on the line prior to the start of the calculation, with the calculation indented. Whichever relations are involved (e.g., = or ≤) should be aligned from one line to the next. The underscores _ used as placeholders for terms indicating the continuation of the calculation should be left-justified. + +As for the justifications, it is not necessary to align the := symbols, but it can be nice if the expressions are short enough. The terms on either side of the first relation can either go on one line or separate lines, which may be decided by the size of the expressions. + +An example of adequate style which can more easily accommodate longer expressions is: + +import Init.Data.List.Basic + +open List + +theorem reverse_reverse : ∀ (l : List α), reverse (reverse l) = l + | [] => rfl + | a :: l => calc + reverse (reverse (a :: l)) + = reverse (reverse l ++ [a]) := by rw [reverse_cons] + _ = reverse [a] ++ reverse (reverse l) := reverse_append _ _ + _ = reverse [a] ++ l := by rw [reverse_reverse l] + _ = a :: l := rfl +The following style has the substantial advantage of having all lines be interchangeable, which is particularly useful when editing the proof in eg VSCode: + +import Init.Data.List.Basic + +open List + +theorem reverse_reverse : ∀ (l : List α), reverse (reverse l) = l + | [] => rfl + | a :: l => calc + reverse (reverse (a :: l)) + _ = reverse (reverse l ++ [a]) := by rw [reverse_cons] + _ = reverse [a] ++ reverse (reverse l) := reverse_append _ _ + _ = reverse [a] ++ l := by rw [reverse_reverse l] + _ = a :: l := rfl +If the expressions and proofs are relatively short, the following style is also an option: + +import Init.Data.List.Basic + +open List + +theorem reverse_reverse : ∀ (l : List α), reverse (reverse l) = l + | [] => rfl + | a :: l => calc + reverse (reverse (a :: l)) = reverse (reverse l ++ [a]) := by rw [reverse_cons] + _ = reverse [a] ++ reverse (reverse l) := reverse_append _ _ + _ = reverse [a] ++ l := by rw [reverse_reverse l] + _ = a :: l := rfl +Tactic mode +As we have already mentioned, when opening a tactic block, by is placed at the end of the line preceding the start of the tactic block, but not on its own line. Everything within the tactic block is indented, as in: + +theorem continuous_uncurry_of_discreteTopology [DiscreteTopology α] {f : α → β → γ} + (hf : ∀ a, Continuous (f a)) : Continuous (uncurry f) := by + apply continuous_iff_continuousAt.2 + rintro ⟨a, x⟩ + change map _ _ ≤ _ + rw [nhds_prod_eq, nhds_discrete, Filter.map_pure_prod] + exact (hf a).continuousAt +One can mix term mode and tactic mode, as in: + +theorem Units.isUnit_units_mul {M : Type*} [Monoid M] (u : Mˣ) (a : M) : + IsUnit (↑u * a) ↔ IsUnit a := + Iff.intro + (fun ⟨v, hv⟩ => by + have : IsUnit (↑u⁻¹ * (↑u * a)) := by exists u⁻¹ * v; rw [← hv, Units.val_mul] + rwa [← mul_assoc, Units.inv_mul, one_mul] at this) + u.isUnit.mul +When new goals arise as side conditions or steps, they are indented and preceded by a focusing dot · (inserted as \.); the dot is not indented. + +import Mathlib.Algebra.Group.Basic + +theorem exists_npow_eq_one_of_zpow_eq_one' [Group G] {n : ℤ} (hn : n ≠ 0) {x : G} (h : x ^ n = 1) : + ∃ n : ℕ, 0 < n ∧ x ^ n = 1 := by + cases n + · simp only [Int.ofNat_eq_coe] at h + rw [zpow_ofNat] at h + refine ⟨_, Nat.pos_of_ne_zero fun n0 ↦ hn ?_, h⟩ + rw [n0] + rfl + · rw [zpow_negSucc, inv_eq_one] at h + refine ⟨_ + 1, Nat.succ_pos _, h⟩ +Certain tactics, such as refine, can create named subgoals which can be proven in whichever order is desired using case. This feature is also useful in aiding readability. However, it is not required to use this instead of the focusing dot (·). + +example {p q : Prop} (h₁ : p → q) (h₂ : q → p) : p ↔ q := by + refine ⟨?imp, ?converse⟩ + case converse => exact h₂ + case imp => exact h₁ +Often t0 <;> t1 is used to execute t0 and then t1 on all new goals. Either write the tactics in one line, or indent the following tactic. + + cases x <;> + simp [a, b, c, d] +For single line tactic proofs (or short tactic proofs embedded in a term), it is acceptable to use by tac1; tac2; tac3 with semicolons instead of a new line with indentation. + +In general, you should put a single tactic invocation per line, unless you are closing a goal with a proof that fits entirely on a single line. Short sequences of tactics that correspond to a single mathematical idea can also be put on a single line, separated by semicolons as in cases bla; clear h or induction n; simp or rw [foo]; simp_rw [bar], but even in these scenarios, newlines are preferred. + +example : ... := by + by_cases h : x = 0 + · rw [h]; exact hzero ha + · rw [h] + have h' : ... := H ha + simp_rw [h', hb] + ... +Very short goals can be closed right away using swap or pick_goal if needed, to avoid additional indentation in the rest of the proof. + +example : ... := by + rw [h] + swap; exact h' + ... +We generally use a blank line to separate theorems and definitions, but this can be omitted, for example, to group together a number of short definitions, or to group together a definition and notation. + +Squeezing simp calls +Unless performance is particularly poor or the proof breaks otherwise, terminal simp calls (a simp call is terminal if it closes the current goal or is only followed by flexible tactics such as ring, field_simp, aesop) should not be squeezed (replaced by the output of simp?). + +There are two main reasons for this: + +A squeezed simp call might be several lines longer than the corresponding unsqueezed one, and therefore drown the useful information of what key lemmas were added to the unsqueezed simp call to close the goal in a sea of basic simp lemmas. +A squeezed simp call refers to many lemmas by name, meaning that it will break when one such lemma gets renamed. Lemma renamings happen often enough for this to matter on a maintenance level. +Profiling for performance +When contributing to mathlib, authors should be aware of the performance impacts of their contributions. The Lean FRO maintains benchmarking infrastructure which can be accessed by commenting !bench on a PR. + +Authors should assure that their contributions do not cause significant performance regressions. In particular, if the PR touches significant components of the language like adding instances, adding simp lemmas, changing imports, or creating new definitions, then authors should benchmark their changes proactively. + +Transparency and API design +Central to Lean being a practically performant proof assistant is avoiding checking of definitional equality for very large terms. In the elaborator (the component of the language that converts syntax to terms), the notion of transparency is the main mechanism to avoid unfolding large definitions when unnecessary. Excluding opaque definitions, there are three levels of transparency: + +reducible definitions are always unfolded +semireducible definitions (the default) are usually not unfolded in main tactics like rw and simp, but can be unfolded with a little effort like explicitly calling rfl or erw. Semireducible definitions are also not unfolded during the computation of keys for storing instances in the instance cache or simp lemmas in the simp cache. +irreducible definitions are never unfolded unless the user explicitly requests it (e.g using the unfold tactic, or by using the unseal command). +def by default creates semireducible definitions and abbrev creates reducible (and @[inline]) definitions. + +When designing definitions, an author should give thought to the transparency level of definitions. Consider how exposing the underlying term of your definition will affect instance search and simplification. The default for mathlib is that definitions should be semireducible unless there is a good reason otherwise which should be clearly articulated in the PR description. This imposes overhead on contributors who will need to declare new instances of the form + +instance : Foo myDef := inferInstanceAs (Foo underlyingTermOfMyDef) +and recycle API lemmas, especially for simp use, like + +@[simp] lemma myDef_bar_eq_bizz (x : X) : myDef.bar = bizz := + underlyingTermOfMyDef_bar_eq_bizz +If the API boundary is meant to be completely sealed, using a type synonym of the form + +structure myDef where + underlying : underlyingTerm +is the library convention in place of irreducible definitions. These structure wrappers are intended for types that are equivalent to an existing type but are clearly mathematically semantically distinct, e.g. Option and WithTop. + +The kernel does not have an analogous notion of transparency so its rules for unfolding are different. There are situations where an author wants to block unfolding in the kernel as well. Mathlib provides a command irreducible_def for this. This should be used only when there is a documented necessity from profiling. + +Use of erw or rfl after tactics like simp or rw that operate at reducible transparency is an indication that there is missing API. Consider adding the necessary lemmas to the API to avoid this. + +The library has existing occurrences of definitional transparency abuse like erw and extra rfl. PRs removing these are very welcome but their PR description should clearly articulate how the removal is achieved addressing the change in the underlying terms in particular and must benchmark their changes. Please treat this an opportunity to improve the API design of the relevant components. + +Whitespace and delimiters +Lean is whitespace-sensitive, and in general we opt for a style which avoids delimiting code. For instance, when writing tactics, it is possible to write them as tac1; tac2; tac3, separated by ;, in order to override the default whitespace sensitivity. However, as mentioned above, we generally try to avoid this except in a few special cases. + +Similarly, sometimes parentheses can be avoided by judicious use of the <| operator (or its cousin |>). Note: while $ is a synonym for <|, its use in mathlib is disallowed in favor of <| for consistency as well as because of the symmetry with |>. These operators have the effect of parenthesizing everything to the right of <| (note that ( is curved the same direction as <) or to the left of |> (and ) curves the same way as >). + +A common example of the usage of |> occurs with dot notation when the term preceding the . is a function applied to some arguments. For instance, ((foo a).bar b).baz can be rewritten as foo a |>.bar b |>.baz + +A common example of the usage of <| is when the user provides a term which is a function applied to multiple arguments whose last argument is a proof in tactic mode, especially one that spans multiple lines. In that case, it is natural to use <| by ... instead of (by ...), as in: + +import Mathlib.Tactic + +example {x y : ℝ} (hxy : x ≤ y) (h : ∀ ε > 0, y - ε ≤ x) : x = y := + le_antisymm hxy <| le_of_forall_pos_le_add <| by + intro ε hε + have := h ε hε + linarith +When using the tactics rw or simp there should be a space after the left arrow ←. For instance rw [← add_comm a b] or simp [← and_or_left]. (There should also be a space between the tactic name and its arguments, as in rw [h].) This rule applies the do notation as well: do return (← f) + (← g) + +Empty lines inside declarations +Empty lines inside declarations are discouraged and there is a linter that enforces that they are not present. This helps maintaining a uniform code style throughout all of mathlib. + +You are however encouraged to add comments to your code: even a short sentence communicates much more than an empty line in the middle of a proof ever will! + +Normal forms +Some statements are equivalent. For instance, there are several equivalent ways to require that a subset s of a type is nonempty. For another example, given a : α, the corresponding element of Option α can be equivalently written as Some a or (a : Option α). In general, we try to settle on one standard form, called the normal form, and use it both in statements and conclusions of theorems. In the above examples, this would be s.Nonempty (which gives access to dot notation) and (a : Option α). Often, simp lemmas will be registered to convert the other equivalent forms to the normal form. + +There is a special case to this rule. In types with a bottom element, it is equivalent to require hlt : ⊥ < x or hne : x ≠ ⊥, and it is not clear which one would be better as a normal form since both have their pros and cons. An analogous situation occurs with hlt : x < ⊤ and hne : x ≠ ⊤ in types with a top element. Since it is very easy to convert from hlt to hne (by using hlt.ne or hlt.ne' depending on the direction we want) while the other conversion is more lengthy, we use hne in assumptions of theorems (as this is the easier assumption to check), and hlt in conclusions of theorems (as this is the more powerful result to use). A common usage of this rule is with naturals, where ⊥ = 0. + +Comments +Use module doc delimiters /-! -/ to provide section headers and separators since these get incorporated into the auto-generated docs, and use /- -/ for more technical comments (e.g. TODOs and implementation notes) or for comments in proofs. Use -- for short or in-line comments. + +Documentation strings for declarations are delimited with /-- -/. When a documentation string for a declaration spans multiple lines, do not indent subsequent lines. + +See our documentation requirements for more suggestions and examples. + +Expressions in error or trace messages +Inside all printed messages (such as, in linters, custom elaborators or other metaprogrammes), names and interpolated data should either be + +inline and surrounded by backticks (e.g., m!"{foo}must have type{bar}"), or +on their own line and indented (via e.g. indentD) +The second style produces output like the following + +Could not find model with corners for domain + src +nor codomain + tgt +of function + f +Not all of mathlib may comply with this rule yet; that is a bug (and PRs fixing this are welcome). + +Deprecation +Deleting, renaming, or changing declarations can cause downstreams projects that rely on these definitions to fail to compile. Any publicly exposed theorems and definitions that are being removed should be gracefully transitioned by keeping the old declaration with a @[deprecated] attribute. This warns downstream projects about the change and gives them the opportunity to adjust before the declarations are deleted. Renamed definitions should use a deprecated alias to the new name. Otherwise, when the deprecated definition does not have a direct replacement, the definition should be deprecated with a message, like so: + +theorem new_name : ... := ... +@[deprecated (since := "YYYY-MM-DD")] alias old_name := new_name + +@[deprecated "This theorem is deprecated in favor of using ... with ..." (since := "YYYY-MM-DD")] +theorem example_thm ... +The @[deprecated] attribute requires the deprecation date, and an alias to the new declaration or a string to explain how transition away from the old definition when a new version is no longer being provided. + +The deprecate to command and scripts/add_deprecations.sh script can help generate alias definitions. + +Deprecations for declarations with the to_additive attribute should ensure the deprecation is also properly tagged with to_additive, like so: + +@[to_additive] theorem Group_bar {G} [Group G] {a : G} : a = a := rfl + +-- Two deprecations required to include the `deprecated` tag on both the additive +-- and multiplicative versions +@[deprecated (since := "YYYY-MM-DD")] alias AddGroup_foo := AddGroup_bar +@[to_additive existing, deprecated (since := "YYYY-MM-DD")] alias Group_foo := Group_bar +We allow, but discourage, contributors from simultaneously renaming declarations X to Y and W to X. In this case, no deprecation attribute is required for X, but it is for W. + +Named instances do not require deprecations. Deprecated declarations can be deleted after 6 months. diff --git a/PhysLean/Mathematics/Geometry/Metric/PseudoRiemannian/Defs.lean b/PhysLean/Mathematics/Geometry/Metric/PseudoRiemannian/Defs.lean index e2b0aa833..045c4ee04 100644 --- a/PhysLean/Mathematics/Geometry/Metric/PseudoRiemannian/Defs.lean +++ b/PhysLean/Mathematics/Geometry/Metric/PseudoRiemannian/Defs.lean @@ -10,7 +10,7 @@ import Mathlib.Geometry.Manifold.VectorBundle.Hom import Mathlib.Geometry.Manifold.VectorBundle.Tangent import Mathlib.LinearAlgebra.BilinearForm.Properties import Mathlib.Topology.LocallyConstant.Basic -import PhysLean.Mathematics.Geometry.Metric.QuadraticForm.Index +import PhysLean.Mathematics.Geometry.Metric.QuadraticForm.NegDim /-! # Pseudo-Riemannian Metrics on Smooth Manifolds diff --git a/PhysLean/Mathematics/Geometry/Metric/QuadraticForm/Index.lean b/PhysLean/Mathematics/Geometry/Metric/QuadraticForm/Index.lean new file mode 100644 index 000000000..dfda30e99 --- /dev/null +++ b/PhysLean/Mathematics/Geometry/Metric/QuadraticForm/Index.lean @@ -0,0 +1,19 @@ +/- +Copyright (c) 2026 +Released under Apache 2.0 license as described in the file LICENSE. +-/ + +import PhysLean.Mathematics.Geometry.Metric.QuadraticForm.NegDim + +/-! +# Quadratic form inertia indices (compatibility layer) + +This file is the single import point for the inertia index/signature API used by the +pseudo-Riemannian metric development (`QuadraticForm.posIndex`, `QuadraticForm.posDim`, +`QuadraticForm.negDim`, `QuadraticForm.zeroDim`, `QuadraticForm.signature`, ...). + +Currently these definitions live in `PhysLean.Mathematics.Geometry.Metric.QuadraticForm.NegDim`. +If Mathlib later provides overlapping canonical inertia invariants, this file is intended to be +the unique switch-point. +-/ + diff --git a/PhysLean/Mathematics/Geometry/Metric/QuadraticForm/NegDim.lean b/PhysLean/Mathematics/Geometry/Metric/QuadraticForm/NegDim.lean index cb6b89012..3902af2c0 100644 --- a/PhysLean/Mathematics/Geometry/Metric/QuadraticForm/NegDim.lean +++ b/PhysLean/Mathematics/Geometry/Metric/QuadraticForm/NegDim.lean @@ -10,32 +10,37 @@ import Mathlib.LinearAlgebra.Dimension.Constructions import Mathlib.LinearAlgebra.FiniteDimensional.Lemmas /-! -## Negative index for real quadratic forms +# Inertia indices for real quadratic forms -This file provides a lightweight notion of the **negative index** (`negDim`) of a real quadratic -form on a finite-dimensional real vector space, defined canonically via maximal positive definite -subspaces (the standard “inertia index” definition). +This file defines canonical inertia data for a real quadratic form on a finite-dimensional real +vector space. -It is intended as a small reusable API for (pseudo-)Riemannian geometry developments. +The main construction is `QuadraticForm.posIndex`, the maximal dimension of a submodule on which a +quadratic form is positive definite. From this we define the canonical indices +`QuadraticForm.posDim`, `QuadraticForm.negDim`, `QuadraticForm.zeroDim`, and the canonical +`QuadraticForm.signature`. -### Rigor note +These constructions are invariant under `QuadraticForm.Equivalent` and do not depend on a choice of +diagonalization. -The *canonical* invariants in this file are: +## Main definitions -- `posIndex`: the maximum dimension of a subspace on which `Q` is positive definite -- `posDim := posIndex Q` -- `negDim := posIndex (-Q)` -- `zeroDim := finrank - posDim - negDim` -- `signature := ⟨posDim, negDim, zeroDim⟩` +- `QuadraticForm.posIndex`: maximal dimension of a submodule where `Q` is positive definite +- `QuadraticForm.posDim`: positive index of inertia (defined as `posIndex Q`) +- `QuadraticForm.negDim`: negative index of inertia (defined as `posIndex (-Q)`) +- `QuadraticForm.zeroDim`: nullity, defined so `posDim + negDim + zeroDim = finrank` +- `QuadraticForm.signature`: the triple `(posDim, negDim, zeroDim)` -These are invariant under `QuadraticForm.Equivalent` (isometry equivalence), and require no -diagonalization choices. +## Implementation notes -Separately, we also record a **noncomputable choice** of Sylvester diagonalization weights -(`signTypeWeights`) and its induced *chosen* signature (`signatureChoice`) as auxiliary data. This -is useful for bridging to Mathlib's existence theorem -`QuadraticForm.equivalent_signType_weighted_sum_squared` (in -`Mathlib/LinearAlgebra/QuadraticForm/Real.lean`). +We also define noncanonical auxiliary data (`QuadraticForm.signTypeWeights`, +`QuadraticForm.signatureChoice`) by choosing Sylvester diagonalization weights, for bridging to +`QuadraticForm.equivalent_signType_weighted_sum_squared` in +`Mathlib.LinearAlgebra.QuadraticForm.Real`. These choices are not claimed to be canonical. + +## Tags + +quadratic form, inertia, signature, Sylvester law, index -/ namespace QuadraticForm @@ -48,34 +53,16 @@ open scoped BigOperators /-- A (finite-dimensional) real quadratic form has a signature `(pos, neg, zero)` given by Sylvester's law of inertia: the numbers of positive, negative, and zero squares in a diagonal representation. -/ -structure Signature where +@[ext] structure Signature where pos : ℕ neg : ℕ zero : ℕ namespace Signature -@[simp] -lemma mk_pos (p n z : ℕ) : (Signature.mk p n z).pos = p := rfl - -@[simp] -lemma mk_neg (p n z : ℕ) : (Signature.mk p n z).neg = n := rfl - -@[simp] -lemma mk_zero (p n z : ℕ) : (Signature.mk p n z).zero = z := rfl - /-- Alias for the `zero` component (common terminology: nullity). -/ abbrev nullity (s : Signature) : ℕ := s.zero -@[simp] lemma nullity_eq_zero (s : Signature) : s.nullity = s.zero := rfl - -@[ext] -lemma ext {s t : Signature} (hpos : s.pos = t.pos) (hneg : s.neg = t.neg) - (hzero : s.zero = t.zero) : s = t := by - cases s - cases t - simp_all - end Signature /-- A choice of `SignType`-weights in Sylvester's diagonalization of a quadratic form. @@ -198,45 +185,42 @@ section InertiaUniqueness variable {V : Type*} [AddCommGroup V] [Module ℝ V] [FiniteDimensional ℝ V] +/-- `IsPosDefOn Q W` means that the restriction of `Q` to the submodule `W` is positive definite. -/ def IsPosDefOn (Q : QuadraticForm ℝ V) (W : Submodule ℝ V) : Prop := (Q.comp W.subtype).PosDef +/-- The finset of candidate dimensions of positive-definite submodules of a quadratic form. -/ +private noncomputable def posIndexCandidates (Q : QuadraticForm ℝ V) : Finset ℕ := by + classical + exact (Finset.range (finrank ℝ V + 1)).filter fun k => + ∃ W : Submodule ℝ V, finrank ℝ W = k ∧ IsPosDefOn (V := V) Q W + +omit [FiniteDimensional ℝ V] in +private lemma posIndexCandidates_nonempty (Q : QuadraticForm ℝ V) : + (posIndexCandidates (V := V) Q).Nonempty := by + classical + refine ⟨0, ?_⟩ + refine Finset.mem_filter.2 ?_ + refine ⟨by simp, ?_⟩ + refine ⟨(⊥ : Submodule ℝ V), by simp, ?_⟩ + intro x hx + exfalso + exact hx (Subsingleton.elim x 0) + /-- The positive index of a real quadratic form: the maximal dimension of a subspace on which the form is positive definite. -/ noncomputable def posIndex (Q : QuadraticForm ℝ V) : ℕ := by classical - let n := finrank ℝ V - let S : Finset ℕ := - (Finset.range (n + 1)).filter (fun k => - ∃ W : Submodule ℝ V, finrank ℝ W = k ∧ IsPosDefOn (V := V) Q W) - have hS : S.Nonempty := by - refine ⟨0, ?_⟩ - refine Finset.mem_filter.2 ?_ - refine ⟨by simp, ?_⟩ - refine ⟨(⊥ : Submodule ℝ V), by simp, ?_⟩ - intro x hx - exfalso - exact hx (Subsingleton.elim x 0) - exact S.max' hS + exact (posIndexCandidates (V := V) Q).max' (posIndexCandidates_nonempty (V := V) Q) omit [FiniteDimensional ℝ V] in lemma posIndex_spec (Q : QuadraticForm ℝ V) : ∃ W : Submodule ℝ V, finrank ℝ W = posIndex (V := V) Q ∧ IsPosDefOn (V := V) Q W := by classical - let n := finrank ℝ V - let S : Finset ℕ := - (Finset.range (n + 1)).filter (fun k => - ∃ W : Submodule ℝ V, finrank ℝ W = k ∧ IsPosDefOn (V := V) Q W) - have hS : S.Nonempty := by - refine ⟨0, ?_⟩ - refine Finset.mem_filter.2 ?_ - refine ⟨by simp, ?_⟩ - refine ⟨(⊥ : Submodule ℝ V), by simp, ?_⟩ - intro x hx - exfalso - exact hx (Subsingleton.elim x 0) - have hmem : posIndex (V := V) Q ∈ S := by - simpa [posIndex, S] using (Finset.max'_mem S hS) + have hmem : + posIndex (V := V) Q ∈ posIndexCandidates (V := V) Q := by + simpa [posIndex] using + (Finset.max'_mem _ (posIndexCandidates_nonempty (V := V) Q)) rcases (Finset.mem_filter.1 hmem).2 with ⟨W, hW, hWpos⟩ exact ⟨W, hW, hWpos⟩ @@ -244,98 +228,65 @@ lemma le_posIndex_of_exists {Q : QuadraticForm ℝ V} {k : ℕ} (hk : ∃ W : Submodule ℝ V, finrank ℝ W = k ∧ IsPosDefOn (V := V) Q W) : k ≤ posIndex (V := V) Q := by classical - let n := finrank ℝ V - let S : Finset ℕ := - (Finset.range (n + 1)).filter (fun k => - ∃ W : Submodule ℝ V, finrank ℝ W = k ∧ IsPosDefOn (V := V) Q W) - have hS : S.Nonempty := by - refine ⟨0, ?_⟩ - refine Finset.mem_filter.2 ?_ - refine ⟨by simp, ?_⟩ - refine ⟨(⊥ : Submodule ℝ V), by simp, ?_⟩ - intro x hx - exfalso - exact hx (Subsingleton.elim x 0) - have hk_mem : k ∈ S := by + have hk_mem : k ∈ posIndexCandidates (V := V) Q := by refine Finset.mem_filter.2 ?_ refine ⟨?_, hk⟩ rcases hk with ⟨W, hW, -⟩ - have hk_le : k ≤ n := by - simpa [hW, n] using (Submodule.finrank_le (R := ℝ) (M := V) W) - exact Finset.mem_range.2 (Nat.lt_succ_of_le hk_le) - simpa [posIndex, S] using (Finset.le_max' S k hk_mem) + have hk_le : k ≤ finrank ℝ V := by + simpa [hW] using (Submodule.finrank_le (R := ℝ) (M := V) W) + simpa [posIndexCandidates] using (Finset.mem_range.2 (Nat.lt_succ_of_le hk_le)) + simpa [posIndex] using Finset.le_max' (posIndexCandidates (V := V) Q) k hk_mem + +/- If `Q` and `Q₂` are equivalent, then `posIndex Q ≤ posIndex Q₂`. -/ +omit [FiniteDimensional ℝ V] in +lemma posIndex_le_of_equivalent {V₂ : Type*} [AddCommGroup V₂] [Module ℝ V₂] + [FiniteDimensional ℝ V₂] {Q : QuadraticForm ℝ V} {Q₂ : QuadraticForm ℝ V₂} + (h : Q.Equivalent Q₂) : + posIndex (V := V) Q ≤ posIndex (V := V₂) Q₂ := by + classical + rcases h with ⟨e⟩ + rcases posIndex_spec (Q := Q) with ⟨W, hWfin, hWpos⟩ + let f : V →ₗ[ℝ] V₂ := (e.toLinearEquiv : V ≃ₗ[ℝ] V₂).toLinearMap + have hf_inj : Function.Injective f := e.toLinearEquiv.injective + let W₂ : Submodule ℝ V₂ := W.map f + have hfinrank : finrank ℝ W₂ = finrank ℝ W := by + simpa [W₂] using (LinearEquiv.finrank_eq (Submodule.equivMapOfInjective f hf_inj W)).symm + have hW₂pos : IsPosDefOn (V := V₂) Q₂ W₂ := by + intro x hx + rcases x with ⟨xv, hxv⟩ + have hx0 : (⟨xv, by simpa [W₂] using hxv⟩ : W.map f) ≠ 0 := by + intro h0 + apply hx + ext + simpa using congrArg Subtype.val h0 + let eqv := Submodule.equivMapOfInjective f hf_inj W + set x' : W.map f := ⟨xv, by simpa [W₂] using hxv⟩ + have hx' : (eqv.symm x') ≠ 0 := by + intro h0 + apply hx0 + have := congrArg eqv h0 + simpa using this + have hpos' : 0 < (Q.comp W.subtype) (eqv.symm x') := hWpos _ hx' + have hxmap : f ((eqv.symm x' : W) : V) = (x' : V₂) := + Submodule.map_equivMapOfInjective_symm_apply f hf_inj W x' + have heq : Q₂ (x' : V₂) = Q ((eqv.symm x' : W) : V) := by + have : Q₂ (f ((eqv.symm x' : W) : V)) = Q ((eqv.symm x' : W) : V) := by + simp [f] + simpa [hxmap] using this + simpa [IsPosDefOn, QuadraticMap.comp_apply, heq, x'] using hpos' + have hk : + ∃ U : Submodule ℝ V₂, finrank ℝ U = posIndex (V := V) Q ∧ IsPosDefOn (V := V₂) Q₂ U := + ⟨W₂, by simp [hWfin, hfinrank], hW₂pos⟩ + exact le_posIndex_of_exists (V := V₂) hk /-- `posIndex` is invariant under equivalence of quadratic forms. -/ theorem posIndex_eq_of_equivalent {V₂ : Type*} [AddCommGroup V₂] [Module ℝ V₂] [FiniteDimensional ℝ V₂] {Q : QuadraticForm ℝ V} {Q₂ : QuadraticForm ℝ V₂} (h : Q.Equivalent Q₂) : posIndex (V := V) Q = posIndex (V := V₂) Q₂ := by - classical + refine le_antisymm (posIndex_le_of_equivalent (V := V) (V₂ := V₂) (Q := Q) (Q₂ := Q₂) h) ?_ rcases h with ⟨e⟩ - have hle : posIndex (V := V) Q ≤ posIndex (V := V₂) Q₂ := by - rcases posIndex_spec (V := V) Q with ⟨W, hWfin, hWpos⟩ - let f : V →ₗ[ℝ] V₂ := (e.toLinearEquiv : V ≃ₗ[ℝ] V₂).toLinearMap - have hf_inj : Function.Injective f := e.toLinearEquiv.injective - let W₂ : Submodule ℝ V₂ := W.map f - have hfinrank : finrank ℝ W₂ = finrank ℝ W := by - simpa [W₂] using (LinearEquiv.finrank_eq (Submodule.equivMapOfInjective f hf_inj W)).symm - have hW₂pos : IsPosDefOn (V := V₂) Q₂ W₂ := by - intro x hx - rcases x with ⟨xv, hxv⟩ - have hx0 : (⟨xv, by simpa [W₂] using hxv⟩ : W.map f) ≠ 0 := by - intro h0; apply hx; ext; simpa using congrArg Subtype.val h0 - let eqv := Submodule.equivMapOfInjective f hf_inj W - set x' : W.map f := ⟨xv, by simpa [W₂] using hxv⟩ - have hx' : (eqv.symm x') ≠ 0 := by - intro h0 - apply hx0 - have := congrArg eqv h0 - simpa using this - have hpos' : 0 < (Q.comp W.subtype) (eqv.symm x') := hWpos _ hx' - have hxmap : f ((eqv.symm x' : W) : V) = (x' : V₂) := - Submodule.map_equivMapOfInjective_symm_apply f hf_inj W x' - have heq : Q₂ (x' : V₂) = Q ((eqv.symm x' : W) : V) := by - have : Q₂ (f ((eqv.symm x' : W) : V)) = Q ((eqv.symm x' : W) : V) := by - simp [f] - simpa [hxmap] using this - simpa [IsPosDefOn, QuadraticMap.comp_apply, heq, x'] using hpos' - have hk : - ∃ U : Submodule ℝ V₂, finrank ℝ U = posIndex (V := V) Q ∧ IsPosDefOn (V := V₂) Q₂ U := - ⟨W₂, by simp [hWfin, hfinrank], hW₂pos⟩ - exact le_posIndex_of_exists (V := V₂) hk - have hge : posIndex (V := V₂) Q₂ ≤ posIndex (V := V) Q := by - have h' : Q₂.Equivalent Q := ⟨e.symm⟩ - rcases posIndex_spec (V := V₂) Q₂ with ⟨W, hWfin, hWpos⟩ - let f : V₂ →ₗ[ℝ] V := (e.symm.toLinearEquiv : V₂ ≃ₗ[ℝ] V).toLinearMap - have hf_inj : Function.Injective f := e.symm.toLinearEquiv.injective - let W₂ : Submodule ℝ V := W.map f - have hfinrank : finrank ℝ W₂ = finrank ℝ W := by - simpa [W₂] using (LinearEquiv.finrank_eq (Submodule.equivMapOfInjective f hf_inj W)).symm - have hW₂pos : IsPosDefOn (V := V) Q W₂ := by - intro x hx - rcases x with ⟨xv, hxv⟩ - have hx0 : (⟨xv, by simpa [W₂] using hxv⟩ : W.map f) ≠ 0 := by - intro h0; apply hx; ext; simpa using congrArg Subtype.val h0 - let eqv := Submodule.equivMapOfInjective f hf_inj W - set x' : W.map f := ⟨xv, by simpa [W₂] using hxv⟩ - have hx' : (eqv.symm x') ≠ 0 := by - intro h0 - apply hx0 - have := congrArg eqv h0 - simpa using this - have hpos' : 0 < (Q₂.comp W.subtype) (eqv.symm x') := hWpos _ hx' - have hxmap : f ((eqv.symm x' : W) : V₂) = (x' : V) := - Submodule.map_equivMapOfInjective_symm_apply f hf_inj W x' - have heq : Q (x' : V) = Q₂ ((eqv.symm x' : W) : V₂) := by - have : Q (f ((eqv.symm x' : W) : V₂)) = Q₂ ((eqv.symm x' : W) : V₂) := by - simp [f] - simpa [hxmap] using this - simpa [IsPosDefOn, QuadraticMap.comp_apply, heq, x'] using hpos' - have hk : - ∃ U : Submodule ℝ V, finrank ℝ U = posIndex (V := V₂) Q₂ ∧ IsPosDefOn (V := V) Q U := - ⟨W₂, by simp [hWfin, hfinrank], hW₂pos⟩ - exact le_posIndex_of_exists (V := V) hk - exact le_antisymm hle hge + exact posIndex_le_of_equivalent (V := V₂) (V₂ := V) (Q := Q₂) (Q₂ := Q) ⟨e.symm⟩ end InertiaUniqueness @@ -347,7 +298,7 @@ variable {E : Type*} [AddCommGroup E] [Module ℝ E] [FiniteDimensional ℝ E] lemma posIndex_le_finrank (Q : QuadraticForm ℝ E) : posIndex (V := E) Q ≤ finrank ℝ E := by - rcases posIndex_spec (V := E) Q with ⟨W, hW, -⟩ + rcases posIndex_spec (Q := Q) with ⟨W, hW, -⟩ have : finrank ℝ W ≤ finrank ℝ E := Submodule.finrank_le (R := ℝ) (M := E) W simpa [hW] using this @@ -373,10 +324,17 @@ lemma posDim_le_finrank (Q : QuadraticForm ℝ E) : Q.posDim ≤ finrank ℝ E : lemma negDim_le_finrank (Q : QuadraticForm ℝ E) : Q.negDim ≤ finrank ℝ E := posIndex_le_finrank (E := E) (-Q) +omit [FiniteDimensional ℝ E] in +@[simp] lemma posDim_neg (Q : QuadraticForm ℝ E) : (-Q).posDim = Q.negDim := rfl + +omit [FiniteDimensional ℝ E] in +lemma negDim_neg (Q : QuadraticForm ℝ E) : (-Q).negDim = Q.posDim := by + simp [negDim, posDim] + lemma posDim_add_negDim_le_finrank (Q : QuadraticForm ℝ E) : Q.posDim + Q.negDim ≤ finrank ℝ E := by - rcases posIndex_spec (V := E) Q with ⟨Wpos, hWpos, hpos⟩ - rcases posIndex_spec (V := E) (-Q) with ⟨Wneg, hWneg, hneg⟩ + rcases posIndex_spec (Q := Q) with ⟨Wpos, hWpos, hpos⟩ + rcases posIndex_spec (Q := -Q) with ⟨Wneg, hWneg, hneg⟩ have hdisj : Disjoint Wpos Wneg := by rw [Submodule.disjoint_def] intro x hxpos hxneg @@ -402,6 +360,11 @@ omit [FiniteDimensional ℝ E] in lemma zeroDim_def (Q : QuadraticForm ℝ E) : Q.zeroDim = finrank ℝ E - Q.posDim - Q.negDim := rfl +omit [FiniteDimensional ℝ E] in +lemma zeroDim_neg (Q : QuadraticForm ℝ E) : (-Q).zeroDim = Q.zeroDim := by + -- Both sides reduce to `finrank - (posIndex Q + posIndex (-Q))`. + simp [zeroDim, posDim, negDim, Nat.sub_sub, Nat.add_comm] + /-- The signature `(pos, neg, zero)` of a real quadratic form (canonical). -/ noncomputable def signature (Q : QuadraticForm ℝ E) : Signature := ⟨Q.posDim, Q.negDim, Q.zeroDim⟩ @@ -468,7 +431,7 @@ theorem posDim_posDef {Q : QuadraticForm ℝ E} (hQ : Q.PosDef) : /-- For a positive definite quadratic form, `negDim = 0`. -/ theorem negDim_posDef {Q : QuadraticForm ℝ E} (hQ : Q.PosDef) : Q.negDim = 0 := by - rcases posIndex_spec (V := E) (-Q) with ⟨W, hW, hWpos⟩ + rcases posIndex_spec (Q := -Q) with ⟨W, hW, hWpos⟩ have hWbot : W = ⊥ := by ext x constructor @@ -512,13 +475,13 @@ lemma Equivalent.neg {E₂ : Type*} [AddCommGroup E₂] [Module ℝ E₂] [Finit theorem posDim_eq_of_equivalent {E₂ : Type*} [AddCommGroup E₂] [Module ℝ E₂] [FiniteDimensional ℝ E₂] {Q : QuadraticForm ℝ E} {Q₂ : QuadraticForm ℝ E₂} (h : Q.Equivalent Q₂) : Q.posDim = Q₂.posDim := by - simp [posDim, posIndex_eq_of_equivalent (V := E) (V₂ := E₂) h] + simp [posDim, posIndex_eq_of_equivalent (Q := Q) (Q₂ := Q₂) h] theorem negDim_eq_of_equivalent {E₂ : Type*} [AddCommGroup E₂] [Module ℝ E₂] [FiniteDimensional ℝ E₂] {Q : QuadraticForm ℝ E} {Q₂ : QuadraticForm ℝ E₂} (h : Q.Equivalent Q₂) : Q.negDim = Q₂.negDim := by have h' : (-Q).Equivalent (-Q₂) := Equivalent.neg (E := E) (E₂ := E₂) h - simp [negDim, posIndex_eq_of_equivalent (V := E) (V₂ := E₂) h'] + simp [negDim, posIndex_eq_of_equivalent (Q := -Q) (Q₂ := -Q₂) h'] theorem zeroDim_eq_of_equivalent {E₂ : Type*} [AddCommGroup E₂] [Module ℝ E₂] [FiniteDimensional ℝ E₂] {Q : QuadraticForm ℝ E} {Q₂ : QuadraticForm ℝ E₂} (h : Q.Equivalent Q₂) : @@ -526,9 +489,9 @@ theorem zeroDim_eq_of_equivalent {E₂ : Type*} [AddCommGroup E₂] [Module ℝ rcases h with ⟨e⟩ have hfin : finrank ℝ E = finrank ℝ E₂ := LinearEquiv.finrank_eq e.toLinearEquiv have hposI : posIndex (V := E) Q = posIndex (V := E₂) Q₂ := - posIndex_eq_of_equivalent (V := E) (V₂ := E₂) ⟨e⟩ + posIndex_eq_of_equivalent (Q := Q) (Q₂ := Q₂) ⟨e⟩ have hnegI : posIndex (V := E) (-Q) = posIndex (V := E₂) (-Q₂) := - posIndex_eq_of_equivalent (V := E) (V₂ := E₂) (Equivalent.neg (E := E) (E₂ := E₂) ⟨e⟩) + posIndex_eq_of_equivalent (Q := -Q) (Q₂ := -Q₂) (Equivalent.neg (E := E) (E₂ := E₂) ⟨e⟩) simp [zeroDim, posDim, negDim, hfin, hposI, hnegI] theorem signature_eq_of_equivalent {E₂ : Type*} [AddCommGroup E₂] [Module ℝ E₂] [FiniteDimensional ℝ E₂] @@ -537,9 +500,9 @@ theorem signature_eq_of_equivalent {E₂ : Type*} [AddCommGroup E₂] [Module rcases h with ⟨e⟩ have hfin : finrank ℝ E = finrank ℝ E₂ := LinearEquiv.finrank_eq e.toLinearEquiv have hposI : posIndex (V := E) Q = posIndex (V := E₂) Q₂ := - posIndex_eq_of_equivalent (V := E) (V₂ := E₂) ⟨e⟩ + posIndex_eq_of_equivalent (Q := Q) (Q₂ := Q₂) ⟨e⟩ have hnegI : posIndex (V := E) (-Q) = posIndex (V := E₂) (-Q₂) := - posIndex_eq_of_equivalent (V := E) (V₂ := E₂) (Equivalent.neg (E := E) (E₂ := E₂) ⟨e⟩) + posIndex_eq_of_equivalent (Q := -Q) (Q₂ := -Q₂) (Equivalent.neg (E := E) (E₂ := E₂) ⟨e⟩) ext <;> simp [signature, posDim, negDim, zeroDim, hfin, hposI, hnegI] end Canonical @@ -560,17 +523,46 @@ noncomputable section variable {n : ℕ} -private abbrev DiagForm (w : Fin n → SignType) : QuadraticForm ℝ (Fin n → ℝ) := +/-- The diagonal quadratic form with `SignType` weights `w`, i.e. the weighted sum of squares +with weights in `{ -1, 0, 1 }`. -/ +abbrev diagForm (w : Fin n → SignType) : QuadraticForm ℝ (Fin n → ℝ) := QuadraticMap.weightedSumSquares ℝ fun i => (w i : ℝ) -private def posSet (w : Fin n → SignType) : Finset (Fin n) := - (Finset.univ.filter fun i => w i = SignType.pos) +/-- The set of indices where `w` takes the value `s`. -/ +def signSet (s : SignType) (w : Fin n → SignType) : Finset (Fin n) := + Finset.univ.filter fun i => w i = s -private lemma mem_posSet_iff {w : Fin n → SignType} {i : Fin n} : +/-- The set of indices where `w` is positive. -/ +abbrev posSet (w : Fin n → SignType) : Finset (Fin n) := + signSet (n := n) SignType.pos w + +/-- The set of indices where `w` is negative. -/ +abbrev negSet (w : Fin n → SignType) : Finset (Fin n) := + signSet (n := n) SignType.neg w + +/-- The set of indices where `w` is zero. -/ +abbrev zeroSet (w : Fin n → SignType) : Finset (Fin n) := + signSet (n := n) 0 w + +lemma mem_posSet {w : Fin n → SignType} {i : Fin n} : i ∈ posSet (n := n) w ↔ w i = SignType.pos := by - simp [posSet] + simp [posSet, signSet] + +lemma mem_negSet {w : Fin n → SignType} {i : Fin n} : + i ∈ negSet (n := n) w ↔ w i = SignType.neg := by + simp [negSet, signSet] -private def supportedOnPos (w : Fin n → SignType) : Submodule ℝ (Fin n → ℝ) where +lemma mem_zeroSet {w : Fin n → SignType} {i : Fin n} : + i ∈ zeroSet (n := n) w ↔ w i = 0 := by + simp [zeroSet, signSet] + +lemma posSet_neg (w : Fin n → SignType) : + posSet (n := n) (fun i => -w i) = negSet (n := n) w := by + ext i + cases hi : w i <;> simp [posSet, negSet, signSet, hi] + +/-- The submodule of vectors supported on the positive-weight coordinates. -/ +def supportedOnPos (w : Fin n → SignType) : Submodule ℝ (Fin n → ℝ) where carrier := {v | ∀ i, w i ≠ SignType.pos → v i = 0} zero_mem' := by intro i hi; simp add_mem' := by @@ -580,29 +572,33 @@ private def supportedOnPos (w : Fin n → SignType) : Submodule ℝ (Fin n → intro a v hv i hi simp [Pi.smul_apply, hv i hi] -private lemma supportedOnPos_mem {w : Fin n → SignType} {v : Fin n → ℝ} : - v ∈ supportedOnPos (n := n) w ↔ ∀ i, w i ≠ SignType.pos → v i = 0 := Iff.rfl +@[simp] lemma mem_supportedOnPos {w : Fin n → SignType} {v : Fin n → ℝ} : + v ∈ supportedOnPos (n := n) w ↔ ∀ i, w i ≠ SignType.pos → v i = 0 := + Iff.rfl -private def restrictPos (w : Fin n → SignType) : +/-- Restriction of a vector to the positive-weight coordinates. -/ +def restrictPos (w : Fin n → SignType) : (Fin n → ℝ) →ₗ[ℝ] ({i // i ∈ posSet (n := n) w} → ℝ) where toFun v i := v i.1 map_add' := by intro v₁ v₂; ext i; rfl map_smul' := by intro a v; ext i; rfl -private lemma restrictPos_apply {w : Fin n → SignType} (v : Fin n → ℝ) +@[simp] lemma restrictPos_apply {w : Fin n → SignType} (v : Fin n → ℝ) (i : {i // i ∈ posSet (n := n) w}) : restrictPos (n := n) w v i = v i.1 := rfl -private lemma diag_nonpos_of_no_pos {w : Fin n → SignType} {v : Fin n → ℝ} +/-- If a vector has no positive-weight coordinates, then its value under `diagForm w` is nonpositive. -/ +lemma diagForm_nonpos_of_no_pos {w : Fin n → SignType} {v : Fin n → ℝ} (hv : ∀ i, w i = SignType.pos → v i = 0) : - DiagForm (n := n) w v ≤ 0 := by - simp only [DiagForm, QuadraticMap.weightedSumSquares_apply] + diagForm (n := n) w v ≤ 0 := by + simp only [diagForm, QuadraticMap.weightedSumSquares_apply] refine Finset.sum_nonpos ?_ intro i _ cases hwi : w i <;> simp [hwi, hv i, mul_self_nonneg] -private lemma supportedOnPos_posDef (w : Fin n → SignType) : - IsPosDefOn (V := Fin n → ℝ) (DiagForm (n := n) w) (supportedOnPos (n := n) w) := by +/-- On `supportedOnPos w`, the diagonal form `diagForm w` is positive definite. -/ +lemma isPosDefOn_diagForm_supportedOnPos (w : Fin n → SignType) : + IsPosDefOn (V := Fin n → ℝ) (diagForm (n := n) w) (supportedOnPos (n := n) w) := by classical intro v hv0 -- pick a coordinate where `v` is nonzero @@ -618,19 +614,19 @@ private lemma supportedOnPos_posDef (w : Fin n → SignType) : have hwpos : w i = SignType.pos := by by_contra hne have : (v : Fin n → ℝ) i = 0 := by - exact (supportedOnPos_mem (n := n) (w := w) |>.1 v.property) i hne + exact (mem_supportedOnPos (n := n) (w := w) |>.1 v.property) i hne exact hi this have hterm_pos : 0 < (v : Fin n → ℝ) i ^ 2 := by simpa [pow_two] using sq_pos_of_ne_zero hi have hle : - (v : Fin n → ℝ) i ^ 2 ≤ DiagForm (n := n) w (v : Fin n → ℝ) := by - simp only [DiagForm, QuadraticMap.weightedSumSquares_apply] + (v : Fin n → ℝ) i ^ 2 ≤ diagForm (n := n) w (v : Fin n → ℝ) := by + simp only [diagForm, QuadraticMap.weightedSumSquares_apply] have hnonneg : ∀ j : Fin n, 0 ≤ ((w j : ℝ) • ((v : Fin n → ℝ) j * (v : Fin n → ℝ) j)) := by intro j by_cases hj : w j = SignType.pos · simp [hj, mul_self_nonneg] - · have : (v : Fin n → ℝ) j = 0 := (supportedOnPos_mem (n := n) (w := w)).1 v.property j hj + · have : (v : Fin n → ℝ) j = 0 := (mem_supportedOnPos (n := n) (w := w)).1 v.property j hj simp [this] have : ((w i : ℝ) • ((v : Fin n → ℝ) i * (v : Fin n → ℝ) i)) ≤ ∑ j : Fin n, (w j : ℝ) • ((v : Fin n → ℝ) j * (v : Fin n → ℝ) j) := by @@ -639,54 +635,70 @@ private lemma supportedOnPos_posDef (w : Fin n → SignType) : simpa [hwpos, pow_two] using this exact lt_of_lt_of_le hterm_pos hle +/-- The submodule `supportedOnPos w` is linearly equivalent to functions on the positive index set. -/ +noncomputable def supportedOnPosEquiv (w : Fin n → SignType) : + supportedOnPos (n := n) w ≃ₗ[ℝ] ({i // i ∈ posSet (n := n) w} → ℝ) := by + classical + refine + { toLinearMap := + { toFun := fun v i => (v : Fin n → ℝ) i.1 + map_add' := by intro v₁ v₂; ext i; rfl + map_smul' := by intro a v; ext i; rfl } + invFun := fun u => + ⟨fun i => if h : i ∈ posSet (n := n) w then u ⟨i, h⟩ else 0, by + intro i hi + by_cases h : i ∈ posSet (n := n) w + · exfalso + exact hi ((mem_posSet (n := n) (w := w)).1 h) + · simp [h]⟩ + left_inv := by + intro v + ext i + by_cases h : i ∈ posSet (n := n) w + · have : w i = SignType.pos := (mem_posSet (n := n) (w := w)).1 h + simp [h] + · have : w i ≠ SignType.pos := by + intro hpos + exact h ((mem_posSet (n := n) (w := w)).2 hpos) + have : (v : Fin n → ℝ) i = 0 := + (mem_supportedOnPos (n := n) (w := w)).1 v.property i this + simp [h, this] + right_inv := by + intro u + ext i + simp } + +lemma finrank_supportedOnPos (w : Fin n → SignType) : + finrank ℝ (supportedOnPos (n := n) w) = (posSet (n := n) w).card := by + classical + have h : + finrank ℝ ({i // i ∈ posSet (n := n) w} → ℝ) = (posSet (n := n) w).card := by + -- Write the index type as the finset `posSet w` itself to avoid rewriting the predicate. + change finrank ℝ ((posSet (n := n) w) → ℝ) = (posSet (n := n) w).card + calc + finrank ℝ ((posSet (n := n) w) → ℝ) = Fintype.card (posSet (n := n) w) := by + simp [Module.finrank_fintype_fun_eq_card] + _ = (posSet (n := n) w).card := by + simp + -- Convert the `LinearEquiv` finrank equality using the computation of the codomain finrank. + simpa [h] using (LinearEquiv.finrank_eq (supportedOnPosEquiv (n := n) w)) + theorem posIndex_diag_signType (w : Fin n → SignType) : - posIndex (V := Fin n → ℝ) (DiagForm (n := n) w) = (posSet (n := n) w).card := by + posIndex (V := Fin n → ℝ) (diagForm (n := n) w) = (posSet (n := n) w).card := by -- lower bound: exhibit the supported-on-positive submodule have h_lower : - (posSet (n := n) w).card ≤ posIndex (V := Fin n → ℝ) (DiagForm (n := n) w) := by - -- `supportedOnPos` is isomorphic to functions on the positive index set - let W := supportedOnPos (n := n) w - have hfin : - finrank ℝ W = (posSet (n := n) w).card := by - -- linear equivalence with functions on the subtype `{i // i ∈ posSet}` - let e : - W ≃ₗ[ℝ] ({i // i ∈ posSet (n := n) w} → ℝ) := - { toLinearMap := - { toFun := fun v i => (v : Fin n → ℝ) i.1 - map_add' := by intro v₁ v₂; ext i; rfl - map_smul' := by intro a v; ext i; rfl } - invFun := fun u => - ⟨fun i => if h : i ∈ posSet (n := n) w then u ⟨i, h⟩ else 0, by - intro i hi - by_cases h : i ∈ posSet (n := n) w - · exfalso - exact hi (mem_posSet_iff (n := n) (w := w) |>.1 h) - · simp [h]⟩ - left_inv := by - intro v - ext i - by_cases h : i ∈ posSet (n := n) w - · have : w i = SignType.pos := (mem_posSet_iff (n := n) (w := w)).1 h - simp [h] - · have : w i ≠ SignType.pos := by - intro hpos; exact h ((mem_posSet_iff (n := n) (w := w)).2 hpos) - have : (v : Fin n → ℝ) i = 0 := (supportedOnPos_mem (n := n) (w := w)).1 v.property i this - simp [h, this] - right_inv := by - intro u - ext i - simp } - simpa using (LinearEquiv.finrank_eq e) + (posSet (n := n) w).card ≤ posIndex (V := Fin n → ℝ) (diagForm (n := n) w) := by have hk : ∃ W' : Submodule ℝ (Fin n → ℝ), finrank ℝ W' = (posSet (n := n) w).card ∧ - IsPosDefOn (V := Fin n → ℝ) (DiagForm (n := n) w) W' := by - refine ⟨W, hfin, supportedOnPos_posDef (n := n) w⟩ - exact le_posIndex_of_exists (V := Fin n → ℝ) (Q := DiagForm (n := n) w) hk + IsPosDefOn (V := Fin n → ℝ) (diagForm (n := n) w) W' := by + refine ⟨supportedOnPos (n := n) w, finrank_supportedOnPos (n := n) w, + isPosDefOn_diagForm_supportedOnPos (n := n) w⟩ + exact le_posIndex_of_exists (V := Fin n → ℝ) (Q := diagForm (n := n) w) hk -- upper bound: any positive definite subspace injects into the positive coordinates have h_upper : - posIndex (V := Fin n → ℝ) (DiagForm (n := n) w) ≤ (posSet (n := n) w).card := by - rcases posIndex_spec (V := Fin n → ℝ) (DiagForm (n := n) w) with ⟨W, hW, hWpos⟩ + posIndex (V := Fin n → ℝ) (diagForm (n := n) w) ≤ (posSet (n := n) w).card := by + rcases posIndex_spec (Q := diagForm (n := n) w) with ⟨W, hW, hWpos⟩ let f := restrictPos (n := n) w let g : W →ₗ[ℝ] ({i // i ∈ posSet (n := n) w} → ℝ) := f.comp W.subtype have hg_inj : Function.Injective g := by @@ -695,17 +707,17 @@ theorem posIndex_diag_signType (w : Fin n → SignType) : simpa [g, map_sub] using congrArg (fun z => z - g y) hxy have hvanish : ∀ i, w i = SignType.pos → ((x - y : W) : (Fin n → ℝ)) i = 0 := by intro i hi - have : (g (x - y)) ⟨i, (mem_posSet_iff (n := n) (w := w)).2 hi⟩ = 0 := by + have : (g (x - y)) ⟨i, (mem_posSet (n := n) (w := w)).2 hi⟩ = 0 := by simp [this] simpa [g, f, restrictPos_apply] using this have hnonpos : - DiagForm (n := n) w ((x - y : W) : (Fin n → ℝ)) ≤ 0 := - diag_nonpos_of_no_pos (n := n) (w := w) hvanish + diagForm (n := n) w ((x - y : W) : (Fin n → ℝ)) ≤ 0 := + diagForm_nonpos_of_no_pos (n := n) (w := w) hvanish by_cases hxy0 : x - y = 0 · have : x = y := sub_eq_zero.mp hxy0 exact this - · have hpos : 0 < (DiagForm (n := n) w).comp W.subtype (x - y) := hWpos _ hxy0 - have : ¬ (0 < (DiagForm (n := n) w).comp W.subtype (x - y)) := + · have hpos : 0 < (diagForm (n := n) w).comp W.subtype (x - y) := hWpos _ hxy0 + have : ¬ (0 < (diagForm (n := n) w).comp W.subtype (x - y)) := not_lt_of_ge (by simpa [QuadraticMap.comp_apply] using hnonpos) exact (this hpos).elim have hfin_le : @@ -717,7 +729,12 @@ theorem posIndex_diag_signType (w : Fin n → SignType) : Submodule.finrank_le (R := ℝ) (M := ({i // i ∈ posSet (n := n) w} → ℝ)) (LinearMap.range g) have htarget : finrank ℝ ({i // i ∈ posSet (n := n) w} → ℝ) = (posSet (n := n) w).card := by - simp + change finrank ℝ ((posSet (n := n) w) → ℝ) = (posSet (n := n) w).card + calc + finrank ℝ ((posSet (n := n) w) → ℝ) = Fintype.card (posSet (n := n) w) := by + simp [Module.finrank_fintype_fun_eq_card] + _ = (posSet (n := n) w).card := by + simp simpa [hWrange, htarget] using hrange_le simpa [hW] using hfin_le exact le_antisymm h_upper h_lower @@ -726,7 +743,11 @@ theorem posIndex_weightedSumSquares_signType (w : Fin n → SignType) : posIndex (V := Fin n → ℝ) (QuadraticMap.weightedSumSquares ℝ fun i : Fin n => (w i : ℝ)) = (Finset.univ.filter fun i : Fin n => w i = SignType.pos).card := by - simpa [DiagForm, posSet] using (posIndex_diag_signType (n := n) w) + simpa [diagForm, posSet] using (posIndex_diag_signType (n := n) w) + +theorem posIndex_diagForm (w : Fin n → SignType) : + posIndex (V := Fin n → ℝ) (diagForm (n := n) w) = (posSet (n := n) w).card := by + simpa [diagForm, posSet, signSet] using (posIndex_weightedSumSquares_signType (n := n) w) theorem posDim_weightedSumSquares_signType (w : Fin n → SignType) : QuadraticForm.posDim (E := Fin n → ℝ) @@ -735,73 +756,59 @@ theorem posDim_weightedSumSquares_signType (w : Fin n → SignType) : (Finset.univ.filter fun i : Fin n => w i = SignType.pos).card := by simp [QuadraticForm.posDim, posIndex_weightedSumSquares_signType (n := n) w] +theorem posDim_diagForm (w : Fin n → SignType) : + QuadraticForm.posDim (E := Fin n → ℝ) (diagForm (n := n) w) = (posSet (n := n) w).card := by + simp [QuadraticForm.posDim, posIndex_diagForm (n := n) w] + theorem negDim_weightedSumSquares_signType (w : Fin n → SignType) : QuadraticForm.negDim (E := Fin n → ℝ) ((QuadraticMap.weightedSumSquares ℝ fun i : Fin n => (w i : ℝ)) : QuadraticForm ℝ (Fin n → ℝ)) = (Finset.univ.filter fun i : Fin n => w i = SignType.neg).card := by + classical set Q : QuadraticForm ℝ (Fin n → ℝ) := (QuadraticMap.weightedSumSquares ℝ fun i : Fin n => (w i : ℝ)) - have hneg : - (-Q) = - (QuadraticMap.weightedSumSquares ℝ fun i : Fin n => ((-w i : SignType) : ℝ)) := by + have hneg : -Q = diagForm (n := n) (fun i => -w i) := by ext v - simp [Q, QuadraticMap.weightedSumSquares_apply] - have hposIndex : - posIndex (V := Fin n → ℝ) - (QuadraticMap.weightedSumSquares ℝ fun i : Fin n => ((-w i : SignType) : ℝ)) = - (Finset.univ.filter fun i : Fin n => (-w i) = SignType.pos).card := by - simpa using (posIndex_weightedSumSquares_signType (n := n) fun i => -w i) - have hcard : - (Finset.univ.filter fun i : Fin n => (-w i) = SignType.pos).card = - (Finset.univ.filter fun i : Fin n => w i = SignType.neg).card := by - have hset : - (Finset.univ.filter fun i : Fin n => (-w i) = SignType.pos) = - (Finset.univ.filter fun i : Fin n => w i = SignType.neg) := by - ext i - cases hi : w i <;> simp [hi] - simpa using congrArg Finset.card hset - have hmain : - posIndex (V := Fin n → ℝ) (-Q) = - (Finset.univ.filter fun i : Fin n => w i = SignType.neg).card := by - calc - posIndex (V := Fin n → ℝ) (-Q) - = posIndex (V := Fin n → ℝ) - (QuadraticMap.weightedSumSquares ℝ fun i : Fin n => ((-w i : SignType) : ℝ)) := by - simp [hneg] - _ = (Finset.univ.filter fun i : Fin n => (-w i) = SignType.pos).card := hposIndex - _ = (Finset.univ.filter fun i : Fin n => w i = SignType.neg).card := hcard - simpa [QuadraticForm.negDim, Q] using hmain - -private lemma card_filters_pos_neg_zero (w : Fin n → SignType) : - (Finset.univ.filter fun i : Fin n => w i = SignType.pos).card + - (Finset.univ.filter fun i : Fin n => w i = SignType.neg).card + - (Finset.univ.filter fun i : Fin n => w i = 0).card = + simp [Q, diagForm, QuadraticMap.weightedSumSquares_apply] + have hpos : posIndex (V := Fin n → ℝ) (-Q) = (posSet (n := n) (fun i => -w i)).card := by + simpa [hneg] using (posIndex_diag_signType (n := n) (w := fun i => -w i)) + -- `negDim Q = posIndex (-Q)` and `posSet (-w) = negSet w`. + simp [QuadraticForm.negDim, Q, hpos, posSet_neg (n := n) w, negSet, signSet] + +theorem negDim_diagForm (w : Fin n → SignType) : + QuadraticForm.negDim (E := Fin n → ℝ) (diagForm (n := n) w) = (negSet (n := n) w).card := by + -- This is exactly the filter-card theorem, restated via `negSet`. + simpa [diagForm, negSet, signSet] using (negDim_weightedSumSquares_signType (n := n) w) + +lemma card_posSet_add_card_negSet_add_card_zeroSet (w : Fin n → SignType) : + (posSet (n := n) w).card + (negSet (n := n) w).card + (zeroSet (n := n) w).card = Fintype.card (Fin n) := by - let A : Finset (Fin n) := Finset.univ.filter fun i => w i = SignType.pos - let B : Finset (Fin n) := Finset.univ.filter fun i => w i = SignType.neg - let C : Finset (Fin n) := Finset.univ.filter fun i => w i = 0 + classical + let A : Finset (Fin n) := posSet (n := n) w + let B : Finset (Fin n) := negSet (n := n) w + let C : Finset (Fin n) := zeroSet (n := n) w have hAB : Disjoint A B := by refine Finset.disjoint_left.2 ?_ intro i hiA hiB - have hiPos : w i = SignType.pos := (Finset.mem_filter.1 hiA).2 - have hiNeg : w i = SignType.neg := (Finset.mem_filter.1 hiB).2 - have : (SignType.pos : SignType) = SignType.neg := by simp [hiPos] at hiNeg + have hiPos : w i = SignType.pos := (mem_posSet (n := n) (w := w)).1 hiA + have hiNeg : w i = SignType.neg := (mem_negSet (n := n) (w := w)).1 hiB + have : (SignType.pos : SignType) = SignType.neg := hiPos.symm.trans hiNeg cases this have hABC : Disjoint (A ∪ B) C := by refine Finset.disjoint_left.2 ?_ intro i hiAB hiC - have hiC' : w i = 0 := (Finset.mem_filter.1 hiC).2 + have hiC' : w i = 0 := (mem_zeroSet (n := n) (w := w)).1 hiC rcases Finset.mem_union.1 hiAB with hiA | hiB - · have : w i = SignType.pos := (Finset.mem_filter.1 hiA).2 - have : (SignType.pos : SignType) = 0 := by simp [this] at hiC' + · have hiPos : w i = SignType.pos := (mem_posSet (n := n) (w := w)).1 hiA + have : (SignType.pos : SignType) = 0 := hiPos.symm.trans hiC' cases this - · have : w i = SignType.neg := (Finset.mem_filter.1 hiB).2 - have : (SignType.neg : SignType) = 0 := by simp [this] at hiC' + · have hiNeg : w i = SignType.neg := (mem_negSet (n := n) (w := w)).1 hiB + have : (SignType.neg : SignType) = 0 := hiNeg.symm.trans hiC' cases this have hunion : (A ∪ B) ∪ C = Finset.univ := by ext i - cases hi : w i <;> simp [A, B, C, hi] + cases hi : w i <;> simp [A, B, C, signSet, hi] have hcardAB : (A ∪ B).card = A.card + B.card := Finset.card_union_of_disjoint hAB have hcardABC : ((A ∪ B) ∪ C).card = (A ∪ B).card + C.card := @@ -829,10 +836,10 @@ theorem zeroDim_weightedSumSquares_signType (w : Fin n → SignType) : have hfin : finrank ℝ (Fin n → ℝ) = Fintype.card (Fin n) := by simp have hpos := posDim_weightedSumSquares_signType (n := n) w have hneg := negDim_weightedSumSquares_signType (n := n) w - have hsum := card_filters_pos_neg_zero (n := n) w - let A : ℕ := (Finset.univ.filter fun i : Fin n => w i = SignType.pos).card - let B : ℕ := (Finset.univ.filter fun i : Fin n => w i = SignType.neg).card - let C : ℕ := (Finset.univ.filter fun i : Fin n => w i = 0).card + have hsum := card_posSet_add_card_negSet_add_card_zeroSet (n := n) w + let A : ℕ := (posSet (n := n) w).card + let B : ℕ := (negSet (n := n) w).card + let C : ℕ := (zeroSet (n := n) w).card have hsum' : A + B + C = Fintype.card (Fin n) := by simpa [A, B, C] using hsum have hz0 : Fintype.card (Fin n) - A - B = C := by have hn : Fintype.card (Fin n) = A + (B + C) := by @@ -847,37 +854,19 @@ theorem zeroDim_weightedSumSquares_signType (w : Fin n → SignType) : set Q : QuadraticForm ℝ (Fin n → ℝ) := (QuadraticMap.weightedSumSquares ℝ fun i : Fin n => (w i : ℝ)) have hposI : posIndex (V := Fin n → ℝ) Q = A := by - simpa [Q, A] using (posIndex_weightedSumSquares_signType (n := n) w) + simpa [Q, A, posSet, signSet] using (posIndex_weightedSumSquares_signType (n := n) w) have hnegI : posIndex (V := Fin n → ℝ) (-Q) = B := by - have h0 : - posIndex (V := Fin n → ℝ) - (QuadraticMap.weightedSumSquares ℝ fun i : Fin n => ((-w i : SignType) : ℝ)) = - (Finset.univ.filter fun i : Fin n => (-w i) = SignType.pos).card := by - simpa using (posIndex_weightedSumSquares_signType (n := n) fun i => -w i) - have hneg' : (-Q) = - (QuadraticMap.weightedSumSquares ℝ fun i : Fin n => ((-w i : SignType) : ℝ)) := by - ext v - simp [Q, QuadraticMap.weightedSumSquares_apply] - have hcard : - (Finset.univ.filter fun i : Fin n => (-w i) = SignType.pos).card = B := by - have hset : - (Finset.univ.filter fun i : Fin n => (-w i) = SignType.pos) = - (Finset.univ.filter fun i : Fin n => w i = SignType.neg) := by - ext i - cases hi : w i <;> simp [hi] - simpa [B] using congrArg Finset.card hset - calc - posIndex (V := Fin n → ℝ) (-Q) - = posIndex (V := Fin n → ℝ) - (QuadraticMap.weightedSumSquares ℝ fun i : Fin n => ((-w i : SignType) : ℝ)) := by - simp [hneg'] - _ = (Finset.univ.filter fun i : Fin n => (-w i) = SignType.pos).card := h0 - _ = B := hcard + -- `negDim Q = posIndex (-Q)` and `negDim Q = card (negSet w)`. + simpa [QuadraticForm.negDim, Q, B, negSet, signSet] using hneg have : QuadraticForm.zeroDim (E := Fin n → ℝ) Q = C := by dsimp [QuadraticForm.zeroDim, QuadraticForm.posDim, QuadraticForm.negDim] rw [hfin, hposI, hnegI] simpa using hz0 - simpa [C] using this + simpa [C, zeroSet, signSet, Q] using this + +theorem zeroDim_diagForm (w : Fin n → SignType) : + QuadraticForm.zeroDim (E := Fin n → ℝ) (diagForm (n := n) w) = (zeroSet (n := n) w).card := by + simpa [diagForm, zeroSet, signSet] using (zeroDim_weightedSumSquares_signType (n := n) w) end diff --git a/PhysLean/Mathematics/Geometry/Metric/Riemannian/Defs.lean b/PhysLean/Mathematics/Geometry/Metric/Riemannian/Defs.lean index 0982b6849..0b4c6c82a 100644 --- a/PhysLean/Mathematics/Geometry/Metric/Riemannian/Defs.lean +++ b/PhysLean/Mathematics/Geometry/Metric/Riemannian/Defs.lean @@ -9,7 +9,7 @@ import Mathlib.Geometry.Manifold.VectorBundle.Tangent /-! # Riemannian Metric Definitions -This file is a PhysLean-facing wrapper around the modern Mathlib Riemannian metric API. +This file builds around the modern Mathlib Riemannian metric API. Concretely, a `C^n` Riemannian metric on a manifold is a smooth section of the bundle of bilinear forms on the tangent bundle, packaged as `Bundle.ContMDiffRiemannianMetric`. From 1cf3af742389c916ac6a799da4b27aaece637db4 Mon Sep 17 00:00:00 2001 From: Matteo CIpollina Date: Tue, 3 Mar 2026 11:46:25 +0100 Subject: [PATCH 18/36] refactor! --- .../Geometry/Metric/Lorentzian/Defs.lean | 8 +- .../Metric/PseudoRiemannian/Defs.lean | 512 +++++++----------- .../Geometry/Metric/QuadraticForm/NegDim.lean | 275 +++------- .../Metric/QuadraticForm/Sylvester.lean | 155 ++++++ .../Geometry/Metric/Riemannian/Defs.lean | 8 +- 5 files changed, 444 insertions(+), 514 deletions(-) create mode 100644 PhysLean/Mathematics/Geometry/Metric/QuadraticForm/Sylvester.lean diff --git a/PhysLean/Mathematics/Geometry/Metric/Lorentzian/Defs.lean b/PhysLean/Mathematics/Geometry/Metric/Lorentzian/Defs.lean index f87345186..2145165cd 100644 --- a/PhysLean/Mathematics/Geometry/Metric/Lorentzian/Defs.lean +++ b/PhysLean/Mathematics/Geometry/Metric/Lorentzian/Defs.lean @@ -7,12 +7,12 @@ Authors: Matteo Cipollina import PhysLean.Mathematics.Geometry.Metric.PseudoRiemannian.Defs /-! -# Lorentzian metrics (PhysLean) +# Lorentzian metrics This file defines Lorentzian metrics as pseudo-Riemannian metrics of index `1` (negative dimension `1`), in the sense of Sylvester's law of inertia (`QuadraticForm.negDim`). -This is intentionally lightweight: it provides a reusable definition that composes with the +It provides a reusable definition that composes with the existing pseudo-Riemannian API (musical isomorphisms, induced bilinear forms, etc.). -/ @@ -28,7 +28,7 @@ variable {E : Type v} [NormedAddCommGroup E] [NormedSpace ℝ E] variable {H : Type w} [TopologicalSpace H] variable {M : Type w} [TopologicalSpace M] [ChartedSpace H M] variable {I : ModelWithCorners ℝ E H} {n : WithTop ℕ∞} -variable [IsManifold I 1 M] [IsManifold I (n + 1) M] +variable [IsManifold I (n + 1) M] variable [∀ x : M, FiniteDimensional ℝ (TangentSpace I x)] /-- A Lorentzian metric is a pseudo-Riemannian metric whose associated quadratic form has @@ -36,7 +36,7 @@ variable [∀ x : M, FiniteDimensional ℝ (TangentSpace I x)] abbrev LorentzianMetric (E : Type v) (H : Type w) (M : Type w) (n : WithTop ℕ∞) [NormedAddCommGroup E] [NormedSpace ℝ E] [TopologicalSpace H] [TopologicalSpace M] [ChartedSpace H M] - (I : ModelWithCorners ℝ E H) [IsManifold I (n + 1) M] [IsManifold I 1 M] + (I : ModelWithCorners ℝ E H) [IsManifold I (n + 1) M] [∀ x : M, FiniteDimensional ℝ (TangentSpace I x)] := PseudoRiemannianMetric E H M n I diff --git a/PhysLean/Mathematics/Geometry/Metric/PseudoRiemannian/Defs.lean b/PhysLean/Mathematics/Geometry/Metric/PseudoRiemannian/Defs.lean index 045c4ee04..d8d1aa219 100644 --- a/PhysLean/Mathematics/Geometry/Metric/PseudoRiemannian/Defs.lean +++ b/PhysLean/Mathematics/Geometry/Metric/PseudoRiemannian/Defs.lean @@ -66,7 +66,7 @@ section PseudoRiemannianBundle variable {B : Type*} [TopologicalSpace B] - {E : B → Type*} [∀ x, NormedAddCommGroup (E x)] [∀ x, NormedSpace ℝ (E x)] + {E : B → Type*} [∀ x, SeminormedAddCommGroup (E x)] [∀ x, NormedSpace ℝ (E x)] /-- A pseudo-Riemannian structure on a family of fibers `E x`: a symmetric, nondegenerate bilinear form on each fiber, expressed as a continuous bilinear map. -/ @@ -86,8 +86,9 @@ abbrev pseudoInner (x : B) (v w : E x) : ℝ := (PseudoRiemannianBundle.metric (B := B) (E := E) x) v w omit [TopologicalSpace B] in -@[simp] lemma pseudoInner_def (x : B) (v w : E x) : - pseudoInner (B := B) (E := E) x v w = metric (B := B) (E := E) x v w := rfl +@[simp] +lemma pseudoInner_def (x : B) (v w : E x) : + pseudoInner (B := B) (E := E) x v w = metric (B := B) (E := E) x v w := rfl omit [TopologicalSpace B] in lemma pseudoInner_symm (x : B) (v w : E x) : @@ -113,26 +114,50 @@ variable {HB : Type*} [TopologicalSpace HB] {IB : ModelWithCorners ℝ EB HB} {n n' : WithTop ℕ∞} {B : Type*} [TopologicalSpace B] [ChartedSpace HB B] {F : Type*} [NormedAddCommGroup F] [NormedSpace ℝ F] - {E : B → Type*} [TopologicalSpace (TotalSpace F E)] [∀ x, NormedAddCommGroup (E x)] + {E : B → Type*} [TopologicalSpace (TotalSpace F E)] [∀ x, SeminormedAddCommGroup (E x)] [∀ x, NormedSpace ℝ (E x)] [FiberBundle F E] [VectorBundle ℝ F E] [PseudoRiemannianBundle (B := B) (E := E)] variable (IB n F E) in -/-- A pseudo-Riemannian bundle is `C^n` if its fiberwise bilinear form depends `C^n`-smoothly on the -base point, when seen as a section of the bundle of bilinear forms. -/ +/-- A `C^n` pseudo-Riemannian metric along a vector bundle `E → B`, packaged bundle-first. + +This mirrors Mathlib's `Bundle.ContMDiffRiemannianMetric`, but replaces positivity by fiberwise +nondegeneracy. -/ +structure ContMDiffPseudoRiemannianMetric : Type _ where + /-- The fiberwise bilinear form. -/ + metric (b : B) : E b →L[ℝ] E b →L[ℝ] ℝ + /-- Symmetry: `g_b(v,w) = g_b(w,v)`. -/ + symm (b : B) (v w : E b) : metric b v w = metric b w v + /-- Nondegeneracy: if `g_b(v, w) = 0` for all `w`, then `v = 0`. -/ + nondegenerate (b : B) (v : E b) (hv : ∀ w : E b, metric b v w = 0) : v = 0 + /-- Smoothness as a section of the bundle of bilinear forms. -/ + contMDiff : + ContMDiff IB (IB.prod 𝓘(ℝ, F →L[ℝ] F →L[ℝ] ℝ)) n + (fun b ↦ TotalSpace.mk' (F →L[ℝ] F →L[ℝ] ℝ) b (metric b)) + +variable (IB n F E) in +/-- Prop-valued smoothness predicate for a pseudo-Riemannian bundle. + +This is stated as an existence statement (as in Mathlib's `IsContinuousRiemannianBundle`), so it is +Prop-valued in terms of existing bundle data. -/ class IsContMDiffPseudoRiemannianBundle : Prop where exists_contMDiff : - ∃ g : Π x : B, E x →L[ℝ] E x →L[ℝ] ℝ, - ContMDiff IB (IB.prod 𝓘(ℝ, F →L[ℝ] F →L[ℝ] ℝ)) n - (fun b ↦ TotalSpace.mk' (F →L[ℝ] F →L[ℝ] ℝ) b (g b)) - ∧ ∀ (x : B) (v w : E x), pseudoInner (B := B) (E := E) x v w = g x v w + ∃ g : ContMDiffPseudoRiemannianMetric (IB := IB) (n := n) (F := F) (E := E), + ∀ (b : B) (v w : E b), pseudoInner (B := B) (E := E) b v w = g.metric b v w lemma IsContMDiffPseudoRiemannianBundle.of_le [h : IsContMDiffPseudoRiemannianBundle (IB := IB) (n := n) (F := F) (E := E)] (h' : n' ≤ n) : IsContMDiffPseudoRiemannianBundle (IB := IB) (n := n') (F := F) (E := E) := by - rcases h.exists_contMDiff with ⟨g, g_smooth, hg⟩ - exact ⟨g, g_smooth.of_le h', hg⟩ + rcases h.exists_contMDiff with ⟨g, hg⟩ + refine ⟨⟨?_, ?_⟩⟩ + · refine + { metric := g.metric + symm := g.symm + nondegenerate := g.nondegenerate + contMDiff := g.contMDiff.of_le h' } + · intro b v w + simpa using hg b v w instance [IsContMDiffPseudoRiemannianBundle (IB := IB) (n := (1 : WithTop ℕ∞)) (F := F) (E := E)] : IsContMDiffPseudoRiemannianBundle (IB := IB) (n := (0 : WithTop ℕ∞)) (F := F) (E := E) := @@ -156,25 +181,42 @@ variable [h : IsContMDiffPseudoRiemannianBundle (IB := IB) (n := n) (F := F) (E := E)] {b : M → B} {v w : ∀ x, E (b x)} {s : Set M} {x : M} -/-- Given two smooth maps into the same fibers of a pseudo-Riemannian bundle, their pairing is smooth. -/ -lemma ContMDiffWithinAt.pseudoInner_bundle +omit [PseudoRiemannianBundle (B := B) (E := E)] h in +/-- Given two smooth maps into the same fibers, their pairing under a smooth pseudo-Riemannian +bundle metric is smooth. -/ +lemma ContMDiffWithinAt.metric_bundle + (g : ContMDiffPseudoRiemannianMetric (IB := IB) (n := n) (F := F) (E := E)) (hv : ContMDiffWithinAt IM (IB.prod 𝓘(ℝ, F)) n (fun m ↦ (v m : TotalSpace F E)) s x) (hw : ContMDiffWithinAt IM (IB.prod 𝓘(ℝ, F)) n (fun m ↦ (w m : TotalSpace F E)) s x) : - ContMDiffWithinAt IM 𝓘(ℝ) n (fun m ↦ pseudoInner (B := B) (E := E) (b m) (v m) (w m)) s x := by - rcases h.exists_contMDiff with ⟨g, g_smooth, hg⟩ + ContMDiffWithinAt IM 𝓘(ℝ) n (fun m ↦ g.metric (b m) (v m) (w m)) s x := by have hb : ContMDiffWithinAt IM IB n b s x := by simp only [contMDiffWithinAt_totalSpace] at hv exact hv.1 - simp only [hg] have : ContMDiffWithinAt IM (IB.prod 𝓘(ℝ)) n - (fun m ↦ TotalSpace.mk' ℝ (E := Bundle.Trivial B ℝ) (b m) (g (b m) (v m) (w m))) s x := by + (fun m ↦ TotalSpace.mk' ℝ (E := Bundle.Trivial B ℝ) (b m) + (g.metric (b m) (v m) (w m))) s x := by apply ContMDiffWithinAt.clm_bundle_apply₂ (F₁ := F) (F₂ := F) - · exact ContMDiffAt.comp_contMDiffWithinAt x g_smooth.contMDiffAt hb + · exact ContMDiffAt.comp_contMDiffWithinAt x g.contMDiff.contMDiffAt hb · exact hv · exact hw simp only [contMDiffWithinAt_totalSpace] at this exact this.2 +/-- Given two smooth maps into the same fibers of a pseudo-Riemannian bundle, their pairing is smooth. -/ +lemma ContMDiffWithinAt.pseudoInner_bundle + (hv : ContMDiffWithinAt IM (IB.prod 𝓘(ℝ, F)) n (fun m ↦ (v m : TotalSpace F E)) s x) + (hw : ContMDiffWithinAt IM (IB.prod 𝓘(ℝ, F)) n (fun m ↦ (w m : TotalSpace F E)) s x) : + ContMDiffWithinAt IM 𝓘(ℝ) n (fun m ↦ pseudoInner (B := B) (E := E) (b m) (v m) (w m)) s x := by + rcases h.exists_contMDiff with ⟨g, hg⟩ + have hpair := ContMDiffWithinAt.metric_bundle (IB := IB) (n := n) (F := F) (E := E) + (b := b) (v := v) (w := w) (s := s) (x := x) g hv hw + have hrewrite : + (fun m ↦ pseudoInner (B := B) (E := E) (b m) (v m) (w m)) = + (fun m ↦ g.metric (b m) (v m) (w m)) := by + funext m + simpa [Bundle.pseudoInner] using (hg (b m) (v m) (w m)) + simpa [hrewrite] using hpair + lemma ContMDiffAt.pseudoInner_bundle (hv : ContMDiffAt IM (IB.prod 𝓘(ℝ, F)) n (fun m ↦ (v m : TotalSpace F E)) x) (hw : ContMDiffAt IM (IB.prod 𝓘(ℝ, F)) n (fun m ↦ (w m : TotalSpace F E)) x) : @@ -224,7 +266,7 @@ This helper is intentionally public: it is the bridge between a bundled descript def valToQuadraticForm {E : Type v} [NormedAddCommGroup E] [NormedSpace ℝ E] {H : Type w} [TopologicalSpace H] - {M : Type w} [TopologicalSpace M] [ChartedSpace H M] + {M : Type*} [TopologicalSpace M] [ChartedSpace H M] {I : ModelWithCorners ℝ E H} (val : ∀ (x : M), TangentSpace I x →L[ℝ] (TangentSpace I x →L[ℝ] ℝ)) (symm : ∀ (x : M) (v w : TangentSpace I x), (val x v) w = (val x w) v) @@ -254,15 +296,14 @@ a smoothly varying symmetric, nondegenerate bilinear form on each tangent space. The pseudo-Riemannian notion will extend this with an index/signature constancy condition. -/ @[ext] structure MetricTensor - (E : Type v) (H : Type w) (M : Type w) (n : WithTop ℕ∞) + (E : Type v) (H : Type w) (M : Type*) (n : WithTop ℕ∞) [inst_norm_grp_E : NormedAddCommGroup E] [inst_norm_sp_E : NormedSpace ℝ E] [inst_top_H : TopologicalSpace H] [inst_top_M : TopologicalSpace M] [inst_chart_M : ChartedSpace H M] (I : ModelWithCorners ℝ E H) - [inst_mani : IsManifold I (n + 1) M] - [inst_mani1 : IsManifold I 1 M] : + [inst_mani : IsManifold I (n + 1) M] : Type _ where /-- The metric tensor at each point `x : M`, represented as a continuous linear map `TₓM →L[ℝ] (TₓM →L[ℝ] ℝ)`. Applying it twice, `(val x v) w`, yields `gₓ(v, w)`. -/ @@ -276,17 +317,22 @@ structure MetricTensor We follow the same pattern as Mathlib's Riemannian metric API, using `TotalSpace.mk'` for the bundled map. -/ - contMDiff : ContMDiff I (I.prod 𝓘(ℝ, E →L[ℝ] E →L[ℝ] ℝ)) n - (fun x ↦ - TotalSpace.mk' (E →L[ℝ] E →L[ℝ] ℝ) x (val x)) + contMDiff : + letI : IsManifold I 1 M := + IsManifold.of_le (I := I) (M := M) (m := (1 : WithTop ℕ∞)) (n := n + 1) + (by + have h0 : (0 : WithTop ℕ∞) ≤ n := by simpa using (zero_le n) + simpa [zero_add] using (add_le_add_right h0 (1 : WithTop ℕ∞))) + ContMDiff I (I.prod 𝓘(ℝ, E →L[ℝ] E →L[ℝ] ℝ)) n + (fun x ↦ TotalSpace.mk' (E →L[ℝ] E →L[ℝ] ℝ) x (val x)) namespace MetricTensor -variable {E : Type v} {H : Type w} {M : Type w} {n : WithTop ℕ∞} +variable {E : Type v} {H : Type w} {M : Type*} {n : WithTop ℕ∞} variable [NormedAddCommGroup E] [NormedSpace ℝ E] variable [TopologicalSpace H] [TopologicalSpace M] [ChartedSpace H M] variable {I : ModelWithCorners ℝ E H} -variable [IsManifold I (n + 1) M] [IsManifold I 1 M] +variable [IsManifold I (n + 1) M] /-- Coercion from `MetricTensor` to its `val` function. -/ instance coeFunInst : CoeFun (MetricTensor E H M n I) @@ -353,6 +399,41 @@ def inner (g : MetricTensor E H M n I) (x : M) (v w : TangentSpace I x) : ℝ := lemma inner_apply (g : MetricTensor E H M n I) (x : M) (v w : TangentSpace I x) : inner g x v w = g.val x v w := rfl +/-! ### Smoothness of the pairing `g(v,w)` -/ + +section PairingSmoothness + +variable [IsManifold I 1 M] + +variable + {EM : Type*} [NormedAddCommGroup EM] [NormedSpace ℝ EM] + {HM : Type*} [TopologicalSpace HM] {IM : ModelWithCorners ℝ EM HM} + {M' : Type*} [TopologicalSpace M'] [ChartedSpace HM M'] + {b : M' → M} {v w : ∀ x, TangentSpace I (b x)} {s : Set M'} {x : M'} + +/-- Smoothness of the metric pairing along a smooth base map, for smooth fields into the fibers. -/ +lemma ContMDiffWithinAt.inner + (g : MetricTensor E H M n I) + (hv : ContMDiffWithinAt IM (I.prod 𝓘(ℝ, E)) n + (fun m ↦ (v m : TotalSpace E fun y : M ↦ TangentSpace I y)) s x) + (hw : ContMDiffWithinAt IM (I.prod 𝓘(ℝ, E)) n + (fun m ↦ (w m : TotalSpace E fun y : M ↦ TangentSpace I y)) s x) : + ContMDiffWithinAt IM 𝓘(ℝ) n (fun m ↦ g.val (b m) (v m) (w m)) s x := by + have hb : ContMDiffWithinAt IM I n b s x := by + simp only [contMDiffWithinAt_totalSpace] at hv + exact hv.1 + have : ContMDiffWithinAt IM (I.prod 𝓘(ℝ)) n + (fun m ↦ TotalSpace.mk' ℝ (E := Bundle.Trivial M ℝ) (b m) + ((g.val (b m)) (v m) (w m))) s x := by + apply ContMDiffWithinAt.clm_bundle_apply₂ (F₁ := E) (F₂ := E) + · exact ContMDiffAt.comp_contMDiffWithinAt x g.contMDiff.contMDiffAt hb + · exact hv + · exact hw + simp only [contMDiffWithinAt_totalSpace] at this + exact this.2 + +end PairingSmoothness + /-! ## Flat / sharp (musical isomorphisms) -/ /-- Index lowering map `v ↦ gₓ(v, -)` as a continuous linear map. -/ @@ -590,7 +671,6 @@ lemma cotangentToBilinForm_nondegenerate (g : MetricTensor E H M n I) (x : M) : /-- The cotangent quadratic form is equivalent to the tangent quadratic form via `sharp`. -/ theorem cotangentToQuadraticForm_equivalent_toQuadraticForm (g : MetricTensor E H M n I) (x : M) : (g.cotangentToQuadraticForm x).Equivalent (g.toQuadraticForm x) := by - classical refine ⟨?_⟩ refine { toLinearEquiv := (g.sharpEquiv x).toLinearEquiv @@ -623,7 +703,7 @@ as a "symmetric non-degenerate (0,2) tensor field on M of constant index." Each `gₓ` is a scalar product (O'Neill, Definition 20, p. 47) on `TₓM`. -/ @[ext] structure PseudoRiemannianMetric - (E : Type v) (H : Type w) (M : Type w) (n : WithTop ℕ∞) + (E : Type v) (H : Type w) (M : Type*) (n : WithTop ℕ∞) [inst_norm_grp_E : NormedAddCommGroup E] [inst_norm_sp_E : NormedSpace ℝ E] [inst_top_H : TopologicalSpace H] @@ -631,7 +711,6 @@ structure PseudoRiemannianMetric [inst_chart_M : ChartedSpace H M] (I : ModelWithCorners ℝ E H) [inst_mani : IsManifold I (n + 1) M] - [inst_mani1 : IsManifold I 1 M] [inst_tangent_findim : ∀ (x : M), FiniteDimensional ℝ (TangentSpace I x)] : Type _ extends toMetricTensor : MetricTensor E H M n I where /-- The negative dimension (`QuadraticForm.negDim`) of the metric's quadratic form is @@ -643,11 +722,11 @@ structure PseudoRiemannianMetric namespace PseudoRiemannianMetric -variable {E : Type v} {H : Type w} {M : Type w} {n : WithTop ℕ∞} +variable {E : Type v} {H : Type w} {M : Type*} {n : WithTop ℕ∞} variable [NormedAddCommGroup E] [NormedSpace ℝ E] variable [TopologicalSpace H] [TopologicalSpace M] [ChartedSpace H M] variable {I : ModelWithCorners ℝ E H} -variable [IsManifold I (n + 1) M] [IsManifold I 1 M] +variable [IsManifold I (n + 1) M] variable [inst_tangent_findim : ∀ (x : M), FiniteDimensional ℝ (TangentSpace I x)] /-! ## Predicate typeclass: pseudo-Riemannian manifolds -/ @@ -753,332 +832,147 @@ lemma index_eq_of_mem_connectedComponent (g : PseudoRiemannianMetric E H M n I) (isConnected_connectedComponent.isPreconnected) hy (mem_connectedComponent : x ∈ connectedComponent x) -@[simp] lemma toBilinForm_isSymm (g : PseudoRiemannianMetric E H M n I) (x : M) : (toBilinForm g x).IsSymm := by - refine { eq := ?_ } - intro v w; simp only [toBilinForm_apply]; exact g.symm x v w + simpa [toBilinForm] using + (MetricTensor.toBilinForm_isSymm (g := g.toMetricTensor) x) -@[simp] lemma toBilinForm_nondegenerate (g : PseudoRiemannianMetric E H M n I) (x : M) : (toBilinForm g x).Nondegenerate := by - unfold LinearMap.BilinForm.Nondegenerate LinearMap.Nondegenerate - LinearMap.SeparatingLeft LinearMap.SeparatingRight - constructor - · intro v hv; simp_rw [toBilinForm_apply] at hv; exact g.nondegenerate x v hv - · intro v hv; simp_rw [toBilinForm_apply] at hv; - have hw : ∀ (w : TangentSpace I x), ((g.val x) v) w = 0 := by - intro w - simpa [g.symm x v w] using hv w - exact g.nondegenerate x v hw + simpa [toBilinForm] using + (MetricTensor.toBilinForm_nondegenerate (g := g.toMetricTensor) x) -/-- The inner product (or scalar product) on the tangent space at point `x` - induced by the pseudo-Riemannian metric `g`. This is `gₓ(v, w)`. -/ -def inner (g : PseudoRiemannianMetric E H M n I) (x : M) (v w : TangentSpace I x) : ℝ := - g.val x v w +/-- The fiberwise pairing \(g_x(v,w)\) of a pseudo-Riemannian metric. -/ +abbrev inner (g : PseudoRiemannianMetric E H M n I) (x : M) (v w : TangentSpace I x) : ℝ := + MetricTensor.inner (g := g.toMetricTensor) x v w -@[simp] -lemma inner_apply (g : PseudoRiemannianMetric E H M n I) (x : M) (v w : TangentSpace I x) : - inner g x v w = g.val x v w := rfl +@[simp] lemma inner_apply (g : PseudoRiemannianMetric E H M n I) (x : M) (v w : TangentSpace I x) : + inner g x v w = g.val x v w := rfl -/-! ## Flat -/ +/-! ### Smoothness of the pairing `g(v,w)` -/ -section Flat +section PairingSmoothness -/-- The "musical" isomorphism (index lowering) `v ↦ gₓ(v, -)`. -The non-degeneracy of `gₓ` (O'Neill, Def 17 (3), p. 46) means its matrix representation -is invertible (O'Neill, Lemma 19, p. 47), and that this map is an isomorphism from `TₓM` -to its dual. -/ -def flat (g : PseudoRiemannianMetric E H M n I) (x : M) : - TangentSpace I x →ₗ[ℝ] (TangentSpace I x →L[ℝ] ℝ) := - MetricTensor.flat (g := g.toMetricTensor) x +variable [IsManifold I 1 M] -@[simp] -lemma flat_apply (g : PseudoRiemannianMetric E H M n I) (x : M) (v w : TangentSpace I x) : - (flat g x v) w = g.val x v w := by rfl +variable + {EM : Type*} [NormedAddCommGroup EM] [NormedSpace ℝ EM] + {HM : Type*} [TopologicalSpace HM] {IM : ModelWithCorners ℝ EM HM} + {M' : Type*} [TopologicalSpace M'] [ChartedSpace HM M'] + {b : M' → M} {v w : ∀ x, TangentSpace I (b x)} {s : Set M'} {x : M'} + +lemma ContMDiffWithinAt.inner + (g : PseudoRiemannianMetric E H M n I) + (hv : ContMDiffWithinAt IM (I.prod 𝓘(ℝ, E)) n + (fun m ↦ (v m : TotalSpace E fun y : M ↦ TangentSpace I y)) s x) + (hw : ContMDiffWithinAt IM (I.prod 𝓘(ℝ, E)) n + (fun m ↦ (w m : TotalSpace E fun y : M ↦ TangentSpace I y)) s x) : + ContMDiffWithinAt IM 𝓘(ℝ) n (fun m ↦ g.inner (b m) (v m) (w m)) s x := by + simpa [PseudoRiemannianMetric.inner] using + (MetricTensor.ContMDiffWithinAt.inner (g := g.toMetricTensor) (b := b) (v := v) (w := w) + (s := s) (x := x) hv hw) + +end PairingSmoothness + +/-! ## Flat / sharp / cotangent API (delegated to `MetricTensor`) -/ + +abbrev flat (g : PseudoRiemannianMetric E H M n I) (x : M) : + TangentSpace I x →ₗ[ℝ] (TangentSpace I x →L[ℝ] ℝ) := + MetricTensor.flat (g := (g.toMetricTensor : MetricTensor E H M n I)) x -/-- The musical isomorphism as a continuous linear map. -/ abbrev flatL (g : PseudoRiemannianMetric E H M n I) (x : M) : TangentSpace I x →L[ℝ] (TangentSpace I x →L[ℝ] ℝ) := - MetricTensor.flatL (g := g.toMetricTensor) x - -@[simp] -lemma flatL_apply (g : PseudoRiemannianMetric E H M n I) (x : M) (v w : TangentSpace I x) : - (flatL g x v) w = g.val x v w := rfl + MetricTensor.flatL (g := (g.toMetricTensor : MetricTensor E H M n I)) x -@[simp] lemma flat_inj (g : PseudoRiemannianMetric E H M n I) (x : M) : Function.Injective (flat g x) := by - rw [← LinearMap.ker_eq_bot]; apply LinearMap.ker_eq_bot'.mpr - intro v hv; apply g.nondegenerate x v; intro w; exact DFunLike.congr_fun hv w + simpa [flat] using (MetricTensor.flat_inj (g := (g.toMetricTensor : MetricTensor E H M n I)) x) -@[simp] lemma flatL_inj (g : PseudoRiemannianMetric E H M n I) (x : M) : - Function.Injective (flatL g x) := - flat_inj g x + Function.Injective (flatL g x) := by + simpa [flatL] using (MetricTensor.flatL_inj (g := (g.toMetricTensor : MetricTensor E H M n I)) x) -@[simp] -lemma flatL_surj - (g : PseudoRiemannianMetric E H M n I) (x : M) : - Function.Surjective (g.flatL x) := by - simpa [PseudoRiemannianMetric.flatL] using - MetricTensor.flatL_surj (g := g.toMetricTensor) x - -/-- The "musical" isomorphism (index lowering) from `TₓM` to its dual, -as a continuous linear equivalence. This equivalence is a direct result of `gₓ` being -a non-degenerate bilinear form (O'Neill, Def 17(3), p. 46; Lemma 19, p. 47). -/ -noncomputable def flatEquiv - (g : PseudoRiemannianMetric E H M n I) - (x : M) : - TangentSpace I x ≃L[ℝ] (TangentSpace I x →L[ℝ] ℝ) := - MetricTensor.flatEquiv (g := g.toMetricTensor) x - -lemma coe_flatEquiv - (g : PseudoRiemannianMetric E H M n I) (x : M) : - (g.flatEquiv x : TangentSpace I x →ₗ[ℝ] (TangentSpace I x →L[ℝ] ℝ)) = g.flatL x := rfl - -@[simp] -lemma flatEquiv_apply - (g : PseudoRiemannianMetric E H M n I) (x : M) (v w : TangentSpace I x) : - (g.flatEquiv x v) w = g.val x v w := rfl +lemma flatL_surj (g : PseudoRiemannianMetric E H M n I) (x : M) : + Function.Surjective (flatL g x) := by + simpa [flatL] using (MetricTensor.flatL_surj (g := (g.toMetricTensor : MetricTensor E H M n I)) x) -end Flat - -/-! ## Sharp -/ - -section Sharp +noncomputable abbrev flatEquiv (g : PseudoRiemannianMetric E H M n I) (x : M) : + TangentSpace I x ≃L[ℝ] (TangentSpace I x →L[ℝ] ℝ) := + MetricTensor.flatEquiv (g := (g.toMetricTensor : MetricTensor E H M n I)) x -/-- The "musical" isomorphism (index raising) from the dual of `TₓM` to `TₓM`. -This is the inverse of `flatEquiv g x`, and its existence as an isomorphism is -guaranteed by the non-degeneracy of `gₓ` (O'Neill, Lemma 19, p. 47). -/ -noncomputable def sharpEquiv - (g : PseudoRiemannianMetric E H M n I) (x : M) : +noncomputable abbrev sharpEquiv (g : PseudoRiemannianMetric E H M n I) (x : M) : (TangentSpace I x →L[ℝ] ℝ) ≃L[ℝ] TangentSpace I x := - MetricTensor.sharpEquiv (g := g.toMetricTensor) x + MetricTensor.sharpEquiv (g := (g.toMetricTensor : MetricTensor E H M n I)) x -/-- The index raising map `sharp` as a continuous linear map. -/ -noncomputable def sharpL - (g : PseudoRiemannianMetric E H M n I) (x : M) : +noncomputable abbrev sharpL (g : PseudoRiemannianMetric E H M n I) (x : M) : (TangentSpace I x →L[ℝ] ℝ) →L[ℝ] TangentSpace I x := - MetricTensor.sharpL (g := g.toMetricTensor) x - -lemma sharpL_eq_toContinuousLinearMap - (g : PseudoRiemannianMetric E H M n I) (x : M) : - g.sharpL x = (g.sharpEquiv x).toContinuousLinearMap := rfl + MetricTensor.sharpL (g := (g.toMetricTensor : MetricTensor E H M n I)) x -lemma coe_sharpEquiv - (g : PseudoRiemannianMetric E H M n I) (x : M) : - (g.sharpEquiv x : (TangentSpace I x →L[ℝ] ℝ) →L[ℝ] TangentSpace I x) = g.sharpL x := rfl - -/-- The index raising map `sharp` as a linear map. -/ -noncomputable def sharp - (g : PseudoRiemannianMetric E H M n I) (x : M) : +noncomputable abbrev sharp (g : PseudoRiemannianMetric E H M n I) (x : M) : (TangentSpace I x →L[ℝ] ℝ) →ₗ[ℝ] TangentSpace I x := - MetricTensor.sharp (g := g.toMetricTensor) x + MetricTensor.sharp (g := (g.toMetricTensor : MetricTensor E H M n I)) x -@[simp] -lemma sharpL_apply_flatL - (g : PseudoRiemannianMetric E H M n I) (x : M) (v : TangentSpace I x) : +@[simp] lemma sharpL_apply_flatL (g : PseudoRiemannianMetric E H M n I) (x : M) (v : TangentSpace I x) : g.sharpL x (g.flatL x v) = v := by - simp [PseudoRiemannianMetric.sharpL, PseudoRiemannianMetric.flatL, - MetricTensor.sharpL_apply_flatL (g := g.toMetricTensor) x v] + simpa [sharpL, flatL] using + (MetricTensor.sharpL_apply_flatL (g := (g.toMetricTensor : MetricTensor E H M n I)) x v) -@[simp] -lemma flatL_apply_sharpL - (g : PseudoRiemannianMetric E H M n I) (x : M) (ω : TangentSpace I x →L[ℝ] ℝ) : +@[simp] lemma flatL_apply_sharpL (g : PseudoRiemannianMetric E H M n I) (x : M) + (ω : TangentSpace I x →L[ℝ] ℝ) : g.flatL x (g.sharpL x ω) = ω := by - simp [PseudoRiemannianMetric.sharpL, PseudoRiemannianMetric.flatL, - MetricTensor.flatL_apply_sharpL (g := g.toMetricTensor) x ω] + simpa [sharpL, flatL] using + (MetricTensor.flatL_apply_sharpL (g := (g.toMetricTensor : MetricTensor E H M n I)) x ω) -/-- Applying `sharp` then `flat` recovers the original covector. -/ -@[simp] -lemma flat_sharp_apply - (g : PseudoRiemannianMetric E H M n I) (x : M) (ω : TangentSpace I x →L[ℝ] ℝ) : +@[simp] lemma flat_sharp_apply (g : PseudoRiemannianMetric E H M n I) (x : M) + (ω : TangentSpace I x →L[ℝ] ℝ) : g.flat x (g.sharp x ω) = ω := by - simp [PseudoRiemannianMetric.flat, PseudoRiemannianMetric.sharp, - MetricTensor.flat_sharp_apply (g := g.toMetricTensor) x ω] + simpa [flat, sharp] using + (MetricTensor.flat_sharp_apply (g := (g.toMetricTensor : MetricTensor E H M n I)) x ω) -@[simp] -lemma sharp_flat_apply - (g : PseudoRiemannianMetric E H M n I) (x : M) (v : TangentSpace I x) : +@[simp] lemma sharp_flat_apply (g : PseudoRiemannianMetric E H M n I) (x : M) (v : TangentSpace I x) : g.sharp x (g.flat x v) = v := by - have h : g.sharpL x (g.flatL x v) = v := - PseudoRiemannianMetric.sharpL_apply_flatL (g := g) x v - convert h using 1 + simpa [flat, sharp] using + (MetricTensor.sharp_flat_apply (g := (g.toMetricTensor : MetricTensor E H M n I)) x v) -/-- The metric evaluated at `sharp ω₁` and `sharp ω₂`. -/ -@[simp] -lemma apply_sharp_sharp - (g : PseudoRiemannianMetric E H M n I) (x : M) (ω₁ ω₂ : TangentSpace I x →L[ℝ] ℝ) : +@[simp] lemma apply_sharp_sharp (g : PseudoRiemannianMetric E H M n I) (x : M) + (ω₁ ω₂ : TangentSpace I x →L[ℝ] ℝ) : g.val x (g.sharpL x ω₁) (g.sharpL x ω₂) = ω₁ (g.sharpL x ω₂) := by - simp [PseudoRiemannianMetric.sharpL] + simpa [sharpL] using + (MetricTensor.apply_sharp_sharp (g := (g.toMetricTensor : MetricTensor E H M n I)) x ω₁ ω₂) -/-- The metric evaluated at `v` and `sharp ω`. -/ -lemma apply_vec_sharp - (g : PseudoRiemannianMetric E H M n I) (x : M) (v : TangentSpace I x) +lemma apply_vec_sharp (g : PseudoRiemannianMetric E H M n I) (x : M) (v : TangentSpace I x) (ω : TangentSpace I x →L[ℝ] ℝ) : g.val x v (g.sharpL x ω) = ω v := by - simp [PseudoRiemannianMetric.sharpL, - MetricTensor.apply_vec_sharp (g := g.toMetricTensor) x v ω] - -end Sharp - -/-! ## Cotangent -/ -section Cotangent - -variable {E : Type v} {H : Type w} {M : Type w} {n : WithTop ℕ∞} -variable [NormedAddCommGroup E] [NormedSpace ℝ E] -variable [TopologicalSpace H] [TopologicalSpace M] [ChartedSpace H M] -variable {I : ModelWithCorners ℝ E H} -variable [IsManifold I (n + 1) M] [IsManifold I 1 M] -variable [inst_tangent_findim : ∀ (x : M), FiniteDimensional ℝ (TangentSpace I x)] + simpa [sharpL] using + (MetricTensor.apply_vec_sharp (g := (g.toMetricTensor : MetricTensor E H M n I)) x v ω) -/-- The value of the induced metric on the cotangent space at point `x`. -/ -noncomputable def cotangentMetricVal (g : PseudoRiemannianMetric E H M n I) (x : M) +noncomputable abbrev cotangentMetricVal (g : PseudoRiemannianMetric E H M n I) (x : M) (ω₁ ω₂ : TangentSpace I x →L[ℝ] ℝ) : ℝ := - g.val x (g.sharpL x ω₁) (g.sharpL x ω₂) + MetricTensor.cotangentMetricVal (g := (g.toMetricTensor : MetricTensor E H M n I)) x ω₁ ω₂ -@[simp] -lemma cotangentMetricVal_eq_apply_sharp (g : PseudoRiemannianMetric E H M n I) (x : M) - (ω₁ ω₂ : TangentSpace I x →L[ℝ] ℝ) : - cotangentMetricVal g x ω₁ ω₂ = ω₁ (g.sharpL x ω₂) := by - rw [cotangentMetricVal, apply_sharp_sharp] +noncomputable abbrev cotangentToBilinForm (g : PseudoRiemannianMetric E H M n I) (x : M) : + LinearMap.BilinForm ℝ (TangentSpace I x →L[ℝ] ℝ) := + MetricTensor.cotangentToBilinForm (g := (g.toMetricTensor : MetricTensor E H M n I)) x -lemma cotangentMetricVal_symm (g : PseudoRiemannianMetric E H M n I) (x : M) - (ω₁ ω₂ : TangentSpace I x →L[ℝ] ℝ) : - cotangentMetricVal g x ω₁ ω₂ = cotangentMetricVal g x ω₂ ω₁ := by - unfold cotangentMetricVal - rw [g.symm x (g.sharpL x ω₁) (g.sharpL x ω₂)] - -/-- The induced metric on the cotangent space at point `x` as a bilinear form. -For covectors `ω₁` and `ω₂`, this gives `g(ω₁^#, ω₂^#)`, where `ω^#` is -the "sharp" musical isomorphism raising indices. -/ -noncomputable def cotangentToBilinForm (g : PseudoRiemannianMetric E H M n I) (x : M) : - LinearMap.BilinForm ℝ (TangentSpace I x →L[ℝ] ℝ) where - toFun ω₁ := { toFun := λ ω₂ => cotangentMetricVal g x ω₁ ω₂, - map_add' := λ ω₂ ω₃ => by - simp only [cotangentMetricVal, - ContinuousLinearMap.map_add], - map_smul' := λ c ω₂ => by - simp only [cotangentMetricVal, - map_smul, smul_eq_mul, RingHom.id_apply] } - map_add' := λ ω₁ ω₂ => by - ext ω₃ - simp only [cotangentMetricVal, - ContinuousLinearMap.map_add, - ContinuousLinearMap.add_apply, - LinearMap.coe_mk, AddHom.coe_mk, LinearMap.add_apply] - map_smul' := λ c ω₁ => by - ext ω₂ - simp only [cotangentMetricVal, - ContinuousLinearMap.map_smul, - ContinuousLinearMap.smul_apply, - LinearMap.coe_mk, AddHom.coe_mk, - RingHom.id_apply, LinearMap.smul_apply] - -/-- The cometric on the cotangent space T_x*M at `x`, expressed as a quadratic form. -It is induced by the pseudo-Riemannian metric on the tangent space T_xM. -/ -noncomputable def cotangentToQuadraticForm (g : PseudoRiemannianMetric E H M n I) (x : M) : - QuadraticForm ℝ (TangentSpace I x →L[ℝ] ℝ) where - toFun ω := cotangentMetricVal g x ω ω - toFun_smul a ω := by - simp only [cotangentMetricVal, - ContinuousLinearMap.map_smul, - ContinuousLinearMap.smul_apply, - smul_smul] - exists_companion' := - ⟨LinearMap.mk₂ ℝ (fun ω₁ ω₂ => - cotangentMetricVal g x ω₁ ω₂ + cotangentMetricVal g x ω₂ ω₁) - (fun ω₁ ω₂ ω₃ => by simp only [cotangentMetricVal, map_add, add_apply]; ring) - (fun a ω₁ ω₂ => by - simp only [cotangentMetricVal, map_smul, smul_apply]; - ring_nf; exact Eq.symm (smul_add ..)) - (fun ω₁ ω₂ ω₃ => by - simp only [cotangentMetricVal, map_add, add_apply]; ring) - (fun a ω₁ ω₂ => by - simp only [cotangentMetricVal, map_smul, smul_apply]; ring_nf; - exact Eq.symm (smul_add ..)), - by - intro ω₁ ω₂ - simp only [LinearMap.mk₂_apply, cotangentMetricVal] - simp only [ContinuousLinearMap.map_add, ContinuousLinearMap.add_apply] - ring⟩ - -@[simp] -lemma cotangentToBilinForm_apply (g : PseudoRiemannianMetric E H M n I) (x : M) - (ω₁ ω₂ : TangentSpace I x →L[ℝ] ℝ) : - cotangentToBilinForm g x ω₁ ω₂ = cotangentMetricVal g x ω₁ ω₂ := rfl - -@[simp] -lemma cotangentToQuadraticForm_apply (g : PseudoRiemannianMetric E H M n I) (x : M) - (ω : TangentSpace I x →L[ℝ] ℝ) : - cotangentToQuadraticForm g x ω = cotangentMetricVal g x ω ω := rfl +noncomputable abbrev cotangentToQuadraticForm (g : PseudoRiemannianMetric E H M n I) (x : M) : + QuadraticForm ℝ (TangentSpace I x →L[ℝ] ℝ) := + MetricTensor.cotangentToQuadraticForm (g := (g.toMetricTensor : MetricTensor E H M n I)) x -@[simp] -lemma cotangentToBilinForm_isSymm (g : PseudoRiemannianMetric E H M n I) (x : M) : - (cotangentToBilinForm g x).IsSymm := by - refine { eq := ?_ } - intro ω₁ ω₂; simp only [cotangentToBilinForm_apply]; exact cotangentMetricVal_symm g x ω₁ ω₂ - -/-- The cotangent metric is non-degenerate: if `cotangentMetricVal g x ω v = 0` for all `v`, - then `ω = 0`. -/ -lemma cotangentMetricVal_nondegenerate (g : PseudoRiemannianMetric E H M n I) (x : M) - (ω : TangentSpace I x →L[ℝ] ℝ) (h : ∀ v : TangentSpace I x →L[ℝ] ℝ, - cotangentMetricVal g x ω v = 0) : - ω = 0 := by - apply ContinuousLinearMap.ext - intro v - have h_forall : ∀ w : TangentSpace I x, ω w = 0 := by - intro w - let ω' : TangentSpace I x →L[ℝ] ℝ := g.flatL x w - have this : g.sharpL x ω' = w := by - simp only [ω', sharpL_apply_flatL] - have h_apply : cotangentMetricVal g x ω ω' = 0 := h ω' - simp only [cotangentMetricVal_eq_apply_sharp] at h_apply - rw [this] at h_apply - exact h_apply - exact h_forall v - -@[simp] -lemma cotangentToBilinForm_nondegenerate (g : PseudoRiemannianMetric E H M n I) (x : M) : - (cotangentToBilinForm g x).Nondegenerate := by - unfold LinearMap.BilinForm.Nondegenerate LinearMap.Nondegenerate - LinearMap.SeparatingLeft LinearMap.SeparatingRight - constructor - · intro ω hω - apply cotangentMetricVal_nondegenerate g x ω - intro v - exact hω v - · intro ω hω - apply cotangentMetricVal_nondegenerate g x ω - intro v - have hv : ∀ (y : TangentSpace I x →L[ℝ] ℝ), ((g.cotangentToBilinForm x) ω) y = 0 := by - intro y; rw [LinearMap.BilinForm.isSymm_def.mp (cotangentToBilinForm_isSymm g x)]; simp [hω] - exact hv v - -/-! ## Cotangent signature -/ - -/-- The cotangent quadratic form is equivalent to the tangent quadratic form via `sharp`. -/ -theorem cotangentToQuadraticForm_equivalent_toQuadraticForm - (g : PseudoRiemannianMetric E H M n I) (x : M) : +theorem cotangentToQuadraticForm_equivalent_toQuadraticForm (g : PseudoRiemannianMetric E H M n I) (x : M) : (g.cotangentToQuadraticForm x).Equivalent (g.toQuadraticForm x) := by - refine ⟨?_⟩ - refine - { toLinearEquiv := (g.sharpEquiv x).toLinearEquiv - map_app' := fun ω => ?_ } - have hsh : g.sharpL x ω = g.sharpEquiv x ω := rfl - simp [cotangentToQuadraticForm_apply, cotangentMetricVal, toQuadraticForm, hsh] + simpa [cotangentToQuadraticForm, toQuadraticForm] using + (MetricTensor.cotangentToQuadraticForm_equivalent_toQuadraticForm + (g := (g.toMetricTensor : MetricTensor E H M n I)) x) theorem cotangent_signature_eq (g : PseudoRiemannianMetric E H M n I) (x : M) : - (g.cotangentToQuadraticForm x).signature = (g.toQuadraticForm x).signature := - QuadraticForm.signature_eq_of_equivalent (E := (TangentSpace I x →L[ℝ] ℝ)) - (E₂ := TangentSpace I x) (cotangentToQuadraticForm_equivalent_toQuadraticForm (g := g) x) + (g.cotangentToQuadraticForm x).signature = (g.toQuadraticForm x).signature := by + exact + QuadraticForm.signature_eq_of_equivalent (E := (TangentSpace I x →L[ℝ] ℝ)) + (E₂ := TangentSpace I x) (cotangentToQuadraticForm_equivalent_toQuadraticForm (g := g) x) theorem cotangent_negDim_eq (g : PseudoRiemannianMetric E H M n I) (x : M) : - (g.cotangentToQuadraticForm x).negDim = (g.toQuadraticForm x).negDim := - congrArg QuadraticForm.Signature.neg (cotangent_signature_eq (g := g) x) - -end Cotangent + (g.cotangentToQuadraticForm x).negDim = (g.toQuadraticForm x).negDim := by + exact congrArg QuadraticForm.Signature.neg (cotangent_signature_eq (g := g) x) end PseudoRiemannianMetric end PseudoRiemannianMetric diff --git a/PhysLean/Mathematics/Geometry/Metric/QuadraticForm/NegDim.lean b/PhysLean/Mathematics/Geometry/Metric/QuadraticForm/NegDim.lean index 3902af2c0..ce8223b79 100644 --- a/PhysLean/Mathematics/Geometry/Metric/QuadraticForm/NegDim.lean +++ b/PhysLean/Mathematics/Geometry/Metric/QuadraticForm/NegDim.lean @@ -6,6 +6,7 @@ Authors: Matteo Cipollina import Mathlib.LinearAlgebra.QuadraticForm.Real import Mathlib.Algebra.Module.Submodule.Map +import Mathlib.Data.Nat.Lattice import Mathlib.LinearAlgebra.Dimension.Constructions import Mathlib.LinearAlgebra.FiniteDimensional.Lemmas @@ -33,10 +34,9 @@ diagonalization. ## Implementation notes -We also define noncanonical auxiliary data (`QuadraticForm.signTypeWeights`, -`QuadraticForm.signatureChoice`) by choosing Sylvester diagonalization weights, for bridging to -`QuadraticForm.equivalent_signType_weighted_sum_squared` in -`Mathlib.LinearAlgebra.QuadraticForm.Real`. These choices are not claimed to be canonical. +Noncanonical Sylvester-diagonalization choices (`QuadraticForm.signTypeWeights`, +`QuadraticForm.signatureChoice`, and the bridge lemmas relating them to the canonical indices) are +kept in `PhysLean.Mathematics.Geometry.Metric.QuadraticForm.Sylvester`. ## Tags @@ -65,54 +65,6 @@ abbrev nullity (s : Signature) : ℕ := s.zero end Signature -/-- A choice of `SignType`-weights in Sylvester's diagonalization of a quadratic form. - -This is auxiliary data: it exists by `QuadraticForm.equivalent_signType_weighted_sum_squared`, -but is not canonical (it is chosen noncomputably). -/ -noncomputable def signTypeWeights {E : Type*} [AddCommGroup E] [Module ℝ E] [FiniteDimensional ℝ E] - (q : QuadraticForm ℝ E) : Fin (finrank ℝ E) → SignType := - Classical.choose (QuadraticForm.equivalent_signType_weighted_sum_squared q) - -lemma equivalent_weightedSumSquares_signTypeWeights {E : Type*} [AddCommGroup E] [Module ℝ E] - [FiniteDimensional ℝ E] (q : QuadraticForm ℝ E) : - QuadraticMap.Equivalent q - (QuadraticMap.weightedSumSquares ℝ fun i => ((signTypeWeights q i : SignType) : ℝ)) := - Classical.choose_spec (QuadraticForm.equivalent_signType_weighted_sum_squared q) - -/-- A *chosen* signature `(pos, neg, zero)` of a real quadratic form, defined using -`signTypeWeights`. - -This is **not** asserted to be canonical/invariant in this file; it is primarily a bridge to -Mathlib's existence result (Sylvester diagonalization). -/ -noncomputable def signatureChoice {E : Type*} [AddCommGroup E] [Module ℝ E] [FiniteDimensional ℝ E] - (q : QuadraticForm ℝ E) : Signature := by - let w := signTypeWeights q - refine ⟨ - (univ.filter fun i => w i = SignType.pos).card, - (univ.filter fun i => w i = SignType.neg).card, - (univ.filter fun i => w i = 0).card⟩ - -/-! -We will define canonical indices (`posDim`, `negDim`, `zeroDim`, `signature`) below using `posIndex`, -and we keep `signatureChoice` only as auxiliary data. --/ - -lemma signatureChoice_le_finrank {E : Type*} [AddCommGroup E] [Module ℝ E] [FiniteDimensional ℝ E] - (q : QuadraticForm ℝ E) : - (signatureChoice q).neg ≤ finrank ℝ E ∧ (signatureChoice q).pos ≤ finrank ℝ E ∧ - (signatureChoice q).zero ≤ finrank ℝ E := by - constructor - · simp [signatureChoice, signTypeWeights] - simpa using (Finset.card_filter_le (s := (Finset.univ : Finset (Fin (finrank ℝ E)))) - (p := fun i => signTypeWeights q i = SignType.neg)) - constructor - · simp [signatureChoice, signTypeWeights] - simpa using (Finset.card_filter_le (s := (Finset.univ : Finset (Fin (finrank ℝ E)))) - (p := fun i => signTypeWeights q i = SignType.pos)) - · simp [signatureChoice, signTypeWeights] - simpa using (Finset.card_filter_le (s := (Finset.univ : Finset (Fin (finrank ℝ E)))) - (p := fun i => signTypeWeights q i = 0)) - /-- For a standard basis vector in a weighted sum of squares, only one term in the sum is nonzero. -/ lemma QuadraticMap.weightedSumSquares_basis_vector {E : Type*} [AddCommGroup E] [Module ℝ E] {weights : Fin (finrank ℝ E) → ℝ} {i : Fin (finrank ℝ E)} (v : Fin (finrank ℝ E) → ℝ) @@ -189,56 +141,63 @@ variable {V : Type*} [AddCommGroup V] [Module ℝ V] [FiniteDimensional ℝ V] def IsPosDefOn (Q : QuadraticForm ℝ V) (W : Submodule ℝ V) : Prop := (Q.comp W.subtype).PosDef -/-- The finset of candidate dimensions of positive-definite submodules of a quadratic form. -/ -private noncomputable def posIndexCandidates (Q : QuadraticForm ℝ V) : Finset ℕ := by - classical - exact (Finset.range (finrank ℝ V + 1)).filter fun k => - ∃ W : Submodule ℝ V, finrank ℝ W = k ∧ IsPosDefOn (V := V) Q W +/-- Predicate asserting that a real quadratic form admits a positive definite submodule of +dimension `k`. -/ +def PosIndexPred (Q : QuadraticForm ℝ V) (k : ℕ) : Prop := + ∃ W : Submodule ℝ V, finrank ℝ W = k ∧ IsPosDefOn (V := V) Q W -omit [FiniteDimensional ℝ V] in -private lemma posIndexCandidates_nonempty (Q : QuadraticForm ℝ V) : - (posIndexCandidates (V := V) Q).Nonempty := by +private lemma posIndexPred_zero (Q : QuadraticForm ℝ V) : PosIndexPred (V := V) Q 0 := by classical - refine ⟨0, ?_⟩ - refine Finset.mem_filter.2 ?_ - refine ⟨by simp, ?_⟩ refine ⟨(⊥ : Submodule ℝ V), by simp, ?_⟩ intro x hx exfalso exact hx (Subsingleton.elim x 0) +private lemma posIndexPred_le_finrank {Q : QuadraticForm ℝ V} {k : ℕ} + (hk : PosIndexPred (V := V) Q k) : k ≤ finrank ℝ V := by + rcases hk with ⟨W, hW, -⟩ + have hk_le : finrank ℝ W ≤ finrank ℝ V := + Submodule.finrank_le (R := ℝ) (M := V) W + simpa [hW] using hk_le + /-- The positive index of a real quadratic form: the maximal dimension of a subspace on which the form is positive definite. -/ -noncomputable def posIndex (Q : QuadraticForm ℝ V) : ℕ := by - classical - exact (posIndexCandidates (V := V) Q).max' (posIndexCandidates_nonempty (V := V) Q) +noncomputable def posIndex (Q : QuadraticForm ℝ V) : ℕ := + sSup {k : ℕ | PosIndexPred (V := V) Q k} + +private lemma posIndex_nonempty (Q : QuadraticForm ℝ V) : + ({k : ℕ | PosIndexPred (V := V) Q k} : Set ℕ).Nonempty := + ⟨0, posIndexPred_zero (V := V) Q⟩ + +private lemma posIndex_bddAbove (Q : QuadraticForm ℝ V) : + BddAbove ({k : ℕ | PosIndexPred (V := V) Q k} : Set ℕ) := by + refine ⟨finrank ℝ V, ?_⟩ + intro k hk + exact posIndexPred_le_finrank (V := V) (Q := Q) hk -omit [FiniteDimensional ℝ V] in lemma posIndex_spec (Q : QuadraticForm ℝ V) : ∃ W : Submodule ℝ V, finrank ℝ W = posIndex (V := V) Q ∧ IsPosDefOn (V := V) Q W := by classical + -- `posIndex` is the supremum of a bounded, nonempty set of attainable dimensions, + -- hence it is itself attainable. have hmem : - posIndex (V := V) Q ∈ posIndexCandidates (V := V) Q := by + posIndex (V := V) Q ∈ ({k : ℕ | PosIndexPred (V := V) Q k} : Set ℕ) := by + -- `Nat.sSup_mem` gives membership of `sSup` for nonempty bounded sets. simpa [posIndex] using - (Finset.max'_mem _ (posIndexCandidates_nonempty (V := V) Q)) - rcases (Finset.mem_filter.1 hmem).2 with ⟨W, hW, hWpos⟩ + (Nat.sSup_mem (s := ({k : ℕ | PosIndexPred (V := V) Q k} : Set ℕ)) + (posIndex_nonempty (V := V) Q) (posIndex_bddAbove (V := V) Q)) + rcases hmem with ⟨W, hW, hWpos⟩ exact ⟨W, hW, hWpos⟩ lemma le_posIndex_of_exists {Q : QuadraticForm ℝ V} {k : ℕ} (hk : ∃ W : Submodule ℝ V, finrank ℝ W = k ∧ IsPosDefOn (V := V) Q W) : k ≤ posIndex (V := V) Q := by - classical - have hk_mem : k ∈ posIndexCandidates (V := V) Q := by - refine Finset.mem_filter.2 ?_ - refine ⟨?_, hk⟩ - rcases hk with ⟨W, hW, -⟩ - have hk_le : k ≤ finrank ℝ V := by - simpa [hW] using (Submodule.finrank_le (R := ℝ) (M := V) W) - simpa [posIndexCandidates] using (Finset.mem_range.2 (Nat.lt_succ_of_le hk_le)) - simpa [posIndex] using Finset.le_max' (posIndexCandidates (V := V) Q) k hk_mem + have hb : BddAbove ({k : ℕ | PosIndexPred (V := V) Q k} : Set ℕ) := + posIndex_bddAbove (V := V) Q + -- `le_csSup` for `ℕ` requires boundedness (and membership gives nonemptiness). + simpa [posIndex, PosIndexPred] using (le_csSup hb hk) /- If `Q` and `Q₂` are equivalent, then `posIndex Q ≤ posIndex Q₂`. -/ -omit [FiniteDimensional ℝ V] in lemma posIndex_le_of_equivalent {V₂ : Type*} [AddCommGroup V₂] [Module ℝ V₂] [FiniteDimensional ℝ V₂] {Q : QuadraticForm ℝ V} {Q₂ : QuadraticForm ℝ V₂} (h : Q.Equivalent Q₂) : @@ -298,9 +257,15 @@ variable {E : Type*} [AddCommGroup E] [Module ℝ E] [FiniteDimensional ℝ E] lemma posIndex_le_finrank (Q : QuadraticForm ℝ E) : posIndex (V := E) Q ≤ finrank ℝ E := by - rcases posIndex_spec (Q := Q) with ⟨W, hW, -⟩ - have : finrank ℝ W ≤ finrank ℝ E := Submodule.finrank_le (R := ℝ) (M := E) W - simpa [hW] using this + classical + let s : Set ℕ := {k : ℕ | PosIndexPred (V := E) Q k} + have hsne : s.Nonempty := ⟨0, posIndexPred_zero (V := E) Q⟩ + refine (csSup_le hsne) ?_ + intro k hk + rcases hk with ⟨W, hW, -⟩ + have hk_le : finrank ℝ W ≤ finrank ℝ E := + Submodule.finrank_le (R := ℝ) (M := E) W + simpa [s, hW] using hk_le /-- The positive index of inertia of a real quadratic form (canonical). -/ noncomputable def posDim (Q : QuadraticForm ℝ E) : ℕ := @@ -673,19 +638,16 @@ lemma finrank_supportedOnPos (w : Fin n → SignType) : classical have h : finrank ℝ ({i // i ∈ posSet (n := n) w} → ℝ) = (posSet (n := n) w).card := by - -- Write the index type as the finset `posSet w` itself to avoid rewriting the predicate. change finrank ℝ ((posSet (n := n) w) → ℝ) = (posSet (n := n) w).card calc finrank ℝ ((posSet (n := n) w) → ℝ) = Fintype.card (posSet (n := n) w) := by simp [Module.finrank_fintype_fun_eq_card] _ = (posSet (n := n) w).card := by simp - -- Convert the `LinearEquiv` finrank equality using the computation of the codomain finrank. simpa [h] using (LinearEquiv.finrank_eq (supportedOnPosEquiv (n := n) w)) -theorem posIndex_diag_signType (w : Fin n → SignType) : +private theorem posIndex_diag_signType (w : Fin n → SignType) : posIndex (V := Fin n → ℝ) (diagForm (n := n) w) = (posSet (n := n) w).card := by - -- lower bound: exhibit the supported-on-positive submodule have h_lower : (posSet (n := n) w).card ≤ posIndex (V := Fin n → ℝ) (diagForm (n := n) w) := by have hk : @@ -743,43 +705,37 @@ theorem posIndex_weightedSumSquares_signType (w : Fin n → SignType) : posIndex (V := Fin n → ℝ) (QuadraticMap.weightedSumSquares ℝ fun i : Fin n => (w i : ℝ)) = (Finset.univ.filter fun i : Fin n => w i = SignType.pos).card := by - simpa [diagForm, posSet] using (posIndex_diag_signType (n := n) w) + simpa [diagForm, posSet, signSet] using (posIndex_diag_signType (n := n) w) -theorem posIndex_diagForm (w : Fin n → SignType) : - posIndex (V := Fin n → ℝ) (diagForm (n := n) w) = (posSet (n := n) w).card := by - simpa [diagForm, posSet, signSet] using (posIndex_weightedSumSquares_signType (n := n) w) +private theorem posDim_diagForm (w : Fin n → SignType) : + QuadraticForm.posDim (E := Fin n → ℝ) (diagForm (n := n) w) = (posSet (n := n) w).card := by + simp [QuadraticForm.posDim, posIndex_diag_signType (n := n) w] theorem posDim_weightedSumSquares_signType (w : Fin n → SignType) : QuadraticForm.posDim (E := Fin n → ℝ) ((QuadraticMap.weightedSumSquares ℝ fun i : Fin n => (w i : ℝ)) : QuadraticForm ℝ (Fin n → ℝ)) = (Finset.univ.filter fun i : Fin n => w i = SignType.pos).card := by - simp [QuadraticForm.posDim, posIndex_weightedSumSquares_signType (n := n) w] - -theorem posDim_diagForm (w : Fin n → SignType) : - QuadraticForm.posDim (E := Fin n → ℝ) (diagForm (n := n) w) = (posSet (n := n) w).card := by - simp [QuadraticForm.posDim, posIndex_diagForm (n := n) w] + simpa [diagForm, posSet, signSet] using (posDim_diagForm (n := n) w) -theorem negDim_weightedSumSquares_signType (w : Fin n → SignType) : - QuadraticForm.negDim (E := Fin n → ℝ) - ((QuadraticMap.weightedSumSquares ℝ fun i : Fin n => (w i : ℝ)) : - QuadraticForm ℝ (Fin n → ℝ)) = - (Finset.univ.filter fun i : Fin n => w i = SignType.neg).card := by +private theorem negDim_diagForm (w : Fin n → SignType) : + QuadraticForm.negDim (E := Fin n → ℝ) (diagForm (n := n) w) = (negSet (n := n) w).card := by classical - set Q : QuadraticForm ℝ (Fin n → ℝ) := - (QuadraticMap.weightedSumSquares ℝ fun i : Fin n => (w i : ℝ)) + set Q : QuadraticForm ℝ (Fin n → ℝ) := diagForm (n := n) w have hneg : -Q = diagForm (n := n) (fun i => -w i) := by ext v simp [Q, diagForm, QuadraticMap.weightedSumSquares_apply] have hpos : posIndex (V := Fin n → ℝ) (-Q) = (posSet (n := n) (fun i => -w i)).card := by simpa [hneg] using (posIndex_diag_signType (n := n) (w := fun i => -w i)) -- `negDim Q = posIndex (-Q)` and `posSet (-w) = negSet w`. - simp [QuadraticForm.negDim, Q, hpos, posSet_neg (n := n) w, negSet, signSet] + simp [Q, QuadraticForm.negDim, hpos, posSet_neg (n := n) w, negSet, signSet] -theorem negDim_diagForm (w : Fin n → SignType) : - QuadraticForm.negDim (E := Fin n → ℝ) (diagForm (n := n) w) = (negSet (n := n) w).card := by - -- This is exactly the filter-card theorem, restated via `negSet`. - simpa [diagForm, negSet, signSet] using (negDim_weightedSumSquares_signType (n := n) w) +theorem negDim_weightedSumSquares_signType (w : Fin n → SignType) : + QuadraticForm.negDim (E := Fin n → ℝ) + ((QuadraticMap.weightedSumSquares ℝ fun i : Fin n => (w i : ℝ)) : + QuadraticForm ℝ (Fin n → ℝ)) = + (Finset.univ.filter fun i : Fin n => w i = SignType.neg).card := by + simpa [diagForm, negSet, signSet] using (negDim_diagForm (n := n) w) lemma card_posSet_add_card_negSet_add_card_zeroSet (w : Fin n → SignType) : (posSet (n := n) w).card + (negSet (n := n) w).card + (zeroSet (n := n) w).card = @@ -828,14 +784,11 @@ lemma card_posSet_add_card_negSet_add_card_zeroSet (w : Fin n → SignType) : _ = Fintype.card (Fin n) := huniv simpa [A, B, C, Nat.add_assoc, Nat.add_left_comm, Nat.add_comm] using this -theorem zeroDim_weightedSumSquares_signType (w : Fin n → SignType) : - QuadraticForm.zeroDim (E := Fin n → ℝ) - ((QuadraticMap.weightedSumSquares ℝ fun i : Fin n => (w i : ℝ)) : - QuadraticForm ℝ (Fin n → ℝ)) = - (Finset.univ.filter fun i : Fin n => w i = 0).card := by +private theorem zeroDim_diagForm (w : Fin n → SignType) : + QuadraticForm.zeroDim (E := Fin n → ℝ) (diagForm (n := n) w) = (zeroSet (n := n) w).card := by have hfin : finrank ℝ (Fin n → ℝ) = Fintype.card (Fin n) := by simp - have hpos := posDim_weightedSumSquares_signType (n := n) w - have hneg := negDim_weightedSumSquares_signType (n := n) w + have hpos := posDim_diagForm (n := n) w + have hneg := negDim_diagForm (n := n) w have hsum := card_posSet_add_card_negSet_add_card_zeroSet (n := n) w let A : ℕ := (posSet (n := n) w).card let B : ℕ := (negSet (n := n) w).card @@ -851,98 +804,26 @@ theorem zeroDim_weightedSumSquares_signType (w : Fin n → SignType) : _ = (B + C) - B := by simpa using congrArg (fun t => t - B) h1 _ = C := by simp - set Q : QuadraticForm ℝ (Fin n → ℝ) := - (QuadraticMap.weightedSumSquares ℝ fun i : Fin n => (w i : ℝ)) + set Q : QuadraticForm ℝ (Fin n → ℝ) := diagForm (n := n) w have hposI : posIndex (V := Fin n → ℝ) Q = A := by - simpa [Q, A, posSet, signSet] using (posIndex_weightedSumSquares_signType (n := n) w) + simpa [Q, A] using (posIndex_diag_signType (n := n) w) have hnegI : posIndex (V := Fin n → ℝ) (-Q) = B := by - -- `negDim Q = posIndex (-Q)` and `negDim Q = card (negSet w)`. - simpa [QuadraticForm.negDim, Q, B, negSet, signSet] using hneg + simpa [Q, B, QuadraticForm.negDim] using hneg have : QuadraticForm.zeroDim (E := Fin n → ℝ) Q = C := by dsimp [QuadraticForm.zeroDim, QuadraticForm.posDim, QuadraticForm.negDim] rw [hfin, hposI, hnegI] simpa using hz0 - simpa [C, zeroSet, signSet, Q] using this + simpa [Q, C] using this -theorem zeroDim_diagForm (w : Fin n → SignType) : - QuadraticForm.zeroDim (E := Fin n → ℝ) (diagForm (n := n) w) = (zeroSet (n := n) w).card := by - simpa [diagForm, zeroSet, signSet] using (zeroDim_weightedSumSquares_signType (n := n) w) +theorem zeroDim_weightedSumSquares_signType (w : Fin n → SignType) : + QuadraticForm.zeroDim (E := Fin n → ℝ) + ((QuadraticMap.weightedSumSquares ℝ fun i : Fin n => (w i : ℝ)) : + QuadraticForm ℝ (Fin n → ℝ)) = + (Finset.univ.filter fun i : Fin n => w i = 0).card := by + simpa [diagForm, zeroSet, signSet] using (zeroDim_diagForm (n := n) w) end end Diagonal -/-! -### Canonical signature equals Sylvester choice - -Using the diagonal computation for `weightedSumSquares` and the Sylvester diagonalization existence -theorem, we show that the canonical signature defined via `posIndex` agrees with the previously -defined `signatureChoice`. In particular, `signatureChoice` becomes an invariant of the quadratic -form (independent of the chosen diagonalization witness). --/ - -section SylvesterBridge - -variable {E : Type*} [AddCommGroup E] [Module ℝ E] [FiniteDimensional ℝ E] - -theorem posDim_eq_signatureChoice_pos (q : QuadraticForm ℝ E) : - q.posDim = (signatureChoice q).pos := by - let w : Fin (finrank ℝ E) → SignType := signTypeWeights q - let Qd : QuadraticForm ℝ (Fin (finrank ℝ E) → ℝ) := - (QuadraticMap.weightedSumSquares ℝ fun i : Fin (finrank ℝ E) => (w i : ℝ)) - have heq : q.Equivalent Qd := by - simpa [Qd, w, signTypeWeights] using (equivalent_weightedSumSquares_signTypeWeights q) - have hpos : q.posDim = Qd.posDim := - posDim_eq_of_equivalent (E := E) (E₂ := Fin (finrank ℝ E) → ℝ) heq - have hposd : - Qd.posDim = (Finset.univ.filter fun i : Fin (finrank ℝ E) => w i = SignType.pos).card := by - simpa [Qd] using (posDim_weightedSumSquares_signType (n := finrank ℝ E) w) - simpa [signatureChoice, w, signTypeWeights] using hpos.trans hposd - -theorem negDim_eq_signatureChoice_neg (q : QuadraticForm ℝ E) : - q.negDim = (signatureChoice q).neg := by - let w : Fin (finrank ℝ E) → SignType := signTypeWeights q - let Qd : QuadraticForm ℝ (Fin (finrank ℝ E) → ℝ) := - (QuadraticMap.weightedSumSquares ℝ fun i : Fin (finrank ℝ E) => (w i : ℝ)) - have heq : q.Equivalent Qd := by - simpa [Qd, w, signTypeWeights] using (equivalent_weightedSumSquares_signTypeWeights q) - have hneg : q.negDim = Qd.negDim := - negDim_eq_of_equivalent (E := E) (E₂ := Fin (finrank ℝ E) → ℝ) heq - have hnegd : - Qd.negDim = (Finset.univ.filter fun i : Fin (finrank ℝ E) => w i = SignType.neg).card := by - simpa [Qd] using (negDim_weightedSumSquares_signType (n := finrank ℝ E) w) - simpa [signatureChoice, w, signTypeWeights] using hneg.trans hnegd - -theorem zeroDim_eq_signatureChoice_zero (q : QuadraticForm ℝ E) : - q.zeroDim = (signatureChoice q).zero := by - let w : Fin (finrank ℝ E) → SignType := signTypeWeights q - let Qd : QuadraticForm ℝ (Fin (finrank ℝ E) → ℝ) := - (QuadraticMap.weightedSumSquares ℝ fun i : Fin (finrank ℝ E) => (w i : ℝ)) - have heq : q.Equivalent Qd := by - simpa [Qd, w, signTypeWeights] using (equivalent_weightedSumSquares_signTypeWeights q) - have hzero : q.zeroDim = Qd.zeroDim := - zeroDim_eq_of_equivalent (E := E) (E₂ := Fin (finrank ℝ E) → ℝ) heq - have hzerod : - Qd.zeroDim = (Finset.univ.filter fun i : Fin (finrank ℝ E) => w i = 0).card := by - simpa [Qd] using (zeroDim_weightedSumSquares_signType (n := finrank ℝ E) w) - simpa [signatureChoice, w, signTypeWeights] using hzero.trans hzerod - -theorem signature_eq_signatureChoice (q : QuadraticForm ℝ E) : - q.signature = signatureChoice q := by - ext - · simpa using posDim_eq_signatureChoice_pos (E := E) q - · simpa using negDim_eq_signatureChoice_neg (E := E) q - · simpa using zeroDim_eq_signatureChoice_zero (E := E) q - -theorem signatureChoice_eq_of_equivalent {E₂ : Type*} [AddCommGroup E₂] [Module ℝ E₂] - [FiniteDimensional ℝ E₂] {Q : QuadraticForm ℝ E} {Q₂ : QuadraticForm ℝ E₂} - (h : Q.Equivalent Q₂) : - signatureChoice Q = signatureChoice Q₂ := by - calc - signatureChoice Q = Q.signature := (signature_eq_signatureChoice (E := E) Q).symm - _ = Q₂.signature := signature_eq_of_equivalent (E := E) (E₂ := E₂) h - _ = signatureChoice Q₂ := signature_eq_signatureChoice (E := E₂) Q₂ - -end SylvesterBridge - end QuadraticForm diff --git a/PhysLean/Mathematics/Geometry/Metric/QuadraticForm/Sylvester.lean b/PhysLean/Mathematics/Geometry/Metric/QuadraticForm/Sylvester.lean new file mode 100644 index 000000000..c6e097537 --- /dev/null +++ b/PhysLean/Mathematics/Geometry/Metric/QuadraticForm/Sylvester.lean @@ -0,0 +1,155 @@ +/- +Copyright (c) 2026 Matteo Cipollina. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Matteo Cipollina +-/ + +import PhysLean.Mathematics.Geometry.Metric.QuadraticForm.NegDim + +/-! +# Noncanonical Sylvester choices for real quadratic forms + +This file provides auxiliary, **noncanonical** data extracted from the existence theorem +`QuadraticForm.equivalent_signType_weighted_sum_squared` in +`Mathlib.LinearAlgebra.QuadraticForm.Real`. + +The idea is that `equivalent_signType_weighted_sum_squared` produces an existential witness +of diagonalization; here we choose such a witness noncomputably. + +These definitions are intended for *bridging* with diagonal computations. They are not meant to be +the primary API: the canonical inertia indices are defined in +`PhysLean.Mathematics.Geometry.Metric.QuadraticForm.NegDim` (via `QuadraticForm.posIndex`) and are +invariant under `QuadraticForm.Equivalent` without any diagonalization choice. + +## Main definitions + +- `QuadraticForm.signTypeWeights`: a chosen Sylvester diagonalization weight function +- `QuadraticForm.signatureChoice`: the resulting chosen signature `(pos, neg, zero)` + +## Main results + +- `QuadraticForm.signature_eq_signatureChoice`: the canonical signature equals the chosen one + +## Tags + +quadratic form, Sylvester law, signature +-/ + +namespace QuadraticForm + +open Finset Module QuadraticMap SignType +open scoped BigOperators + +/-- A choice of `SignType`-weights in Sylvester's diagonalization of a quadratic form. + +This is auxiliary data: it exists by `QuadraticForm.equivalent_signType_weighted_sum_squared`, +but is not canonical (it is chosen noncomputably). -/ +noncomputable def signTypeWeights {E : Type*} [AddCommGroup E] [Module ℝ E] [FiniteDimensional ℝ E] + (q : QuadraticForm ℝ E) : Fin (finrank ℝ E) → SignType := + Classical.choose (QuadraticForm.equivalent_signType_weighted_sum_squared q) + +lemma equivalent_weightedSumSquares_signTypeWeights {E : Type*} [AddCommGroup E] [Module ℝ E] + [FiniteDimensional ℝ E] (q : QuadraticForm ℝ E) : + QuadraticMap.Equivalent q + (QuadraticMap.weightedSumSquares ℝ fun i => ((signTypeWeights q i : SignType) : ℝ)) := + Classical.choose_spec (QuadraticForm.equivalent_signType_weighted_sum_squared q) + +/-- A *chosen* signature `(pos, neg, zero)` of a real quadratic form, defined using +`signTypeWeights`. -/ +noncomputable def signatureChoice {E : Type*} [AddCommGroup E] [Module ℝ E] [FiniteDimensional ℝ E] + (q : QuadraticForm ℝ E) : Signature := by + let w := signTypeWeights q + refine ⟨ + (univ.filter fun i => w i = SignType.pos).card, + (univ.filter fun i => w i = SignType.neg).card, + (univ.filter fun i => w i = 0).card⟩ + +lemma signatureChoice_le_finrank {E : Type*} [AddCommGroup E] [Module ℝ E] [FiniteDimensional ℝ E] + (q : QuadraticForm ℝ E) : + (signatureChoice q).neg ≤ finrank ℝ E ∧ (signatureChoice q).pos ≤ finrank ℝ E ∧ + (signatureChoice q).zero ≤ finrank ℝ E := by + constructor + · simp [signatureChoice, signTypeWeights] + simpa using (Finset.card_filter_le (s := (Finset.univ : Finset (Fin (finrank ℝ E)))) + (p := fun i => signTypeWeights q i = SignType.neg)) + constructor + · simp [signatureChoice, signTypeWeights] + simpa using (Finset.card_filter_le (s := (Finset.univ : Finset (Fin (finrank ℝ E)))) + (p := fun i => signTypeWeights q i = SignType.pos)) + · simp [signatureChoice, signTypeWeights] + simpa using (Finset.card_filter_le (s := (Finset.univ : Finset (Fin (finrank ℝ E)))) + (p := fun i => signTypeWeights q i = 0)) + +/-! +### Canonical signature equals Sylvester choice + +Using the diagonal computation for `weightedSumSquares` and the Sylvester diagonalization existence +theorem, we show that the canonical signature defined via `posIndex` agrees with the chosen +`signatureChoice`. +-/ + +section SylvesterBridge + +variable {E : Type*} [AddCommGroup E] [Module ℝ E] [FiniteDimensional ℝ E] + +theorem posDim_eq_signatureChoice_pos (q : QuadraticForm ℝ E) : + q.posDim = (signatureChoice q).pos := by + let w : Fin (finrank ℝ E) → SignType := signTypeWeights q + let Qd : QuadraticForm ℝ (Fin (finrank ℝ E) → ℝ) := + (QuadraticMap.weightedSumSquares ℝ fun i : Fin (finrank ℝ E) => (w i : ℝ)) + have heq : q.Equivalent Qd := by + simpa [Qd, w, signTypeWeights] using (equivalent_weightedSumSquares_signTypeWeights q) + have hpos : q.posDim = Qd.posDim := + posDim_eq_of_equivalent (E := E) (E₂ := Fin (finrank ℝ E) → ℝ) heq + have hposd : + Qd.posDim = (Finset.univ.filter fun i : Fin (finrank ℝ E) => w i = SignType.pos).card := by + simpa [Qd] using (posDim_weightedSumSquares_signType (n := finrank ℝ E) w) + simpa [signatureChoice, w, signTypeWeights] using hpos.trans hposd + +theorem negDim_eq_signatureChoice_neg (q : QuadraticForm ℝ E) : + q.negDim = (signatureChoice q).neg := by + let w : Fin (finrank ℝ E) → SignType := signTypeWeights q + let Qd : QuadraticForm ℝ (Fin (finrank ℝ E) → ℝ) := + (QuadraticMap.weightedSumSquares ℝ fun i : Fin (finrank ℝ E) => (w i : ℝ)) + have heq : q.Equivalent Qd := by + simpa [Qd, w, signTypeWeights] using (equivalent_weightedSumSquares_signTypeWeights q) + have hneg : q.negDim = Qd.negDim := + negDim_eq_of_equivalent (E := E) (E₂ := Fin (finrank ℝ E) → ℝ) heq + have hnegd : + Qd.negDim = (Finset.univ.filter fun i : Fin (finrank ℝ E) => w i = SignType.neg).card := by + simpa [Qd] using (negDim_weightedSumSquares_signType (n := finrank ℝ E) w) + simpa [signatureChoice, w, signTypeWeights] using hneg.trans hnegd + +theorem zeroDim_eq_signatureChoice_zero (q : QuadraticForm ℝ E) : + q.zeroDim = (signatureChoice q).zero := by + let w : Fin (finrank ℝ E) → SignType := signTypeWeights q + let Qd : QuadraticForm ℝ (Fin (finrank ℝ E) → ℝ) := + (QuadraticMap.weightedSumSquares ℝ fun i : Fin (finrank ℝ E) => (w i : ℝ)) + have heq : q.Equivalent Qd := by + simpa [Qd, w, signTypeWeights] using (equivalent_weightedSumSquares_signTypeWeights q) + have hzero : q.zeroDim = Qd.zeroDim := + zeroDim_eq_of_equivalent (E := E) (E₂ := Fin (finrank ℝ E) → ℝ) heq + have hzerod : + Qd.zeroDim = (Finset.univ.filter fun i : Fin (finrank ℝ E) => w i = 0).card := by + simpa [Qd] using (zeroDim_weightedSumSquares_signType (n := finrank ℝ E) w) + simpa [signatureChoice, w, signTypeWeights] using hzero.trans hzerod + +theorem signature_eq_signatureChoice (q : QuadraticForm ℝ E) : + q.signature = signatureChoice q := by + ext + · simpa using posDim_eq_signatureChoice_pos (E := E) q + · simpa using negDim_eq_signatureChoice_neg (E := E) q + · simpa using zeroDim_eq_signatureChoice_zero (E := E) q + +theorem signatureChoice_eq_of_equivalent {E₂ : Type*} [AddCommGroup E₂] [Module ℝ E₂] + [FiniteDimensional ℝ E₂] {Q : QuadraticForm ℝ E} {Q₂ : QuadraticForm ℝ E₂} + (h : Q.Equivalent Q₂) : + signatureChoice Q = signatureChoice Q₂ := by + calc + signatureChoice Q = Q.signature := (signature_eq_signatureChoice (E := E) Q).symm + _ = Q₂.signature := signature_eq_of_equivalent (E := E) (E₂ := E₂) h + _ = signatureChoice Q₂ := signature_eq_signatureChoice (E := E₂) Q₂ + +end SylvesterBridge + +end QuadraticForm diff --git a/PhysLean/Mathematics/Geometry/Metric/Riemannian/Defs.lean b/PhysLean/Mathematics/Geometry/Metric/Riemannian/Defs.lean index 0b4c6c82a..da51dc27b 100644 --- a/PhysLean/Mathematics/Geometry/Metric/Riemannian/Defs.lean +++ b/PhysLean/Mathematics/Geometry/Metric/Riemannian/Defs.lean @@ -31,14 +31,14 @@ section variable {E : Type v} [NormedAddCommGroup E] [NormedSpace ℝ E] variable {H : Type w} [TopologicalSpace H] -variable {M : Type w} [TopologicalSpace M] [ChartedSpace H M] +variable {M : Type*} [TopologicalSpace M] [ChartedSpace H M] variable {I : ModelWithCorners ℝ E H} {n : WithTop ℕ∞} -variable [IsManifold I 1 M] [IsManifold I (n + 1) M] +variable [IsManifold I (n + 1) M] [IsManifold I 1 M] /-- A `C^n` Riemannian metric on `M`, packaged using Mathlib's modern bundle API. -/ abbrev RiemannianMetric - (I : ModelWithCorners ℝ E H) (n : WithTop ℕ∞) (M : Type w) - [TopologicalSpace M] [ChartedSpace H M] [IsManifold I 1 M] [IsManifold I (n + 1) M] := + (I : ModelWithCorners ℝ E H) (n : WithTop ℕ∞) (M : Type*) + [TopologicalSpace M] [ChartedSpace H M] [IsManifold I (n + 1) M] [IsManifold I 1 M] := Bundle.ContMDiffRiemannianMetric (IB := I) (n := n) (F := E) (E := fun x : M ↦ TangentSpace I x) namespace RiemannianMetric From 9283f59e1741ceb4a04fb71e58cb2f0f255dff04 Mon Sep 17 00:00:00 2001 From: Matteo CIpollina Date: Tue, 3 Mar 2026 11:57:35 +0100 Subject: [PATCH 19/36] fix --- MathlibDocumentationStyle.md | 186 -------- MathlibNamingConventions.md | 367 -------------- MathlibStyle.md | 450 ------------------ .../Metric/PseudoRiemannian/Defs.lean | 1 - 4 files changed, 1004 deletions(-) delete mode 100644 MathlibDocumentationStyle.md delete mode 100644 MathlibNamingConventions.md delete mode 100644 MathlibStyle.md diff --git a/MathlibDocumentationStyle.md b/MathlibDocumentationStyle.md deleted file mode 100644 index 5272d8981..000000000 --- a/MathlibDocumentationStyle.md +++ /dev/null @@ -1,186 +0,0 @@ -Documentation style -All pull requests must meet the following documentation standards. See the doc-gen repo for information about the automatically generated doc pages. - -You can preview the markdown processing of a GitHub page or pull request using the Lean doc preview page. - -Header comment -Each mathlib file should start with: - -a header comment with copyright information (see the recommendations in our style guidelines); -the list of imports (one on each line); -a module docstring containing general documentation, written using Markdown and LaTeX. -(See the example below.) - -Headers use atx-style headers (with hash signs, no underlying dash). The open and close delimiters /-! and -/ should appear on their own lines. - -The mandatory title of the file is a first level header. It is followed by a summary of the content of the file. - -The other sections, with second level headers are (in this order): - -Main definitions (optional, can be covered in the summary) -Main statements (optional, can be covered in the summary) -Notation (omitted only if no notation is introduced in this file) -Implementation notes (description of important design decisions or interface features, including use of type classes and simp canonical form for new definitions) -References (references to textbooks or papers, or Wikipedia pages) -Tags (a list of keywords that could be useful when doing text search in mathlib to find where something is covered) -References should refer to bibtex entries in the mathlib citations file. See the Citing other works section below. - -The following code block is an example of a file header. - -/- -Copyright (c) 2018 Robert Y. Lewis. All rights reserved. -Released under Apache 2.0 license as described in the file LICENSE. -Authors: Robert Y. Lewis --/ -module - -public import Mathlib.Algebra.Order.AbsoluteValue.Basic -public import Mathlib.NumberTheory.Padics.PadicVal.Basic - -/-! -# p-adic norm - -This file defines the `p`-adic norm on `ℚ`. - -The `p`-adic valuation on `ℚ` is the difference of the multiplicities of `p` in the numerator and -denominator of `q`. This function obeys the standard properties of a valuation, with the appropriate -assumptions on `p`. - -The valuation induces a norm on `ℚ`. This norm is a nonarchimedean absolute value. -It takes values in {0} ∪ {1/p^k | k ∈ ℤ}. - -## Implementation notes - -Much, but not all, of this file assumes that `p` is prime. This assumption is inferred automatically -by taking `[Fact p.Prime]` as a type class argument. - -## References - -* [F. Q. Gouvêa, *p-adic numbers*][gouvea1997] -* [R. Y. Lewis, *A formal proof of Hensel's lemma over the p-adic integers*][lewis2019] -* - -## Tags - -p-adic, p adic, padic, norm, valuation --/ -Doc strings -Every definition and major theorem is required to have a doc string. (Doc strings on lemmas are also encouraged, particularly if the lemma has any mathematical content or might be useful in another file.) These are introduced using /-- and closed by -/ above the definition, with either newlines or single spaces between the markers and the text. Subsequent lines in a doc-string should not be indented. They can contain Markdown and LaTeX as well: see the next section. If a doc string is a complete sentence, then it should end in a period. Named theorems, such as the mean value theorem should be bold faced (i.e., with two asterisks before and after). - -Doc strings should convey the mathematical meaning of the definition. They are allowed to lie slightly about the actual implementation. The following is a doc string example: - -/-- If `q ≠ 0`, the `p`-adic norm of a rational `q` is `p ^ (-padicValRat p q)`. -If `q = 0`, the `p`-adic norm of `q` is `0`. -/ -def padicNorm (p : ℕ) (q : ℚ) : ℚ := - if q = 0 then 0 else (p : ℚ) ^ (-padicValRat p q) -An example that is slightly lying but still describes the mathematical content would be: - -/-- `padicValRat` defines the valuation of a rational `q` to be the valuation of `q.num` minus the -valuation of `q.den`. If `q = 0` or `p = 1`, then `padicValRat p q` defaults to `0`. -/ -def padicValRat (p : ℕ) (q : ℚ) : ℤ := - padicValInt p q.num - padicValNat p q.den -Tactic documentation -Tactics should have a docstring that matches the Lean documentation style guide. Key points are: Be complete and self-contained but brief. The docstring should start with a full sentence that has the tactic as the subject. (Example: "rewrite [e] uses the expression e as a rewrite rule on the main goal.") All different options and forms of the tactic should appear in a bulleted list. The "Examples:" section, if any, should be a sequence of code blocks. - -Linting -The docBlame linter lists all definitions that do not have doc strings. The docBlameThm linter will lists theorems and lemmas that do not have doc strings. The tacticDocs linter lists all tactics that do not have docstrings. - -To run only the docBlame linter, add the following to the end of your lean file: - -#lint only docBlame -To run only the docBlame and docBlameThm linters, add the following to the end of your lean file: - -#lint only docBlame docBlameThm -To run the all default linters, including docBlame and tacticDocs, add the following to the end of your lean file: - -#lint -To run the all default linters, including docBlame and run docBlameThm, add the following to the end of your lean file: - -#lint docBlameThm -LaTeX and Markdown -We generally put references to Lean declarations or variables in between backticks. Writing the fully-qualified name (e.g. finset.card_pos instead of just card_pos) will turn the name into a link on our online docs. - -Raw URLs should be enclosed in angle brackets <...> to ensure that they will be clickable online. (Some URLs, especially those with parentheses or other special symbols, may not be parsed correctly by the markdown renderer.) - -When talking about mathematical symbols instead, it may be preferable to use LaTeX. LaTeX can be included in doc strings in three ways: - -using single dollar signs $ ... $ to render math inline, -using double dollar signs $$ ... $$ to render math in "display mode", or -using environments \begin{*} ... \end{*} (without dollar signs). -These correspond to the MathJax settings of our online docs. The interaction between the Markdown and LaTeX there is similar to that on https://math.stackexchange.com and https://mathoverflow.net, so you can paste a doc string into an editing sandbox there to preview the final result. See also the math.stackexchange MathJax tutorial. - -Sectioning comments -It is common to structure a file in sections, where each section contains related declarations. By describing the sections with module documentation /-! ... -/ at the beginning, these sections can be seen in the documentation. - -While these sectioning comments will often correspond to section or namespace commands, this is not required. You can use sectioning comments inside of a section or namespace, and you can have multiple sections or namespaces following one sectioning comment. - -Sectioning comments are for display and readability only. They have no semantic meaning. - -Third-level headers ### should be used for titles inside sectioning comments. - -If the comment is more than one line long, the delimiters /-! and -/ should appear on their own lines. - -See Lean/Expr/Basic.lean for an example in practice. - -namespace BinderInfo - -/-! ### Declarations about `BinderInfo` -/ - -/-- The brackets corresponding to a given `BinderInfo`. -/ -def brackets : BinderInfo → String × String - | BinderInfo.implicit => ("{", "}") - | BinderInfo.strictImplicit => ("{{", "}}") - | BinderInfo.instImplicit => ("[", "]") - | _ => ("(", ")") - -end BinderInfo - -namespace Name - -/-! ### Declarations about `name` -/ - -/-- Find the largest prefix `n` of a `Name` such that `f n != none`, then replace this prefix -with the value of `f n`. -/ -def mapPrefix (f : Name → Option Name) (n : Name) : Name := Id.run do - if let some n' := f n then return n' - match n with - | anonymous => anonymous - | str n' s => mkStr (mapPrefix f n') s - | num n' i => mkNum (mapPrefix f n') i -Theories documentation -In addition to documentation living in Lean files, we have theories documentation where we give overviews spanning several Lean files, and more mathematical explanations in cases where formalization requires slightly exotic points of view, see for instance the topology documentation. - -Citing other works -To cite papers and books in doc strings, the references should first be added to the BibTeX file: docs/references.bib. To normalize the file with bibtool, you can run: - -bibtool --preserve.key.case=on --preserve.keys=on --print.use.tab=off --pass.comments=on -s -i docs/references.bib -o docs/references.bib -To ensure that your citations become links in the online docs, you can use either of the following two styles: - -First, you may enclose the citation key used in docs/references.bib in square brackets: - -The proof can be found in [Boole1854]. -In the online docs, this will become something like: - -The proof can be found in [Boo54] - -(The key will change into an alpha style label and become a link to the References page of the docs.) - -Alternatively, you can use custom text for the citation by putting text in square brackets ahead of the citation key: - -See [Grundlagen der Geometrie][hilbert1999] for an alternative axiomatization. -See Grundlagen der Geometrie for an alternative axiomatization. - -Note that you currently cannot use the closing square bracket ] symbol in the link text. So the following will not result in a working link: - -We follow [Euclid's *Elements* [Prop. 1]][heath1956a]. -We follow [Euclid's Elements [Prop. 1]][heath1956a]. - -Language -Documentation should be written in English. Any common spelling (e.g. British, American or Australian English) is acceptable. Pull requests should not be made that only change which of these spellings are used, but it is acceptable to change the spelling as part of a PR that substantially enhances the documentation. Contrast this with the rule for declaration names, which should use American English spelling. - -Examples -The following files are maintained as examples of good documentation style: - -Mathlib.NumberTheory.Padics.PadicNorm -Mathlib.Topology.Basic -Analysis.Calculus.ContDiff.Basic diff --git a/MathlibNamingConventions.md b/MathlibNamingConventions.md deleted file mode 100644 index 6292c4b2b..000000000 --- a/MathlibNamingConventions.md +++ /dev/null @@ -1,367 +0,0 @@ -Mathlib naming conventions -This guide is written for Lean 4. - -File names -.lean files in mathlib should generally be named in UpperCamelCase. A (very rare) exception are files named after some specifically lower-cased object, e.g. lp.lean for a file specifically about the space (and not ). Such exceptions should be discussed on Zulip first. - -General conventions -Capitalization -Unlike Lean 3, in which the convention was that all declarations used snake_case, in mathlib under Lean 4 we use a combination of snake_case, lowerCamelCase and UpperCamelCase according to the following naming scheme. - -Terms of Props (e.g. proofs, theorem names) use snake_case. -Props and Types (or Sort) (inductive types, structures, classes) are in UpperCamelCase. There are some rare exceptions: some fields of structures are currently wrongly lower-cased (see the example for the class LT below). -Functions are named the same way as their return values (e.g. a function of type A → B → C is named as though it is a term of type C). -All other terms of Types (basically anything else) are in lowerCamelCase. -When something named with UpperCamelCase is part of something named with snake_case, it is referenced in lowerCamelCase. -Acronyms like LE are written upper-/lowercase as a group, depending on what the first character would be. -Rules 1-6 apply to fields of a structure or constructors of an inductive type in the same way. -There are some rare exceptions to preserve local naming symmetry: e.g., we use Ne rather than NE to follow the example of Eq; outParam has a Sort output but is not UpperCamelCase. Some other exceptions include intervals (Set.Icc, Set.Iic, etc.), where the I is capitalized despite the fact that it should be lowerCamelCase according to the convention. Any such exceptions should be discussed on Zulip. - -Examples --- follows rule 2 -structure OneHom (M : Type _) (N : Type _) [One M] [One N] where - toFun : M → N -- follows rule 4 via rule 3 and rule 7 - map_one' : toFun 1 = 1 -- follows rule 1 via rule 7 - --- follows rule 2 via rule 3 -class CoeIsOneHom [One M] [One N] : Prop where - coe_one : (↑(1 : M) : N) = 1 -- follows rule 1 via rule 6 - --- follows rule 1 via rule 3 -theorem map_one [OneHomClass F M N] (f : F) : f 1 = 1 := sorry - --- follows rules 1 and 5 -theorem MonoidHom.toOneHom_injective [MulOneClass M] [MulOneClass N] : - Function.Injective (MonoidHom.toOneHom : (M →* N) → OneHom M N) := sorry --- manual align is needed due to `lowerCamelCase` with several words inside `snake_case` -#align monoid_hom.to_one_hom_injective MonoidHom.toOneHom_injective - --- follows rule 2 -class HPow (α : Type u) (β : Type v) (γ : Type w) where - hPow : α → β → γ -- follows rule 3 via rule 6; note that rule 5 does not apply - --- follows rules 2 and 6 -class LT (α : Type u) where - lt : α → α → Prop -- this is an exception to rule 2 - --- follows rules 2 (for `Semifield`) and 4 (for `toIsField`) -theorem Semifield.toIsField (R : Type u) [Semifield R] : - IsField R -- follows rule 2 - --- follows rules 1 and 6 -theorem gt_iff_lt [LT α] {a b : α} : a > b ↔ b < a := sorry - --- follows rule 2; `Ne` is an exception to rule 6 -class NeZero : Prop := sorry - --- follows rules 1 and 5 -theorem neZero_iff {R : Type _} [Zero R] {n : R} : NeZero n ↔ n ≠ 0 := sorry --- manual align is needed due to `lowerCamelCase` with several words inside `snake_case` -#align ne_zero_iff neZero_iff -Spelling -Declaration names use American English spelling. So e.g. we use factorization, Localization and FiberBundle and not factorisation, Localisation or FibreBundle. Contrast this with the rule for documentation, which is allowed to use other common English spellings. - -Names of symbols -When translating the statements of theorems into words, the following dictionary is often used. - -Logic -symbol shortcut name notes -∨ \or or -∧ \and and -→ \r of / imp the conclusion is stated first and the hypotheses are often omitted -↔ \iff iff sometimes omitted along with the right hand side of the iff -¬ \n not -∃ \ex exists / bex bex stands for "bounded exists" -∀ \fo all / forall / ball ball stands for "bounded forall" -= eq often omitted -≠ \ne ne -∘ \o comp -ball and bex are still used in Lean core, but should not be used in mathlib. - -Set -symbol shortcut name notes -∈ \in mem -∉ \notin notMem -∪ \cup union -∩ \cap inter -⋃ \bigcup iUnion / biUnion i for "indexed", bi for "bounded indexed" -⋂ \bigcap iInter / biInter i for "indexed", bi for "bounded indexed" -⋃₀ \bigcup\0 sUnion s for "set" -⋂₀ \bigcap\0 sInter s for "set" -\ \\ sdiff -ᶜ \^c compl -{x | p x} setOf -{x} singleton -{x, y} pair -Algebra -symbol shortcut name notes -0 zero -+ add -- neg / sub neg for the unary function, sub for the binary function -1 one -* mul -^ pow -/ div -• \bu smul -⁻¹ \-1 inv -⅟ \frac1 invOf -∣ \| dvd -∑ \sum sum -∏ \prod prod -Lattices -symbol shortcut name notes -< lt / gt -≤ \le le / ge -⊔ \sup sup a binary operator -⊓ \inf inf a binary operator -⨆ \supr iSup / biSup / ciSup c for "conditionally complete" -⨅ \infi iInf / biInf / ciInf c for "conditionally complete" -⊥ \bot bot -⊤ \top top -The symbols ≤ and < have a special naming convention. In mathlib, we almost always use ≤ and < instead of ≥ and >, so we can use both le/lt and ge/gt for naming ≤ and <. There are a few reasons to use ge/gt: - -We use ge/gt if the arguments to ≤ or < appear in different orders. We use le/lt for the first occurrence of ≤/< in the theorem name, and then ge/gt indicates that the arguments are swapped. -We use ge/gt to match the argument order of another relation, such as = or ≠. -We use ge/gt to describe the ≤ or < relation with its arguments swapped. -We use ge/gt if the second argument to ≤ or < is 'more variable'. --- follows rule 1 -theorem lt_iff_le_not_ge [Preorder α] {a b : α} : a < b ↔ a ≤ b ∧ ¬b ≤ a := sorry -theorem not_le_of_gt [Preorder α] {a b : α} (h : a < b) : ¬b ≤ a := sorry -theorem LT.lt.not_ge [Preorder α] {a b : α} (h : a < b) : ¬b ≤ a := sorry - --- follows rule 2 -theorem Eq.ge [Preorder α] {a b : α} (h : a = b) : b ≤ a := sorry -theorem ne_of_gt [Preorder α] {a b : α} (h : b < a) : a ≠ b := sorry - --- follows rule 3 -theorem ge_trans [Preorder α] {a b : α} : b ≤ a → c ≤ b → c ≤ a := sorry - --- follows rule 4 -theorem le_of_forall_gt [LinearOrder α] {a b : α} (H : ∀ (c : α), a < c → b < c) : b ≤ a := sorry -Dots -Dots are used for namespaces, and also for automatically generated names like recursors, eliminators and structure projections. They can also be introduced manually, for example, where projector notation is useful. Thus they are used in all of the following situations. - -Note: since And is a (binary function into) Prop, it is UpperCamelCased according to the naming conventions, and so its namespace is And.*. This may seem at odds with the dictionary ∧ --> and but because upper camel case types get lower camel cased when they appear in names of theorems, the dictionary is still valid in general. The same applies to Or, Iff, Not, Eq, HEq, Ne, etc. - -Intro, elim, and destruct rules for logical connectives, whether they are automatically generated or not: - -And.intro -And.elim -And.left -And.right -Or.inl -Or.inr -Or.intro_left -Or.intro_right -Iff.intro -Iff.elim -Iff.mp -Iff.mpr -Not.intro -Not.elim -Eq.refl -Eq.rec -Eq.subst -HEq.refl -HEq.rec -HEq.subst -Exists.intro -Exists.elim -True.intro -False.elim -Places where projection notation is useful, for example: - -And.symm -Or.symm -Or.resolve_left -Or.resolve_right -Eq.symm -Eq.trans -HEq.symm -HEq.trans -Iff.symm -Iff.refl -It is useful to use dot notation even for types which are not inductive types. For instance, we use: - -LE.trans -LT.trans_le -LE.trans_lt -Axiomatic descriptions -Some theorems are described using axiomatic names, rather than describing their conclusions. - -def (for unfolding a definition) -refl -irrefl -symm -trans -antisymm -asymm -congr -comm -assoc -left_comm -right_comm -mul_left_cancel -mul_right_cancel -inj (injective) -Variable conventions -u, v, w, ... for universes -α, β, γ, ... for generic types -a, b, c, ... for propositions -x, y, z, ... for elements of a generic type -h, h₁, ... for assumptions -p, q, r, ... for predicates and relations -s, t, ... for lists -s, t, ... for sets -m, n, k, ... for natural numbers -i, j, k, ... for integers -Types with a mathematical content are expressed with the usual mathematical notation, often with an upper case letter (G for a group, R for a ring, K or 𝕜 for a field, E for a vector space, ...). This convention is not followed in older files, where greek letters are used for all types. Pull requests renaming type variables in these files are welcome. - -Identifiers and theorem names -We adopt the following naming guidelines to make it easier for users to guess the name of a theorem or find it using tab completion. Common "axiomatic" properties of an operation like conjunction or disjunction are put in a namespace that begins with the name of the operation: - -import Mathlib.Logic.Basic - -#check And.comm -#check Or.comm -In particular, this includes intro and elim operations for logical connectives, and properties of relations: - -import Mathlib.Logic.Basic - -#check And.intro -#check And.elim -#check Or.intro_left -#check Or.intro_right -#check Or.elim - -#check Eq.refl -#check Eq.symm -#check Eq.trans -Note however we do not do this for axiomatic logical and arithmetic operations. - -import Mathlib.Algebra.Group.Basic - -#check and_assoc -#check mul_comm -#check mul_assoc -#check @mul_left_cancel -- multiplication is left cancelative -For the most part, however, we rely on descriptive names. Often the name of theorem simply describes the conclusion: - -import Mathlib.Algebra.Ring.Basic -open Nat -#check succ_ne_zero -#check mul_zero -#check mul_one -#check @sub_add_eq_add_sub -#check @le_iff_lt_or_eq -If only a prefix of the description is enough to convey the meaning, the name may be made even shorter: - -import Mathlib.Algebra.Ring.Basic - -#check @neg_neg -#check Nat.pred_succ -When an operation is written as infix, the theorem names follow suit. For example, we write neg_mul_neg rather than mul_neg_neg to describe the pattern -a * -b. - -Sometimes, to disambiguate the name of theorem or better convey the intended reference, it is necessary to describe some of the hypotheses. The word "of" is used to separate these hypotheses: - -import Mathlib.Algebra.Order.Monoid.Lemmas - -open Nat - -#check lt_of_succ_le -#check lt_of_not_ge -#check lt_of_le_of_ne -#check add_lt_add_of_lt_of_le -The hypotheses are listed in the order they appear, not reverse order. For example, the theorem A → B → C would be named C_of_A_of_B. - -Sometimes abbreviations or alternative descriptions are easier to work with. For example, we use pos, neg, nonpos, nonneg rather than zero_lt, lt_zero, le_zero, and zero_le. - -import Mathlib.Algebra.Order.Monoid.Lemmas -import Mathlib.Algebra.Order.Ring.Lemmas - -open Nat - -#check mul_pos -#check mul_nonpos_of_nonneg_of_nonpos -#check add_lt_of_lt_of_nonpos -#check add_lt_of_nonpos_of_lt -These conventions are not perfect. They cannot distinguish compound expressions up to associativity, or repeated occurrences in a pattern. For that, we make do as best we can. For example, a + b - b = a could be named either add_sub_self or add_sub_cancel. - -Sometimes the word "left" or "right" is helpful to describe variants of a theorem. - -import Mathlib.Algebra.Order.Monoid.Lemmas -import Mathlib.Algebra.Order.Ring.Lemmas - -open Nat - -#check add_le_add_left -#check add_le_add_right -#check le_of_mul_le_mul_left -#check le_of_mul_le_mul_right -When referring to a namespaced definition in a lemma name not in the same namespace, the definition should have its namespace removed. If the definition name is unambiguous without its namespace, it can be used as is. Else, the namespace is prepended back to it in lowerCamelCase. This is to ensure that _-separated strings in a lemma name correspond to a definition name or connective. - -import Mathlib.Data.Int.Cast.Basic -import Mathlib.Data.Nat.Cast.Basic -import Mathlib.Topology.Constructions - -#check Prod.fst -#check continuous_fst - -#check Nat.cast -#check map_natCast -#check Int.cast_natCast -Naming of structural lemmas -We are trying to standardize certain naming patterns for structural lemmas. - -Extensionality -A lemma of the form (∀ x, f x = g x) → f = g should be named .ext, and labelled with the @[ext] attribute. Often this type of lemma can be generated automatically by putting the @[ext] attribute on a structure. (However an automatically generated lemma will always be written in terms of the structure projections, and often there is a better statement, e.g. using coercions, that should be written by hand then marked with @[ext].) - -A lemma of the form f = g ↔ ∀ x, f x = g x should be named .ext_iff. - -Injectivity -Where possible, injectivity lemmas should be written in terms of an Function.Injective f conclusion which use the full word injective, typically as f_injective. The form injective_f still appears often in mathlib. - -In addition to these, a variant should usually be provided as a bidirectional implication, e.g. as f x = f y ↔ x = y, which can be obtained from Function.Injective.eq_iff. Such lemmas should be named f_inj (although if they are in an appropriate namespace .inj is good too). Bidirectional injectivity lemmas are often good candidates for @[simp]. There are still many unidirectional implications named inj in mathlib, and it is reasonable to update and replace these as you come across them. - -Note however that constructors for inductive types have automatically generated unidirectional implications, named .inj, and there is no intention to change this. When such an automatically generated lemma already exists, and a bidirectional lemma is needed, it may be named .inj_iff. - -An injectivity lemma that uses "left" or "right" should refer to the argument that "changes". For example, a lemma with the statement a - b = a - c ↔ b = c could be called sub_right_inj. - -Induction and recursion principles -Induction/recursion principles are ways to construct data or proofs for all elements of some type T, by providing ways to construct this data or proof in more constrained specific contexts. These principles should be phrased to accept a motive argument, which declares what property we are proving or what data we are constructing for all T. When the motive eliminates into Prop, it is an induction principle, and the name should contain induction. On the other hand, when the motive eliminates into Sort u or Type u, it is a recursive principle, and the name should contain rec instead. - -Additionally, the name should contain on iff in the argument order, the value comes before the constructions. - -The following table summarizes these naming conventions: - -motive eliminates into: Prop Sort u or Type u -value first T.induction_on T.recOn -constructions first T.induction T.rec -Variation on these names are acceptable when necessary (e.g. for disambiguation). - -Predicates as suffixes -Most predicates should be added as prefixes. Eg IsClosed (Icc a b) should be called isClosed_Icc, not Icc_isClosed. - -Some widely used predicates don't follow this rule. Those are the predicates that are analogous to an atom already suffixed by the naming convention. Here is a non-exhaustive list: - -We use _inj for f a = f b ↔ a = b, so we also use _injective for Injective f, _surjective for Surjective f, _bijective for Bijective f... -We use _mono for a ≤ b → f a ≤ f b and _anti for a ≤ b → f b ≤ f a, so we also use _monotone for Monotone f, _antitone for Antitone f, _strictMono for StrictMono f, _strictAnti for StrictAnti f, etc... -Predicates as suffixes can be preceded by either _left or _right to signify that a binary operation is left- or right-monotone. For example, mul_left_monotone : Monotone (· * a) proves left-monotonicity of multiplication and not monotonicity of left-multiplication. - -Prop-valued classes -Mathlib has many Prop-valued classes and other definitions. For example "let be a topological ring" is written variable (R : Type*) [Ring R] [TopologicalSpace R] [IsTopologicalRing R] and "let be a group and let be a normal subgroup" is written variable (G : Type*) [Group G] (H : Subgroup G) [Normal H]. Here IsTopologicalRing R and Normal H are not extra data, but are extra assumptions on data we have already. - -Mathlib currently strives towards the following naming convention for these Prop-valued classes. If the class is a noun then its name should begin with Is. If however is it an adjective then its name does not need to begin with an Is. So for example IsNormal would be acceptable for the "normal subgroup" typeclass, but Normal is also fine; we might say "assume the subgroup H is normal" in informal language. However IsTopologicalRing is preferred for the "topological ring" typeclass, as we do not say "assume the ring R is topological" informally. - -Unexpanded and expanded forms of functions -The multiplication of two functions f and g can be denoted equivalently as f * g or fun x ↦ f x * g x. These expressions are definitionally equal, but not syntactically (and they don't share the same key in indexing trees), which means that tools like rw, fun_prop or apply? will not use a theorem with one form on an expression with the other form. Therefore, it is sometimes convenient to have variants of the statements using the two forms. If one needs to distinguish between them, statements involving the first unexpanded form are written using just mul, while statements using the second expanded form should instead use fun_mul. If there is no need to disambiguate because a lemma is given using only the expanded form, the prefix fun_ is not required. - -For instance, the fact that the multiplication of two continuous functions is continuous is - -theorem Continuous.fun_mul (hf : Continuous f) (hg : Continuous g) : Continuous fun x ↦ f x * g x -and - -theorem Continuous.mul (hf : Continuous f) (hg : Continuous g) : Continuous (f * g) -Both theorems deserve tagging with the fun_prop attribute. - -The same goes for addition, subtraction, negation, powers and compositions of functions. diff --git a/MathlibStyle.md b/MathlibStyle.md deleted file mode 100644 index b5840a5d5..000000000 --- a/MathlibStyle.md +++ /dev/null @@ -1,450 +0,0 @@ -Library Style Guidelines -In addition to the naming conventions, files in the Lean library generally adhere to the following guidelines and conventions. Having a uniform style makes it easier to browse the library and read the contents, but these are meant to be guidelines rather than rigid rules. - -Variable conventions -u, v, w, ... for universes -α, β, γ, ... for generic types -a, b, c, ... for propositions -x, y, z, ... for elements of a generic type -h, h₁, ... for assumptions -p, q, r, ... for predicates and relations -s, t, ... for lists -s, t, ... for sets -m, n, k, ... for natural numbers -i, j, k, ... for integers -Types with a mathematical content are expressed with the usual mathematical notation, often with an upper case letter (G for a group, R for a ring, K or 𝕜 for a field, E for a vector space, ...). This convention is not followed in older files, where greek letters are used for all types. Pull requests renaming type variables in these files are welcome. - -Line length -Lines should not be longer than 100 characters. This makes files easier to read, especially on a small screen or in a small window. If you are editing with VS Code, there is a visual marker which will indicate a 100 character limit. - -Header and imports -The file header should contain copyright information, a list of all the authors who have made significant contributions to the file, and a description of the contents. Do all imports right after the header, without a line break, on separate lines. - -/- -Copyright (c) 2024 Joe Cool. All rights reserved. -Released under Apache 2.0 license as described in the file LICENSE. -Authors: Joe Cool --/ -import Mathlib.Data.Nat.Basic -import Mathlib.Algebra.Group.Defs -(Tip: If you're editing mathlib in VS Code, you can write copy and then press TAB to generate a skeleton of the copyright header.) - -Regarding the list of authors: use Authors even when there is only a single author. Don't end the line with a period, and use commas (, ) to separate all author names (so don't use and between the penultimate and final author.) We don't have strict rules on what contributions qualify for inclusion there. The general idea is that the people listed there should be the ones we would reach out to if we had questions about the design or development of the Lean code. - -Module docstrings -After the copyright header and the imports, please add a module docstring (delimited with /-! and -/) containing - -a title of the file, -a summary of the contents (the main definitions and theorems, proof techniques, etc…) -notation that has been used in the file (if any) -references to the literature (if any) -In total, the module docstring should look something like this: - -/-! -# Foos and bars - -In this file we introduce `foo` and `bar`, -two main concepts in the theory of xyzzyology. - -## Main results - -- `exists_foo`: the main existence theorem of `foo`s. -- `bar_of_foo_of_baz`: a construction of a `bar`, given a `foo` and a `baz`. - If this doc-string is longer than one line, subsequent lines should be indented by two spaces - (as required by markdown syntax). -- `bar_eq` : the main classification theorem of `bar`s. - -## Notation - - - `|_|` : The barrification operator, see `bar_of_foo`. - -## References - -See [Thales600BC] for the original account on Xyzzyology. --/ -New bibliography entries should be added to docs/references.bib. - -See our documentation requirements for more suggestions and examples. - -Structuring definitions and theorems -All declarations (e.g., def, lemma, theorem, class, structure, inductive, instance, etc.) and commands (e.g., variable, open, section, namespace, notation, etc.) are considered top-level and these words should appear flush-left in the document. In particular, opening a namespace or section does not result in indenting the contents of that namespace or section. (Note: within VS Code, hovering over any declaration such as def Foo ... will show the fully qualified name, like MyNamespace Foo if Foo is declared while the namespace MyNamespace is open.) - -These guidelines hold for declarations starting with def, lemma and theorem. For "theorem statement", also read "type of a definition" and for "proof" also read "definition body". - -Use spaces on both sides of ":", ":=" or infix operators. Put them before a line break rather than at the beginning of the next line. - -In what follows, "indent" without an explicit indication of the amount means "indent by 2 additional spaces". - -After stating the theorem, we indent the lines in the subsequent proof by 2 spaces. - -open Nat -theorem nat_case {P : Nat → Prop} (n : Nat) (H1 : P 0) (H2 : ∀ m, P (succ m)) : P n := - Nat.recOn n H1 (fun m IH ↦ H2 m) -If the theorem statement requires multiple lines, indent the subsequent lines by 4 spaces. The proof is still indented only 2 spaces (not 6 = 4 + 2). When providing a proof in tactic mode, the by is placed on the line prior to the first tactic; however, by should not be placed on a line by itself. In practice this means you will often see := by at the end of a theorem statement. - -import Mathlib.Data.Nat.Basic - -theorem le_induction {P : Nat → Prop} {m} - (h0 : P m) (h1 : ∀ n, m ≤ n → P n → P (n + 1)) : - ∀ n, m ≤ n → P n := by - apply Nat.le.rec - · exact h0 - · exact h1 _ - -def decreasingInduction {P : ℕ → Sort*} (h : ∀ n, P (n + 1) → P n) {m n : ℕ} (mn : m ≤ n) - (hP : P n) : P m := - Nat.leRecOn mn (fun {k} ih hsk => ih <| h k hsk) (fun h => h) hP -When a proof term takes multiple arguments, it is sometimes clearer, and often necessary, to put some of the arguments on subsequent lines. In that case, indent each argument. This rule, i.e., indent an additional two spaces, applies more generally whenever a term spans multiple lines. - -open Nat -axiom zero_or_succ (n : Nat) : n = zero ∨ n = succ (pred n) -theorem nat_discriminate {B : Prop} {n : Nat} (H1: n = 0 → B) (H2 : ∀ m, n = succ m → B) : B := - Or.elim (zero_or_succ n) - (fun H3 : n = zero ↦ H1 H3) - (fun H3 : n = succ (pred n) ↦ H2 (pred n) H3) -Don't orphan parentheses; keep them with their arguments. - -Here is a longer example. - -import Mathlib.Init.Data.List.Lemmas - -open List -variable {T : Type} - -theorem mem_split {x : T} {l : List T} : x ∈ l → ∃ s t : List T, l = s ++ (x :: t) := - List.recOn l - (fun H : x ∈ [] ↦ False.elim ((mem_nil_iff _).mp H)) - (fun y l ↦ - fun IH : x ∈ l → ∃ s t : List T, l = s ++ (x :: t) ↦ - fun H : x ∈ y :: l ↦ - Or.elim (eq_or_mem_of_mem_cons H) - (fun H1 : x = y ↦ - Exists.intro [] (Exists.intro l (by rw [H1]; rfl))) - (fun H1 : x ∈ l ↦ - let ⟨s, (H2 : ∃ t : List T, l = s ++ (x :: t))⟩ := IH H1 - let ⟨t, (H3 : l = s ++ (x :: t))⟩ := H2 - have H4 : y :: l = (y :: s) ++ (x :: t) := by rw [H3]; rfl - Exists.intro (y :: s) (Exists.intro t H4))) -A short declaration can be written on a single line: - -open Nat -theorem succ_pos : ∀ n : Nat, 0 < succ n := zero_lt_succ - -def square (x : Nat) : Nat := x * x -A have can be put on a single line when the justification is short. - -example (n k : Nat) (h : n < k) : ... := - have h1 : n ≠ k := ne_of_lt h - ... -When the justification is too long, you should put it on the next line, indented by an additional two spaces. - -example (n k : Nat) (h : n < k) : ... := - have h1 : n ≠ k := - ne_of_lt h - ... -When the justification of the have uses tactic mode, the by should be placed on the same line, regardless of whether the justification spans multiple lines. - -example (n k : Nat) (h : n < k) : ... := - have h1 : n ≠ k := by apply ne_of_lt; exact h - ... - -example (n k : Nat) (h : n < k) : ... := - have h1 : n ≠ k := by - apply ne_of_lt - exact h - ... -When the arguments themselves are long enough to require line breaks, use an additional indent for every line after the first, as in the following example: - -import Mathlib.Data.Nat.Basic - -theorem Nat.add_right_inj {n m k : Nat} : n + m = n + k → m = k := - Nat.recOn n - (fun H : 0 + m = 0 + k ↦ calc - m = 0 + m := Eq.symm (zero_add m) - _ = 0 + k := H - _ = k := zero_add _) - (fun (n : Nat) (IH : n + m = n + k → m = k) (H : succ n + m = succ n + k) ↦ - have H2 : succ (n + m) = succ (n + k) := calc - succ (n + m) = succ n + m := Eq.symm (succ_add n m) - _ = succ n + k := H - _ = succ (n + k) := succ_add n k - have H3 : n + m = n + k := succ.inj H2 - IH H3) -In a class or structure definition, fields are indented 2 spaces, and moreover each field should have a docstring, as in: - -structure PrincipalSeg {α β : Type*} (r : α → α → Prop) (s : β → β → Prop) extends r ↪r s where - /-- The supremum of the principal segment -/ - top : β - /-- The image of the order embedding is the set of elements `b` such that `s b top` -/ - down' : ∀ b, s b top ↔ ∃ a, toRelEmbedding a = b - -class Module (R : Type u) (M : Type v) [Semiring R] [AddCommMonoid M] extends - DistribMulAction R M where - /-- Scalar multiplication distributes over addition from the right. -/ - protected add_smul : ∀ (r s : R) (x : M), (r + s) • x = r • x + s • x - /-- Scalar multiplication by zero gives zero. -/ - protected zero_smul : ∀ x : M, (0 : R) • x = 0 -When using a constructor taking several arguments in a definition, arguments line up, as in: - -theorem Ordinal.sub_eq_zero_iff_le {a b : Ordinal} : a - b = 0 ↔ a ≤ b := - ⟨fun h => by simpa only [h, add_zero] using le_add_sub a b, - fun h => by rwa [← Ordinal.le_zero, sub_le, add_zero]⟩ -Instances -When providing terms of structures or instances of classes, the where syntax should be used to avoid the need for enclosing braces, as in: - -instance instOrderBot : OrderBot ℕ where - bot := 0 - bot_le := Nat.zero_le -If there is already an instance instBot, then one can write - -instance instOrderBot : OrderBot ℕ where - __ := instBot - bot_le := Nat.zero_le -Hypotheses Left of Colon -Generally, having arguments to the left of the colon is preferred over having arguments in universal quantifiers or implications, if the proof starts by introducing these variables. For instance: - -example (n : ℝ) (h : 1 < n) : 0 < n := by linarith -is preferred over - -example (n : ℝ) : 1 < n → 0 < n := fun h ↦ by linarith -and - -example (n : ℕ) : 0 ≤ n := Nat.zero_le n -is preferred over - -example : ∀ (n : ℕ), 0 ≤ n := Nat.zero_le -Note that pattern-matching does not count as the proof starting by introducing variables. For example, the following is a valid use case of having a hypothesis right of the column: - -lemma zero_le : ∀ n : ℕ, 0 ≤ n - | 0 => le_rfl - | n + 1 => add_nonneg (zero_le n) zero_le_one -Binders -Use a space after binders: - -example : ∀ α : Type, ∀ x : α, ∃ y, y = x := - fun (α : Type) (x : α) ↦ Exists.intro x rfl -Anonymous functions -Lean has several nice syntax options for declaring anonymous functions. For very simple functions, one can use the centered dot as the function argument, as in (· ^ 2) to represent the squaring function. However, sometimes it is necessary to refer to the arguments by name (e.g., if they appear in multiple places in the function body). The Lean default for this is fun x => x * x, but the ↦ arrow (inserted with \mapsto) is also valid. In mathlib the pretty printer displays ↦, and we slightly prefer this in the source as well. The lambda notation λ x ↦ x * x, while syntactically valid, is disallowed in mathlib in favor of the fun keyword. - -Calculations -There is some flexibility in how you write calculational proofs, although there are some rules enforced by the syntax requirements of calc itself. However, there are some general guidelines. - -As with by, the calc keyword should be placed on the line prior to the start of the calculation, with the calculation indented. Whichever relations are involved (e.g., = or ≤) should be aligned from one line to the next. The underscores _ used as placeholders for terms indicating the continuation of the calculation should be left-justified. - -As for the justifications, it is not necessary to align the := symbols, but it can be nice if the expressions are short enough. The terms on either side of the first relation can either go on one line or separate lines, which may be decided by the size of the expressions. - -An example of adequate style which can more easily accommodate longer expressions is: - -import Init.Data.List.Basic - -open List - -theorem reverse_reverse : ∀ (l : List α), reverse (reverse l) = l - | [] => rfl - | a :: l => calc - reverse (reverse (a :: l)) - = reverse (reverse l ++ [a]) := by rw [reverse_cons] - _ = reverse [a] ++ reverse (reverse l) := reverse_append _ _ - _ = reverse [a] ++ l := by rw [reverse_reverse l] - _ = a :: l := rfl -The following style has the substantial advantage of having all lines be interchangeable, which is particularly useful when editing the proof in eg VSCode: - -import Init.Data.List.Basic - -open List - -theorem reverse_reverse : ∀ (l : List α), reverse (reverse l) = l - | [] => rfl - | a :: l => calc - reverse (reverse (a :: l)) - _ = reverse (reverse l ++ [a]) := by rw [reverse_cons] - _ = reverse [a] ++ reverse (reverse l) := reverse_append _ _ - _ = reverse [a] ++ l := by rw [reverse_reverse l] - _ = a :: l := rfl -If the expressions and proofs are relatively short, the following style is also an option: - -import Init.Data.List.Basic - -open List - -theorem reverse_reverse : ∀ (l : List α), reverse (reverse l) = l - | [] => rfl - | a :: l => calc - reverse (reverse (a :: l)) = reverse (reverse l ++ [a]) := by rw [reverse_cons] - _ = reverse [a] ++ reverse (reverse l) := reverse_append _ _ - _ = reverse [a] ++ l := by rw [reverse_reverse l] - _ = a :: l := rfl -Tactic mode -As we have already mentioned, when opening a tactic block, by is placed at the end of the line preceding the start of the tactic block, but not on its own line. Everything within the tactic block is indented, as in: - -theorem continuous_uncurry_of_discreteTopology [DiscreteTopology α] {f : α → β → γ} - (hf : ∀ a, Continuous (f a)) : Continuous (uncurry f) := by - apply continuous_iff_continuousAt.2 - rintro ⟨a, x⟩ - change map _ _ ≤ _ - rw [nhds_prod_eq, nhds_discrete, Filter.map_pure_prod] - exact (hf a).continuousAt -One can mix term mode and tactic mode, as in: - -theorem Units.isUnit_units_mul {M : Type*} [Monoid M] (u : Mˣ) (a : M) : - IsUnit (↑u * a) ↔ IsUnit a := - Iff.intro - (fun ⟨v, hv⟩ => by - have : IsUnit (↑u⁻¹ * (↑u * a)) := by exists u⁻¹ * v; rw [← hv, Units.val_mul] - rwa [← mul_assoc, Units.inv_mul, one_mul] at this) - u.isUnit.mul -When new goals arise as side conditions or steps, they are indented and preceded by a focusing dot · (inserted as \.); the dot is not indented. - -import Mathlib.Algebra.Group.Basic - -theorem exists_npow_eq_one_of_zpow_eq_one' [Group G] {n : ℤ} (hn : n ≠ 0) {x : G} (h : x ^ n = 1) : - ∃ n : ℕ, 0 < n ∧ x ^ n = 1 := by - cases n - · simp only [Int.ofNat_eq_coe] at h - rw [zpow_ofNat] at h - refine ⟨_, Nat.pos_of_ne_zero fun n0 ↦ hn ?_, h⟩ - rw [n0] - rfl - · rw [zpow_negSucc, inv_eq_one] at h - refine ⟨_ + 1, Nat.succ_pos _, h⟩ -Certain tactics, such as refine, can create named subgoals which can be proven in whichever order is desired using case. This feature is also useful in aiding readability. However, it is not required to use this instead of the focusing dot (·). - -example {p q : Prop} (h₁ : p → q) (h₂ : q → p) : p ↔ q := by - refine ⟨?imp, ?converse⟩ - case converse => exact h₂ - case imp => exact h₁ -Often t0 <;> t1 is used to execute t0 and then t1 on all new goals. Either write the tactics in one line, or indent the following tactic. - - cases x <;> - simp [a, b, c, d] -For single line tactic proofs (or short tactic proofs embedded in a term), it is acceptable to use by tac1; tac2; tac3 with semicolons instead of a new line with indentation. - -In general, you should put a single tactic invocation per line, unless you are closing a goal with a proof that fits entirely on a single line. Short sequences of tactics that correspond to a single mathematical idea can also be put on a single line, separated by semicolons as in cases bla; clear h or induction n; simp or rw [foo]; simp_rw [bar], but even in these scenarios, newlines are preferred. - -example : ... := by - by_cases h : x = 0 - · rw [h]; exact hzero ha - · rw [h] - have h' : ... := H ha - simp_rw [h', hb] - ... -Very short goals can be closed right away using swap or pick_goal if needed, to avoid additional indentation in the rest of the proof. - -example : ... := by - rw [h] - swap; exact h' - ... -We generally use a blank line to separate theorems and definitions, but this can be omitted, for example, to group together a number of short definitions, or to group together a definition and notation. - -Squeezing simp calls -Unless performance is particularly poor or the proof breaks otherwise, terminal simp calls (a simp call is terminal if it closes the current goal or is only followed by flexible tactics such as ring, field_simp, aesop) should not be squeezed (replaced by the output of simp?). - -There are two main reasons for this: - -A squeezed simp call might be several lines longer than the corresponding unsqueezed one, and therefore drown the useful information of what key lemmas were added to the unsqueezed simp call to close the goal in a sea of basic simp lemmas. -A squeezed simp call refers to many lemmas by name, meaning that it will break when one such lemma gets renamed. Lemma renamings happen often enough for this to matter on a maintenance level. -Profiling for performance -When contributing to mathlib, authors should be aware of the performance impacts of their contributions. The Lean FRO maintains benchmarking infrastructure which can be accessed by commenting !bench on a PR. - -Authors should assure that their contributions do not cause significant performance regressions. In particular, if the PR touches significant components of the language like adding instances, adding simp lemmas, changing imports, or creating new definitions, then authors should benchmark their changes proactively. - -Transparency and API design -Central to Lean being a practically performant proof assistant is avoiding checking of definitional equality for very large terms. In the elaborator (the component of the language that converts syntax to terms), the notion of transparency is the main mechanism to avoid unfolding large definitions when unnecessary. Excluding opaque definitions, there are three levels of transparency: - -reducible definitions are always unfolded -semireducible definitions (the default) are usually not unfolded in main tactics like rw and simp, but can be unfolded with a little effort like explicitly calling rfl or erw. Semireducible definitions are also not unfolded during the computation of keys for storing instances in the instance cache or simp lemmas in the simp cache. -irreducible definitions are never unfolded unless the user explicitly requests it (e.g using the unfold tactic, or by using the unseal command). -def by default creates semireducible definitions and abbrev creates reducible (and @[inline]) definitions. - -When designing definitions, an author should give thought to the transparency level of definitions. Consider how exposing the underlying term of your definition will affect instance search and simplification. The default for mathlib is that definitions should be semireducible unless there is a good reason otherwise which should be clearly articulated in the PR description. This imposes overhead on contributors who will need to declare new instances of the form - -instance : Foo myDef := inferInstanceAs (Foo underlyingTermOfMyDef) -and recycle API lemmas, especially for simp use, like - -@[simp] lemma myDef_bar_eq_bizz (x : X) : myDef.bar = bizz := - underlyingTermOfMyDef_bar_eq_bizz -If the API boundary is meant to be completely sealed, using a type synonym of the form - -structure myDef where - underlying : underlyingTerm -is the library convention in place of irreducible definitions. These structure wrappers are intended for types that are equivalent to an existing type but are clearly mathematically semantically distinct, e.g. Option and WithTop. - -The kernel does not have an analogous notion of transparency so its rules for unfolding are different. There are situations where an author wants to block unfolding in the kernel as well. Mathlib provides a command irreducible_def for this. This should be used only when there is a documented necessity from profiling. - -Use of erw or rfl after tactics like simp or rw that operate at reducible transparency is an indication that there is missing API. Consider adding the necessary lemmas to the API to avoid this. - -The library has existing occurrences of definitional transparency abuse like erw and extra rfl. PRs removing these are very welcome but their PR description should clearly articulate how the removal is achieved addressing the change in the underlying terms in particular and must benchmark their changes. Please treat this an opportunity to improve the API design of the relevant components. - -Whitespace and delimiters -Lean is whitespace-sensitive, and in general we opt for a style which avoids delimiting code. For instance, when writing tactics, it is possible to write them as tac1; tac2; tac3, separated by ;, in order to override the default whitespace sensitivity. However, as mentioned above, we generally try to avoid this except in a few special cases. - -Similarly, sometimes parentheses can be avoided by judicious use of the <| operator (or its cousin |>). Note: while $ is a synonym for <|, its use in mathlib is disallowed in favor of <| for consistency as well as because of the symmetry with |>. These operators have the effect of parenthesizing everything to the right of <| (note that ( is curved the same direction as <) or to the left of |> (and ) curves the same way as >). - -A common example of the usage of |> occurs with dot notation when the term preceding the . is a function applied to some arguments. For instance, ((foo a).bar b).baz can be rewritten as foo a |>.bar b |>.baz - -A common example of the usage of <| is when the user provides a term which is a function applied to multiple arguments whose last argument is a proof in tactic mode, especially one that spans multiple lines. In that case, it is natural to use <| by ... instead of (by ...), as in: - -import Mathlib.Tactic - -example {x y : ℝ} (hxy : x ≤ y) (h : ∀ ε > 0, y - ε ≤ x) : x = y := - le_antisymm hxy <| le_of_forall_pos_le_add <| by - intro ε hε - have := h ε hε - linarith -When using the tactics rw or simp there should be a space after the left arrow ←. For instance rw [← add_comm a b] or simp [← and_or_left]. (There should also be a space between the tactic name and its arguments, as in rw [h].) This rule applies the do notation as well: do return (← f) + (← g) - -Empty lines inside declarations -Empty lines inside declarations are discouraged and there is a linter that enforces that they are not present. This helps maintaining a uniform code style throughout all of mathlib. - -You are however encouraged to add comments to your code: even a short sentence communicates much more than an empty line in the middle of a proof ever will! - -Normal forms -Some statements are equivalent. For instance, there are several equivalent ways to require that a subset s of a type is nonempty. For another example, given a : α, the corresponding element of Option α can be equivalently written as Some a or (a : Option α). In general, we try to settle on one standard form, called the normal form, and use it both in statements and conclusions of theorems. In the above examples, this would be s.Nonempty (which gives access to dot notation) and (a : Option α). Often, simp lemmas will be registered to convert the other equivalent forms to the normal form. - -There is a special case to this rule. In types with a bottom element, it is equivalent to require hlt : ⊥ < x or hne : x ≠ ⊥, and it is not clear which one would be better as a normal form since both have their pros and cons. An analogous situation occurs with hlt : x < ⊤ and hne : x ≠ ⊤ in types with a top element. Since it is very easy to convert from hlt to hne (by using hlt.ne or hlt.ne' depending on the direction we want) while the other conversion is more lengthy, we use hne in assumptions of theorems (as this is the easier assumption to check), and hlt in conclusions of theorems (as this is the more powerful result to use). A common usage of this rule is with naturals, where ⊥ = 0. - -Comments -Use module doc delimiters /-! -/ to provide section headers and separators since these get incorporated into the auto-generated docs, and use /- -/ for more technical comments (e.g. TODOs and implementation notes) or for comments in proofs. Use -- for short or in-line comments. - -Documentation strings for declarations are delimited with /-- -/. When a documentation string for a declaration spans multiple lines, do not indent subsequent lines. - -See our documentation requirements for more suggestions and examples. - -Expressions in error or trace messages -Inside all printed messages (such as, in linters, custom elaborators or other metaprogrammes), names and interpolated data should either be - -inline and surrounded by backticks (e.g., m!"{foo}must have type{bar}"), or -on their own line and indented (via e.g. indentD) -The second style produces output like the following - -Could not find model with corners for domain - src -nor codomain - tgt -of function - f -Not all of mathlib may comply with this rule yet; that is a bug (and PRs fixing this are welcome). - -Deprecation -Deleting, renaming, or changing declarations can cause downstreams projects that rely on these definitions to fail to compile. Any publicly exposed theorems and definitions that are being removed should be gracefully transitioned by keeping the old declaration with a @[deprecated] attribute. This warns downstream projects about the change and gives them the opportunity to adjust before the declarations are deleted. Renamed definitions should use a deprecated alias to the new name. Otherwise, when the deprecated definition does not have a direct replacement, the definition should be deprecated with a message, like so: - -theorem new_name : ... := ... -@[deprecated (since := "YYYY-MM-DD")] alias old_name := new_name - -@[deprecated "This theorem is deprecated in favor of using ... with ..." (since := "YYYY-MM-DD")] -theorem example_thm ... -The @[deprecated] attribute requires the deprecation date, and an alias to the new declaration or a string to explain how transition away from the old definition when a new version is no longer being provided. - -The deprecate to command and scripts/add_deprecations.sh script can help generate alias definitions. - -Deprecations for declarations with the to_additive attribute should ensure the deprecation is also properly tagged with to_additive, like so: - -@[to_additive] theorem Group_bar {G} [Group G] {a : G} : a = a := rfl - --- Two deprecations required to include the `deprecated` tag on both the additive --- and multiplicative versions -@[deprecated (since := "YYYY-MM-DD")] alias AddGroup_foo := AddGroup_bar -@[to_additive existing, deprecated (since := "YYYY-MM-DD")] alias Group_foo := Group_bar -We allow, but discourage, contributors from simultaneously renaming declarations X to Y and W to X. In this case, no deprecation attribute is required for X, but it is for W. - -Named instances do not require deprecations. Deprecated declarations can be deleted after 6 months. diff --git a/PhysLean/Mathematics/Geometry/Metric/PseudoRiemannian/Defs.lean b/PhysLean/Mathematics/Geometry/Metric/PseudoRiemannian/Defs.lean index 0a98ff5b3..6408c4b3a 100644 --- a/PhysLean/Mathematics/Geometry/Metric/PseudoRiemannian/Defs.lean +++ b/PhysLean/Mathematics/Geometry/Metric/PseudoRiemannian/Defs.lean @@ -3,7 +3,6 @@ Copyright (c) 2025 Matteo Cipollina. All rights reserved. Released under Apache 2.0 license as described in the file LICENSE. Authors: Matteo Cipollina -/ -import PhysLean.Mathematics.Geometry.Metric.PseudoRiemannian.Defs import Mathlib.Geometry.Manifold.VectorBundle.Riemannian import Mathlib.Geometry.Manifold.VectorBundle.Tangent import Mathlib.LinearAlgebra.BilinearForm.Properties From f27a67d7630f03e0873240929e97bea11e37139c Mon Sep 17 00:00:00 2001 From: Matteo CIpollina Date: Tue, 3 Mar 2026 14:35:32 +0100 Subject: [PATCH 20/36] refactor-golf --- PhysLean.lean | 4 + .../Geometry/Metric/Lorentzian/Defs.lean | 27 ++-- .../Metric/PseudoRiemannian/Defs.lean | 103 +++++---------- .../Geometry/Metric/QuadraticForm/Index.lean | 19 --- .../Geometry/Metric/QuadraticForm/NegDim.lean | 120 ++++++++---------- .../Geometry/Metric/Riemannian/Defs.lean | 49 +++---- 6 files changed, 118 insertions(+), 204 deletions(-) delete mode 100644 PhysLean/Mathematics/Geometry/Metric/QuadraticForm/Index.lean diff --git a/PhysLean.lean b/PhysLean.lean index b45cee7ae..81c5c35dc 100644 --- a/PhysLean.lean +++ b/PhysLean.lean @@ -52,6 +52,10 @@ import PhysLean.Mathematics.Distribution.PowMul import PhysLean.Mathematics.FDerivCurry import PhysLean.Mathematics.Fin import PhysLean.Mathematics.Fin.Involutions +import PhysLean.Mathematics.Geometry.Metric.Lorentzian.Defs +import PhysLean.Mathematics.Geometry.Metric.QuadraticForm.Index +import PhysLean.Mathematics.Geometry.MetricQuadraticForm.NegDim +import PhysLean.Mathematics.Geometry.MetricQuadraticForm.Sylvester import PhysLean.Mathematics.Geometry.Metric.PseudoRiemannian.Defs import PhysLean.Mathematics.Geometry.Metric.Riemannian.Defs import PhysLean.Mathematics.InnerProductSpace.Adjoint diff --git a/PhysLean/Mathematics/Geometry/Metric/Lorentzian/Defs.lean b/PhysLean/Mathematics/Geometry/Metric/Lorentzian/Defs.lean index 2145165cd..51c0533c6 100644 --- a/PhysLean/Mathematics/Geometry/Metric/Lorentzian/Defs.lean +++ b/PhysLean/Mathematics/Geometry/Metric/Lorentzian/Defs.lean @@ -31,37 +31,26 @@ variable {I : ModelWithCorners ℝ E H} {n : WithTop ℕ∞} variable [IsManifold I (n + 1) M] variable [∀ x : M, FiniteDimensional ℝ (TangentSpace I x)] -/-- A Lorentzian metric is a pseudo-Riemannian metric whose associated quadratic form has -`negDim = 1` at every point. -/ -abbrev LorentzianMetric (E : Type v) (H : Type w) (M : Type w) (n : WithTop ℕ∞) - [NormedAddCommGroup E] [NormedSpace ℝ E] - [TopologicalSpace H] [TopologicalSpace M] [ChartedSpace H M] - (I : ModelWithCorners ℝ E H) [IsManifold I (n + 1) M] - [∀ x : M, FiniteDimensional ℝ (TangentSpace I x)] := - PseudoRiemannianMetric E H M n I - /-- Typeclass asserting that a pseudo-Riemannian metric is Lorentzian (index `1`). -/ -class IsLorentzianMetric (g : LorentzianMetric (E := E) (H := H) (M := M) (I := I) (n := n)) : - Prop where +class IsLorentzianMetric (g : PseudoRiemannianMetric E H M n I) : Prop where negDim_eq_one : ∀ x : M, (g.toQuadraticForm x).negDim = 1 -namespace LorentzianMetric - -variable (g : LorentzianMetric (E := E) (H := H) (M := M) (I := I) (n := n)) +namespace IsLorentzianMetric -@[simp] lemma negDim_eq_one (x : M) [IsLorentzianMetric (g := g)] : - (g.toQuadraticForm x).negDim = 1 := - IsLorentzianMetric.negDim_eq_one (g := g) x +variable (g : PseudoRiemannianMetric E H M n I) -@[simp] lemma index_eq_one (x : M) [IsLorentzianMetric (g := g)] : +/-- For a Lorentzian metric, the index is `1` at every point. -/ +lemma index_eq_one (x : M) [IsLorentzianMetric (g := g)] : g.index x = 1 := by simpa [PseudoRiemannianMetric.index] using (IsLorentzianMetric.negDim_eq_one (g := g) x) -end LorentzianMetric +end IsLorentzianMetric end end end PseudoRiemannianMetric + +#lint diff --git a/PhysLean/Mathematics/Geometry/Metric/PseudoRiemannian/Defs.lean b/PhysLean/Mathematics/Geometry/Metric/PseudoRiemannian/Defs.lean index 6408c4b3a..413e6845e 100644 --- a/PhysLean/Mathematics/Geometry/Metric/PseudoRiemannian/Defs.lean +++ b/PhysLean/Mathematics/Geometry/Metric/PseudoRiemannian/Defs.lean @@ -68,8 +68,11 @@ variable /-- A pseudo-Riemannian structure on a family of fibers `E x`: a symmetric, nondegenerate bilinear form on each fiber, expressed as a continuous bilinear map. -/ class PseudoRiemannianBundle : Type _ where + /-- The fiberwise pseudo-inner product as a continuous bilinear map. -/ metric : ∀ x : B, E x →L[ℝ] E x →L[ℝ] ℝ + /-- Symmetry: `gₓ(v,w) = gₓ(w,v)`. -/ symm : ∀ (x : B) (v w : E x), metric x v w = metric x w v + /-- Nondegeneracy: if `gₓ(v,w)=0` for all `w`, then `v=0`. -/ nondegenerate : ∀ (x : B) (v : E x), (∀ w : E x, metric x v w = 0) → v = 0 variable [PseudoRiemannianBundle (B := B) (E := E)] @@ -312,15 +315,12 @@ structure MetricTensor nondegenerate : ∀ (x : M) (v : TangentSpace I x), (∀ w : TangentSpace I x, (val x v) w = 0) → v = 0 /-- Smoothness of the metric tensor as a smooth section of the bundle of bilinear forms. - We follow the same pattern as Mathlib's Riemannian metric API, using `TotalSpace.mk'` for the bundled map. -/ contMDiff : letI : IsManifold I 1 M := IsManifold.of_le (I := I) (M := M) (m := (1 : WithTop ℕ∞)) (n := n + 1) - (by - have h0 : (0 : WithTop ℕ∞) ≤ n := by simpa using (zero_le n) - simpa [zero_add] using (add_le_add_right h0 (1 : WithTop ℕ∞))) + (by simp) ContMDiff I (I.prod 𝓘(ℝ, E →L[ℝ] E →L[ℝ] ℝ)) n (fun x ↦ TotalSpace.mk' (E →L[ℝ] E →L[ℝ] ℝ) x (val x)) @@ -385,7 +385,6 @@ lemma toBilinForm_nondegenerate (g : MetricTensor E H M n I) (x : M) : simp_rw [toBilinForm_apply] at hv have hw : ∀ w : TangentSpace I x, (g.val x v) w = 0 := by intro w - -- symmetry gives `g(v,w)=g(w,v)=0` simpa [g.symm x v w] using hv w exact g.nondegenerate x v hw @@ -505,6 +504,7 @@ noncomputable def sharpEquiv (g : MetricTensor E H M n I) (x : M) : (TangentSpace I x →L[ℝ] ℝ) ≃L[ℝ] TangentSpace I x := (g.flatEquiv x).symm +/-- Index raising map `ω ↦ sharp ω` as a continuous linear map. -/ noncomputable def sharpL (g : MetricTensor E H M n I) (x : M) : (TangentSpace I x →L[ℝ] ℝ) →L[ℝ] TangentSpace I x := (g.sharpEquiv x).toContinuousLinearMap @@ -533,14 +533,12 @@ lemma flat_sharp_apply (g : MetricTensor E H M n I) (x : M) have h := congrArg (fun f : TangentSpace I x →L[ℝ] ℝ => f v) (flatL_apply_sharpL (g := g) x ω) simpa [flat, flatL, sharp, sharpL] using h -@[simp] lemma sharp_flat_apply (g : MetricTensor E H M n I) (x : M) (v : TangentSpace I x) : g.sharp x (g.flat x v) = v := by have h := sharpL_apply_flatL (g := g) x v simpa [sharp, sharpL, flat, flatL] using h /-- Metric evaluated at `sharp ω₁` and `sharp ω₂`. -/ -@[simp] lemma apply_sharp_sharp (g : MetricTensor E H M n I) (x : M) (ω₁ ω₂ : TangentSpace I x →L[ℝ] ℝ) : g.val x (g.sharpL x ω₁) (g.sharpL x ω₂) = ω₁ (g.sharpL x ω₂) := by @@ -675,20 +673,20 @@ theorem cotangentToQuadraticForm_equivalent_toQuadraticForm (g : MetricTensor E map_app' := fun ω => ?_ } simp [cotangentToQuadraticForm_apply, cotangentMetricVal, toQuadraticForm, sharpEquiv, sharpL] -/-! ## Pointwise index (finite-dimensional) -/ +end FiniteDimensional /-- The (negative) index of a metric tensor at a point, defined as the negative index of the associated quadratic form `v ↦ gₓ(v,v)`. This is a pointwise invariant; it need not be locally constant. -/ -noncomputable def index (g : MetricTensor E H M n I) (x : M) : ℕ := +noncomputable def index (g : MetricTensor E H M n I) (x : M) + [FiniteDimensional ℝ (TangentSpace I x)] : ℕ := (g.toQuadraticForm x).negDim -omit inst_tangent_findim in -@[simp] lemma index_def (g : MetricTensor E H M n I) (x : M) : - g.index x = (g.toQuadraticForm x).negDim := rfl - -end FiniteDimensional +@[simp] lemma index_def (g : MetricTensor E H M n I) (x : M) + [FiniteDimensional ℝ (TangentSpace I x)] : + g.index x = (g.toQuadraticForm x).negDim := + rfl end MetricTensor @@ -727,46 +725,6 @@ variable {I : ModelWithCorners ℝ E H} variable [IsManifold I (n + 1) M] variable [inst_tangent_findim : ∀ (x : M), FiniteDimensional ℝ (TangentSpace I x)] -/-! ## Predicate typeclass: pseudo-Riemannian manifolds -/ - -/-- Prop-valued predicate recording existence of a `C^n` Riemannian metric (bundle-first), without -making any noncanonical choice. -/ -class HasRiemannianMetric : Prop where - out : Nonempty (RiemannianMetric (I := I) (n := n) M) - -instance (g : RiemannianMetric (I := I) (n := n) M) : - HasRiemannianMetric (I := I) (n := n) (M := M) := - ⟨⟨g⟩⟩ - -theorem hasRiemannianMetric_iff : - HasRiemannianMetric (I := I) (n := n) (M := M) ↔ - Nonempty (RiemannianMetric (I := I) (n := n) M) := - ⟨fun h => h.out, fun h => ⟨h⟩⟩ - -theorem hasPseudoRiemannianMetric_iff_exists : - HasPseudoRiemannianMetric (E := E) (H := H) (M := M) (n := n) (I := I) ↔ - ∃ _ : PseudoRiemannianMetric E H M n I, True := - ⟨fun h => by - rcases h.out with ⟨g⟩ - exact ⟨g, trivial⟩, - fun h => by - rcases h with ⟨g, -⟩ - exact ⟨⟨g⟩⟩⟩ - -/-- Typeclass carrying a *chosen* pseudo-Riemannian metric (as opposed to the mere existence -predicate `HasPseudoRiemannianMetric`). This is the convenient form for downstream constructions. -/ -class PseudoRiemannianManifold : Type _ where - metric : PseudoRiemannianMetric E H M n I - -/-- The chosen pseudo-Riemannian metric on a manifold typeclass. -/ -abbrev pseudoRiemannianMetric [PseudoRiemannianManifold (E := E) (H := H) (M := M) (n := n) (I := I)] : - PseudoRiemannianMetric E H M n I := - (PseudoRiemannianManifold.metric (E := E) (H := H) (M := M) (n := n) (I := I)) - -instance [PseudoRiemannianManifold (E := E) (H := H) (M := M) (n := n) (I := I)] : - HasPseudoRiemannianMetric (E := E) (H := H) (M := M) (n := n) (I := I) := - ⟨⟨pseudoRiemannianMetric (E := E) (H := H) (M := M) (n := n) (I := I)⟩⟩ - /-- Given a pseudo-Riemannian metric `g` on manifold `M` and a point `x : M`, this function constructs a bilinear form on the tangent space at `x`. For tangent vectors `u v : T_x M`, the bilinear form is given by: @@ -829,13 +787,11 @@ lemma index_eq_of_mem_connectedComponent (g : PseudoRiemannianMetric E H M n I) lemma toBilinForm_isSymm (g : PseudoRiemannianMetric E H M n I) (x : M) : (toBilinForm g x).IsSymm := by - simpa [toBilinForm] using - (MetricTensor.toBilinForm_isSymm (g := g.toMetricTensor) x) + simp [toBilinForm] lemma toBilinForm_nondegenerate (g : PseudoRiemannianMetric E H M n I) (x : M) : (toBilinForm g x).Nondegenerate := by - simpa [toBilinForm] using - (MetricTensor.toBilinForm_nondegenerate (g := g.toMetricTensor) x) + simp [toBilinForm] /-- The fiberwise pairing \(g_x(v,w)\) of a pseudo-Riemannian metric. -/ abbrev inner (g : PseudoRiemannianMetric E H M n I) (x : M) (v w : TangentSpace I x) : ℝ := @@ -871,10 +827,12 @@ end PairingSmoothness /-! ## Flat / sharp / cotangent API (delegated to `MetricTensor`) -/ +/-- Index lowering map `v ↦ gₓ(v, -)` as a linear map. -/ abbrev flat (g : PseudoRiemannianMetric E H M n I) (x : M) : TangentSpace I x →ₗ[ℝ] (TangentSpace I x →L[ℝ] ℝ) := MetricTensor.flat (g := (g.toMetricTensor : MetricTensor E H M n I)) x +/-- Index lowering map `v ↦ gₓ(v, -)` as a continuous linear map. -/ abbrev flatL (g : PseudoRiemannianMetric E H M n I) (x : M) : TangentSpace I x →L[ℝ] (TangentSpace I x →L[ℝ] ℝ) := MetricTensor.flatL (g := (g.toMetricTensor : MetricTensor E H M n I)) x @@ -891,49 +849,49 @@ lemma flatL_surj (g : PseudoRiemannianMetric E H M n I) (x : M) : Function.Surjective (flatL g x) := by simpa [flatL] using (MetricTensor.flatL_surj (g := (g.toMetricTensor : MetricTensor E H M n I)) x) + /-- The `flat` musical equivalence `TₓM ≃ (TₓM)⋆`. -/ noncomputable abbrev flatEquiv (g : PseudoRiemannianMetric E H M n I) (x : M) : TangentSpace I x ≃L[ℝ] (TangentSpace I x →L[ℝ] ℝ) := MetricTensor.flatEquiv (g := (g.toMetricTensor : MetricTensor E H M n I)) x + /-- The `sharp` musical equivalence `(TₓM)⋆ ≃ TₓM`. -/ noncomputable abbrev sharpEquiv (g : PseudoRiemannianMetric E H M n I) (x : M) : (TangentSpace I x →L[ℝ] ℝ) ≃L[ℝ] TangentSpace I x := MetricTensor.sharpEquiv (g := (g.toMetricTensor : MetricTensor E H M n I)) x + /-- Index raising map `ω ↦ sharp ω` as a continuous linear map. -/ noncomputable abbrev sharpL (g : PseudoRiemannianMetric E H M n I) (x : M) : (TangentSpace I x →L[ℝ] ℝ) →L[ℝ] TangentSpace I x := MetricTensor.sharpL (g := (g.toMetricTensor : MetricTensor E H M n I)) x + /-- Index raising map `ω ↦ sharp ω` as a linear map. -/ noncomputable abbrev sharp (g : PseudoRiemannianMetric E H M n I) (x : M) : (TangentSpace I x →L[ℝ] ℝ) →ₗ[ℝ] TangentSpace I x := MetricTensor.sharp (g := (g.toMetricTensor : MetricTensor E H M n I)) x -@[simp] lemma sharpL_apply_flatL (g : PseudoRiemannianMetric E H M n I) (x : M) (v : TangentSpace I x) : +lemma sharpL_apply_flatL (g : PseudoRiemannianMetric E H M n I) (x : M) (v : TangentSpace I x) : g.sharpL x (g.flatL x v) = v := by - simpa [sharpL, flatL] using - (MetricTensor.sharpL_apply_flatL (g := (g.toMetricTensor : MetricTensor E H M n I)) x v) + simp [sharpL, flatL] -@[simp] lemma flatL_apply_sharpL (g : PseudoRiemannianMetric E H M n I) (x : M) +lemma flatL_apply_sharpL (g : PseudoRiemannianMetric E H M n I) (x : M) (ω : TangentSpace I x →L[ℝ] ℝ) : g.flatL x (g.sharpL x ω) = ω := by - simpa [sharpL, flatL] using - (MetricTensor.flatL_apply_sharpL (g := (g.toMetricTensor : MetricTensor E H M n I)) x ω) + simp [sharpL, flatL] -@[simp] lemma flat_sharp_apply (g : PseudoRiemannianMetric E H M n I) (x : M) +lemma flat_sharp_apply (g : PseudoRiemannianMetric E H M n I) (x : M) (ω : TangentSpace I x →L[ℝ] ℝ) : g.flat x (g.sharp x ω) = ω := by - simpa [flat, sharp] using - (MetricTensor.flat_sharp_apply (g := (g.toMetricTensor : MetricTensor E H M n I)) x ω) + simp [flat, sharp] -@[simp] lemma sharp_flat_apply (g : PseudoRiemannianMetric E H M n I) (x : M) (v : TangentSpace I x) : +lemma sharp_flat_apply (g : PseudoRiemannianMetric E H M n I) (x : M) (v : TangentSpace I x) : g.sharp x (g.flat x v) = v := by simpa [flat, sharp] using (MetricTensor.sharp_flat_apply (g := (g.toMetricTensor : MetricTensor E H M n I)) x v) -@[simp] lemma apply_sharp_sharp (g : PseudoRiemannianMetric E H M n I) (x : M) +lemma apply_sharp_sharp (g : PseudoRiemannianMetric E H M n I) (x : M) (ω₁ ω₂ : TangentSpace I x →L[ℝ] ℝ) : g.val x (g.sharpL x ω₁) (g.sharpL x ω₂) = ω₁ (g.sharpL x ω₂) := by - simpa [sharpL] using - (MetricTensor.apply_sharp_sharp (g := (g.toMetricTensor : MetricTensor E H M n I)) x ω₁ ω₂) + simp [sharpL] lemma apply_vec_sharp (g : PseudoRiemannianMetric E H M n I) (x : M) (v : TangentSpace I x) (ω : TangentSpace I x →L[ℝ] ℝ) : @@ -941,14 +899,17 @@ lemma apply_vec_sharp (g : PseudoRiemannianMetric E H M n I) (x : M) (v : Tangen simpa [sharpL] using (MetricTensor.apply_vec_sharp (g := (g.toMetricTensor : MetricTensor E H M n I)) x v ω) + /-- The induced cotangent metric value `g⁻¹(ω₁, ω₂)`. -/ noncomputable abbrev cotangentMetricVal (g : PseudoRiemannianMetric E H M n I) (x : M) (ω₁ ω₂ : TangentSpace I x →L[ℝ] ℝ) : ℝ := MetricTensor.cotangentMetricVal (g := (g.toMetricTensor : MetricTensor E H M n I)) x ω₁ ω₂ + /-- The induced cotangent metric as a bilinear form. -/ noncomputable abbrev cotangentToBilinForm (g : PseudoRiemannianMetric E H M n I) (x : M) : LinearMap.BilinForm ℝ (TangentSpace I x →L[ℝ] ℝ) := MetricTensor.cotangentToBilinForm (g := (g.toMetricTensor : MetricTensor E H M n I)) x + /-- The induced cotangent metric as a quadratic form. -/ noncomputable abbrev cotangentToQuadraticForm (g : PseudoRiemannianMetric E H M n I) (x : M) : QuadraticForm ℝ (TangentSpace I x →L[ℝ] ℝ) := MetricTensor.cotangentToQuadraticForm (g := (g.toMetricTensor : MetricTensor E H M n I)) x diff --git a/PhysLean/Mathematics/Geometry/Metric/QuadraticForm/Index.lean b/PhysLean/Mathematics/Geometry/Metric/QuadraticForm/Index.lean deleted file mode 100644 index dfda30e99..000000000 --- a/PhysLean/Mathematics/Geometry/Metric/QuadraticForm/Index.lean +++ /dev/null @@ -1,19 +0,0 @@ -/- -Copyright (c) 2026 -Released under Apache 2.0 license as described in the file LICENSE. --/ - -import PhysLean.Mathematics.Geometry.Metric.QuadraticForm.NegDim - -/-! -# Quadratic form inertia indices (compatibility layer) - -This file is the single import point for the inertia index/signature API used by the -pseudo-Riemannian metric development (`QuadraticForm.posIndex`, `QuadraticForm.posDim`, -`QuadraticForm.negDim`, `QuadraticForm.zeroDim`, `QuadraticForm.signature`, ...). - -Currently these definitions live in `PhysLean.Mathematics.Geometry.Metric.QuadraticForm.NegDim`. -If Mathlib later provides overlapping canonical inertia invariants, this file is intended to be -the unique switch-point. --/ - diff --git a/PhysLean/Mathematics/Geometry/Metric/QuadraticForm/NegDim.lean b/PhysLean/Mathematics/Geometry/Metric/QuadraticForm/NegDim.lean index ce8223b79..658cfb590 100644 --- a/PhysLean/Mathematics/Geometry/Metric/QuadraticForm/NegDim.lean +++ b/PhysLean/Mathematics/Geometry/Metric/QuadraticForm/NegDim.lean @@ -54,8 +54,11 @@ open scoped BigOperators Sylvester's law of inertia: the numbers of positive, negative, and zero squares in a diagonal representation. -/ @[ext] structure Signature where + /-- The positive index of inertia. -/ pos : ℕ + /-- The negative index of inertia. -/ neg : ℕ + /-- The nullity (dimension of the radical). -/ zero : ℕ namespace Signature @@ -135,7 +138,7 @@ the inertia counts. section InertiaUniqueness -variable {V : Type*} [AddCommGroup V] [Module ℝ V] [FiniteDimensional ℝ V] +variable {V : Type*} [AddCommGroup V] [Module ℝ V] /-- `IsPosDefOn Q W` means that the restriction of `Q` to the submodule `W` is positive definite. -/ def IsPosDefOn (Q : QuadraticForm ℝ V) (W : Submodule ℝ V) : Prop := @@ -143,17 +146,17 @@ def IsPosDefOn (Q : QuadraticForm ℝ V) (W : Submodule ℝ V) : Prop := /-- Predicate asserting that a real quadratic form admits a positive definite submodule of dimension `k`. -/ -def PosIndexPred (Q : QuadraticForm ℝ V) (k : ℕ) : Prop := +def PosIndexPred [FiniteDimensional ℝ V] (Q : QuadraticForm ℝ V) (k : ℕ) : Prop := ∃ W : Submodule ℝ V, finrank ℝ W = k ∧ IsPosDefOn (V := V) Q W -private lemma posIndexPred_zero (Q : QuadraticForm ℝ V) : PosIndexPred (V := V) Q 0 := by - classical +private lemma posIndexPred_zero [FiniteDimensional ℝ V] (Q : QuadraticForm ℝ V) : + PosIndexPred (V := V) Q 0 := by refine ⟨(⊥ : Submodule ℝ V), by simp, ?_⟩ intro x hx exfalso exact hx (Subsingleton.elim x 0) -private lemma posIndexPred_le_finrank {Q : QuadraticForm ℝ V} {k : ℕ} +private lemma posIndexPred_le_finrank [FiniteDimensional ℝ V] {Q : QuadraticForm ℝ V} {k : ℕ} (hk : PosIndexPred (V := V) Q k) : k ≤ finrank ℝ V := by rcases hk with ⟨W, hW, -⟩ have hk_le : finrank ℝ W ≤ finrank ℝ V := @@ -162,47 +165,41 @@ private lemma posIndexPred_le_finrank {Q : QuadraticForm ℝ V} {k : ℕ} /-- The positive index of a real quadratic form: the maximal dimension of a subspace on which the form is positive definite. -/ -noncomputable def posIndex (Q : QuadraticForm ℝ V) : ℕ := +noncomputable def posIndex [FiniteDimensional ℝ V] (Q : QuadraticForm ℝ V) : ℕ := sSup {k : ℕ | PosIndexPred (V := V) Q k} -private lemma posIndex_nonempty (Q : QuadraticForm ℝ V) : +private lemma posIndex_nonempty [FiniteDimensional ℝ V] (Q : QuadraticForm ℝ V) : ({k : ℕ | PosIndexPred (V := V) Q k} : Set ℕ).Nonempty := ⟨0, posIndexPred_zero (V := V) Q⟩ -private lemma posIndex_bddAbove (Q : QuadraticForm ℝ V) : +private lemma posIndex_bddAbove [FiniteDimensional ℝ V] (Q : QuadraticForm ℝ V) : BddAbove ({k : ℕ | PosIndexPred (V := V) Q k} : Set ℕ) := by refine ⟨finrank ℝ V, ?_⟩ intro k hk exact posIndexPred_le_finrank (V := V) (Q := Q) hk -lemma posIndex_spec (Q : QuadraticForm ℝ V) : +lemma posIndex_spec [FiniteDimensional ℝ V] (Q : QuadraticForm ℝ V) : ∃ W : Submodule ℝ V, finrank ℝ W = posIndex (V := V) Q ∧ IsPosDefOn (V := V) Q W := by - classical - -- `posIndex` is the supremum of a bounded, nonempty set of attainable dimensions, - -- hence it is itself attainable. have hmem : posIndex (V := V) Q ∈ ({k : ℕ | PosIndexPred (V := V) Q k} : Set ℕ) := by - -- `Nat.sSup_mem` gives membership of `sSup` for nonempty bounded sets. simpa [posIndex] using (Nat.sSup_mem (s := ({k : ℕ | PosIndexPred (V := V) Q k} : Set ℕ)) (posIndex_nonempty (V := V) Q) (posIndex_bddAbove (V := V) Q)) rcases hmem with ⟨W, hW, hWpos⟩ exact ⟨W, hW, hWpos⟩ -lemma le_posIndex_of_exists {Q : QuadraticForm ℝ V} {k : ℕ} +lemma le_posIndex_of_exists [FiniteDimensional ℝ V] {Q : QuadraticForm ℝ V} {k : ℕ} (hk : ∃ W : Submodule ℝ V, finrank ℝ W = k ∧ IsPosDefOn (V := V) Q W) : k ≤ posIndex (V := V) Q := by have hb : BddAbove ({k : ℕ | PosIndexPred (V := V) Q k} : Set ℕ) := posIndex_bddAbove (V := V) Q - -- `le_csSup` for `ℕ` requires boundedness (and membership gives nonemptiness). simpa [posIndex, PosIndexPred] using (le_csSup hb hk) /- If `Q` and `Q₂` are equivalent, then `posIndex Q ≤ posIndex Q₂`. -/ -lemma posIndex_le_of_equivalent {V₂ : Type*} [AddCommGroup V₂] [Module ℝ V₂] +lemma posIndex_le_of_equivalent [FiniteDimensional ℝ V] {V₂ : Type*} [AddCommGroup V₂] [Module ℝ V₂] [FiniteDimensional ℝ V₂] {Q : QuadraticForm ℝ V} {Q₂ : QuadraticForm ℝ V₂} (h : Q.Equivalent Q₂) : posIndex (V := V) Q ≤ posIndex (V := V₂) Q₂ := by - classical rcases h with ⟨e⟩ rcases posIndex_spec (Q := Q) with ⟨W, hWfin, hWpos⟩ let f : V →ₗ[ℝ] V₂ := (e.toLinearEquiv : V ≃ₗ[ℝ] V₂).toLinearMap @@ -239,7 +236,7 @@ lemma posIndex_le_of_equivalent {V₂ : Type*} [AddCommGroup V₂] [Module ℝ V exact le_posIndex_of_exists (V := V₂) hk /-- `posIndex` is invariant under equivalence of quadratic forms. -/ -theorem posIndex_eq_of_equivalent {V₂ : Type*} [AddCommGroup V₂] [Module ℝ V₂] +theorem posIndex_eq_of_equivalent [FiniteDimensional ℝ V] {V₂ : Type*} [AddCommGroup V₂] [Module ℝ V₂] [FiniteDimensional ℝ V₂] {Q : QuadraticForm ℝ V} {Q₂ : QuadraticForm ℝ V₂} (h : Q.Equivalent Q₂) : posIndex (V := V) Q = posIndex (V := V₂) Q₂ := by @@ -253,11 +250,10 @@ end InertiaUniqueness section Canonical -variable {E : Type*} [AddCommGroup E] [Module ℝ E] [FiniteDimensional ℝ E] +variable {E : Type*} [AddCommGroup E] [Module ℝ E] -lemma posIndex_le_finrank (Q : QuadraticForm ℝ E) : +lemma posIndex_le_finrank [FiniteDimensional ℝ E] (Q : QuadraticForm ℝ E) : posIndex (V := E) Q ≤ finrank ℝ E := by - classical let s : Set ℕ := {k : ℕ | PosIndexPred (V := E) Q k} have hsne : s.Nonempty := ⟨0, posIndexPred_zero (V := E) Q⟩ refine (csSup_le hsne) ?_ @@ -268,35 +264,34 @@ lemma posIndex_le_finrank (Q : QuadraticForm ℝ E) : simpa [s, hW] using hk_le /-- The positive index of inertia of a real quadratic form (canonical). -/ -noncomputable def posDim (Q : QuadraticForm ℝ E) : ℕ := +noncomputable def posDim [FiniteDimensional ℝ E] (Q : QuadraticForm ℝ E) : ℕ := posIndex (V := E) Q /-- The negative index of inertia of a real quadratic form (canonical). -/ -noncomputable def negDim (Q : QuadraticForm ℝ E) : ℕ := +noncomputable def negDim [FiniteDimensional ℝ E] (Q : QuadraticForm ℝ E) : ℕ := posIndex (V := E) (-Q) -omit [FiniteDimensional ℝ E] in @[simp] -lemma posDim_def (Q : QuadraticForm ℝ E) : Q.posDim = posIndex (V := E) Q := rfl +lemma posDim_def [FiniteDimensional ℝ E] (Q : QuadraticForm ℝ E) : + Q.posDim = posIndex (V := E) Q := rfl -omit [FiniteDimensional ℝ E] in @[simp] -lemma negDim_def (Q : QuadraticForm ℝ E) : Q.negDim = posIndex (V := E) (-Q) := rfl +lemma negDim_def [FiniteDimensional ℝ E] (Q : QuadraticForm ℝ E) : + Q.negDim = posIndex (V := E) (-Q) := rfl -lemma posDim_le_finrank (Q : QuadraticForm ℝ E) : Q.posDim ≤ finrank ℝ E := +lemma posDim_le_finrank [FiniteDimensional ℝ E] (Q : QuadraticForm ℝ E) : Q.posDim ≤ finrank ℝ E := posIndex_le_finrank (E := E) Q -lemma negDim_le_finrank (Q : QuadraticForm ℝ E) : Q.negDim ≤ finrank ℝ E := +lemma negDim_le_finrank [FiniteDimensional ℝ E] (Q : QuadraticForm ℝ E) : Q.negDim ≤ finrank ℝ E := posIndex_le_finrank (E := E) (-Q) -omit [FiniteDimensional ℝ E] in -@[simp] lemma posDim_neg (Q : QuadraticForm ℝ E) : (-Q).posDim = Q.negDim := rfl +@[simp] +lemma posDim_neg [FiniteDimensional ℝ E] (Q : QuadraticForm ℝ E) : (-Q).posDim = Q.negDim := rfl -omit [FiniteDimensional ℝ E] in -lemma negDim_neg (Q : QuadraticForm ℝ E) : (-Q).negDim = Q.posDim := by +lemma negDim_neg [FiniteDimensional ℝ E] (Q : QuadraticForm ℝ E) : (-Q).negDim = Q.posDim := by simp [negDim, posDim] -lemma posDim_add_negDim_le_finrank (Q : QuadraticForm ℝ E) : +lemma posDim_add_negDim_le_finrank [FiniteDimensional ℝ E] (Q : QuadraticForm ℝ E) : Q.posDim + Q.negDim ≤ finrank ℝ E := by rcases posIndex_spec (Q := Q) with ⟨Wpos, hWpos, hpos⟩ rcases posIndex_spec (Q := -Q) with ⟨Wneg, hWneg, hneg⟩ @@ -317,41 +312,34 @@ lemma posDim_add_negDim_le_finrank (Q : QuadraticForm ℝ E) : simpa [posDim, negDim, hWpos, hWneg] using hdim /-- The nullity of a real quadratic form, defined so that `pos + neg + zero = finrank`. -/ -noncomputable def zeroDim (Q : QuadraticForm ℝ E) : ℕ := +noncomputable def zeroDim [FiniteDimensional ℝ E] (Q : QuadraticForm ℝ E) : ℕ := finrank ℝ E - Q.posDim - Q.negDim -omit [FiniteDimensional ℝ E] in @[simp] -lemma zeroDim_def (Q : QuadraticForm ℝ E) : +lemma zeroDim_def [FiniteDimensional ℝ E] (Q : QuadraticForm ℝ E) : Q.zeroDim = finrank ℝ E - Q.posDim - Q.negDim := rfl -omit [FiniteDimensional ℝ E] in -lemma zeroDim_neg (Q : QuadraticForm ℝ E) : (-Q).zeroDim = Q.zeroDim := by - -- Both sides reduce to `finrank - (posIndex Q + posIndex (-Q))`. +lemma zeroDim_neg [FiniteDimensional ℝ E] (Q : QuadraticForm ℝ E) : (-Q).zeroDim = Q.zeroDim := by simp [zeroDim, posDim, negDim, Nat.sub_sub, Nat.add_comm] /-- The signature `(pos, neg, zero)` of a real quadratic form (canonical). -/ -noncomputable def signature (Q : QuadraticForm ℝ E) : Signature := +noncomputable def signature [FiniteDimensional ℝ E] (Q : QuadraticForm ℝ E) : Signature := ⟨Q.posDim, Q.negDim, Q.zeroDim⟩ -omit [FiniteDimensional ℝ E] in @[simp] -lemma signature_pos (Q : QuadraticForm ℝ E) : Q.signature.pos = Q.posDim := rfl +lemma signature_pos [FiniteDimensional ℝ E] (Q : QuadraticForm ℝ E) : Q.signature.pos = Q.posDim := rfl -omit [FiniteDimensional ℝ E] in @[simp] -lemma signature_neg (Q : QuadraticForm ℝ E) : Q.signature.neg = Q.negDim := rfl +lemma signature_neg [FiniteDimensional ℝ E] (Q : QuadraticForm ℝ E) : Q.signature.neg = Q.negDim := rfl -omit [FiniteDimensional ℝ E] in @[simp] -lemma signature_zero (Q : QuadraticForm ℝ E) : Q.signature.zero = Q.zeroDim := rfl +lemma signature_zero [FiniteDimensional ℝ E] (Q : QuadraticForm ℝ E) : Q.signature.zero = Q.zeroDim := rfl -omit [FiniteDimensional ℝ E] in @[simp] -lemma signature_def (Q : QuadraticForm ℝ E) : +lemma signature_def [FiniteDimensional ℝ E] (Q : QuadraticForm ℝ E) : Q.signature = ⟨Q.posDim, Q.negDim, Q.zeroDim⟩ := rfl -lemma posDim_add_negDim_add_zeroDim (Q : QuadraticForm ℝ E) : +lemma posDim_add_negDim_add_zeroDim [FiniteDimensional ℝ E] (Q : QuadraticForm ℝ E) : Q.posDim + Q.negDim + Q.zeroDim = finrank ℝ E := by set n : ℕ := finrank ℝ E set p : ℕ := Q.posDim @@ -372,12 +360,12 @@ lemma posDim_add_negDim_add_zeroDim (Q : QuadraticForm ℝ E) : _ = n := by simp [Nat.add_sub_of_le hp] -lemma signature_sum (Q : QuadraticForm ℝ E) : +lemma signature_sum [FiniteDimensional ℝ E] (Q : QuadraticForm ℝ E) : Q.signature.pos + Q.signature.neg + Q.signature.zero = finrank ℝ E := by simpa using (posDim_add_negDim_add_zeroDim (E := E) Q) /-- For a positive definite quadratic form, `posDim = finrank`. -/ -theorem posDim_posDef {Q : QuadraticForm ℝ E} (hQ : Q.PosDef) : +theorem posDim_posDef [FiniteDimensional ℝ E] {Q : QuadraticForm ℝ E} (hQ : Q.PosDef) : Q.posDim = finrank ℝ E := by apply le_antisymm (posDim_le_finrank (E := E) Q) have hk : @@ -395,7 +383,8 @@ theorem posDim_posDef {Q : QuadraticForm ℝ E} (hQ : Q.PosDef) : simpa [posDim] using this /-- For a positive definite quadratic form, `negDim = 0`. -/ -theorem negDim_posDef {Q : QuadraticForm ℝ E} (hQ : Q.PosDef) : Q.negDim = 0 := by +theorem negDim_posDef [FiniteDimensional ℝ E] {Q : QuadraticForm ℝ E} (hQ : Q.PosDef) : + Q.negDim = 0 := by rcases posIndex_spec (Q := -Q) with ⟨W, hW, hWpos⟩ have hWbot : W = ⊥ := by ext x @@ -418,7 +407,7 @@ theorem negDim_posDef {Q : QuadraticForm ℝ E} (hQ : Q.PosDef) : Q.negDim = 0 : simp [negDim, this] /-- For a positive definite quadratic form, `zeroDim = 0`. -/ -theorem zeroDim_posDef {Q : QuadraticForm ℝ E} (hQ : Q.PosDef) : +theorem zeroDim_posDef [FiniteDimensional ℝ E] {Q : QuadraticForm ℝ E} (hQ : Q.PosDef) : Q.zeroDim = 0 := by have hpos : Q.posDim = finrank ℝ E := posDim_posDef (E := E) hQ have hneg : Q.negDim = 0 := negDim_posDef (E := E) hQ @@ -426,8 +415,7 @@ theorem zeroDim_posDef {Q : QuadraticForm ℝ E} (hQ : Q.PosDef) : have hneg' : posIndex (V := E) (-Q) = 0 := by simpa [negDim] using hneg simp [zeroDim, posDim, negDim, hpos', hneg'] -omit [FiniteDimensional ℝ E] in -lemma Equivalent.neg {E₂ : Type*} [AddCommGroup E₂] [Module ℝ E₂] [FiniteDimensional ℝ E₂] +lemma Equivalent.neg {E₂ : Type*} [AddCommGroup E₂] [Module ℝ E₂] {Q : QuadraticForm ℝ E} {Q₂ : QuadraticForm ℝ E₂} (h : Q.Equivalent Q₂) : (-Q).Equivalent (-Q₂) := by rcases h with ⟨e⟩ @@ -437,18 +425,21 @@ lemma Equivalent.neg {E₂ : Type*} [AddCommGroup E₂] [Module ℝ E₂] [Finit map_app' := fun x => ?_ } simp -theorem posDim_eq_of_equivalent {E₂ : Type*} [AddCommGroup E₂] [Module ℝ E₂] [FiniteDimensional ℝ E₂] +theorem posDim_eq_of_equivalent [FiniteDimensional ℝ E] {E₂ : Type*} [AddCommGroup E₂] [Module ℝ E₂] + [FiniteDimensional ℝ E₂] {Q : QuadraticForm ℝ E} {Q₂ : QuadraticForm ℝ E₂} (h : Q.Equivalent Q₂) : Q.posDim = Q₂.posDim := by simp [posDim, posIndex_eq_of_equivalent (Q := Q) (Q₂ := Q₂) h] -theorem negDim_eq_of_equivalent {E₂ : Type*} [AddCommGroup E₂] [Module ℝ E₂] [FiniteDimensional ℝ E₂] +theorem negDim_eq_of_equivalent [FiniteDimensional ℝ E] {E₂ : Type*} [AddCommGroup E₂] [Module ℝ E₂] + [FiniteDimensional ℝ E₂] {Q : QuadraticForm ℝ E} {Q₂ : QuadraticForm ℝ E₂} (h : Q.Equivalent Q₂) : Q.negDim = Q₂.negDim := by have h' : (-Q).Equivalent (-Q₂) := Equivalent.neg (E := E) (E₂ := E₂) h simp [negDim, posIndex_eq_of_equivalent (Q := -Q) (Q₂ := -Q₂) h'] -theorem zeroDim_eq_of_equivalent {E₂ : Type*} [AddCommGroup E₂] [Module ℝ E₂] [FiniteDimensional ℝ E₂] +theorem zeroDim_eq_of_equivalent [FiniteDimensional ℝ E] {E₂ : Type*} [AddCommGroup E₂] [Module ℝ E₂] + [FiniteDimensional ℝ E₂] {Q : QuadraticForm ℝ E} {Q₂ : QuadraticForm ℝ E₂} (h : Q.Equivalent Q₂) : Q.zeroDim = Q₂.zeroDim := by rcases h with ⟨e⟩ @@ -459,7 +450,8 @@ theorem zeroDim_eq_of_equivalent {E₂ : Type*} [AddCommGroup E₂] [Module ℝ posIndex_eq_of_equivalent (Q := -Q) (Q₂ := -Q₂) (Equivalent.neg (E := E) (E₂ := E₂) ⟨e⟩) simp [zeroDim, posDim, negDim, hfin, hposI, hnegI] -theorem signature_eq_of_equivalent {E₂ : Type*} [AddCommGroup E₂] [Module ℝ E₂] [FiniteDimensional ℝ E₂] +theorem signature_eq_of_equivalent [FiniteDimensional ℝ E] {E₂ : Type*} [AddCommGroup E₂] [Module ℝ E₂] + [FiniteDimensional ℝ E₂] {Q : QuadraticForm ℝ E} {Q₂ : QuadraticForm ℝ E₂} (h : Q.Equivalent Q₂) : Q.signature = Q₂.signature := by rcases h with ⟨e⟩ @@ -537,7 +529,8 @@ def supportedOnPos (w : Fin n → SignType) : Submodule ℝ (Fin n → ℝ) wher intro a v hv i hi simp [Pi.smul_apply, hv i hi] -@[simp] lemma mem_supportedOnPos {w : Fin n → SignType} {v : Fin n → ℝ} : +@[simp] +lemma mem_supportedOnPos {w : Fin n → SignType} {v : Fin n → ℝ} : v ∈ supportedOnPos (n := n) w ↔ ∀ i, w i ≠ SignType.pos → v i = 0 := Iff.rfl @@ -564,7 +557,6 @@ lemma diagForm_nonpos_of_no_pos {w : Fin n → SignType} {v : Fin n → ℝ} /-- On `supportedOnPos w`, the diagonal form `diagForm w` is positive definite. -/ lemma isPosDefOn_diagForm_supportedOnPos (w : Fin n → SignType) : IsPosDefOn (V := Fin n → ℝ) (diagForm (n := n) w) (supportedOnPos (n := n) w) := by - classical intro v hv0 -- pick a coordinate where `v` is nonzero have hv' : ∃ i, (v : Fin n → ℝ) i ≠ 0 := by @@ -603,7 +595,6 @@ lemma isPosDefOn_diagForm_supportedOnPos (w : Fin n → SignType) : /-- The submodule `supportedOnPos w` is linearly equivalent to functions on the positive index set. -/ noncomputable def supportedOnPosEquiv (w : Fin n → SignType) : supportedOnPos (n := n) w ≃ₗ[ℝ] ({i // i ∈ posSet (n := n) w} → ℝ) := by - classical refine { toLinearMap := { toFun := fun v i => (v : Fin n → ℝ) i.1 @@ -635,7 +626,6 @@ noncomputable def supportedOnPosEquiv (w : Fin n → SignType) : lemma finrank_supportedOnPos (w : Fin n → SignType) : finrank ℝ (supportedOnPos (n := n) w) = (posSet (n := n) w).card := by - classical have h : finrank ℝ ({i // i ∈ posSet (n := n) w} → ℝ) = (posSet (n := n) w).card := by change finrank ℝ ((posSet (n := n) w) → ℝ) = (posSet (n := n) w).card @@ -720,7 +710,6 @@ theorem posDim_weightedSumSquares_signType (w : Fin n → SignType) : private theorem negDim_diagForm (w : Fin n → SignType) : QuadraticForm.negDim (E := Fin n → ℝ) (diagForm (n := n) w) = (negSet (n := n) w).card := by - classical set Q : QuadraticForm ℝ (Fin n → ℝ) := diagForm (n := n) w have hneg : -Q = diagForm (n := n) (fun i => -w i) := by ext v @@ -740,7 +729,6 @@ theorem negDim_weightedSumSquares_signType (w : Fin n → SignType) : lemma card_posSet_add_card_negSet_add_card_zeroSet (w : Fin n → SignType) : (posSet (n := n) w).card + (negSet (n := n) w).card + (zeroSet (n := n) w).card = Fintype.card (Fin n) := by - classical let A : Finset (Fin n) := posSet (n := n) w let B : Finset (Fin n) := negSet (n := n) w let C : Finset (Fin n) := zeroSet (n := n) w diff --git a/PhysLean/Mathematics/Geometry/Metric/Riemannian/Defs.lean b/PhysLean/Mathematics/Geometry/Metric/Riemannian/Defs.lean index da51dc27b..70a14f4b9 100644 --- a/PhysLean/Mathematics/Geometry/Metric/Riemannian/Defs.lean +++ b/PhysLean/Mathematics/Geometry/Metric/Riemannian/Defs.lean @@ -38,7 +38,7 @@ variable [IsManifold I (n + 1) M] [IsManifold I 1 M] /-- A `C^n` Riemannian metric on `M`, packaged using Mathlib's modern bundle API. -/ abbrev RiemannianMetric (I : ModelWithCorners ℝ E H) (n : WithTop ℕ∞) (M : Type*) - [TopologicalSpace M] [ChartedSpace H M] [IsManifold I (n + 1) M] [IsManifold I 1 M] := + [TopologicalSpace M] [ChartedSpace H M] [IsManifold I 1 M] := Bundle.ContMDiffRiemannianMetric (IB := I) (n := n) (F := E) (E := fun x : M ↦ TangentSpace I x) namespace RiemannianMetric @@ -83,14 +83,19 @@ def toPseudoRiemannianMetric (g : RiemannianMetric (I := I) (n := n) M) : simp [hx', hy'] @[simp] -lemma toPseudoRiemannianMetric_index (g : RiemannianMetric (I := I) (n := n) M) (x : M) : - (toPseudoRiemannianMetric (I := I) (n := n) (M := M) g).index x = 0 := by +lemma toPseudoRiemannianMetric_posIndex_neg_toQuadraticForm (g : RiemannianMetric (I := I) (n := n) M) + (x : M) : + (-(toPseudoRiemannianMetric (I := I) (n := n) (M := M) g).toQuadraticForm x).posIndex = 0 := by have hx : (PseudoRiemannianMetric.valToQuadraticForm g.inner g.symm x).negDim = 0 := by apply QuadraticForm.negDim_posDef intro v hv simpa [PseudoRiemannianMetric.valToQuadraticForm] using g.pos x v hv - simpa [PseudoRiemannianMetric.index, PseudoRiemannianMetric.toQuadraticForm, - toPseudoRiemannianMetric] using hx + -- `negDim Q = (-Q).posIndex`. + simpa [PseudoRiemannianMetric.toQuadraticForm, toPseudoRiemannianMetric, QuadraticForm.negDim] using hx + +lemma toPseudoRiemannianMetric_index (g : RiemannianMetric (I := I) (n := n) M) (x : M) : + (toPseudoRiemannianMetric (I := I) (n := n) (M := M) g).index x = 0 := by + simp [PseudoRiemannianMetric.index, QuadraticForm.negDim] instance : Coe (RiemannianMetric (I := I) (n := n) M) @@ -99,30 +104,16 @@ instance : end RiemannianMetric -/-! ## Existence predicates (non-choosing) -/ - -/-- Prop-valued predicate recording existence of a `C^n` Riemannian metric (bundle-first), without -making any noncanonical choice. -/ -class HasRiemannianMetric : Prop where - out : Nonempty (RiemannianMetric (I := I) (n := n) M) - -instance (g : RiemannianMetric (I := I) (n := n) M) : - HasRiemannianMetric (I := I) (n := n) (M := M) := - ⟨⟨g⟩⟩ - -theorem hasRiemannianMetric_iff : - HasRiemannianMetric (I := I) (n := n) (M := M) ↔ - Nonempty (RiemannianMetric (I := I) (n := n) M) := - ⟨fun h => h.out, fun h => ⟨h⟩⟩ - -/-- Any Riemannian metric gives a pseudo-Riemannian metric (of index `0`), hence existence of a -Riemannian metric implies existence of a pseudo-Riemannian metric. -/ -instance [h : HasRiemannianMetric (I := I) (n := n) (M := M)] - [∀ x : M, FiniteDimensional ℝ (TangentSpace I x)] : - PseudoRiemannianMetric.HasPseudoRiemannianMetric (E := E) (H := H) (M := M) (n := n) (I := I) := - ⟨by - rcases h.out with ⟨g⟩ - exact ⟨RiemannianMetric.toPseudoRiemannianMetric (I := I) (n := n) (M := M) g⟩⟩ +/-! ## Existence helpers -/ + +/-- Existence of a Riemannian metric implies existence of a pseudo-Riemannian metric (of index `0`), +by forgetting positivity. -/ +theorem nonempty_pseudoRiemannianMetric_of_nonempty_riemannianMetric + [∀ x : M, FiniteDimensional ℝ (TangentSpace I x)] + (h : Nonempty (RiemannianMetric (I := I) (n := n) M)) : + Nonempty (PseudoRiemannianMetric E H M n I) := by + rcases h with ⟨g⟩ + exact ⟨RiemannianMetric.toPseudoRiemannianMetric (I := I) (n := n) (M := M) g⟩ end From 6611e34eaf8e19f29d56b3182f8011b5ca0d69bf Mon Sep 17 00:00:00 2001 From: Matteo CIpollina Date: Tue, 3 Mar 2026 14:51:48 +0100 Subject: [PATCH 21/36] fix lints + minor improvements --- PhysLean.lean | 1 - .../Geometry/Metric/Lorentzian/Defs.lean | 15 ++-- .../Metric/PseudoRiemannian/Defs.lean | 79 +++++++------------ .../Geometry/Metric/QuadraticForm/NegDim.lean | 26 +++--- .../Metric/QuadraticForm/Sylvester.lean | 5 +- .../Geometry/Metric/Riemannian/Defs.lean | 23 +++--- 6 files changed, 69 insertions(+), 80 deletions(-) diff --git a/PhysLean.lean b/PhysLean.lean index 81c5c35dc..54a8418cb 100644 --- a/PhysLean.lean +++ b/PhysLean.lean @@ -53,7 +53,6 @@ import PhysLean.Mathematics.FDerivCurry import PhysLean.Mathematics.Fin import PhysLean.Mathematics.Fin.Involutions import PhysLean.Mathematics.Geometry.Metric.Lorentzian.Defs -import PhysLean.Mathematics.Geometry.Metric.QuadraticForm.Index import PhysLean.Mathematics.Geometry.MetricQuadraticForm.NegDim import PhysLean.Mathematics.Geometry.MetricQuadraticForm.Sylvester import PhysLean.Mathematics.Geometry.Metric.PseudoRiemannian.Defs diff --git a/PhysLean/Mathematics/Geometry/Metric/Lorentzian/Defs.lean b/PhysLean/Mathematics/Geometry/Metric/Lorentzian/Defs.lean index 51c0533c6..50311ed38 100644 --- a/PhysLean/Mathematics/Geometry/Metric/Lorentzian/Defs.lean +++ b/PhysLean/Mathematics/Geometry/Metric/Lorentzian/Defs.lean @@ -9,11 +9,16 @@ import PhysLean.Mathematics.Geometry.Metric.PseudoRiemannian.Defs /-! # Lorentzian metrics -This file defines Lorentzian metrics as pseudo-Riemannian metrics of index `1` (negative dimension -`1`), in the sense of Sylvester's law of inertia (`QuadraticForm.negDim`). +This file records the Lorentzian condition (index `1`) for a pseudo-Riemannian metric. -It provides a reusable definition that composes with the -existing pseudo-Riemannian API (musical isomorphisms, induced bilinear forms, etc.). +## Main definitions + +* `PseudoRiemannianMetric.IsLorentzianMetric`: the Prop-valued predicate asserting + `(g.toQuadraticForm x).negDim = 1` for all `x`. + +## Tags + +Lorentzian, pseudo-Riemannian, index -/ namespace PseudoRiemannianMetric @@ -31,7 +36,7 @@ variable {I : ModelWithCorners ℝ E H} {n : WithTop ℕ∞} variable [IsManifold I (n + 1) M] variable [∀ x : M, FiniteDimensional ℝ (TangentSpace I x)] -/-- Typeclass asserting that a pseudo-Riemannian metric is Lorentzian (index `1`). -/ +/-- Predicate asserting that a pseudo-Riemannian metric has index `1` at every point. -/ class IsLorentzianMetric (g : PseudoRiemannianMetric E H M n I) : Prop where negDim_eq_one : ∀ x : M, (g.toQuadraticForm x).negDim = 1 diff --git a/PhysLean/Mathematics/Geometry/Metric/PseudoRiemannian/Defs.lean b/PhysLean/Mathematics/Geometry/Metric/PseudoRiemannian/Defs.lean index 413e6845e..27910e1db 100644 --- a/PhysLean/Mathematics/Geometry/Metric/PseudoRiemannian/Defs.lean +++ b/PhysLean/Mathematics/Geometry/Metric/PseudoRiemannian/Defs.lean @@ -10,42 +10,30 @@ import Mathlib.Topology.LocallyConstant.Basic import PhysLean.Mathematics.Geometry.Metric.QuadraticForm.NegDim /-! -# Pseudo-Riemannian Metrics on Smooth Manifolds +# Pseudo-Riemannian metrics -This file formalizes pseudo-Riemannian metrics on smooth manifolds and establishes their basic -properties. +This file defines pseudo-Riemannian metrics on smooth manifolds, in a way that mirrors Mathlib's +bundle-first Riemannian metric API. -A pseudo-Riemannian metric equips a manifold with a smoothly varying, non-degenerate, symmetric -bilinear form of *constant index* on each tangent space, generalizing the concept of an inner -product space to curved spaces. The index here refers to `QuadraticForm.negDim`, the dimension -of a maximal negative definite subspace. +## Main definitions -## Main Definitions +* `Bundle.PseudoRiemannianBundle`: fiberwise data of a symmetric nondegenerate bilinear form. +* `Bundle.ContMDiffPseudoRiemannianMetric`: a `C^n` pseudo-Riemannian metric along a vector bundle, + expressed as a smooth section into the bundle of bilinear forms. +* `Bundle.IsContMDiffPseudoRiemannianBundle`: the corresponding Prop-valued existence predicate. +* `MetricTensor E H M n I`: the common core data of a smooth symmetric nondegenerate bilinear form on + each tangent space. +* `PseudoRiemannianMetric E H M n I`: a `MetricTensor` whose pointwise index is locally constant. -* `PseudoRiemannianMetric E H M n I`: A structure representing a `C^n` pseudo-Riemannian metric - on a manifold `M` modelled on `(E, H)` with model with corners `I`. It consists of a family - of non-degenerate, symmetric, continuous bilinear forms `gₓ` on each tangent space `TₓM`, - varying `C^n`-smoothly with `x` and having a locally constant negative dimension (`negDim`). - The model space `E` must be finite-dimensional, and the manifold `M` must be `C^{n+1}` smooth. +## Implementation notes -* `PseudoRiemannianMetric.flatEquiv g x`: The "musical isomorphism" from the tangent space at `x` - to its dual space, representing the canonical isomorphism induced by the metric. +Smoothness is stated as a `ContMDiff` assumption for a bundled map `x ↦ TotalSpace.mk' … x (gₓ)` as +in Mathlib. Index-type constructions use `QuadraticForm.negDim` and therefore require +finite-dimensional tangent spaces. -* `PseudoRiemannianMetric.sharpEquiv g x`: The inverse of the flat isomorphism, mapping from - the dual space back to the tangent space. +## Tags -* `PseudoRiemannianMetric.toQuadraticForm g x`: The quadratic form `v ↦ gₓ(v, v)` associated - with the metric at point `x`. - -We show smoothness with same pattern as mathlib Riemannian API: -the metric is a section of the (vector-bundle) bundle of bilinear forms, and the smoothness -assumption is a `ContMDiff` statement for this section (instead of being phrased chartwise). - -## Reference - -* Barrett O'Neill, "Semi-Riemannian Geometry With Applications to Relativity" (Academic Press, 1983) -* [Discussion on Zulip about (Pseudo) Riemannian metrics] https. -leanprover.zulipchat.com/#narrow/channel/113488-general/topic/.28Pseudo.29.20Riemannian.20metric +pseudo-Riemannian, metric tensor, musical isomorphisms, index -/ section PseudoRiemannianMetric @@ -202,7 +190,8 @@ lemma ContMDiffWithinAt.metric_bundle simp only [contMDiffWithinAt_totalSpace] at this exact this.2 -/-- Given two smooth maps into the same fibers of a pseudo-Riemannian bundle, their pairing is smooth. -/ +/-- Given two smooth maps into the same fibers of a pseudo-Riemannian bundle, their pairing is +smooth. -/ lemma ContMDiffWithinAt.pseudoInner_bundle (hv : ContMDiffWithinAt IM (IB.prod 𝓘(ℝ, F)) n (fun m ↦ (v m : TotalSpace F E)) s x) (hw : ContMDiffWithinAt IM (IB.prod 𝓘(ℝ, F)) n (fun m ↦ (w m : TotalSpace F E)) s x) : @@ -241,29 +230,13 @@ end ContMDiff end Bundle -/-! ## Pseudo-Riemannian Metric -/ - -/-! -Constructs a `QuadraticForm` on the tangent space `TₓM` at a point `x` from the -value of a pseudo-Riemannian metric at that point. -(O'Neill, p. 47, "The function q: V → R given by q(v) = b(v,v) is the associated quadratic -form of b.") -The pseudo-Riemannian metric is given by `val`, a family of continuous bilinear forms -`gₓ: TₓM × TₓM → ℝ` for each `x : M`. -The quadratic form `Qₓ` at `x` is defined as `Qₓ(v) = gₓ(v,v)`. -The associated symmetric bilinear form required by `QuadraticForm.exists_companion'` -is `Bₓ(v,w) = gₓ(v,w) + gₓ(w,v)`. Given the symmetry `symm`, this is `2 * gₓ(v,w)`. --/ +/-! ## Quadratic-form helper -/ namespace PseudoRiemannianMetric -/-- -Turn a (curried) bilinear form `val` on each tangent space into the associated quadratic form -`v ↦ val x v v`. - -This helper is intentionally public: it is the bridge between a bundled description of a metric -(`val` + symmetry) and quadratic-form invariants such as `QuadraticForm.negDim`. --/ +/-- Turn a (curried) symmetric bilinear map on each tangent space into the associated quadratic form +`v ↦ val x v v`. This is the entry point to quadratic-form invariants (e.g. `QuadraticForm.negDim`) +from bundled metric data. -/ def valToQuadraticForm {E : Type v} [NormedAddCommGroup E] [NormedSpace ℝ E] {H : Type w} [TopologicalSpace H] @@ -577,7 +550,8 @@ noncomputable def cotangentToBilinForm (g : MetricTensor E H M n I) (x : M) : toFun ω₁ := { toFun := fun ω₂ => cotangentMetricVal g x ω₁ ω₂ map_add' := fun ω₂ ω₃ => by simp [cotangentMetricVal, ContinuousLinearMap.map_add] - map_smul' := fun c ω₂ => by simp [cotangentMetricVal, map_smul, smul_eq_mul, RingHom.id_apply] } + map_smul' := fun c ω₂ => by simp [cotangentMetricVal, map_smul, smul_eq_mul, RingHom.id_apply] + } map_add' := fun ω₁ ω₂ => by ext ω₃ simp [cotangentMetricVal, ContinuousLinearMap.map_add, ContinuousLinearMap.add_apply, @@ -914,7 +888,8 @@ noncomputable abbrev cotangentToQuadraticForm (g : PseudoRiemannianMetric E H M QuadraticForm ℝ (TangentSpace I x →L[ℝ] ℝ) := MetricTensor.cotangentToQuadraticForm (g := (g.toMetricTensor : MetricTensor E H M n I)) x -theorem cotangentToQuadraticForm_equivalent_toQuadraticForm (g : PseudoRiemannianMetric E H M n I) (x : M) : +theorem cotangentToQuadraticForm_equivalent_toQuadraticForm (g : PseudoRiemannianMetric E H M n I) + (x : M) : (g.cotangentToQuadraticForm x).Equivalent (g.toQuadraticForm x) := by simpa [cotangentToQuadraticForm, toQuadraticForm] using (MetricTensor.cotangentToQuadraticForm_equivalent_toQuadraticForm diff --git a/PhysLean/Mathematics/Geometry/Metric/QuadraticForm/NegDim.lean b/PhysLean/Mathematics/Geometry/Metric/QuadraticForm/NegDim.lean index 658cfb590..ac78f3ada 100644 --- a/PhysLean/Mathematics/Geometry/Metric/QuadraticForm/NegDim.lean +++ b/PhysLean/Mathematics/Geometry/Metric/QuadraticForm/NegDim.lean @@ -68,7 +68,7 @@ abbrev nullity (s : Signature) : ℕ := s.zero end Signature -/-- For a standard basis vector in a weighted sum of squares, only one term in the sum is nonzero. -/ +/-- For a basis vector in a weighted sum of squares, only one term in the sum is nonzero. -/ lemma QuadraticMap.weightedSumSquares_basis_vector {E : Type*} [AddCommGroup E] [Module ℝ E] {weights : Fin (finrank ℝ E) → ℝ} {i : Fin (finrank ℝ E)} (v : Fin (finrank ℝ E) → ℝ) (hv : ∀ j, v j = if j = i then 1 else 0) : @@ -236,7 +236,8 @@ lemma posIndex_le_of_equivalent [FiniteDimensional ℝ V] {V₂ : Type*} [AddCom exact le_posIndex_of_exists (V := V₂) hk /-- `posIndex` is invariant under equivalence of quadratic forms. -/ -theorem posIndex_eq_of_equivalent [FiniteDimensional ℝ V] {V₂ : Type*} [AddCommGroup V₂] [Module ℝ V₂] +theorem posIndex_eq_of_equivalent [FiniteDimensional ℝ V] {V₂ : Type*} [AddCommGroup V₂] + [Module ℝ V₂] [FiniteDimensional ℝ V₂] {Q : QuadraticForm ℝ V} {Q₂ : QuadraticForm ℝ V₂} (h : Q.Equivalent Q₂) : posIndex (V := V) Q = posIndex (V := V₂) Q₂ := by @@ -327,13 +328,16 @@ noncomputable def signature [FiniteDimensional ℝ E] (Q : QuadraticForm ℝ E) ⟨Q.posDim, Q.negDim, Q.zeroDim⟩ @[simp] -lemma signature_pos [FiniteDimensional ℝ E] (Q : QuadraticForm ℝ E) : Q.signature.pos = Q.posDim := rfl +lemma signature_pos [FiniteDimensional ℝ E] (Q : QuadraticForm ℝ E) : Q.signature.pos = Q.posDim := + rfl @[simp] -lemma signature_neg [FiniteDimensional ℝ E] (Q : QuadraticForm ℝ E) : Q.signature.neg = Q.negDim := rfl +lemma signature_neg [FiniteDimensional ℝ E] (Q : QuadraticForm ℝ E) : Q.signature.neg = Q.negDim := + rfl @[simp] -lemma signature_zero [FiniteDimensional ℝ E] (Q : QuadraticForm ℝ E) : Q.signature.zero = Q.zeroDim := rfl +lemma signature_zero [FiniteDimensional ℝ E] (Q : QuadraticForm ℝ E) : Q.signature.zero = + Q.zeroDim := rfl @[simp] lemma signature_def [FiniteDimensional ℝ E] (Q : QuadraticForm ℝ E) : @@ -438,7 +442,8 @@ theorem negDim_eq_of_equivalent [FiniteDimensional ℝ E] {E₂ : Type*} [AddCom have h' : (-Q).Equivalent (-Q₂) := Equivalent.neg (E := E) (E₂ := E₂) h simp [negDim, posIndex_eq_of_equivalent (Q := -Q) (Q₂ := -Q₂) h'] -theorem zeroDim_eq_of_equivalent [FiniteDimensional ℝ E] {E₂ : Type*} [AddCommGroup E₂] [Module ℝ E₂] +theorem zeroDim_eq_of_equivalent [FiniteDimensional ℝ E] {E₂ : Type*} [AddCommGroup E₂] + [Module ℝ E₂] [FiniteDimensional ℝ E₂] {Q : QuadraticForm ℝ E} {Q₂ : QuadraticForm ℝ E₂} (h : Q.Equivalent Q₂) : Q.zeroDim = Q₂.zeroDim := by @@ -450,7 +455,8 @@ theorem zeroDim_eq_of_equivalent [FiniteDimensional ℝ E] {E₂ : Type*} [AddCo posIndex_eq_of_equivalent (Q := -Q) (Q₂ := -Q₂) (Equivalent.neg (E := E) (E₂ := E₂) ⟨e⟩) simp [zeroDim, posDim, negDim, hfin, hposI, hnegI] -theorem signature_eq_of_equivalent [FiniteDimensional ℝ E] {E₂ : Type*} [AddCommGroup E₂] [Module ℝ E₂] +theorem signature_eq_of_equivalent [FiniteDimensional ℝ E] {E₂ : Type*} [AddCommGroup E₂] + [Module ℝ E₂] [FiniteDimensional ℝ E₂] {Q : QuadraticForm ℝ E} {Q₂ : QuadraticForm ℝ E₂} (h : Q.Equivalent Q₂) : Q.signature = Q₂.signature := by @@ -545,7 +551,8 @@ def restrictPos (w : Fin n → SignType) : (i : {i // i ∈ posSet (n := n) w}) : restrictPos (n := n) w v i = v i.1 := rfl -/-- If a vector has no positive-weight coordinates, then its value under `diagForm w` is nonpositive. -/ +/-- If a vector has no positive-weight coordinates, then its value under `diagForm w` is +nonpositive. -/ lemma diagForm_nonpos_of_no_pos {w : Fin n → SignType} {v : Fin n → ℝ} (hv : ∀ i, w i = SignType.pos → v i = 0) : diagForm (n := n) w v ≤ 0 := by @@ -592,7 +599,8 @@ lemma isPosDefOn_diagForm_supportedOnPos (w : Fin n → SignType) : simpa [hwpos, pow_two] using this exact lt_of_lt_of_le hterm_pos hle -/-- The submodule `supportedOnPos w` is linearly equivalent to functions on the positive index set. -/ +/-- The submodule `supportedOnPos w` is linearly equivalent to functions on the positive index +set. -/ noncomputable def supportedOnPosEquiv (w : Fin n → SignType) : supportedOnPos (n := n) w ≃ₗ[ℝ] ({i // i ∈ posSet (n := n) w} → ℝ) := by refine diff --git a/PhysLean/Mathematics/Geometry/Metric/QuadraticForm/Sylvester.lean b/PhysLean/Mathematics/Geometry/Metric/QuadraticForm/Sylvester.lean index c6e097537..000d8f343 100644 --- a/PhysLean/Mathematics/Geometry/Metric/QuadraticForm/Sylvester.lean +++ b/PhysLean/Mathematics/Geometry/Metric/QuadraticForm/Sylvester.lean @@ -16,8 +16,7 @@ This file provides auxiliary, **noncanonical** data extracted from the existence The idea is that `equivalent_signType_weighted_sum_squared` produces an existential witness of diagonalization; here we choose such a witness noncomputably. -These definitions are intended for *bridging* with diagonal computations. They are not meant to be -the primary API: the canonical inertia indices are defined in +The canonical inertia indices are defined in `PhysLean.Mathematics.Geometry.Metric.QuadraticForm.NegDim` (via `QuadraticForm.posIndex`) and are invariant under `QuadraticForm.Equivalent` without any diagonalization choice. @@ -26,7 +25,7 @@ invariant under `QuadraticForm.Equivalent` without any diagonalization choice. - `QuadraticForm.signTypeWeights`: a chosen Sylvester diagonalization weight function - `QuadraticForm.signatureChoice`: the resulting chosen signature `(pos, neg, zero)` -## Main results +## Main statements - `QuadraticForm.signature_eq_signatureChoice`: the canonical signature equals the chosen one diff --git a/PhysLean/Mathematics/Geometry/Metric/Riemannian/Defs.lean b/PhysLean/Mathematics/Geometry/Metric/Riemannian/Defs.lean index 70a14f4b9..23be1b216 100644 --- a/PhysLean/Mathematics/Geometry/Metric/Riemannian/Defs.lean +++ b/PhysLean/Mathematics/Geometry/Metric/Riemannian/Defs.lean @@ -7,17 +7,21 @@ import PhysLean.Mathematics.Geometry.Metric.PseudoRiemannian.Defs import Mathlib.Geometry.Manifold.VectorBundle.Riemannian import Mathlib.Geometry.Manifold.VectorBundle.Tangent /-! -# Riemannian Metric Definitions +# Riemannian metrics (tangent bundle) -This file builds around the modern Mathlib Riemannian metric API. +This file defines `RiemannianMetric` as the specialization of Mathlib's bundle-level +`Bundle.ContMDiffRiemannianMetric` to the tangent bundle, and provides a coercion to +`PseudoRiemannianMetric` by forgetting positivity. -Concretely, a `C^n` Riemannian metric on a manifold is a smooth section of the bundle of bilinear -forms on the tangent bundle, packaged as `Bundle.ContMDiffRiemannianMetric`. +## Main definitions -We provide: -- an abbreviation `RiemannianMetric` for the tangent-bundle specialization, and -- a coercion to the PhysLean `PseudoRiemannianMetric` (by forgetting positivity and remembering - nondegeneracy + constant index `0`). +* `PseudoRiemannianMetric.RiemannianMetric`: a `C^n` Riemannian metric on `M`, packaged bundle-first. +* `PseudoRiemannianMetric.RiemannianMetric.toPseudoRiemannianMetric`: forget positivity to obtain a + pseudo-Riemannian metric (index `0`). + +## Tags + +Riemannian, pseudo-Riemannian -/ namespace PseudoRiemannianMetric @@ -90,7 +94,6 @@ lemma toPseudoRiemannianMetric_posIndex_neg_toQuadraticForm (g : RiemannianMetri apply QuadraticForm.negDim_posDef intro v hv simpa [PseudoRiemannianMetric.valToQuadraticForm] using g.pos x v hv - -- `negDim Q = (-Q).posIndex`. simpa [PseudoRiemannianMetric.toQuadraticForm, toPseudoRiemannianMetric, QuadraticForm.negDim] using hx lemma toPseudoRiemannianMetric_index (g : RiemannianMetric (I := I) (n := n) M) (x : M) : @@ -104,7 +107,7 @@ instance : end RiemannianMetric -/-! ## Existence helpers -/ +/-! ## Existence helper -/ /-- Existence of a Riemannian metric implies existence of a pseudo-Riemannian metric (of index `0`), by forgetting positivity. -/ From 634eed22197147b8cba7612c0e79a877f5ba503c Mon Sep 17 00:00:00 2001 From: Matteo CIpollina Date: Tue, 3 Mar 2026 14:59:49 +0100 Subject: [PATCH 22/36] fix lints --- .../Geometry/Metric/PseudoRiemannian/Defs.lean | 15 ++++++++------- .../Geometry/Metric/Riemannian/Defs.lean | 10 ++++++---- 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/PhysLean/Mathematics/Geometry/Metric/PseudoRiemannian/Defs.lean b/PhysLean/Mathematics/Geometry/Metric/PseudoRiemannian/Defs.lean index 27910e1db..c72c6169c 100644 --- a/PhysLean/Mathematics/Geometry/Metric/PseudoRiemannian/Defs.lean +++ b/PhysLean/Mathematics/Geometry/Metric/PseudoRiemannian/Defs.lean @@ -31,6 +31,10 @@ Smoothness is stated as a `ContMDiff` assumption for a bundled map `x ↦ TotalS in Mathlib. Index-type constructions use `QuadraticForm.negDim` and therefore require finite-dimensional tangent spaces. +If the fibers already carry a topology (e.g. the tangent bundle), we register the fiberwise metric +through `Bundle.PseudoRiemannianBundle` to avoid introducing diamonds in typeclass inference, in the +same spirit as Mathlib's `Bundle.RiemannianBundle`. + ## Tags pseudo-Riemannian, metric tensor, musical isomorphisms, index @@ -664,13 +668,10 @@ noncomputable def index (g : MetricTensor E H M n I) (x : M) end MetricTensor -/-- A pseudo-Riemannian metric of smoothness class `C^n` on a manifold `M` modelled on `(E, H)` -with model `I`. This structure defines a smoothly varying, non-degenerate, symmetric, -continuous bilinear form `gₓ` of constant negative dimension on the tangent space `TₓM` -at each point `x`. Requires `M` to be `C^{n+1}` smooth. -This structure formalizes O'Neill's Definition 3.1 (p. 54) of a metric tensor `g` on `M` -as a "symmetric non-degenerate (0,2) tensor field on M of constant index." -Each `gₓ` is a scalar product (O'Neill, Definition 20, p. 47) on `TₓM`. -/ +/-- A `C^n` pseudo-Riemannian metric on a manifold. + +This is a `MetricTensor` whose pointwise index (defined using `QuadraticForm.negDim`) is locally +constant. -/ @[ext] structure PseudoRiemannianMetric (E : Type v) (H : Type w) (M : Type*) (n : WithTop ℕ∞) diff --git a/PhysLean/Mathematics/Geometry/Metric/Riemannian/Defs.lean b/PhysLean/Mathematics/Geometry/Metric/Riemannian/Defs.lean index 23be1b216..5fced587a 100644 --- a/PhysLean/Mathematics/Geometry/Metric/Riemannian/Defs.lean +++ b/PhysLean/Mathematics/Geometry/Metric/Riemannian/Defs.lean @@ -15,7 +15,7 @@ This file defines `RiemannianMetric` as the specialization of Mathlib's bundle-l ## Main definitions -* `PseudoRiemannianMetric.RiemannianMetric`: a `C^n` Riemannian metric on `M`, packaged bundle-first. +* `PseudoRiemannianMetric.RiemannianMetric`: a `C^n` Riemannian metric on `M`. * `PseudoRiemannianMetric.RiemannianMetric.toPseudoRiemannianMetric`: forget positivity to obtain a pseudo-Riemannian metric (index `0`). @@ -66,7 +66,7 @@ def toPseudoRiemannianMetric (g : RiemannianMetric (I := I) (n := n) M) : exact (ne_of_gt hp h0).elim contMDiff := g.contMDiff negDim_isLocallyConstant := by - -- On a Riemannian metric, the associated quadratic form is positive definite, hence `negDim = 0`. + -- On a Riemannian metric, the associated quadratic form is positive definite, so `negDim = 0`. refine IsLocallyConstant.of_constant _ (fun x y => ?_) have hx : (PseudoRiemannianMetric.valToQuadraticForm g.inner g.symm x).negDim = 0 := by @@ -87,14 +87,16 @@ def toPseudoRiemannianMetric (g : RiemannianMetric (I := I) (n := n) M) : simp [hx', hy'] @[simp] -lemma toPseudoRiemannianMetric_posIndex_neg_toQuadraticForm (g : RiemannianMetric (I := I) (n := n) M) +lemma toPseudoRiemannianMetric_posIndex_neg_toQuadraticForm (g : RiemannianMetric (I := I) + [FiniteDimensional ℝ (TangentSpace I x)] (n := n) M) (x : M) : (-(toPseudoRiemannianMetric (I := I) (n := n) (M := M) g).toQuadraticForm x).posIndex = 0 := by have hx : (PseudoRiemannianMetric.valToQuadraticForm g.inner g.symm x).negDim = 0 := by apply QuadraticForm.negDim_posDef intro v hv simpa [PseudoRiemannianMetric.valToQuadraticForm] using g.pos x v hv - simpa [PseudoRiemannianMetric.toQuadraticForm, toPseudoRiemannianMetric, QuadraticForm.negDim] using hx + simpa [PseudoRiemannianMetric.toQuadraticForm, toPseudoRiemannianMetric, QuadraticForm.negDim] + using hx lemma toPseudoRiemannianMetric_index (g : RiemannianMetric (I := I) (n := n) M) (x : M) : (toPseudoRiemannianMetric (I := I) (n := n) (M := M) g).index x = 0 := by From 0ba2585d59c9038cba57a8ee3b3c7eef8dee6ac5 Mon Sep 17 00:00:00 2001 From: Matteo CIpollina Date: Tue, 3 Mar 2026 15:02:22 +0100 Subject: [PATCH 23/36] Update NegDim.lean --- PhysLean/Mathematics/Geometry/Metric/QuadraticForm/NegDim.lean | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PhysLean/Mathematics/Geometry/Metric/QuadraticForm/NegDim.lean b/PhysLean/Mathematics/Geometry/Metric/QuadraticForm/NegDim.lean index ac78f3ada..f83652c8f 100644 --- a/PhysLean/Mathematics/Geometry/Metric/QuadraticForm/NegDim.lean +++ b/PhysLean/Mathematics/Geometry/Metric/QuadraticForm/NegDim.lean @@ -341,7 +341,7 @@ lemma signature_zero [FiniteDimensional ℝ E] (Q : QuadraticForm ℝ E) : Q.sig @[simp] lemma signature_def [FiniteDimensional ℝ E] (Q : QuadraticForm ℝ E) : - Q.signature = ⟨Q.posDim, Q.negDim, Q.zeroDim⟩ := rfl + Q.signature = ⟨Q.posDim, Q.negDim, Q.zeroDim⟩ := rfl lemma posDim_add_negDim_add_zeroDim [FiniteDimensional ℝ E] (Q : QuadraticForm ℝ E) : Q.posDim + Q.negDim + Q.zeroDim = finrank ℝ E := by From d77efc96bd5e7340b3da65cd9bfa1b41a5fa5922 Mon Sep 17 00:00:00 2001 From: Matteo CIpollina Date: Tue, 3 Mar 2026 15:12:12 +0100 Subject: [PATCH 24/36] Update Defs.lean --- .../Metric/PseudoRiemannian/Defs.lean | 65 +++++++++++++------ 1 file changed, 45 insertions(+), 20 deletions(-) diff --git a/PhysLean/Mathematics/Geometry/Metric/PseudoRiemannian/Defs.lean b/PhysLean/Mathematics/Geometry/Metric/PseudoRiemannian/Defs.lean index c72c6169c..1c4734c09 100644 --- a/PhysLean/Mathematics/Geometry/Metric/PseudoRiemannian/Defs.lean +++ b/PhysLean/Mathematics/Geometry/Metric/PseudoRiemannian/Defs.lean @@ -15,6 +15,11 @@ import PhysLean.Mathematics.Geometry.Metric.QuadraticForm.NegDim This file defines pseudo-Riemannian metrics on smooth manifolds, in a way that mirrors Mathlib's bundle-first Riemannian metric API. +A pseudo-Riemannian metric is a smoothly varying symmetric nondegenerate bilinear form on each +tangent space, whose index (negative inertia) is locally constant. The index is formalized using +`QuadraticForm.negDim`. In finite dimension, a metric induces the usual musical isomorphisms +(`flat`/`sharp`) and an induced metric on the cotangent spaces. + ## Main definitions * `Bundle.PseudoRiemannianBundle`: fiberwise data of a symmetric nondegenerate bilinear form. @@ -38,6 +43,10 @@ same spirit as Mathlib's `Bundle.RiemannianBundle`. ## Tags pseudo-Riemannian, metric tensor, musical isomorphisms, index + +## References + +* Barrett O'Neill, *Semi-Riemannian Geometry with Applications to Relativity*, Academic Press (1983). -/ section PseudoRiemannianMetric @@ -164,6 +173,26 @@ instance [IsContMDiffPseudoRiemannianBundle (IB := IB) (n := (3 : WithTop ℕ∞ IsContMDiffPseudoRiemannianBundle.of_le (IB := IB) (n := (3 : WithTop ℕ∞)) (F := F) (E := E) (by norm_cast) +namespace ContMDiffPseudoRiemannianMetric + +/-- A smooth pseudo-Riemannian metric along a bundle induces the corresponding fiberwise structure. -/ +def toPseudoRiemannianBundle + (g : ContMDiffPseudoRiemannianMetric (IB := IB) (n := n) (F := F) (E := E)) : + PseudoRiemannianBundle (B := B) (E := E) where + metric := g.metric + symm := g.symm + nondegenerate := g.nondegenerate + +instance (g : ContMDiffPseudoRiemannianMetric (IB := IB) (n := n) (F := F) (E := E)) : + letI : PseudoRiemannianBundle (B := B) (E := E) := toPseudoRiemannianBundle (IB := IB) (n := n) + (F := F) (E := E) g + IsContMDiffPseudoRiemannianBundle (IB := IB) (n := n) (F := F) (E := E) := + letI : PseudoRiemannianBundle (B := B) (E := E) := toPseudoRiemannianBundle (IB := IB) (n := n) + (F := F) (E := E) g + ⟨⟨g, fun _ _ _ => rfl⟩⟩ + +end ContMDiffPseudoRiemannianMetric + section ContMDiffPairing variable @@ -238,9 +267,11 @@ end Bundle namespace PseudoRiemannianMetric -/-- Turn a (curried) symmetric bilinear map on each tangent space into the associated quadratic form -`v ↦ val x v v`. This is the entry point to quadratic-form invariants (e.g. `QuadraticForm.negDim`) -from bundled metric data. -/ +/-- Turn a (curried) symmetric bilinear map on a tangent space into the associated quadratic form +`v ↦ val x v v`. + +This is the entry point to quadratic-form invariants (e.g. `QuadraticForm.negDim`) from bundled +metric data; compare O'Neill, *Semi-Riemannian Geometry* (1983), p. 47. -/ def valToQuadraticForm {E : Type v} [NormedAddCommGroup E] [NormedSpace ℝ E] {H : Type w} [TopologicalSpace H] @@ -498,14 +529,12 @@ lemma sharpL_apply_flatL (g : MetricTensor E H M n I) (x : M) (v : TangentSpace @[simp] lemma flatL_apply_sharpL (g : MetricTensor E H M n I) (x : M) - (ω : TangentSpace I x →L[ℝ] ℝ) : - g.flatL x (g.sharpL x ω) = ω := + (ω : TangentSpace I x →L[ℝ] ℝ) : g.flatL x (g.sharpL x ω) = ω := (g.flatEquiv x).right_inv ω @[simp] lemma flat_sharp_apply (g : MetricTensor E H M n I) (x : M) - (ω : TangentSpace I x →L[ℝ] ℝ) : - g.flat x (g.sharp x ω) = ω := by + (ω : TangentSpace I x →L[ℝ] ℝ) : g.flat x (g.sharp x ω) = ω := by ext v have h := congrArg (fun f : TangentSpace I x →L[ℝ] ℝ => f v) (flatL_apply_sharpL (g := g) x ω) simpa [flat, flatL, sharp, sharpL] using h @@ -670,8 +699,9 @@ end MetricTensor /-- A `C^n` pseudo-Riemannian metric on a manifold. -This is a `MetricTensor` whose pointwise index (defined using `QuadraticForm.negDim`) is locally -constant. -/ +This is a smooth symmetric nondegenerate bilinear form on each tangent space whose index +(`QuadraticForm.negDim`) is locally constant (O'Neill, *Semi-Riemannian Geometry* (1983), +Definition 3.1). -/ @[ext] structure PseudoRiemannianMetric (E : Type v) (H : Type w) (M : Type*) (n : WithTop ℕ∞) @@ -715,18 +745,16 @@ abbrev toQuadraticForm (g : PseudoRiemannianMetric E H M n I) (x : M) : /-- Coercion from PseudoRiemannianMetric to its function representation. -/ instance coeFunInst : CoeFun (PseudoRiemannianMetric E H M n I) - (fun _ => ∀ x : M, TangentSpace I x →L[ℝ] (TangentSpace I x →L[ℝ] ℝ)) where + (fun _ => ∀ x : M, TangentSpace I x →L[ℝ] (TangentSpace I x →L[ℝ] ℝ)) where coe g := g.val @[simp] lemma toBilinForm_apply (g : PseudoRiemannianMetric E H M n I) (x : M) - (v w : TangentSpace I x) : - toBilinForm g x v w = g.val x v w := rfl + (v w : TangentSpace I x) : toBilinForm g x v w = g.val x v w := rfl @[simp] lemma toQuadraticForm_apply (g : PseudoRiemannianMetric E H M n I) (x : M) - (v : TangentSpace I x) : - toQuadraticForm g x v = g.val x v v := rfl + (v : TangentSpace I x) : toQuadraticForm g x v = g.val x v v := rfl /-! ## Index (negative inertia) -/ @@ -744,18 +772,15 @@ lemma index_isLocallyConstant (g : PseudoRiemannianMetric E H M n I) : simpa [index, toQuadraticForm] using g.negDim_isLocallyConstant lemma index_eq_of_isPreconnected (g : PseudoRiemannianMetric E H M n I) {s : Set M} - (hs : IsPreconnected s) {x y : M} (hx : x ∈ s) (hy : y ∈ s) : - g.index x = g.index y := + (hs : IsPreconnected s) {x y : M} (hx : x ∈ s) (hy : y ∈ s) : g.index x = g.index y := (index_isLocallyConstant (g := g)).apply_eq_of_isPreconnected hs hx hy lemma index_eq_of_preconnectedSpace [PreconnectedSpace M] (g : PseudoRiemannianMetric E H M n I) - (x y : M) : - g.index x = g.index y := + (x y : M) : g.index x = g.index y := (index_isLocallyConstant (g := g)).apply_eq_of_preconnectedSpace x y lemma index_eq_of_mem_connectedComponent (g : PseudoRiemannianMetric E H M n I) (x y : M) - (hy : y ∈ connectedComponent x) : - g.index y = g.index x := + (hy : y ∈ connectedComponent x) : g.index y = g.index x := (index_isLocallyConstant (g := g)).apply_eq_of_isPreconnected (isConnected_connectedComponent.isPreconnected) hy (mem_connectedComponent : x ∈ connectedComponent x) From 2129af0e1c080f750e5b0cf8012f5d976f6ef730 Mon Sep 17 00:00:00 2001 From: Matteo CIpollina Date: Tue, 3 Mar 2026 15:16:57 +0100 Subject: [PATCH 25/36] fix lints + minor fixes --- .../Geometry/Metric/PseudoRiemannian/Defs.lean | 12 ++++++++---- .../Geometry/Metric/QuadraticForm/NegDim.lean | 6 ++++-- .../Mathematics/Geometry/Metric/Riemannian/Defs.lean | 12 ++++++++---- 3 files changed, 20 insertions(+), 10 deletions(-) diff --git a/PhysLean/Mathematics/Geometry/Metric/PseudoRiemannian/Defs.lean b/PhysLean/Mathematics/Geometry/Metric/PseudoRiemannian/Defs.lean index 1c4734c09..465c1d8d6 100644 --- a/PhysLean/Mathematics/Geometry/Metric/PseudoRiemannian/Defs.lean +++ b/PhysLean/Mathematics/Geometry/Metric/PseudoRiemannian/Defs.lean @@ -89,7 +89,8 @@ abbrev pseudoInner (x : B) (v w : E x) : ℝ := omit [TopologicalSpace B] in @[simp] lemma pseudoInner_def (x : B) (v w : E x) : - pseudoInner (B := B) (E := E) x v w = metric (B := B) (E := E) x v w := rfl + pseudoInner (B := B) (E := E) x v w = metric (B := B) (E := E) x v w := + rfl omit [TopologicalSpace B] in lemma pseudoInner_symm (x : B) (v w : E x) : @@ -750,11 +751,13 @@ instance coeFunInst : CoeFun (PseudoRiemannianMetric E H M n I) @[simp] lemma toBilinForm_apply (g : PseudoRiemannianMetric E H M n I) (x : M) - (v w : TangentSpace I x) : toBilinForm g x v w = g.val x v w := rfl + (v w : TangentSpace I x) : toBilinForm g x v w = g.val x v w := + rfl @[simp] lemma toQuadraticForm_apply (g : PseudoRiemannianMetric E H M n I) (x : M) - (v : TangentSpace I x) : toQuadraticForm g x v = g.val x v v := rfl + (v : TangentSpace I x) : toQuadraticForm g x v = g.val x v v := + rfl /-! ## Index (negative inertia) -/ @@ -765,7 +768,8 @@ noncomputable def index (g : PseudoRiemannianMetric E H M n I) (x : M) : ℕ := @[simp] lemma index_def (g : PseudoRiemannianMetric E H M n I) (x : M) : - g.index x = (g.toQuadraticForm x).negDim := rfl + g.index x = (g.toQuadraticForm x).negDim := + rfl lemma index_isLocallyConstant (g : PseudoRiemannianMetric E H M n I) : IsLocallyConstant (fun x : M => g.index x) := by diff --git a/PhysLean/Mathematics/Geometry/Metric/QuadraticForm/NegDim.lean b/PhysLean/Mathematics/Geometry/Metric/QuadraticForm/NegDim.lean index f83652c8f..1e63e764c 100644 --- a/PhysLean/Mathematics/Geometry/Metric/QuadraticForm/NegDim.lean +++ b/PhysLean/Mathematics/Geometry/Metric/QuadraticForm/NegDim.lean @@ -337,11 +337,13 @@ lemma signature_neg [FiniteDimensional ℝ E] (Q : QuadraticForm ℝ E) : Q.sign @[simp] lemma signature_zero [FiniteDimensional ℝ E] (Q : QuadraticForm ℝ E) : Q.signature.zero = - Q.zeroDim := rfl + Q.zeroDim := + rfl @[simp] lemma signature_def [FiniteDimensional ℝ E] (Q : QuadraticForm ℝ E) : - Q.signature = ⟨Q.posDim, Q.negDim, Q.zeroDim⟩ := rfl + Q.signature = ⟨Q.posDim, Q.negDim, Q.zeroDim⟩ := + rfl lemma posDim_add_negDim_add_zeroDim [FiniteDimensional ℝ E] (Q : QuadraticForm ℝ E) : Q.posDim + Q.negDim + Q.zeroDim = finrank ℝ E := by diff --git a/PhysLean/Mathematics/Geometry/Metric/Riemannian/Defs.lean b/PhysLean/Mathematics/Geometry/Metric/Riemannian/Defs.lean index 5fced587a..8b543fcf1 100644 --- a/PhysLean/Mathematics/Geometry/Metric/Riemannian/Defs.lean +++ b/PhysLean/Mathematics/Geometry/Metric/Riemannian/Defs.lean @@ -87,9 +87,8 @@ def toPseudoRiemannianMetric (g : RiemannianMetric (I := I) (n := n) M) : simp [hx', hy'] @[simp] -lemma toPseudoRiemannianMetric_posIndex_neg_toQuadraticForm (g : RiemannianMetric (I := I) - [FiniteDimensional ℝ (TangentSpace I x)] (n := n) M) - (x : M) : +lemma toPseudoRiemannianMetric_posIndex_neg_toQuadraticForm + (g : RiemannianMetric (I := I) (n := n) M) (x : M) : (-(toPseudoRiemannianMetric (I := I) (n := n) (M := M) g).toQuadraticForm x).posIndex = 0 := by have hx : (PseudoRiemannianMetric.valToQuadraticForm g.inner g.symm x).negDim = 0 := by apply QuadraticForm.negDim_posDef @@ -100,7 +99,12 @@ lemma toPseudoRiemannianMetric_posIndex_neg_toQuadraticForm (g : RiemannianMetri lemma toPseudoRiemannianMetric_index (g : RiemannianMetric (I := I) (n := n) M) (x : M) : (toPseudoRiemannianMetric (I := I) (n := n) (M := M) g).index x = 0 := by - simp [PseudoRiemannianMetric.index, QuadraticForm.negDim] + have hx : (PseudoRiemannianMetric.valToQuadraticForm g.inner g.symm x).negDim = 0 := by + apply QuadraticForm.negDim_posDef + intro v hv + simpa [PseudoRiemannianMetric.valToQuadraticForm] using g.pos x v hv + simpa [PseudoRiemannianMetric.index, PseudoRiemannianMetric.toQuadraticForm, + toPseudoRiemannianMetric] using hx instance : Coe (RiemannianMetric (I := I) (n := n) M) From f25761100ce663aac28394a44efafc9ad14570f3 Mon Sep 17 00:00:00 2001 From: Matteo CIpollina Date: Tue, 3 Mar 2026 15:19:28 +0100 Subject: [PATCH 26/36] Update Defs.lean --- .../Geometry/Metric/PseudoRiemannian/Defs.lean | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/PhysLean/Mathematics/Geometry/Metric/PseudoRiemannian/Defs.lean b/PhysLean/Mathematics/Geometry/Metric/PseudoRiemannian/Defs.lean index 465c1d8d6..86c2f6022 100644 --- a/PhysLean/Mathematics/Geometry/Metric/PseudoRiemannian/Defs.lean +++ b/PhysLean/Mathematics/Geometry/Metric/PseudoRiemannian/Defs.lean @@ -26,8 +26,8 @@ tangent space, whose index (negative inertia) is locally constant. The index is * `Bundle.ContMDiffPseudoRiemannianMetric`: a `C^n` pseudo-Riemannian metric along a vector bundle, expressed as a smooth section into the bundle of bilinear forms. * `Bundle.IsContMDiffPseudoRiemannianBundle`: the corresponding Prop-valued existence predicate. -* `MetricTensor E H M n I`: the common core data of a smooth symmetric nondegenerate bilinear form on - each tangent space. +* `MetricTensor E H M n I`: the common core data of a smooth symmetric nondegenerate bilinear form + on each tangent space. * `PseudoRiemannianMetric E H M n I`: a `MetricTensor` whose pointwise index is locally constant. ## Implementation notes @@ -46,7 +46,8 @@ pseudo-Riemannian, metric tensor, musical isomorphisms, index ## References -* Barrett O'Neill, *Semi-Riemannian Geometry with Applications to Relativity*, Academic Press (1983). +* Barrett O'Neill, *Semi-Riemannian Geometry with Applications to Relativity*, Academic +Press (1983). -/ section PseudoRiemannianMetric @@ -176,7 +177,8 @@ instance [IsContMDiffPseudoRiemannianBundle (IB := IB) (n := (3 : WithTop ℕ∞ namespace ContMDiffPseudoRiemannianMetric -/-- A smooth pseudo-Riemannian metric along a bundle induces the corresponding fiberwise structure. -/ +/-- A smooth pseudo-Riemannian metric along a bundle induces the corresponding fiberwise +structure. -/ def toPseudoRiemannianBundle (g : ContMDiffPseudoRiemannianMetric (IB := IB) (n := n) (F := F) (E := E)) : PseudoRiemannianBundle (B := B) (E := E) where From 8f5b14bf8c89bf484ab482c155a986bd4bc8352f Mon Sep 17 00:00:00 2001 From: Matteo CIpollina Date: Tue, 3 Mar 2026 15:24:35 +0100 Subject: [PATCH 27/36] Update PhysLean.lean --- PhysLean.lean | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PhysLean.lean b/PhysLean.lean index 54a8418cb..628c1177e 100644 --- a/PhysLean.lean +++ b/PhysLean.lean @@ -53,7 +53,7 @@ import PhysLean.Mathematics.FDerivCurry import PhysLean.Mathematics.Fin import PhysLean.Mathematics.Fin.Involutions import PhysLean.Mathematics.Geometry.Metric.Lorentzian.Defs -import PhysLean.Mathematics.Geometry.MetricQuadraticForm.NegDim +import PhysLean.Mathematics.Geometry.Metric.QuadraticForm.NegDim import PhysLean.Mathematics.Geometry.MetricQuadraticForm.Sylvester import PhysLean.Mathematics.Geometry.Metric.PseudoRiemannian.Defs import PhysLean.Mathematics.Geometry.Metric.Riemannian.Defs From 367cee20f518e12eb546dfcefc940eb1208725fe Mon Sep 17 00:00:00 2001 From: Matteo CIpollina Date: Tue, 3 Mar 2026 15:27:40 +0100 Subject: [PATCH 28/36] Update PhysLean.lean --- PhysLean.lean | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PhysLean.lean b/PhysLean.lean index 628c1177e..fafde4db6 100644 --- a/PhysLean.lean +++ b/PhysLean.lean @@ -54,7 +54,7 @@ import PhysLean.Mathematics.Fin import PhysLean.Mathematics.Fin.Involutions import PhysLean.Mathematics.Geometry.Metric.Lorentzian.Defs import PhysLean.Mathematics.Geometry.Metric.QuadraticForm.NegDim -import PhysLean.Mathematics.Geometry.MetricQuadraticForm.Sylvester +import PhysLean.Mathematics.Geometry.Metric.QuadraticForm.Sylvester import PhysLean.Mathematics.Geometry.Metric.PseudoRiemannian.Defs import PhysLean.Mathematics.Geometry.Metric.Riemannian.Defs import PhysLean.Mathematics.InnerProductSpace.Adjoint From 3b2b425b877ac4258a540a008bf6fa7e3e3124a6 Mon Sep 17 00:00:00 2001 From: Matteo CIpollina Date: Tue, 3 Mar 2026 16:34:21 +0100 Subject: [PATCH 29/36] Update NegDim.lean --- .../Geometry/Metric/QuadraticForm/NegDim.lean | 89 ++++++++++--------- 1 file changed, 47 insertions(+), 42 deletions(-) diff --git a/PhysLean/Mathematics/Geometry/Metric/QuadraticForm/NegDim.lean b/PhysLean/Mathematics/Geometry/Metric/QuadraticForm/NegDim.lean index 1e63e764c..b1aea8123 100644 --- a/PhysLean/Mathematics/Geometry/Metric/QuadraticForm/NegDim.lean +++ b/PhysLean/Mathematics/Geometry/Metric/QuadraticForm/NegDim.lean @@ -146,29 +146,32 @@ def IsPosDefOn (Q : QuadraticForm ℝ V) (W : Submodule ℝ V) : Prop := /-- Predicate asserting that a real quadratic form admits a positive definite submodule of dimension `k`. -/ -def PosIndexPred [FiniteDimensional ℝ V] (Q : QuadraticForm ℝ V) (k : ℕ) : Prop := - ∃ W : Submodule ℝ V, finrank ℝ W = k ∧ IsPosDefOn (V := V) Q W +def PosIndexPred (Q : QuadraticForm ℝ V) (k : ℕ) : Prop := + ∃ W : Submodule ℝ V, FiniteDimensional ℝ W ∧ finrank ℝ W = k ∧ IsPosDefOn (V := V) Q W -private lemma posIndexPred_zero [FiniteDimensional ℝ V] (Q : QuadraticForm ℝ V) : +private lemma posIndexPred_zero (Q : QuadraticForm ℝ V) : PosIndexPred (V := V) Q 0 := by - refine ⟨(⊥ : Submodule ℝ V), by simp, ?_⟩ + refine ⟨(⊥ : Submodule ℝ V), ?_, by simp, ?_⟩ + · infer_instance intro x hx exfalso exact hx (Subsingleton.elim x 0) private lemma posIndexPred_le_finrank [FiniteDimensional ℝ V] {Q : QuadraticForm ℝ V} {k : ℕ} (hk : PosIndexPred (V := V) Q k) : k ≤ finrank ℝ V := by - rcases hk with ⟨W, hW, -⟩ + rcases hk with ⟨W, hWfd, hW, -⟩ + classical + haveI : FiniteDimensional ℝ W := hWfd have hk_le : finrank ℝ W ≤ finrank ℝ V := Submodule.finrank_le (R := ℝ) (M := V) W simpa [hW] using hk_le /-- The positive index of a real quadratic form: the maximal dimension of a subspace on which the form is positive definite. -/ -noncomputable def posIndex [FiniteDimensional ℝ V] (Q : QuadraticForm ℝ V) : ℕ := +noncomputable def posIndex (Q : QuadraticForm ℝ V) : ℕ := sSup {k : ℕ | PosIndexPred (V := V) Q k} -private lemma posIndex_nonempty [FiniteDimensional ℝ V] (Q : QuadraticForm ℝ V) : +private lemma posIndex_nonempty (Q : QuadraticForm ℝ V) : ({k : ℕ | PosIndexPred (V := V) Q k} : Set ℕ).Nonempty := ⟨0, posIndexPred_zero (V := V) Q⟩ @@ -185,15 +188,17 @@ lemma posIndex_spec [FiniteDimensional ℝ V] (Q : QuadraticForm ℝ V) : simpa [posIndex] using (Nat.sSup_mem (s := ({k : ℕ | PosIndexPred (V := V) Q k} : Set ℕ)) (posIndex_nonempty (V := V) Q) (posIndex_bddAbove (V := V) Q)) - rcases hmem with ⟨W, hW, hWpos⟩ + rcases hmem with ⟨W, hWfd, hW, hWpos⟩ + classical + haveI : FiniteDimensional ℝ W := hWfd exact ⟨W, hW, hWpos⟩ lemma le_posIndex_of_exists [FiniteDimensional ℝ V] {Q : QuadraticForm ℝ V} {k : ℕ} - (hk : ∃ W : Submodule ℝ V, finrank ℝ W = k ∧ IsPosDefOn (V := V) Q W) : + (hk : PosIndexPred (V := V) Q k) : k ≤ posIndex (V := V) Q := by have hb : BddAbove ({k : ℕ | PosIndexPred (V := V) Q k} : Set ℕ) := posIndex_bddAbove (V := V) Q - simpa [posIndex, PosIndexPred] using (le_csSup hb hk) + simpa [posIndex] using (le_csSup hb hk) /- If `Q` and `Q₂` are equivalent, then `posIndex Q ≤ posIndex Q₂`. -/ lemma posIndex_le_of_equivalent [FiniteDimensional ℝ V] {V₂ : Type*} [AddCommGroup V₂] [Module ℝ V₂] @@ -231,8 +236,10 @@ lemma posIndex_le_of_equivalent [FiniteDimensional ℝ V] {V₂ : Type*} [AddCom simpa [hxmap] using this simpa [IsPosDefOn, QuadraticMap.comp_apply, heq, x'] using hpos' have hk : - ∃ U : Submodule ℝ V₂, finrank ℝ U = posIndex (V := V) Q ∧ IsPosDefOn (V := V₂) Q₂ U := - ⟨W₂, by simp [hWfin, hfinrank], hW₂pos⟩ + PosIndexPred (V := V₂) Q₂ (posIndex (V := V) Q) := by + refine ⟨W₂, ?_, ?_, hW₂pos⟩ + · infer_instance + · simp [hWfin, hfinrank] exact le_posIndex_of_exists (V := V₂) hk /-- `posIndex` is invariant under equivalence of quadratic forms. -/ @@ -259,25 +266,22 @@ lemma posIndex_le_finrank [FiniteDimensional ℝ E] (Q : QuadraticForm ℝ E) : have hsne : s.Nonempty := ⟨0, posIndexPred_zero (V := E) Q⟩ refine (csSup_le hsne) ?_ intro k hk - rcases hk with ⟨W, hW, -⟩ - have hk_le : finrank ℝ W ≤ finrank ℝ E := - Submodule.finrank_le (R := ℝ) (M := E) W - simpa [s, hW] using hk_le + exact posIndexPred_le_finrank (V := E) (Q := Q) hk /-- The positive index of inertia of a real quadratic form (canonical). -/ -noncomputable def posDim [FiniteDimensional ℝ E] (Q : QuadraticForm ℝ E) : ℕ := +noncomputable def posDim (Q : QuadraticForm ℝ E) : ℕ := posIndex (V := E) Q /-- The negative index of inertia of a real quadratic form (canonical). -/ -noncomputable def negDim [FiniteDimensional ℝ E] (Q : QuadraticForm ℝ E) : ℕ := +noncomputable def negDim (Q : QuadraticForm ℝ E) : ℕ := posIndex (V := E) (-Q) @[simp] -lemma posDim_def [FiniteDimensional ℝ E] (Q : QuadraticForm ℝ E) : +lemma posDim_def (Q : QuadraticForm ℝ E) : Q.posDim = posIndex (V := E) Q := rfl @[simp] -lemma negDim_def [FiniteDimensional ℝ E] (Q : QuadraticForm ℝ E) : +lemma negDim_def (Q : QuadraticForm ℝ E) : Q.negDim = posIndex (V := E) (-Q) := rfl lemma posDim_le_finrank [FiniteDimensional ℝ E] (Q : QuadraticForm ℝ E) : Q.posDim ≤ finrank ℝ E := @@ -287,9 +291,9 @@ lemma negDim_le_finrank [FiniteDimensional ℝ E] (Q : QuadraticForm ℝ E) : Q. posIndex_le_finrank (E := E) (-Q) @[simp] -lemma posDim_neg [FiniteDimensional ℝ E] (Q : QuadraticForm ℝ E) : (-Q).posDim = Q.negDim := rfl +lemma posDim_neg (Q : QuadraticForm ℝ E) : (-Q).posDim = Q.negDim := rfl -lemma negDim_neg [FiniteDimensional ℝ E] (Q : QuadraticForm ℝ E) : (-Q).negDim = Q.posDim := by +lemma negDim_neg (Q : QuadraticForm ℝ E) : (-Q).negDim = Q.posDim := by simp [negDim, posDim] lemma posDim_add_negDim_le_finrank [FiniteDimensional ℝ E] (Q : QuadraticForm ℝ E) : @@ -313,35 +317,34 @@ lemma posDim_add_negDim_le_finrank [FiniteDimensional ℝ E] (Q : QuadraticForm simpa [posDim, negDim, hWpos, hWneg] using hdim /-- The nullity of a real quadratic form, defined so that `pos + neg + zero = finrank`. -/ -noncomputable def zeroDim [FiniteDimensional ℝ E] (Q : QuadraticForm ℝ E) : ℕ := +noncomputable def zeroDim (Q : QuadraticForm ℝ E) : ℕ := finrank ℝ E - Q.posDim - Q.negDim @[simp] -lemma zeroDim_def [FiniteDimensional ℝ E] (Q : QuadraticForm ℝ E) : +lemma zeroDim_def (Q : QuadraticForm ℝ E) : Q.zeroDim = finrank ℝ E - Q.posDim - Q.negDim := rfl -lemma zeroDim_neg [FiniteDimensional ℝ E] (Q : QuadraticForm ℝ E) : (-Q).zeroDim = Q.zeroDim := by +lemma zeroDim_neg (Q : QuadraticForm ℝ E) : (-Q).zeroDim = Q.zeroDim := by simp [zeroDim, posDim, negDim, Nat.sub_sub, Nat.add_comm] /-- The signature `(pos, neg, zero)` of a real quadratic form (canonical). -/ -noncomputable def signature [FiniteDimensional ℝ E] (Q : QuadraticForm ℝ E) : Signature := +noncomputable def signature (Q : QuadraticForm ℝ E) : Signature := ⟨Q.posDim, Q.negDim, Q.zeroDim⟩ @[simp] -lemma signature_pos [FiniteDimensional ℝ E] (Q : QuadraticForm ℝ E) : Q.signature.pos = Q.posDim := +lemma signature_pos (Q : QuadraticForm ℝ E) : Q.signature.pos = Q.posDim := rfl @[simp] -lemma signature_neg [FiniteDimensional ℝ E] (Q : QuadraticForm ℝ E) : Q.signature.neg = Q.negDim := +lemma signature_neg (Q : QuadraticForm ℝ E) : Q.signature.neg = Q.negDim := rfl @[simp] -lemma signature_zero [FiniteDimensional ℝ E] (Q : QuadraticForm ℝ E) : Q.signature.zero = - Q.zeroDim := +lemma signature_zero (Q : QuadraticForm ℝ E) : Q.signature.zero = Q.zeroDim := rfl @[simp] -lemma signature_def [FiniteDimensional ℝ E] (Q : QuadraticForm ℝ E) : +lemma signature_def (Q : QuadraticForm ℝ E) : Q.signature = ⟨Q.posDim, Q.negDim, Q.zeroDim⟩ := rfl @@ -375,8 +378,9 @@ theorem posDim_posDef [FiniteDimensional ℝ E] {Q : QuadraticForm ℝ E} (hQ : Q.posDim = finrank ℝ E := by apply le_antisymm (posDim_le_finrank (E := E) Q) have hk : - ∃ W : Submodule ℝ E, finrank ℝ W = finrank ℝ E ∧ IsPosDefOn (V := E) Q W := by - refine ⟨(⊤ : Submodule ℝ E), by simp, ?_⟩ + PosIndexPred (V := E) Q (finrank ℝ E) := by + refine ⟨(⊤ : Submodule ℝ E), ?_, by simp, ?_⟩ + · infer_instance intro x hx have hx' : (x : E) ≠ 0 := by intro h0 @@ -549,9 +553,10 @@ def restrictPos (w : Fin n → SignType) : map_add' := by intro v₁ v₂; ext i; rfl map_smul' := by intro a v; ext i; rfl -@[simp] lemma restrictPos_apply {w : Fin n → SignType} (v : Fin n → ℝ) - (i : {i // i ∈ posSet (n := n) w}) : - restrictPos (n := n) w v i = v i.1 := rfl +@[simp] +lemma restrictPos_apply {w : Fin n → SignType} (v : Fin n → ℝ) + (i : {i // i ∈ posSet (n := n) w}) : restrictPos (n := n) w v i = v i.1 := + rfl /-- If a vector has no positive-weight coordinates, then its value under `diagForm w` is nonpositive. -/ @@ -651,11 +656,10 @@ private theorem posIndex_diag_signType (w : Fin n → SignType) : have h_lower : (posSet (n := n) w).card ≤ posIndex (V := Fin n → ℝ) (diagForm (n := n) w) := by have hk : - ∃ W' : Submodule ℝ (Fin n → ℝ), - finrank ℝ W' = (posSet (n := n) w).card ∧ - IsPosDefOn (V := Fin n → ℝ) (diagForm (n := n) w) W' := by - refine ⟨supportedOnPos (n := n) w, finrank_supportedOnPos (n := n) w, - isPosDefOn_diagForm_supportedOnPos (n := n) w⟩ + PosIndexPred (V := Fin n → ℝ) (diagForm (n := n) w) (posSet (n := n) w).card := by + refine ⟨supportedOnPos (n := n) w, ?_, finrank_supportedOnPos (n := n) w, ?_⟩ + · infer_instance + · exact isPosDefOn_diagForm_supportedOnPos (n := n) w exact le_posIndex_of_exists (V := Fin n → ℝ) (Q := diagForm (n := n) w) hk -- upper bound: any positive definite subspace injects into the positive coordinates have h_upper : @@ -726,7 +730,6 @@ private theorem negDim_diagForm (w : Fin n → SignType) : simp [Q, diagForm, QuadraticMap.weightedSumSquares_apply] have hpos : posIndex (V := Fin n → ℝ) (-Q) = (posSet (n := n) (fun i => -w i)).card := by simpa [hneg] using (posIndex_diag_signType (n := n) (w := fun i => -w i)) - -- `negDim Q = posIndex (-Q)` and `posSet (-w) = negSet w`. simp [Q, QuadraticForm.negDim, hpos, posSet_neg (n := n) w, negSet, signSet] theorem negDim_weightedSumSquares_signType (w : Fin n → SignType) : @@ -825,3 +828,5 @@ end end Diagonal end QuadraticForm + +#lint From 9d6e71c810e8025b35a874a930a81a5ace97a4d5 Mon Sep 17 00:00:00 2001 From: Matteo CIpollina Date: Tue, 3 Mar 2026 16:35:25 +0100 Subject: [PATCH 30/36] Update NegDim.lean --- PhysLean/Mathematics/Geometry/Metric/QuadraticForm/NegDim.lean | 2 -- 1 file changed, 2 deletions(-) diff --git a/PhysLean/Mathematics/Geometry/Metric/QuadraticForm/NegDim.lean b/PhysLean/Mathematics/Geometry/Metric/QuadraticForm/NegDim.lean index b1aea8123..7a7355e9e 100644 --- a/PhysLean/Mathematics/Geometry/Metric/QuadraticForm/NegDim.lean +++ b/PhysLean/Mathematics/Geometry/Metric/QuadraticForm/NegDim.lean @@ -160,7 +160,6 @@ private lemma posIndexPred_zero (Q : QuadraticForm ℝ V) : private lemma posIndexPred_le_finrank [FiniteDimensional ℝ V] {Q : QuadraticForm ℝ V} {k : ℕ} (hk : PosIndexPred (V := V) Q k) : k ≤ finrank ℝ V := by rcases hk with ⟨W, hWfd, hW, -⟩ - classical haveI : FiniteDimensional ℝ W := hWfd have hk_le : finrank ℝ W ≤ finrank ℝ V := Submodule.finrank_le (R := ℝ) (M := V) W @@ -189,7 +188,6 @@ lemma posIndex_spec [FiniteDimensional ℝ V] (Q : QuadraticForm ℝ V) : (Nat.sSup_mem (s := ({k : ℕ | PosIndexPred (V := V) Q k} : Set ℕ)) (posIndex_nonempty (V := V) Q) (posIndex_bddAbove (V := V) Q)) rcases hmem with ⟨W, hWfd, hW, hWpos⟩ - classical haveI : FiniteDimensional ℝ W := hWfd exact ⟨W, hW, hWpos⟩ From 7d28d7f8a55ffd8052ed5bc03fd3d41879931607 Mon Sep 17 00:00:00 2001 From: Matteo CIpollina Date: Tue, 3 Mar 2026 17:54:04 +0100 Subject: [PATCH 31/36] Update Defs.lean --- .../Geometry/Metric/PseudoRiemannian/Defs.lean | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/PhysLean/Mathematics/Geometry/Metric/PseudoRiemannian/Defs.lean b/PhysLean/Mathematics/Geometry/Metric/PseudoRiemannian/Defs.lean index 86c2f6022..9b4c6e56d 100644 --- a/PhysLean/Mathematics/Geometry/Metric/PseudoRiemannian/Defs.lean +++ b/PhysLean/Mathematics/Geometry/Metric/PseudoRiemannian/Defs.lean @@ -7,7 +7,7 @@ import Mathlib.Geometry.Manifold.VectorBundle.Riemannian import Mathlib.Geometry.Manifold.VectorBundle.Tangent import Mathlib.LinearAlgebra.BilinearForm.Properties import Mathlib.Topology.LocallyConstant.Basic -import PhysLean.Mathematics.Geometry.Metric.QuadraticForm.NegDim +import PhysLean.Mathematics.Geometry.Metric.QuadraticForm.Index /-! # Pseudo-Riemannian metrics @@ -689,12 +689,11 @@ end FiniteDimensional associated quadratic form `v ↦ gₓ(v,v)`. This is a pointwise invariant; it need not be locally constant. -/ -noncomputable def index (g : MetricTensor E H M n I) (x : M) - [FiniteDimensional ℝ (TangentSpace I x)] : ℕ := +noncomputable def index (g : MetricTensor E H M n I) (x : M) : ℕ := (g.toQuadraticForm x).negDim -@[simp] lemma index_def (g : MetricTensor E H M n I) (x : M) - [FiniteDimensional ℝ (TangentSpace I x)] : +@[simp] +lemma index_def (g : MetricTensor E H M n I) (x : M) : g.index x = (g.toQuadraticForm x).negDim := rfl From d171fe464f285b72d8204bc5d00b3faf08edf898 Mon Sep 17 00:00:00 2001 From: Matteo CIpollina Date: Tue, 3 Mar 2026 18:03:40 +0100 Subject: [PATCH 32/36] Update Defs.lean --- PhysLean/Mathematics/Geometry/Metric/PseudoRiemannian/Defs.lean | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PhysLean/Mathematics/Geometry/Metric/PseudoRiemannian/Defs.lean b/PhysLean/Mathematics/Geometry/Metric/PseudoRiemannian/Defs.lean index 9b4c6e56d..0f7fc1bb8 100644 --- a/PhysLean/Mathematics/Geometry/Metric/PseudoRiemannian/Defs.lean +++ b/PhysLean/Mathematics/Geometry/Metric/PseudoRiemannian/Defs.lean @@ -7,7 +7,7 @@ import Mathlib.Geometry.Manifold.VectorBundle.Riemannian import Mathlib.Geometry.Manifold.VectorBundle.Tangent import Mathlib.LinearAlgebra.BilinearForm.Properties import Mathlib.Topology.LocallyConstant.Basic -import PhysLean.Mathematics.Geometry.Metric.QuadraticForm.Index +import PhysLean.Mathematics.Geometry.Metric.QuadraticForm.NegDim /-! # Pseudo-Riemannian metrics From 161a0980f13e20b94ee202ace01b1eb0b5b83a4b Mon Sep 17 00:00:00 2001 From: Matteo CIpollina Date: Tue, 3 Mar 2026 18:52:37 +0100 Subject: [PATCH 33/36] Update PhysLean.lean --- PhysLean.lean | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PhysLean.lean b/PhysLean.lean index fafde4db6..b23996fdc 100644 --- a/PhysLean.lean +++ b/PhysLean.lean @@ -52,9 +52,9 @@ import PhysLean.Mathematics.Distribution.PowMul import PhysLean.Mathematics.FDerivCurry import PhysLean.Mathematics.Fin import PhysLean.Mathematics.Fin.Involutions -import PhysLean.Mathematics.Geometry.Metric.Lorentzian.Defs import PhysLean.Mathematics.Geometry.Metric.QuadraticForm.NegDim import PhysLean.Mathematics.Geometry.Metric.QuadraticForm.Sylvester +import PhysLean.Mathematics.Geometry.Metric.Lorentzian.Defs import PhysLean.Mathematics.Geometry.Metric.PseudoRiemannian.Defs import PhysLean.Mathematics.Geometry.Metric.Riemannian.Defs import PhysLean.Mathematics.InnerProductSpace.Adjoint From 8cdc4a0fedd51d37028fcd07fbcc037daf75d5f3 Mon Sep 17 00:00:00 2001 From: Matteo Cipollina Date: Tue, 3 Mar 2026 19:28:28 +0100 Subject: [PATCH 34/36] Reorder imports for QuadraticForm module --- PhysLean.lean | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/PhysLean.lean b/PhysLean.lean index b23996fdc..f14fd4e1c 100644 --- a/PhysLean.lean +++ b/PhysLean.lean @@ -52,10 +52,10 @@ import PhysLean.Mathematics.Distribution.PowMul import PhysLean.Mathematics.FDerivCurry import PhysLean.Mathematics.Fin import PhysLean.Mathematics.Fin.Involutions -import PhysLean.Mathematics.Geometry.Metric.QuadraticForm.NegDim -import PhysLean.Mathematics.Geometry.Metric.QuadraticForm.Sylvester import PhysLean.Mathematics.Geometry.Metric.Lorentzian.Defs import PhysLean.Mathematics.Geometry.Metric.PseudoRiemannian.Defs +import PhysLean.Mathematics.Geometry.Metric.QuadraticForm.NegDim +import PhysLean.Mathematics.Geometry.Metric.QuadraticForm.Sylvester import PhysLean.Mathematics.Geometry.Metric.Riemannian.Defs import PhysLean.Mathematics.InnerProductSpace.Adjoint import PhysLean.Mathematics.InnerProductSpace.Basic From da432275213c83bf551682932ec6098a4f3ee8fa Mon Sep 17 00:00:00 2001 From: Matteo Cipollina Date: Tue, 3 Mar 2026 19:41:20 +0100 Subject: [PATCH 35/36] Delete PhysLean/Mathematics/Geometry/Metric/QuadraticForm/Sylvester.lean --- .../Metric/QuadraticForm/Sylvester.lean | 154 ------------------ 1 file changed, 154 deletions(-) delete mode 100644 PhysLean/Mathematics/Geometry/Metric/QuadraticForm/Sylvester.lean diff --git a/PhysLean/Mathematics/Geometry/Metric/QuadraticForm/Sylvester.lean b/PhysLean/Mathematics/Geometry/Metric/QuadraticForm/Sylvester.lean deleted file mode 100644 index 000d8f343..000000000 --- a/PhysLean/Mathematics/Geometry/Metric/QuadraticForm/Sylvester.lean +++ /dev/null @@ -1,154 +0,0 @@ -/- -Copyright (c) 2026 Matteo Cipollina. All rights reserved. -Released under Apache 2.0 license as described in the file LICENSE. -Authors: Matteo Cipollina --/ - -import PhysLean.Mathematics.Geometry.Metric.QuadraticForm.NegDim - -/-! -# Noncanonical Sylvester choices for real quadratic forms - -This file provides auxiliary, **noncanonical** data extracted from the existence theorem -`QuadraticForm.equivalent_signType_weighted_sum_squared` in -`Mathlib.LinearAlgebra.QuadraticForm.Real`. - -The idea is that `equivalent_signType_weighted_sum_squared` produces an existential witness -of diagonalization; here we choose such a witness noncomputably. - -The canonical inertia indices are defined in -`PhysLean.Mathematics.Geometry.Metric.QuadraticForm.NegDim` (via `QuadraticForm.posIndex`) and are -invariant under `QuadraticForm.Equivalent` without any diagonalization choice. - -## Main definitions - -- `QuadraticForm.signTypeWeights`: a chosen Sylvester diagonalization weight function -- `QuadraticForm.signatureChoice`: the resulting chosen signature `(pos, neg, zero)` - -## Main statements - -- `QuadraticForm.signature_eq_signatureChoice`: the canonical signature equals the chosen one - -## Tags - -quadratic form, Sylvester law, signature --/ - -namespace QuadraticForm - -open Finset Module QuadraticMap SignType -open scoped BigOperators - -/-- A choice of `SignType`-weights in Sylvester's diagonalization of a quadratic form. - -This is auxiliary data: it exists by `QuadraticForm.equivalent_signType_weighted_sum_squared`, -but is not canonical (it is chosen noncomputably). -/ -noncomputable def signTypeWeights {E : Type*} [AddCommGroup E] [Module ℝ E] [FiniteDimensional ℝ E] - (q : QuadraticForm ℝ E) : Fin (finrank ℝ E) → SignType := - Classical.choose (QuadraticForm.equivalent_signType_weighted_sum_squared q) - -lemma equivalent_weightedSumSquares_signTypeWeights {E : Type*} [AddCommGroup E] [Module ℝ E] - [FiniteDimensional ℝ E] (q : QuadraticForm ℝ E) : - QuadraticMap.Equivalent q - (QuadraticMap.weightedSumSquares ℝ fun i => ((signTypeWeights q i : SignType) : ℝ)) := - Classical.choose_spec (QuadraticForm.equivalent_signType_weighted_sum_squared q) - -/-- A *chosen* signature `(pos, neg, zero)` of a real quadratic form, defined using -`signTypeWeights`. -/ -noncomputable def signatureChoice {E : Type*} [AddCommGroup E] [Module ℝ E] [FiniteDimensional ℝ E] - (q : QuadraticForm ℝ E) : Signature := by - let w := signTypeWeights q - refine ⟨ - (univ.filter fun i => w i = SignType.pos).card, - (univ.filter fun i => w i = SignType.neg).card, - (univ.filter fun i => w i = 0).card⟩ - -lemma signatureChoice_le_finrank {E : Type*} [AddCommGroup E] [Module ℝ E] [FiniteDimensional ℝ E] - (q : QuadraticForm ℝ E) : - (signatureChoice q).neg ≤ finrank ℝ E ∧ (signatureChoice q).pos ≤ finrank ℝ E ∧ - (signatureChoice q).zero ≤ finrank ℝ E := by - constructor - · simp [signatureChoice, signTypeWeights] - simpa using (Finset.card_filter_le (s := (Finset.univ : Finset (Fin (finrank ℝ E)))) - (p := fun i => signTypeWeights q i = SignType.neg)) - constructor - · simp [signatureChoice, signTypeWeights] - simpa using (Finset.card_filter_le (s := (Finset.univ : Finset (Fin (finrank ℝ E)))) - (p := fun i => signTypeWeights q i = SignType.pos)) - · simp [signatureChoice, signTypeWeights] - simpa using (Finset.card_filter_le (s := (Finset.univ : Finset (Fin (finrank ℝ E)))) - (p := fun i => signTypeWeights q i = 0)) - -/-! -### Canonical signature equals Sylvester choice - -Using the diagonal computation for `weightedSumSquares` and the Sylvester diagonalization existence -theorem, we show that the canonical signature defined via `posIndex` agrees with the chosen -`signatureChoice`. --/ - -section SylvesterBridge - -variable {E : Type*} [AddCommGroup E] [Module ℝ E] [FiniteDimensional ℝ E] - -theorem posDim_eq_signatureChoice_pos (q : QuadraticForm ℝ E) : - q.posDim = (signatureChoice q).pos := by - let w : Fin (finrank ℝ E) → SignType := signTypeWeights q - let Qd : QuadraticForm ℝ (Fin (finrank ℝ E) → ℝ) := - (QuadraticMap.weightedSumSquares ℝ fun i : Fin (finrank ℝ E) => (w i : ℝ)) - have heq : q.Equivalent Qd := by - simpa [Qd, w, signTypeWeights] using (equivalent_weightedSumSquares_signTypeWeights q) - have hpos : q.posDim = Qd.posDim := - posDim_eq_of_equivalent (E := E) (E₂ := Fin (finrank ℝ E) → ℝ) heq - have hposd : - Qd.posDim = (Finset.univ.filter fun i : Fin (finrank ℝ E) => w i = SignType.pos).card := by - simpa [Qd] using (posDim_weightedSumSquares_signType (n := finrank ℝ E) w) - simpa [signatureChoice, w, signTypeWeights] using hpos.trans hposd - -theorem negDim_eq_signatureChoice_neg (q : QuadraticForm ℝ E) : - q.negDim = (signatureChoice q).neg := by - let w : Fin (finrank ℝ E) → SignType := signTypeWeights q - let Qd : QuadraticForm ℝ (Fin (finrank ℝ E) → ℝ) := - (QuadraticMap.weightedSumSquares ℝ fun i : Fin (finrank ℝ E) => (w i : ℝ)) - have heq : q.Equivalent Qd := by - simpa [Qd, w, signTypeWeights] using (equivalent_weightedSumSquares_signTypeWeights q) - have hneg : q.negDim = Qd.negDim := - negDim_eq_of_equivalent (E := E) (E₂ := Fin (finrank ℝ E) → ℝ) heq - have hnegd : - Qd.negDim = (Finset.univ.filter fun i : Fin (finrank ℝ E) => w i = SignType.neg).card := by - simpa [Qd] using (negDim_weightedSumSquares_signType (n := finrank ℝ E) w) - simpa [signatureChoice, w, signTypeWeights] using hneg.trans hnegd - -theorem zeroDim_eq_signatureChoice_zero (q : QuadraticForm ℝ E) : - q.zeroDim = (signatureChoice q).zero := by - let w : Fin (finrank ℝ E) → SignType := signTypeWeights q - let Qd : QuadraticForm ℝ (Fin (finrank ℝ E) → ℝ) := - (QuadraticMap.weightedSumSquares ℝ fun i : Fin (finrank ℝ E) => (w i : ℝ)) - have heq : q.Equivalent Qd := by - simpa [Qd, w, signTypeWeights] using (equivalent_weightedSumSquares_signTypeWeights q) - have hzero : q.zeroDim = Qd.zeroDim := - zeroDim_eq_of_equivalent (E := E) (E₂ := Fin (finrank ℝ E) → ℝ) heq - have hzerod : - Qd.zeroDim = (Finset.univ.filter fun i : Fin (finrank ℝ E) => w i = 0).card := by - simpa [Qd] using (zeroDim_weightedSumSquares_signType (n := finrank ℝ E) w) - simpa [signatureChoice, w, signTypeWeights] using hzero.trans hzerod - -theorem signature_eq_signatureChoice (q : QuadraticForm ℝ E) : - q.signature = signatureChoice q := by - ext - · simpa using posDim_eq_signatureChoice_pos (E := E) q - · simpa using negDim_eq_signatureChoice_neg (E := E) q - · simpa using zeroDim_eq_signatureChoice_zero (E := E) q - -theorem signatureChoice_eq_of_equivalent {E₂ : Type*} [AddCommGroup E₂] [Module ℝ E₂] - [FiniteDimensional ℝ E₂] {Q : QuadraticForm ℝ E} {Q₂ : QuadraticForm ℝ E₂} - (h : Q.Equivalent Q₂) : - signatureChoice Q = signatureChoice Q₂ := by - calc - signatureChoice Q = Q.signature := (signature_eq_signatureChoice (E := E) Q).symm - _ = Q₂.signature := signature_eq_of_equivalent (E := E) (E₂ := E₂) h - _ = signatureChoice Q₂ := signature_eq_signatureChoice (E := E₂) Q₂ - -end SylvesterBridge - -end QuadraticForm From 723018de72add59449cf92430e7ed23aa9a65b5d Mon Sep 17 00:00:00 2001 From: Matteo Cipollina Date: Tue, 3 Mar 2026 19:42:09 +0100 Subject: [PATCH 36/36] Update PhysLean.lean --- PhysLean.lean | 1 - 1 file changed, 1 deletion(-) diff --git a/PhysLean.lean b/PhysLean.lean index f14fd4e1c..72176fc53 100644 --- a/PhysLean.lean +++ b/PhysLean.lean @@ -55,7 +55,6 @@ import PhysLean.Mathematics.Fin.Involutions import PhysLean.Mathematics.Geometry.Metric.Lorentzian.Defs import PhysLean.Mathematics.Geometry.Metric.PseudoRiemannian.Defs import PhysLean.Mathematics.Geometry.Metric.QuadraticForm.NegDim -import PhysLean.Mathematics.Geometry.Metric.QuadraticForm.Sylvester import PhysLean.Mathematics.Geometry.Metric.Riemannian.Defs import PhysLean.Mathematics.InnerProductSpace.Adjoint import PhysLean.Mathematics.InnerProductSpace.Basic