diff --git a/LeanPool/CCDN.lean b/LeanPool/CCDN.lean new file mode 100644 index 0000000000..aba5d0c6b2 --- /dev/null +++ b/LeanPool/CCDN.lean @@ -0,0 +1,26 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +public import LeanPool.CCDN.AffineCurveBound +public import LeanPool.CCDN.AffineHypersurfaceDimensionGrowth +public import LeanPool.CCDN.ClassGroupBound +public import LeanPool.CCDN.ProjectiveCurveBound +public import LeanPool.CCDN.ProjectiveDimensionGrowth +public import LeanPool.CCDN.ProjectiveCurveLowerBound + +/-! +# Polynomial degree bounds for rational points on varieties + +Source: arxiv:1904.13109, doi:10.2140/ant.2020.14.2261, url:https://github.com/jasondavies/ccdn-dimension-growth/tree/bb025e7207f3a35a21b898629f5678b72aa7e44f +Authors: Jason Davies, Ralf Stephan, PrimeNumberTheoremAnd contributors +Status: verified +Main declarations: `CCDN.projective_dimension_growth` +Tags: arithmetic-geometry, rational-points, determinant-method, class-groups +MSC: 11D45, 14G05, 11R29 +-/ + +-- Dependency licenses and upstream notices are recorded in CCDN/Attribution.lean. diff --git a/LeanPool/CCDN/AffineCurveBound.lean b/LeanPool/CCDN/AffineCurveBound.lean new file mode 100644 index 0000000000..119f3f2289 --- /dev/null +++ b/LeanPool/CCDN/AffineCurveBound.lean @@ -0,0 +1,28 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.Statements +import LeanPool.CCDN.Curve.AffineCurveCount + +/-! The full coefficient-uniform affine curve counting theorem, with constants +depending only on ambient dimension and with the exact degree dependence in +CCDN Theorem 3. The proof is `affine_curve_count`, in `CCDN.Curve.AffineCurveCount`. -/ + +@[expose] public section +namespace CCDN +noncomputable section +open MvPolynomial + +/-- The affine curve counting bound `AffineCurveBound` of CCDN Theorem 3. -/ +theorem affineCurveBound : AffineCurveBound := + affine_curve_count + +end +end CCDN diff --git a/LeanPool/CCDN/AffineHypersurfaceDimensionGrowth.lean b/LeanPool/CCDN/AffineHypersurfaceDimensionGrowth.lean new file mode 100644 index 0000000000..5dad4ab6c2 --- /dev/null +++ b/LeanPool/CCDN/AffineHypersurfaceDimensionGrowth.lean @@ -0,0 +1,48 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.Statements +import LeanPool.CCDN.Hypersurface.AffineHypersurfaceCount + +/-! CCDN Theorem 4 with the corrected degree exponent of Remark 4.3.8. +In `N = n + 3` variables the exponent is `18 + 3n = 3N + 9`: the corrected surface +bound `d^18` and a cost of `d^3` for each bounded good hyperplane slice +(`affineHypersurfaceGrowth`, in `CCDN.Hypersurface.AffineHypersurfaceCount`). -/ + +@[expose] public section +namespace CCDN +noncomputable section +open MvPolynomial + +/-- CCDN Theorem 4 for integer equations in all affine ambient dimensions +at least three. The constant depends only on dimension, and the explicit +degree exponent is 18 + 3n in n+3 variables. -/ +theorem affine_hypersurface_dimension_growth (n : ℕ) : + ∃ C : ℝ, 0 < C ∧ + ∀ f : MvPolynomial (Fin (n + 3)) ℤ, + HasAbsIrreducibleTopForm f → + 5 ≤ f.totalDegree → ∀ B : ℝ, 1 ≤ B → + (count (zeroLocus (map (Int.castRingHom ℚ) f)) B : ℝ) ≤ + C * (f.totalDegree : ℝ) ^ (18 + 3 * n) * B ^ (n + 1) := + affineHypersurfaceGrowth n + +/-- CCDN Theorem 4 for rational equations, with the degree exponent 18 + 3n in +n+3 variables. -/ +theorem affine_hypersurface_dimension_growth_rat (n : ℕ) : + ∃ C : ℝ, 0 < C ∧ + ∀ f : MvPolynomial (Fin (n + 3)) ℚ, + HasAbsIrreducibleTopFormRat f → + 5 ≤ f.totalDegree → ∀ B : ℝ, 1 ≤ B → + (count (zeroLocus f) B : ℝ) ≤ + C * (f.totalDegree : ℝ) ^ (18 + 3 * n) * B ^ (n + 1) := + (affineHypersurfaceGrowth n).rational + +end +end CCDN diff --git a/LeanPool/CCDN/Algebra/AffineLineLeading.lean b/LeanPool/CCDN/Algebra/AffineLineLeading.lean new file mode 100644 index 0000000000..d85102d486 --- /dev/null +++ b/LeanPool/CCDN/Algebra/AffineLineLeading.lean @@ -0,0 +1,94 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import Mathlib.RingTheory.MvPolynomial.Homogeneous + +/-! The coefficient of highest possible degree on an affine line depends only +on the top homogeneous component and the direction, not the base point. -/ + +@[expose] public section +namespace CCDN +noncomputable section + +private lemma polynomial_coeff_prod_sum_of_natDegree_le + {R ι : Type*} [CommRing R] (s : Finset ι) (q : ι → Polynomial R) (e : ι → ℕ) + (hq : ∀ i ∈ s, (q i).natDegree ≤ e i) : + (∏ i ∈ s, q i).coeff (∑ i ∈ s, e i) = ∏ i ∈ s, (q i).coeff (e i) := by + classical + induction s using Finset.induction_on with + | empty => simp + | @insert i s hi ih => + rw [Finset.prod_insert hi, Finset.sum_insert hi, Finset.prod_insert hi] + have ht : (∏ j ∈ s, q j).natDegree ≤ ∑ j ∈ s, e j := + (Polynomial.natDegree_prod_le _ _).trans + (Finset.sum_le_sum fun j hj => hq j (Finset.mem_insert_of_mem hj)) + rw [Polynomial.coeff_mul_add_eq_of_natDegree_le + (hq i (Finset.mem_insert_self _ _)) ht, + ih (fun j hj => hq j (Finset.mem_insert_of_mem hj))] + +/-- The affine polynomial `C a + C b * X` has degree at most `1`. -/ +lemma polynomial_affine_natDegree_le {R : Type*} [CommRing R] (a b : R) : + (Polynomial.C a + Polynomial.C b * Polynomial.X).natDegree ≤ 1 := by + apply Polynomial.natDegree_add_le_of_degree_le + · simp + · exact Polynomial.natDegree_mul_le.trans (by simpa using (Polynomial.natDegree_X_le (R := R))) + +/-- Restriction to the affine line a+t*b. -/ +def affineLinePolynomial {R σ : Type*} [CommRing R] (a b : σ → R) : + MvPolynomial σ R →+* Polynomial R := + MvPolynomial.eval₂Hom Polynomial.C + (fun i => Polynomial.C (a i) + Polynomial.C (b i) * Polynomial.X) + +private lemma affineLinePolynomial_monomial_top {R σ : Type*} [CommRing R] + (a b : σ → R) (m : σ →₀ ℕ) (c : R) (d : ℕ) (hd : m.degree ≤ d) : + (affineLinePolynomial a b (MvPolynomial.monomial m c)).coeff d = + if d = m.degree then c * m.prod (fun i e => b i ^ e) else 0 := by + classical + let q : σ → Polynomial R := fun i => Polynomial.C (a i) + Polynomial.C (b i) * Polynomial.X + have hq (i : σ) : (q i).natDegree ≤ 1 := polynomial_affine_natDegree_le _ _ + have hp (i : σ) : ((q i) ^ m i).natDegree ≤ m i := by + exact Polynomial.natDegree_pow_le.trans (by simpa using Nat.mul_le_mul_left (m i) (hq i)) + have hprod : (m.prod (fun i e => (q i) ^ e)).natDegree ≤ m.degree := + (Polynomial.natDegree_prod_le _ _).trans (Finset.sum_le_sum fun i _ => hp i) + rw [affineLinePolynomial, MvPolynomial.eval₂Hom_monomial] + change (Polynomial.C c * m.prod (fun i e => (q i) ^ e)).coeff d = _ + rw [Polynomial.coeff_C_mul] + by_cases he : d = m.degree + · rw [ite_eq_left he, he] + congr 1 + simp only [Finsupp.prod] + rw [show m.degree = ∑ i ∈ m.support, m i from rfl, + polynomial_coeff_prod_sum_of_natDegree_le m.support (fun i => q i ^ m i) m (fun i _ => hp i)] + apply Finset.prod_congr rfl + intro i _ + have hh := Polynomial.coeff_pow_of_natDegree_le (m := m i) (hq i) + simpa only [mul_one, q, Polynomial.coeff_add, Polynomial.coeff_C_succ, + Polynomial.coeff_C_mul_X, zero_add, ite_true] using hh + · rw [ite_eq_right he, + Polynomial.coeff_eq_zero_of_natDegree_lt (hprod.trans_lt (by omega)), mul_zero] + +/-- At any degree at least the total degree, the affine-line coefficient equals +the corresponding homogeneous component evaluated on its direction. -/ +theorem affineLinePolynomial_top_coefficient {R σ : Type*} [CommRing R] + (a b : σ → R) (f : MvPolynomial σ R) (d : ℕ) (hd : f.totalDegree ≤ d) : + (affineLinePolynomial a b f).coeff d = + MvPolynomial.eval b (MvPolynomial.homogeneousComponent d f) := by + classical + conv_lhs => rw [f.as_sum] + conv_rhs => rw [f.as_sum] + simp only [map_sum, Polynomial.finsetSum_coeff] + apply Finset.sum_congr rfl + intro m hm + rw [affineLinePolynomial_monomial_top a b m _ d ((MvPolynomial.le_totalDegree hm).trans hd), + MvPolynomial.homogeneousComponent_of_mem (MvPolynomial.isHomogeneous_monomial _ rfl)] + split_ifs <;> simp [MvPolynomial.eval_monomial] + +end +end CCDN diff --git a/LeanPool/CCDN/Algebra/AffineTranslation.lean b/LeanPool/CCDN/Algebra/AffineTranslation.lean new file mode 100644 index 0000000000..b869c8c755 --- /dev/null +++ b/LeanPool/CCDN/Algebra/AffineTranslation.lean @@ -0,0 +1,93 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.Algebra.HomogeneousChart +public import LeanPool.CCDN.Algebra.HomogeneousTranslation +public import LeanPool.CCDN.Algebra.OriginShift + +/-! The affine translations underlying the homogeneous coordinate changes +preserve degree and irreducibility, over every coefficient field. -/ + +@[expose] public section +namespace CCDN +noncomputable section +open MvPolynomial + +/-- The translation `X j ↦ X j + a j` of polynomials in `Fin n` variables, as an algebra +automorphism over `R`. -/ +def affineTranslation (R : Type*) [CommRing R] {n : ℕ} (a : Fin n → R) : + MvPolynomial (Fin n) R ≃ₐ[R] MvPolynomial (Fin n) R := + originShiftEquiv a + +/-- An affine translation of variables preserves and reflects irreducibility. -/ +lemma affineTranslation_irreducible_iff {R : Type*} [CommRing R] {n : ℕ} + (a : Fin n → R) (f : MvPolynomial (Fin n) R) : + Irreducible (affineTranslation R a f) ↔ Irreducible f := + MulEquiv.irreducible_iff (f := affineTranslation R a) + +/-- An affine translation of variables does not increase the total degree. -/ +lemma affineTranslation_totalDegree_le {R : Type*} [CommRing R] [Nontrivial R] {n : ℕ} + (a : Fin n → R) (f : MvPolynomial (Fin n) R) : + (affineTranslation R a f).totalDegree ≤ f.totalDegree := by + classical + have hm (m : Fin n →₀ ℕ) (c : R) : + (affineTranslation R a (monomial m c)).totalDegree ≤ m.sum (fun _ e => e) := by + change (aeval (fun j => X j + C (a j)) (monomial m c)).totalDegree ≤ _ + rw [aeval_monomial] + have hp : (m.prod (fun j e => (X j + C (a j)) ^ e)).totalDegree ≤ m.sum (fun _ e => e) := by + apply (totalDegree_finsetProd _ _).trans + apply Finset.sum_le_sum + intro j _ + have hh : (X j + C (a j) : MvPolynomial (Fin n) R).totalDegree ≤ 1 := by + exact (totalDegree_add _ _).trans (by simp) + exact (totalDegree_pow _ _).trans (by simpa using Nat.mul_le_mul_left (m j) hh) + have hh := totalDegree_mul (C c) (m.prod (fun j e => (X j + C (a j)) ^ e)) + simp only [totalDegree_C, zero_add] at hh + exact hh.trans hp + conv_lhs => rw [f.as_sum, map_sum] + apply totalDegree_finsetSum_le + intro m hm' + exact (hm m _).trans (le_totalDegree hm') + +/-- An affine translation of variables preserves the total degree over a nontrivial ring. -/ +lemma affineTranslation_totalDegree {R : Type*} [CommRing R] [Nontrivial R] {n : ℕ} + (a : Fin n → R) (f : MvPolynomial (Fin n) R) : + (affineTranslation R a f).totalDegree = f.totalDegree := by + apply le_antisymm (affineTranslation_totalDegree_le a f) + have hh := affineTranslation_totalDegree_le (-a) (affineTranslation R a f) + have he : affineTranslation R (-a) (affineTranslation R a f) = f := + (affineTranslation R a).symm_apply_apply f + rwa [he] at hh + +/-- Affine translation commutes with mapping coefficients along a ring homomorphism `φ`, the +translation vector being mapped by `φ` as well. -/ +lemma affineTranslation_map {R S : Type*} [CommRing R] [CommRing S] {n : ℕ} + (φ : R →+* S) (a : Fin n → R) (f : MvPolynomial (Fin n) R) : + MvPolynomial.map φ (affineTranslation R a f) = + affineTranslation S (φ ∘ a) (MvPolynomial.map φ f) := + originShift_map φ a f + +/-- Dehomogenizing at `X 0` intertwines the homogeneous translation +`Xⱼ₊₁ ↦ Xⱼ₊₁ + aⱼ X₀` with the affine translation `Xⱼ ↦ Xⱼ + aⱼ`. -/ +lemma affineChart_homogeneousTranslation {R : Type*} [CommRing R] {n : ℕ} + (a : Fin n → R) (f : MvPolynomial (Fin (n + 1)) R) : + affineChart n (homogeneousTranslation R a f) = affineTranslation R a (affineChart n f) := by + have he : (affineChart n).comp (homogeneousTranslation R a).toRingHom = + (affineTranslation R a).toRingHom.comp (affineChart n) := by + apply MvPolynomial.ringHom_ext + · intro c + simp [affineChart, homogeneousTranslation, homogeneousTranslationHom, affineTranslation] + · intro i + refine Fin.cases ?_ (fun j => ?_) i <;> + simp [affineChart, homogeneousTranslation, homogeneousTranslationHom, affineTranslation] + exact RingHom.congr_fun he f + +end +end CCDN diff --git a/LeanPool/CCDN/Algebra/BinaryHomogeneousIrreducibility.lean b/LeanPool/CCDN/Algebra/BinaryHomogeneousIrreducibility.lean new file mode 100644 index 0000000000..ef8b67cbe2 --- /dev/null +++ b/LeanPool/CCDN/Algebra/BinaryHomogeneousIrreducibility.lean @@ -0,0 +1,69 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import Mathlib.FieldTheory.IsAlgClosed.Basic +public import Mathlib.RingTheory.MvPolynomial.Homogeneous +import LeanPool.CCDN.Algebra.HomogeneousChartIrreducible + +/-! Over an algebraically closed field, an irreducible homogeneous binary +form has degree one. The affine chart reduces this to a univariate polynomial. -/ + +@[expose] public section +namespace CCDN +noncomputable section +open MvPolynomial + +private theorem uniqueAlgEquiv_natDegree {R σ : Type*} [CommRing R] [Unique σ] + (f : MvPolynomial σ R) : + ((uniqueAlgEquiv R σ) f).natDegree = f.totalDegree := by + classical + apply le_antisymm + · apply Polynomial.natDegree_le_iff_coeff_eq_zero.mpr + intro k hk + rw [coeff_uniqueAlgEquiv] + apply coeff_eq_zero_of_totalDegree_lt + change f.totalDegree < (Finsupp.single (default : σ) k).degree + rwa [Finsupp.degree_single] + · unfold totalDegree + apply Finset.sup_le + intro m hm + have hc : ((uniqueAlgEquiv R σ) f).coeff (m default) ≠ 0 := by + rw [coeff_uniqueAlgEquiv, ← Finsupp.unique_single m] + exact mem_support_iff.mp hm + have hle := Polynomial.le_natDegree_of_ne_zero hc + have he : m.degree = m default := by + calc + _ = (Finsupp.single default (m default)).degree := + congrArg Finsupp.degree (Finsupp.unique_single m) + _ = _ := Finsupp.degree_single _ _ + change m.degree ≤ _ + rwa [he] + +/-- Over an algebraically closed field, an irreducible homogeneous polynomial in two variables +has degree at most `1`. -/ +theorem binary_homogeneous_irreducible_degree_le_one + {K : Type*} [Field K] [IsAlgClosed K] {d : ℕ} + (f : MvPolynomial (Fin 2) K) (hf : f.IsHomogeneous d) + (hir : Irreducible f) : d ≤ 1 := by + by_contra h + have hd : 2 ≤ d := by omega + have hX := variable_not_dvd_irreducible_homogeneous f hf hir hd 0 + have hdeg := affineChart_degree_of_not_variable_dvd f hf hX + have hc := affineChart_irreducible_of_not_variable_dvd f hf hir hX + have hu : Irreducible ((uniqueAlgEquiv K (Fin 1)) (affineChart 1 f)) := + (MulEquiv.irreducible_iff (f := (uniqueAlgEquiv K (Fin 1)).toMulEquiv)).mpr hc + have hh : ((uniqueAlgEquiv K (Fin 1)) (affineChart 1 f)).natDegree = 1 := + Polynomial.natDegree_eq_of_degree_eq_some + (IsAlgClosed.degree_eq_one_of_irreducible K hu) + rw [uniqueAlgEquiv_natDegree, hdeg] at hh + omega + +end +end CCDN diff --git a/LeanPool/CCDN/Algebra/BinomialSectionBound.lean b/LeanPool/CCDN/Algebra/BinomialSectionBound.lean new file mode 100644 index 0000000000..e015c24871 --- /dev/null +++ b/LeanPool/CCDN/Algebra/BinomialSectionBound.lean @@ -0,0 +1,93 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.Hilbert.FilteredIntersection +import LeanPool.CCDN.ForMathlib.MvPolynomial + +/-! Two finite differences of a binomial Hilbert function. This gives a +coefficient-uniform codimension-two bound for filtered coordinate spaces. -/ + +@[expose] public section +namespace CCDN +noncomputable section + +private lemma choose_shift_sum (n k d : ℕ) : + (n + 1 + (k + d)).choose (n + 1) = (n + 1 + k).choose (n + 1) + + ∑ i ∈ Finset.range d, (n + (k + i + 1)).choose n := by + induction d with + | zero => simp + | succ d ih => + rw [Finset.sum_range_succ] + rw [show n + 1 + (k + (d + 1)) = (n + 1 + (k + d)) + 1 by omega, + Nat.choose_succ_succ', ih] + rw [show n + 1 + (k + d) = n + (k + d + 1) by omega] + omega + +private lemma choose_shift_bound (n k d : ℕ) : + (n + 1 + (k + d)).choose (n + 1) ≤ (n + 1 + k).choose (n + 1) + + d * (n + (k + d)).choose n := by + rw [choose_shift_sum] + apply Nat.add_le_add_left + calc + _ ≤ ∑ _i ∈ Finset.range d, (n + (k + d)).choose n := by + apply Finset.sum_le_sum + intro i hi + exact Nat.choose_le_choose n (by have := Finset.mem_range.mp hi; omega) + _ = _ := by simp + +/-- The second finite difference has the product-of-degrees factor and loses +two powers of the cutoff, in every ambient dimension. -/ +private theorem choose_double_difference_bound (n k d e : ℕ) : + (n + 2 + (k + d + e)).choose (n + 2) + (n + 2 + k).choose (n + 2) ≤ + (n + 2 + (k + d)).choose (n + 2) + (n + 2 + (k + e)).choose (n + 2) + + d * e * (n + (k + d + e)).choose n := by + have h1 := choose_shift_sum (n + 1) (k + e) d + have h2 := choose_shift_sum (n + 1) k d + have hsum : (∑ i ∈ Finset.range d, (n + 1 + (k + e + i + 1)).choose (n + 1)) ≤ + (∑ i ∈ Finset.range d, (n + 1 + (k + i + 1)).choose (n + 1)) + + d * e * (n + (k + d + e)).choose n := by + calc + _ ≤ ∑ i ∈ Finset.range d, + ((n + 1 + (k + i + 1)).choose (n + 1) + + e * (n + (k + d + e)).choose n) := by + apply Finset.sum_le_sum + intro i hi + have h := choose_shift_bound n (k + i + 1) e + have hmono := Nat.choose_le_choose n + (show n + (k + i + 1 + e) ≤ n + (k + d + e) by + have := Finset.mem_range.mp hi; omega) + have hm := Nat.mul_le_mul_left e hmono + rw [show k + i + 1 + e = k + e + i + 1 by omega] at h hm + omega + _ = _ := by rw [Finset.sum_add_distrib]; simp [Nat.mul_assoc] + rw [show k + e + d = k + d + e by omega] at h1 + rw [show n + 1 + 1 = n + 2 by omega] at h1 h2 + omega + +/-- The filtered Koszul bound reduces to a codimension-two binomial count. -/ +theorem filteredCoordinateSpace_codim_two_bound {K : Type*} [Field K] {n N : ℕ} + {f g : MvPolynomial (Fin (n + 2)) K} (hf : Prime f) (hfg : ¬ f ∣ g) + (I : Ideal (MvPolynomial (Fin (n + 2)) K)) (hIf : f ∈ I) (hIg : g ∈ I) + (hN : f.totalDegree + g.totalDegree ≤ N) : + Module.finrank K (filteredCoordinateSpace I N) ≤ + f.totalDegree * g.totalDegree * (n + N).choose n := by + have h := filteredCoordinateSpace_finrank_bound hf hfg I hIf hIg hN + simp only [MvPolynomial.finrank_restrictTotalDegree, Fintype.card_fin, + ← Nat.choose_symm_add (a := n + 2)] at h + have hc := choose_double_difference_bound n (N - (f.totalDegree + g.totalDegree)) + f.totalDegree g.totalDegree + have h0 : N - (f.totalDegree + g.totalDegree) + f.totalDegree + g.totalDegree = N := by omega + have h1 : N - (f.totalDegree + g.totalDegree) + f.totalDegree = N - g.totalDegree := by omega + have h2 : N - (f.totalDegree + g.totalDegree) + g.totalDegree = N - f.totalDegree := by omega + rw [h0, h1, h2] at hc + omega + +end +end CCDN diff --git a/LeanPool/CCDN/Algebra/BoundedLinearSeparation.lean b/LeanPool/CCDN/Algebra/BoundedLinearSeparation.lean new file mode 100644 index 0000000000..4cee8057ee --- /dev/null +++ b/LeanPool/CCDN/Algebra/BoundedLinearSeparation.lean @@ -0,0 +1,97 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import Mathlib.Algebra.Polynomial.Degree.Defs +public import Mathlib.Algebra.Polynomial.Eval.Defs +import Mathlib.Algebra.Polynomial.OfFn +import Mathlib.Algebra.Polynomial.Roots + +/-! A finite family of distinct coordinate vectors is separated by a bounded +integer linear form on the moment curve (1,t,...,t^(n-1)). The proof avoids +an unquantified genericity argument. -/ + +@[expose] public section +namespace CCDN +noncomputable section + +/-- In characteristic zero, a nonzero polynomial `P` does not vanish at some natural number +`k ≤ P.natDegree`. -/ +lemma polynomial_exists_small_nat_eval_ne_zero {K : Type*} [Field K] [CharZero K] + (P : Polynomial K) (hP : P ≠ 0) : + ∃ k : ℕ, k ≤ P.natDegree ∧ P.eval (k : K) ≠ 0 := by + by_contra! hn + apply hP + apply Polynomial.eq_zero_of_natDegree_lt_card_of_eval_eq_zero P + (f := fun k : Fin (P.natDegree + 1) => (k.val : K)) + (fun i j h => Fin.ext (Nat.cast_injective h)) + · intro i + exact hn i.val (by omega) + · simp only [Fintype.card_fin] + omega + +/-- A moment form with parameter at most n times the number of directions +is nonzero on all those directions. -/ +theorem exists_bounded_moment_nonvanishing {K ι : Type*} [Field K] [CharZero K] + [Fintype ι] {n : ℕ} (v : ι → Fin n → K) (hv : ∀ i, v i ≠ 0) : + ∃ k : ℕ, k ≤ n * Fintype.card ι ∧ + ∀ i, ∑ j : Fin n, v i j * (k : K) ^ j.val ≠ 0 := by + classical + let P (i : ι) : Polynomial K := Polynomial.ofFn n (v i) + have hPn (i : ι) : P i ≠ 0 := by + intro he + apply hv i + funext j + have hc := congrArg (fun q : Polynomial K => q.coeff j.val) he + simpa only [P, Polynomial.ofFn_coeff_eq_val_of_lt _ j.isLt, Polynomial.coeff_zero, + Pi.zero_apply] using hc + have hPd (i : ι) : (P i).natDegree ≤ n := by + cases n with + | zero => simp [P] + | succ n => exact Nat.le_of_lt (Polynomial.ofFn_natDegree_lt (by omega) _) + let Q := ∏ i, P i + have hQ : Q ≠ 0 := Finset.prod_ne_zero_iff.mpr (fun i _ => hPn i) + have hd : Q.natDegree ≤ n * Fintype.card ι := by + calc + _ ≤ ∑ _i : ι, n := (Polynomial.natDegree_prod_le _ _).trans + (Finset.sum_le_sum (fun i _ => hPd i)) + _ = _ := by simp [Nat.mul_comm] + obtain ⟨k, hk, hval⟩ := polynomial_exists_small_nat_eval_ne_zero Q hQ + refine ⟨k, hk.trans hd, ?_⟩ + intro i + have hp : (∏ i, (P i).eval (k : K)) ≠ 0 := by + simpa only [Q, Polynomial.eval_prod] using hval + have hi := (Finset.prod_ne_zero_iff.mp hp) i (Finset.mem_univ i) + simpa only [P, Polynomial.ofFn_eq_sum_monomial, Polynomial.eval_finsetSum, + Polynomial.eval_monomial] using hi + +/-- Bounded integer moment-curve coefficients separate every pair of a finite +family of distinct vectors. -/ +theorem exists_bounded_moment_separation {K ι : Type*} [Field K] [CharZero K] + [Fintype ι] {n : ℕ} + (v : ι → Fin n → K) (hv : Function.Injective v) : + ∃ k : ℕ, k ≤ n * (Fintype.card ι) ^ 2 ∧ + Function.Injective (fun i => ∑ j : Fin n, v i j * (k : K) ^ j.val) := by + classical + let T := {t : ι × ι // t.1 ≠ t.2} + have hne (t : T) : v t.val.1 - v t.val.2 ≠ 0 := + sub_ne_zero.mpr (fun he => t.property (hv he)) + obtain ⟨k, hk, hnonzero⟩ := exists_bounded_moment_nonvanishing + (fun t : T => v t.val.1 - v t.val.2) hne + have hcard : Fintype.card T ≤ (Fintype.card ι) ^ 2 := by + have hh := Fintype.card_subtype_le (fun t : ι × ι => t.1 ≠ t.2) + simpa only [Fintype.card_prod, pow_two] using hh + refine ⟨k, hk.trans (Nat.mul_le_mul_left n hcard), ?_⟩ + intro i j he + by_contra hij + apply hnonzero ⟨(i, j), hij⟩ + simpa only [Pi.sub_apply, sub_mul, Finset.sum_sub_distrib, sub_eq_zero] using he + +end +end CCDN diff --git a/LeanPool/CCDN/Algebra/BoundedRowSeparation.lean b/LeanPool/CCDN/Algebra/BoundedRowSeparation.lean new file mode 100644 index 0000000000..34737e0c3c --- /dev/null +++ b/LeanPool/CCDN/Algebra/BoundedRowSeparation.lean @@ -0,0 +1,110 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import Mathlib.LinearAlgebra.Matrix.Adjugate +import LeanPool.CCDN.ForMathlib.Matrix +import Mathlib.LinearAlgebra.Matrix.AbsoluteValue + +/-! Bounded integral linear forms dual to independent integer rows. +A nonzero minor and its cofactors give the separation required in CCDN +Lemma 5.4, with the bound s! T^s for s+1 independent equations. -/ + +@[expose] public section +namespace CCDN +noncomputable section + +private lemma integer_adjugate_entry_abs_le {s : ℕ} + (M : Matrix (Fin (s + 1)) (Fin (s + 1)) ℤ) (T : ℕ) + (hM : ∀ i j, |M i j| ≤ (T : ℤ)) (i j : Fin (s + 1)) : + |M.adjugate i j| ≤ (s.factorial : ℤ) * (T : ℤ) ^ s := by + rw [Matrix.adjugate_fin_succ_eq_det_submatrix, abs_mul, abs_pow, abs_neg, + abs_one, one_pow, one_mul] + have hh := Matrix.det_le (abv := AbsoluteValue.abs) + (A := M.submatrix j.succAbove i.succAbove) + (fun a b => hM (j.succAbove a) (i.succAbove b)) + simpa only [AbsoluteValue.abs_apply, Fintype.card_fin, nsmul_eq_mul] using hh + +/-- A full independent family admits simultaneous bounded integral dual +forms. -/ +theorem exists_bounded_integer_dual_rows {s r : ℕ} + (A : Matrix (Fin (s + 1)) (Fin r) ℤ) + (hli : LinearIndependent ℚ (fun i => fun j => (A i j : ℚ))) + (T : ℕ) (hA : ∀ i j, |A i j| ≤ (T : ℤ)) : + ∃ D : ℤ, D ≠ 0 ∧ + |D| ≤ ((s + 1).factorial : ℤ) * (T : ℤ) ^ (s + 1) ∧ + ∃ b : Fin (s + 1) → Fin r → ℤ, + (∀ i ℓ, ∑ j, A ℓ j * b i j = if ℓ = i then D else 0) ∧ + (∀ i j, |b i j| ≤ (s.factorial : ℤ) * (T : ℤ) ^ s) := by + classical + let AQ : Matrix (Fin (s + 1)) (Fin r) ℚ := Matrix.of (fun i j => (A i j : ℚ)) + obtain ⟨c, hc, hminor⟩ := Matrix.exists_det_submatrix_ne_zero_of_linearIndependent_row AQ hli + let M := A.submatrix id c + have hmap : (Int.castRingHom ℚ).mapMatrix M = AQ.submatrix id c := by + ext i j + rfl + have hdet : M.det ≠ 0 := by + intro hz + have hh := congrArg (Int.castRingHom ℚ) hz + rw [RingHom.map_det, hmap, map_zero] at hh + exact hminor hh + have hdetbound : |M.det| ≤ ((s + 1).factorial : ℤ) * (T : ℤ) ^ (s + 1) := by + have hh := Matrix.det_le (abv := AbsoluteValue.abs) (A := M) + (fun i j => hA i (c j)) + simpa only [AbsoluteValue.abs_apply, Fintype.card_fin, nsmul_eq_mul] using hh + let b : Fin (s + 1) → Fin r → ℤ := + fun i j => ∑ k, if c k = j then M.adjugate k i else 0 + have hbimage (i k : Fin (s + 1)) : b i (c k) = M.adjugate k i := by + simp only [b, hc.eq_iff, Finset.sum_ite_eq', Finset.mem_univ, ite_true] + have hboutside (i : Fin (s + 1)) (j : Fin r) (hj : ¬∃ k, c k = j) : + b i j = 0 := by + apply Finset.sum_eq_zero + intro k _ + exact ite_eq_right (fun he => hj ⟨k, he⟩) + refine ⟨M.det, hdet, hdetbound, b, ?_, ?_⟩ + · intro i ℓ + have hrow : (∑ j, A ℓ j * b i j) = ∑ k, M ℓ k * M.adjugate k i := by + simp only [b, Finset.mul_sum] + rw [Finset.sum_comm] + apply Finset.sum_congr rfl + intro k _ + simp only [mul_ite, mul_zero, Finset.sum_ite_eq, Finset.mem_univ, ite_true, + M, Matrix.submatrix_apply, id_eq] + rw [hrow] + have hh := congrArg (fun Q : Matrix (Fin (s + 1)) (Fin (s + 1)) ℤ => Q ℓ i) + (Matrix.mul_adjugate M) + simpa only [Matrix.mul_apply, Matrix.smul_apply, Matrix.one_apply, smul_eq_mul, + mul_ite, mul_one, mul_zero] using hh + · intro i j + by_cases hj : ∃ k, c k = j + · obtain ⟨k, rfl⟩ := hj + rw [hbimage] + exact integer_adjugate_entry_abs_le M T (fun a b => hA a (c b)) k i + · rw [hboutside i j hj, abs_zero] + positivity + +/-- A valid factorial cofactor bound for the separation conclusion of CCDN +Lemma 5.4. The different numerical constant printed in that lemma is refuted +in `CCDNPaper/Counterexample/RowSeparatorConstant.lean`. -/ +theorem exists_bounded_row_separator {s r : ℕ} + (A : Matrix (Fin (s + 1)) (Fin r) ℤ) + (hli : LinearIndependent ℚ (fun i => fun j => (A i j : ℚ))) + (T : ℕ) (hA : ∀ i j, |A i j| ≤ (T : ℤ)) (i : Fin (s + 1)) : + ∃ b : Fin r → ℤ, + (∑ j, A i j * b j) ≠ 0 ∧ + (∀ ℓ, ℓ ≠ i → ∑ j, A ℓ j * b j = 0) ∧ + (∀ j, |b j| ≤ (s.factorial : ℤ) * (T : ℤ) ^ s) := by + obtain ⟨D, hD, _, b, hb, hbound⟩ := exists_bounded_integer_dual_rows A hli T hA + refine ⟨b i, ?_, ?_, hbound i⟩ + · simpa only [hb, ite_true] using hD + · intro ℓ hℓ + simp only [hb, ite_eq_right hℓ] + +end +end CCDN diff --git a/LeanPool/CCDN/Algebra/CharZeroOrGT.lean b/LeanPool/CCDN/Algebra/CharZeroOrGT.lean new file mode 100644 index 0000000000..036ea7c888 --- /dev/null +++ b/LeanPool/CCDN/Algebra/CharZeroOrGT.lean @@ -0,0 +1,82 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import Mathlib.Algebra.CharP.Defs +public import Mathlib.Algebra.Field.Defs +import LeanPool.CCDN.ForMathlib.MvPolynomialDerivative + +/-! +# Characteristic zero or larger than a bound + +`CharZeroOrGT R n` says that the positive natural numbers up to `n` are nonzero in `R`. For a +field this means that the characteristic is zero or larger than `n`. Results that hold both in +characteristic zero and in characteristic `p > n` are stated once under this hypothesis. + +## Main results + +* `CCDN.CharZeroOrGT.of_charZero`, `CCDN.CharZeroOrGT.of_charP`: the two sources of the + hypothesis. +* `CCDN.CharZeroOrGT.map`: the hypothesis passes to any extension of a field. +* `CCDN.CharZeroOrGT.iff_ringChar`: the characterization by `ringChar`. +-/ + +@[expose] public section + +namespace CCDN + +/-- The positive natural numbers up to `n` are nonzero in `R`; for a field, the characteristic +of `R` is zero or larger than `n`. -/ +def CharZeroOrGT (R : Type*) [AddMonoidWithOne R] (n : ℕ) : Prop := + ∀ m : ℕ, 0 < m → m ≤ n → (m : R) ≠ 0 + +namespace CharZeroOrGT + +variable {R S : Type*} + +/-- Characteristic zero exceeds every bound. -/ +theorem of_charZero [AddMonoidWithOne R] [CharZero R] (n : ℕ) : CharZeroOrGT R n := + fun _ hm _ ↦ Nat.cast_ne_zero.mpr hm.ne' + +/-- Characteristic `p` exceeds every bound below `p`. -/ +theorem of_charP [AddMonoidWithOne R] {p n : ℕ} [CharP R p] (h : n < p) : CharZeroOrGT R n := + fun _ hm hmn ↦ CharP.natCast_ne_zero_of_lt R hm.ne' (hmn.trans_lt h) + +/-- The hypothesis weakens to any smaller bound. -/ +theorem mono [AddMonoidWithOne R] {m n : ℕ} (h : CharZeroOrGT R n) (hmn : m ≤ n) : + CharZeroOrGT R m := + fun k hk hkm ↦ h k hk (hkm.trans hmn) + +/-- A positive natural number up to the bound is nonzero. -/ +theorem natCast_ne_zero [AddMonoidWithOne R] {m n : ℕ} (h : CharZeroOrGT R n) (hm : m ≠ 0) + (hmn : m ≤ n) : (m : R) ≠ 0 := + h m (Nat.pos_of_ne_zero hm) hmn + +/-- The hypothesis passes along a ring homomorphism out of a division ring, such as the +algebra map of a field extension. -/ +theorem map [DivisionRing R] [Semiring S] [Nontrivial S] {n : ℕ} (h : CharZeroOrGT R n) + (φ : R →+* S) : CharZeroOrGT S n := + fun m hm hmn ↦ by simpa only [map_natCast] using (map_ne_zero φ).mpr (h m hm hmn) + +/-- `CharZeroOrGT R n` says exactly that the characteristic of `R` is zero or exceeds `n`. -/ +theorem iff_ringChar [NonAssocSemiring R] {n : ℕ} : + CharZeroOrGT R n ↔ ringChar R = 0 ∨ n < ringChar R := by + refine ⟨fun h ↦ ?_, fun h m hm hmn hz ↦ ?_⟩ + · refine (Nat.eq_zero_or_pos (ringChar R)).imp id fun hp ↦ ?_ + by_contra! hle + exact h _ hp hle (ringChar.Nat.cast_ringChar) + · have hdvd := (ringChar.spec R m).mp hz + rcases h with h | h + · rw [h, zero_dvd_iff] at hdvd + omega + · exact absurd (Nat.le_of_dvd hm hdvd) (by omega) + +end CharZeroOrGT + +end CCDN diff --git a/LeanPool/CCDN/Algebra/CoefficientwiseHomogeneous.lean b/LeanPool/CCDN/Algebra/CoefficientwiseHomogeneous.lean new file mode 100644 index 0000000000..4b7a09e5c3 --- /dev/null +++ b/LeanPool/CCDN/Algebra/CoefficientwiseHomogeneous.lean @@ -0,0 +1,136 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import Mathlib.LinearAlgebra.Matrix.Determinant.Basic +public import Mathlib.RingTheory.MvPolynomial.Homogeneous +import Mathlib.Algebra.MvPolynomial.CommRing +import Mathlib.Tactic.Polynomial.Basic + +/-! Homogeneity in the coefficient variables, while retaining independent +polynomial parameters. This property survives determinant expansion and +extraction of any parameter coefficient. -/ + +@[expose] public section +namespace CCDN +noncomputable section +open MvPolynomial + +/-- A polynomial `f` in the variables `σ` with coefficients in `MvPolynomial τ R` is coefficientwise +homogeneous of degree `d` if each of its coefficients is a homogeneous polynomial of degree `d` in +the variables `τ`. -/ +def CoefficientwiseHomogeneous {R σ τ : Type*} [CommRing R] + (f : MvPolynomial σ (MvPolynomial τ R)) (d : ℕ) : Prop := + ∀ m, (f.coeff m).IsHomogeneous d + +namespace CoefficientwiseHomogeneous + +variable {R σ τ : Type*} [CommRing R] + +/-- The zero polynomial is coefficientwise homogeneous of every degree. -/ +lemma zero (d : ℕ) : CoefficientwiseHomogeneous (0 : MvPolynomial σ (MvPolynomial τ R)) d := by + intro m + simpa only [AddMonoidAlgebra.coeff_zero, Finsupp.zero_apply] using isHomogeneous_zero τ R d + +/-- A sum of two coefficientwise homogeneous polynomials of degree `d` is coefficientwise +homogeneous of degree `d`. -/ +lemma add {f g : MvPolynomial σ (MvPolynomial τ R)} {d : ℕ} + (hf : CoefficientwiseHomogeneous f d) (hg : CoefficientwiseHomogeneous g d) : + CoefficientwiseHomogeneous (f + g) d := by + intro m + rw [AddMonoidAlgebra.coeff_add, Finsupp.add_apply] + exact (hf m).add (hg m) + +/-- The negation of a coefficientwise homogeneous polynomial of degree `d` is coefficientwise +homogeneous of degree `d`. -/ +lemma neg {f : MvPolynomial σ (MvPolynomial τ R)} {d : ℕ} + (hf : CoefficientwiseHomogeneous f d) : + CoefficientwiseHomogeneous (-f) d := by + intro m + rw [coeff_neg] + exact (hf m).neg + +/-- A finite sum of coefficientwise homogeneous polynomials of degree `d` is coefficientwise +homogeneous of degree `d`. -/ +lemma sum {ι : Type*} (s : Finset ι) (f : ι → MvPolynomial σ (MvPolynomial τ R)) (d : ℕ) + (hf : ∀ i ∈ s, CoefficientwiseHomogeneous (f i) d) : + CoefficientwiseHomogeneous (∑ i ∈ s, f i) d := by + intro m + rw [coeff_sum] + exact IsHomogeneous.sum s _ d (fun i hi => hf i hi m) + +/-- The product of coefficientwise homogeneous polynomials of degrees `d` and `e` is coefficientwise +homogeneous of degree `d + e`. -/ +lemma mul {f g : MvPolynomial σ (MvPolynomial τ R)} {d e : ℕ} + (hf : CoefficientwiseHomogeneous f d) (hg : CoefficientwiseHomogeneous g e) : + CoefficientwiseHomogeneous (f * g) (d + e) := by + classical + intro m + rw [coeff_mul] + exact IsHomogeneous.sum _ _ _ (fun ij _ => (hf ij.1).mul (hg ij.2)) + +/-- The constant polynomial `C f` is coefficientwise homogeneous of degree `d` when `f` is a +homogeneous polynomial of degree `d` in the variables `τ`. -/ +lemma C_of_isHomogeneous {f : MvPolynomial τ R} {d : ℕ} (hf : f.IsHomogeneous d) : + CoefficientwiseHomogeneous (MvPolynomial.C f : MvPolynomial σ (MvPolynomial τ R)) d := by + classical + intro m + rw [coeff_C] + split_ifs + · exact hf + · exact isHomogeneous_zero τ R d + +/-- The polynomial `1` is coefficientwise homogeneous of degree `0`. -/ +lemma one : CoefficientwiseHomogeneous (1 : MvPolynomial σ (MvPolynomial τ R)) 0 := by + simpa only [map_one] using C_of_isHomogeneous (σ := σ) (isHomogeneous_one τ R) + +/-- A polynomial over `R`, with its coefficients viewed as constants of `MvPolynomial τ R`, is +coefficientwise homogeneous of degree `0`. -/ +lemma map_C (f : MvPolynomial σ R) : + CoefficientwiseHomogeneous + (MvPolynomial.map (MvPolynomial.C : R →+* MvPolynomial τ R) f) 0 := by + intro m + rw [coeff_map] + exact isHomogeneous_C _ _ + +/-- A finite product of coefficientwise homogeneous polynomials of degrees `d i` is coefficientwise +homogeneous of degree `∑ i ∈ s, d i`. -/ +lemma prod {ι : Type*} (s : Finset ι) (f : ι → MvPolynomial σ (MvPolynomial τ R)) + (d : ι → ℕ) (hf : ∀ i ∈ s, CoefficientwiseHomogeneous (f i) (d i)) : + CoefficientwiseHomogeneous (∏ i ∈ s, f i) (∑ i ∈ s, d i) := by + classical + induction s using Finset.induction_on with + | empty => simpa only [Finset.prod_empty, Finset.sum_empty] using one (R := R) (σ := σ) (τ := τ) + | @insert a s ha ih => + rw [Finset.prod_insert ha, Finset.sum_insert ha] + exact (hf a (Finset.mem_insert_self _ _)).mul + (ih (fun i hi => hf i (Finset.mem_insert_of_mem hi))) + +/-- The determinant of an `r × r` matrix whose entries are coefficientwise homogeneous of degree `1` +is coefficientwise homogeneous of degree `r`. -/ +lemma det {r : ℕ} (M : Matrix (Fin r) (Fin r) (MvPolynomial σ (MvPolynomial τ R))) + (hM : ∀ i j, CoefficientwiseHomogeneous (M i j) 1) : + CoefficientwiseHomogeneous M.det r := by + classical + rw [Matrix.det_apply'] + apply sum + intro π _ + have hp := prod Finset.univ (fun i => M (π i) i) (fun _ => 1) (fun i _ => hM _ i) + have hp' : CoefficientwiseHomogeneous (∏ i, M (π i) i) r := by + simpa only [Finset.sum_const, Finset.card_univ, Fintype.card_fin, smul_eq_mul, mul_one] using hp + have hc : CoefficientwiseHomogeneous + (MvPolynomial.C (MvPolynomial.C ((Equiv.Perm.sign π : ℤ) : R)) : + MvPolynomial σ (MvPolynomial τ R)) 0 := + C_of_isHomogeneous (isHomogeneous_C τ _) + have hh := hc.mul hp' + simpa only [zero_add, map_intCast] using hh + +end CoefficientwiseHomogeneous +end +end CCDN diff --git a/LeanPool/CCDN/Algebra/CoordinatePowerGeneration.lean b/LeanPool/CCDN/Algebra/CoordinatePowerGeneration.lean new file mode 100644 index 0000000000..4866c4ef05 --- /dev/null +++ b/LeanPool/CCDN/Algebra/CoordinatePowerGeneration.lean @@ -0,0 +1,129 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.Hilbert.FilteredIntersection + +/-! Balanced coordinate-power reductions imply eventual generation by a +linear parameter in arbitrary ambient dimension. -/ + +@[expose] public section +namespace CCDN +noncomputable section +open MvPolynomial + +/-- The degree filtration of the coordinate ring `MvPolynomial σ K ⧸ I` is increasing. -/ +lemma filteredCoordinateSpace_mono {K σ : Type*} [Field K] + (I : Ideal (MvPolynomial σ K)) {m n : ℕ} (hmn : m ≤ n) : + filteredCoordinateSpace I m ≤ filteredCoordinateSpace I n := by + apply Submodule.map_mono + intro f hf + exact (mem_restrictTotalDegree _ _ _).mpr (((mem_restrictTotalDegree _ _ _).mp hf).trans hmn) + +/-- The degree filtration of the coordinate ring is multiplicative: a product of classes of +degree at most `m` and `n` has degree at most `m + n`. -/ +lemma filteredCoordinateSpace_mul_mem {K σ : Type*} [Field K] + (I : Ideal (MvPolynomial σ K)) {m n : ℕ} {x y : MvPolynomial σ K ⧸ I} + (hx : x ∈ filteredCoordinateSpace I m) (hy : y ∈ filteredCoordinateSpace I n) : + x * y ∈ filteredCoordinateSpace I (m + n) := by + obtain ⟨f, hf, rfl⟩ := Submodule.mem_map.mp hx + obtain ⟨g, hg, rfl⟩ := Submodule.mem_map.mp hy + apply Submodule.mem_map.mpr + refine ⟨f * g, ?_, by simp⟩ + rw [mem_restrictTotalDegree] at hf hg ⊢ + exact (totalDegree_mul _ _).trans (Nat.add_le_add hf hg) + +/-- An exponent vector of positive degree at least `Fintype.card σ * N` has some coordinate +at least `N`. -/ +lemma large_exponent_of_degree {σ : Type*} [Fintype σ] + (e : σ →₀ ℕ) (N : ℕ) (hpos : 0 < e.degree) + (he : Fintype.card σ * N ≤ e.degree) : ∃ j, N ≤ e j := by + classical + by_contra! hn + have hs := Finsupp.degree_eq_sum e + have hnon : (Finset.univ : Finset σ).Nonempty := by + by_contra h + have hz : (Finset.univ : Finset σ) = ∅ := Finset.not_nonempty_iff_eq_empty.mp h + rw [hz, Finset.sum_empty] at hs + omega + have hlt := Finset.sum_lt_sum_of_nonempty hnon (fun j _ => hn j) + simp only [Finset.sum_const, Finset.card_univ, smul_eq_mul] at hlt + omega + +/-- Let `l` have total degree at most `1`. If each coordinate power `X j ^ N` is congruent +modulo `I` to `r + l * s` with `r` and `s` of total degree at most `N - 1`, then for every +`n ≥ Fintype.card σ * N` the degree-`(n + 1)` piece of the filtered coordinate ring is the +degree-`n` piece plus `l` times the degree-`n` piece. -/ +theorem eventual_generation_of_power_reductions {K σ : Type*} [Field K] [Fintype σ] + (I : Ideal (MvPolynomial σ K)) (l : MvPolynomial σ K) (hl : l.totalDegree ≤ 1) + (N : ℕ) (hN : 0 < N) + (hpow : ∀ j : σ, ∃ r s : MvPolynomial σ K, + r.totalDegree ≤ N - 1 ∧ s.totalDegree ≤ N - 1 ∧ + (Ideal.Quotient.mk I) (X j ^ N) = (Ideal.Quotient.mk I) r + + (Ideal.Quotient.mk I) l * (Ideal.Quotient.mk I) s) : + ∀ n ≥ Fintype.card σ * N, filteredCoordinateSpace I (n + 1) = + filteredCoordinateSpace I n ⊔ (filteredCoordinateSpace I n).map + (LinearMap.mulLeft K ((Ideal.Quotient.mk I) l)) := by + classical + intro n hn + have hlmem : (Ideal.Quotient.mk I) l ∈ filteredCoordinateSpace I 1 := + ⟨l, (mem_restrictTotalDegree _ _ _).mpr hl, rfl⟩ + apply le_antisymm + · have hmono (e : σ →₀ ℕ) (c : K) (he : e.degree ≤ n + 1) : + (Ideal.Quotient.mk I) (monomial e c) ∈ filteredCoordinateSpace I n ⊔ + (filteredCoordinateSpace I n).map (LinearMap.mulLeft K ((Ideal.Quotient.mk I) l)) := by + by_cases hsmall : e.degree ≤ n + · apply (le_sup_left : filteredCoordinateSpace I n ≤ _) + exact ⟨monomial e c, (mem_restrictTotalDegree _ _ _).mpr + ((totalDegree_monomial_le e c).trans hsmall), rfl⟩ + have heq : e.degree = n + 1 := by omega + obtain ⟨j, hj⟩ := large_exponent_of_degree e N (by omega) (by omega) + obtain ⟨r, s, hr, hs, hrel⟩ := hpow j + let u := e - Finsupp.single j N + have hu : u + Finsupp.single j N = e := tsub_add_cancel_of_le (Finsupp.single_le_iff.mpr hj) + have hud : u.degree + N = e.degree := by + simpa only [map_add, Finsupp.degree_single] using congrArg Finsupp.degree hu + have hm : monomial e c = monomial u c * X j ^ N := by + rw [X_pow_eq_monomial, monomial_mul_monomial, mul_one, hu] + have hr' : (monomial u c * r).totalDegree ≤ n := by + apply (totalDegree_mul _ _).trans + have hh := totalDegree_monomial_le u c + change (monomial u c).totalDegree ≤ u.degree at hh + omega + have hs' : (monomial u c * s).totalDegree ≤ n := by + apply (totalDegree_mul _ _).trans + have hh := totalDegree_monomial_le u c + change (monomial u c).totalDegree ≤ u.degree at hh + omega + rw [hm, map_mul, hrel, mul_add] + apply Submodule.add_mem + · apply (le_sup_left : filteredCoordinateSpace I n ≤ _) + exact ⟨monomial u c * r, (mem_restrictTotalDegree _ _ _).mpr hr', by simp⟩ + · apply (le_sup_right : (filteredCoordinateSpace I n).map + (LinearMap.mulLeft K ((Ideal.Quotient.mk I) l)) ≤ _) + refine ⟨(Ideal.Quotient.mk I) (monomial u c * s), + ⟨monomial u c * s, (mem_restrictTotalDegree _ _ _).mpr hs', rfl⟩, ?_⟩ + simp only [LinearMap.mulLeft_apply, map_mul] + ring + intro x hx + obtain ⟨f, hf, rfl⟩ := hx + have hfd := (mem_restrictTotalDegree _ _ _).mp hf + change (Ideal.Quotient.mk I) f ∈ _ + rw [← f.support_sum_monomial_coeff, map_sum] + apply Submodule.sum_mem + intro e he + exact hmono e (f.coeff e) ((Finset.le_sup he).trans hfd) + · apply sup_le + · exact filteredCoordinateSpace_mono I (by omega) + · rintro x ⟨y, hy, rfl⟩ + simpa only [LinearMap.mulLeft_apply, Nat.add_comm] using + filteredCoordinateSpace_mul_mem I hlmem hy + +end +end CCDN diff --git a/LeanPool/CCDN/Algebra/FiniteFieldProjectiveCone.lean b/LeanPool/CCDN/Algebra/FiniteFieldProjectiveCone.lean new file mode 100644 index 0000000000..985f9a9df4 --- /dev/null +++ b/LeanPool/CCDN/Algebra/FiniteFieldProjectiveCone.lean @@ -0,0 +1,113 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.Height.ProjectiveZeroLocus +import Mathlib.LinearAlgebra.Projectivization.Cardinality + +/-! The exact relation between a homogeneous affine cone and its projective +zero locus. The origin is counted once and each projective zero contributes +exactly one nonzero vector for every unit in the field. -/ + +@[expose] public section +namespace CCDN +noncomputable section +open MvPolynomial + +private theorem homogeneous_affine_cone_card {K : Type*} [Field K] [Fintype K] {n d : ℕ} + (f : MvPolynomial (Fin (n + 1)) K) (hf : f.IsHomogeneous d) (hd : 0 < d) : + Nat.card {v : Fin (n + 1) → K // eval v f = 0} = + Nat.card (projectiveZeroLocus f) * (Fintype.card K - 1) + 1 := by + classical + let A := {v : Fin (n + 1) → K // eval v f = 0} + let P := projectiveZeroLocus f + let : Fintype A := Fintype.ofFinite _ + let : Fintype P := Fintype.ofFinite _ + have hzero : eval (0 : Fin (n + 1) → K) f = 0 := by + have h := homogeneous_eval_smul hf (0 : K) (0 : Fin (n + 1) → K) + simpa [zero_pow (Nat.ne_of_gt hd)] using h + let zeroA : A := ⟨0, hzero⟩ + let g : P × Kˣ → {v : A // v ≠ zeroA} := fun ru => + ⟨⟨(ru.2 : K) • ru.1.val.rep, + (homogeneous_eval_zero_iff_smul hf ru.2.ne_zero _).mpr ru.1.property⟩, + by + intro h + have hv : (ru.2 : K) • ru.1.val.rep = 0 := congrArg Subtype.val h + exact ru.1.val.rep_nonzero ((smul_eq_zero.mp hv).resolve_left ru.2.ne_zero)⟩ + have hg : Function.Bijective g := by + constructor + · rintro ⟨r, u⟩ ⟨t, w⟩ h + have hv : (u : K) • r.val.rep = (w : K) • t.val.rep := + congrArg (fun v => v.val.val) h + have hrt : r = t := by + apply Subtype.ext + rw [← r.val.mk_rep, ← t.val.mk_rep, Projectivization.mk_eq_mk_iff'] + refine ⟨(u : K)⁻¹ * (w : K), ?_⟩ + have hh := congrArg (fun v => (u : K)⁻¹ • v) hv.symm + simpa only [smul_smul, inv_mul_cancel₀ u.ne_zero, one_smul] using hh + subst t + have huw : u = w := by + obtain ⟨j, hj⟩ := Function.ne_iff.mp r.val.rep_nonzero + apply Units.ext + exact mul_right_cancel₀ hj (congrFun hv j) + subst w + rfl + · intro v + have hv : v.val.val ≠ 0 := by + intro h + apply v.property + exact Subtype.ext h + let r : P := ⟨Projectivization.mk K v.val.val hv, + (mem_projectiveZeroLocus_mk hf _ hv).mpr v.val.property⟩ + obtain ⟨u, hu⟩ := Projectivization.exists_smul_eq_mk_rep K v.val.val hv + refine ⟨(r, u⁻¹), ?_⟩ + apply Subtype.ext + apply Subtype.ext + change (↑u⁻¹ : K) • (Projectivization.mk K v.val.val hv).rep = v.val.val + rw [← hu] + change (↑u⁻¹ : K) • (u : K) • v.val.val = v.val.val + simp [smul_smul] + have hc : Fintype.card {v : A // v ≠ zeroA} = Fintype.card P * (Fintype.card K - 1) := by + rw [← Fintype.card_of_bijective hg, Fintype.card_prod, Fintype.card_units] + have hn : 0 < Fintype.card A := Fintype.card_pos_iff.mpr ⟨zeroA⟩ + have hcompl : Fintype.card {v : A // v ≠ zeroA} = Fintype.card A - 1 := by simp + rw [hcompl] at hc + have hfinish : Fintype.card A = Fintype.card P * (Fintype.card K - 1) + 1 := by omega + simpa only [Fintype.card_eq_nat_card] using hfinish + +/-- Passing from the affine cone to its projective zero locus preserves the +leading coefficient one, with an explicit lower-order error. -/ +theorem homogeneous_projective_count_of_cone_bound {K : Type*} [Field K] [Fintype K] + {n d : ℕ} (f : MvPolynomial (Fin (n + 3)) K) (hf : f.IsHomogeneous d) (hd : 0 < d) + {H : ℝ} (hH : 0 ≤ H) + (hcone : (Nat.card {v : Fin (n + 3) → K // eval v f = 0} : ℝ) ≤ + (Fintype.card K : ℝ) ^ (n + 2) + H * (Fintype.card K : ℝ) ^ (n + 1)) : + (Nat.card (projectiveZeroLocus f) : ℝ) ≤ + (Fintype.card K : ℝ) ^ (n + 1) + 2 * (H + 1) * (Fintype.card K : ℝ) ^ n := by + have hqN : 2 ≤ Fintype.card K := Fintype.one_lt_card + have hq : (2 : ℝ) ≤ Fintype.card K := by exact_mod_cast hqN + have hc := homogeneous_affine_cone_card f hf hd + rw [hc] at hcone + push_cast at hcone + rw [Nat.cast_sub (by omega : 1 ≤ Fintype.card K), Nat.cast_one] at hcone + have herror : 0 ≤ (H + 1) * ((Fintype.card K : ℝ) - 2) * (Fintype.card K : ℝ) ^ n := + mul_nonneg (mul_nonneg (by linarith) (by linarith)) (by positivity) + have hmajor : + (Fintype.card K : ℝ) ^ (n + 2) + H * (Fintype.card K : ℝ) ^ (n + 1) ≤ + ((Fintype.card K : ℝ) ^ (n + 1) + 2 * (H + 1) * (Fintype.card K : ℝ) ^ n) * + (Fintype.card K - 1) := by + simp only [pow_succ] at * + nlinarith only [herror] + have hmul : (Nat.card (projectiveZeroLocus f) : ℝ) * (Fintype.card K - 1) ≤ + ((Fintype.card K : ℝ) ^ (n + 1) + 2 * (H + 1) * (Fintype.card K : ℝ) ^ n) * + (Fintype.card K - 1) := by linarith [hcone.trans hmajor] + exact (mul_le_mul_iff_left₀ (by linarith : (0 : ℝ) < Fintype.card K - 1)).mp hmul + +end +end CCDN diff --git a/LeanPool/CCDN/Algebra/GridPointInterpolation.lean b/LeanPool/CCDN/Algebra/GridPointInterpolation.lean new file mode 100644 index 0000000000..b05be304e1 --- /dev/null +++ b/LeanPool/CCDN/Algebra/GridPointInterpolation.lean @@ -0,0 +1,109 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.Hilbert.FilteredIntersection +import Mathlib.LinearAlgebra.Dimension.Finite +import Mathlib.LinearAlgebra.FreeModule.StrongRankCondition + +/-! Coordinatewise Lagrange interpolation on a finite grid. The degree +depends on the side length of the grid rather than its number of points. -/ + +@[expose] public section +namespace CCDN +noncomputable section +open MvPolynomial + +private def gridPointSeparator {K σ : Type*} [Field K] [Fintype σ] [DecidableEq K] + (S : Finset K) (a : σ → K) : MvPolynomial σ K := + ∏ j, ∏ t ∈ S.erase (a j), + C ((a j - t)⁻¹) * (X j - C t) + +private lemma gridPointSeparator_eval {K σ : Type*} [Field K] [Fintype σ] + [DecidableEq K] + (S : Finset K) (a x : σ → K) (hx : ∀ j, x j ∈ S) : + eval x (gridPointSeparator S a) = if x = a then 1 else 0 := by + classical + rw [gridPointSeparator, eval_prod] + by_cases h : x = a + · subst x + rw [ite_eq_left rfl] + apply Finset.prod_eq_one + intro j _ + rw [eval_prod] + apply Finset.prod_eq_one + intro t ht + simp only [eval_mul, eval_C, eval_sub, eval_X] + exact inv_mul_cancel₀ (sub_ne_zero.mpr (Finset.mem_erase.mp ht).1.symm) + · rw [ite_eq_right h] + obtain ⟨j, hj⟩ := Function.ne_iff.mp h + apply Finset.prod_eq_zero (Finset.mem_univ j) + rw [eval_prod] + apply Finset.prod_eq_zero (Finset.mem_erase.mpr ⟨hj, hx j⟩) + simp + +private lemma gridPointSeparator_degree {K σ : Type*} [Field K] [Fintype σ] + [DecidableEq K] + (S : Finset K) (a : σ → K) (ha : ∀ j, a j ∈ S) : + (gridPointSeparator S a).totalDegree ≤ Fintype.card σ * (S.card - 1) := by + classical + unfold gridPointSeparator + calc + _ ≤ ∑ j : σ, (∏ t ∈ S.erase (a j), + C ((a j - t)⁻¹) * (X j - C t)).totalDegree := totalDegree_finsetProd _ _ + _ ≤ ∑ _j : σ, (S.card - 1) := by + apply Finset.sum_le_sum + intro j _ + calc + _ ≤ ∑ t ∈ S.erase (a j), + (C ((a j - t)⁻¹) * (X j - C t)).totalDegree := totalDegree_finsetProd _ _ + _ ≤ ∑ _t ∈ S.erase (a j), 1 := by + apply Finset.sum_le_sum + intro t _ + apply (totalDegree_mul _ _).trans + rw [totalDegree_C, Nat.zero_add] + exact (totalDegree_sub _ _).trans (by simp) + _ = _ := by simp [Finset.card_erase_of_mem (ha j)] + _ = _ := by simp + +/-- Distinct points of a fixed coordinate grid give independent filtered +coordinate classes at the grid-interpolation degree. -/ +theorem grid_points_filtered_lower {K σ ι : Type*} [Field K] + [Fintype σ] [Fintype ι] + (I : Ideal (MvPolynomial σ K)) (S : Finset K) + (z : ι → σ → K) (hz : Function.Injective z) + (hzS : ∀ i j, z i j ∈ S) + (hzI : ∀ i f, f ∈ I → eval (z i) f = 0) {N : ℕ} + (hN : Fintype.card σ * (S.card - 1) ≤ N) : + Fintype.card ι ≤ Module.finrank K (filteredCoordinateSpace I N) := by + classical + let q := Ideal.Quotient.mkₐ K I + let ψ : ι → (MvPolynomial σ K ⧸ I) →ₐ[K] K := fun i => + Ideal.Quotient.liftₐ I (aeval (z i)) (by + intro f hf + simpa only [aeval_eq_eval] using hzI i f hf) + have hψ (i : ι) (f : MvPolynomial σ K) : ψ i (q f) = eval (z i) f := rfl + let v : ι → filteredCoordinateSpace I N := fun j => + ⟨q (gridPointSeparator S (z j)), Submodule.mem_map.mpr + ⟨gridPointSeparator S (z j), + (mem_restrictTotalDegree _ _ _).mpr + ((gridPointSeparator_degree S (z j) (hzS j)).trans hN), rfl⟩⟩ + have hlin : LinearIndependent K v := by + rw [Fintype.linearIndependent_iff] + intro c hc i + have he := congrArg (fun w : filteredCoordinateSpace I N => ψ i w.val) hc + simp only [Submodule.coe_sum, Submodule.coe_smul, map_sum, map_smul, + ZeroMemClass.coe_zero, map_zero] at he + change (∑ j, c j • ψ i (q (gridPointSeparator S (z j)))) = 0 at he + simp only [hψ, gridPointSeparator_eval S _ _ (hzS i), hz.eq_iff, smul_eq_mul] at he + simpa using he + exact hlin.fintype_card_le_finrank + +end +end CCDN diff --git a/LeanPool/CCDN/Algebra/HomogeneousChart.lean b/LeanPool/CCDN/Algebra/HomogeneousChart.lean new file mode 100644 index 0000000000..77e0f05d67 --- /dev/null +++ b/LeanPool/CCDN/Algebra/HomogeneousChart.lean @@ -0,0 +1,258 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.Algebra.HomogeneousMonomials +public import LeanPool.CCDN.Height.PrimitiveIntegerPolynomial +import LeanPool.CCDN.Algebra.HomogeneousEvaluation +import LeanPool.CCDN.ForMathlib.MvPolynomial +import Mathlib.Algebra.MvPolynomial.NoZeroDivisors + +/-! Restriction to x₀=1 is injective on forms of a fixed homogeneous degree. +Explicit homogenization supplies its inverse on bounded-degree polynomials. -/ + +@[expose] public section +namespace CCDN +noncomputable section +open MvPolynomial + +/-- The dehomogenization at the first coordinate: the ring homomorphism sending `X 0` to `1` and +`X i.succ` to `X i`, which restricts a polynomial in `n + 1` variables to the affine chart +`x₀ = 1`. -/ +def affineChart {K : Type*} [CommRing K] (n : ℕ) : + MvPolynomial (Fin (n + 1)) K →+* MvPolynomial (Fin n) K := + eval₂Hom C (Fin.cons 1 X) + +/-- The exponent in `n` variables obtained from an exponent `m` in `n + 1` variables by forgetting +the exponent of the first variable. -/ +def chartExponent {n : ℕ} (m : Fin (n + 1) →₀ ℕ) : Fin n →₀ ℕ := + Finsupp.equivFunOnFinite.symm (fun i => m i.succ) + +/-- The entry of `chartExponent m` at `i` is the entry of `m` at `i.succ`. -/ +@[simp] lemma chartExponent_apply {n : ℕ} (m : Fin (n + 1) →₀ ℕ) (i : Fin n) : + chartExponent m i = m i.succ := rfl + +/-- The degree of an exponent `m` in `n + 1` variables is the exponent of the first variable plus +the degree of `chartExponent m`. -/ +lemma exponent_degree_split {n : ℕ} (m : Fin (n + 1) →₀ ℕ) : + m.sum (fun _ e => e) = m 0 + (chartExponent m).sum (fun _ e => e) := by + rw [Finsupp.sum_fintype _ _ (by simp), Fin.sum_univ_succ, + Finsupp.sum_fintype _ _ (by simp)] + rfl + +private lemma chartExponent_injective_on_degree {n M : ℕ} {m k : Fin (n + 1) →₀ ℕ} + (hm : m.sum (fun _ e => e) = M) (hk : k.sum (fun _ e => e) = M) + (he : chartExponent m = chartExponent k) : m = k := by + have h0 : m 0 = k 0 := by + rw [exponent_degree_split] at hm hk + rw [he] at hm + omega + ext i + refine Fin.cases h0 (fun j => ?_) i + exact congrArg (fun t => t j) he + +/-- The affine chart of the monomial `monomial m c` is the monomial +`monomial (chartExponent m) c`. -/ +lemma affineChart_monomial {K : Type*} [CommRing K] {n : ℕ} + (m : Fin (n + 1) →₀ ℕ) (c : K) : + affineChart n (monomial m c) = monomial (chartExponent m) c := by + change eval₂ C (Fin.cons 1 X) (monomial m c) = _ + rw [eval₂_monomial, monomial_eq, + Finsupp.prod_fintype _ _ (by simp), Finsupp.prod_fintype _ _ (by simp), Fin.prod_univ_succ] + simp + +/-- For `f` homogeneous of degree `M`, the coefficient of `affineChart n f` at the chart exponent of +a degree-`M` exponent `m` is the coefficient of `f` at `m`. -/ +lemma affineChart_homogeneous_coefficient {K : Type*} [CommRing K] {n M : ℕ} + (f : MvPolynomial (Fin (n + 1)) K) (hf : f.IsHomogeneous M) + (j : Fin ((n + M).choose M)) : + (affineChart n f).coeff (chartExponent (homogeneousMonomials n M j)) = + f.coeff (homogeneousMonomials n M j) := by + classical + have hinj : Function.Injective (fun j => chartExponent (homogeneousMonomials n M j)) := by + intro j k he + apply homogeneousMonomials_injective n M + exact chartExponent_injective_on_degree (homogeneousMonomials_degree n M j) + (homogeneousMonomials_degree n M k) he + conv_lhs => rw [homogeneous_eq_sum_coeff f hf] + simp [map_sum, affineChart_monomial, coeff_monomial, hinj.eq_iff] + +/-- Two homogeneous polynomials of the same degree with equal affine charts are equal. -/ +lemma affineChart_injective_on_homogeneous {K : Type*} [CommRing K] {n M : ℕ} + {f g : MvPolynomial (Fin (n + 1)) K} (hf : f.IsHomogeneous M) (hg : g.IsHomogeneous M) + (he : affineChart n f = affineChart n g) : f = g := by + have hc : ∀ j, f.coeff (homogeneousMonomials n M j) = g.coeff (homogeneousMonomials n M j) := by + intro j + rw [← affineChart_homogeneous_coefficient f hf j, + ← affineChart_homogeneous_coefficient g hg j, he] + rw [homogeneous_eq_sum_coeff f hf, homogeneous_eq_sum_coeff g hg] + simp only [hc] + +/-- Restriction to the affine chart `x₀ = 1` does not increase the total degree. -/ +lemma affineChart_totalDegree_le {K : Type*} [CommRing K] {n : ℕ} + (f : MvPolynomial (Fin (n + 1)) K) : (affineChart n f).totalDegree ≤ f.totalDegree := by + classical + conv_lhs => rw [f.as_sum, map_sum] + apply totalDegree_finsetSum_le + intro m hm + rw [affineChart_monomial] + have h := le_totalDegree hm + rw [exponent_degree_split] at h + apply (totalDegree_monomial_le _ _).trans + change (chartExponent m).sum (fun _ e => e) ≤ f.totalDegree + omega + +/-- The exponent in `n + 1` variables obtained from `m` by prepending `M - |m|` as the exponent of +the first variable, where `|m|` is the degree of `m`; it has degree `M` when `|m| ≤ M`. -/ +def homogenizedExponent {n : ℕ} (M : ℕ) (m : Fin n →₀ ℕ) : Fin (n + 1) →₀ ℕ := + Finsupp.equivFunOnFinite.symm (Fin.cons (M - m.sum (fun _ e => e)) m) + +/-- Forgetting the first entry of `homogenizedExponent M m` recovers `m`. -/ +@[simp] lemma chartExponent_homogenizedExponent {n : ℕ} (M : ℕ) (m : Fin n →₀ ℕ) : + chartExponent (homogenizedExponent M m) = m := by + ext i + rfl + +private lemma homogenizedExponent_degree {n M : ℕ} (m : Fin n →₀ ℕ) + (hm : m.sum (fun _ e => e) ≤ M) : + (homogenizedExponent M m).sum (fun _ e => e) = M := by + rw [exponent_degree_split, chartExponent_homogenizedExponent] + change M - m.sum (fun _ e => e) + m.sum (fun _ e => e) = M + omega + +private lemma homogenizedExponent_injective {n : ℕ} (d : ℕ) : + Function.Injective (homogenizedExponent (n := n) d) := by + intro m k h + simpa only [chartExponent_homogenizedExponent] using congrArg chartExponent h + +/-- Scaled homogenization in degree `d`: the monomial `x^m` becomes +`h ^ |m| * x₀ ^ (d - |m|) * x^m`. With `h = 1` this is the usual homogenization, +which is a section of `affineChart` (see `affineChart_scaledHomogenize_one`). -/ +def scaledHomogenize {R : Type*} [CommRing R] {n : ℕ} (d : ℕ) (h : R) + (f : MvPolynomial (Fin n) R) : MvPolynomial (Fin (n + 1)) R := + ∑ m ∈ f.support, monomial (homogenizedExponent d m) (h ^ m.sum (fun _ e => e) * f.coeff m) + +/-- The coefficient of `scaledHomogenize d h f` at `homogenizedExponent d m` is +`h ^ |m| * f.coeff m`, where `|m|` is the degree of `m`. -/ +lemma scaledHomogenize_coefficient {R : Type*} [CommRing R] {n : ℕ} + (d : ℕ) (h : R) (f : MvPolynomial (Fin n) R) (m : Fin n →₀ ℕ) : + (scaledHomogenize d h f).coeff (homogenizedExponent d m) = + h ^ m.sum (fun _ e => e) * f.coeff m := by + classical + by_cases hm : m ∈ f.support + · simp [scaledHomogenize, coeff_monomial, (homogenizedExponent_injective d).eq_iff, hm] + · have hz := notMem_support_iff.mp hm + simp [scaledHomogenize, coeff_monomial, (homogenizedExponent_injective d).eq_iff, hm, hz] + +/-- If `f` has total degree at most `d`, then its scaled homogenization in degree `d` is homogeneous +of degree `d`. -/ +lemma scaledHomogenize_homogeneous {R : Type*} [CommRing R] {n d : ℕ} + (h : R) (f : MvPolynomial (Fin n) R) (hf : f.totalDegree ≤ d) : + (scaledHomogenize d h f).IsHomogeneous d := by + apply IsHomogeneous.sum + intro m hm + exact isHomogeneous_monomial _ (homogenizedExponent_degree m ((le_totalDegree hm).trans hf)) + +/-- Homogenization in degree `d`, that is scaled homogenization with `h = 1`, is a right inverse of +the affine chart `x₀ = 1`. -/ +lemma affineChart_scaledHomogenize_one {R : Type*} [CommRing R] {n : ℕ} + (d : ℕ) (f : MvPolynomial (Fin n) R) : affineChart n (scaledHomogenize d 1 f) = f := by + classical + simp only [scaledHomogenize, map_sum, affineChart_monomial, chartExponent_homogenizedExponent, + one_pow, one_mul] + exact f.as_sum.symm + +/-- The affine chart `x₀ = 1` commutes with mapping the coefficients along a ring +homomorphism `φ`. -/ +lemma affineChart_map {K L : Type*} [CommRing K] [CommRing L] {n : ℕ} + (φ : K →+* L) (f : MvPolynomial (Fin (n + 1)) K) : + affineChart n (MvPolynomial.map φ f) = MvPolynomial.map φ (affineChart n f) := by + have he : (affineChart n).comp (MvPolynomial.map φ) = + (MvPolynomial.map φ).comp (affineChart n) := by + apply MvPolynomial.ringHom_ext + · intro c + simp [affineChart] + · intro i + refine Fin.cases ?_ (fun j => ?_) i <;> simp [affineChart] + exact RingHom.congr_fun he f + +/-- Evaluating the affine chart of `f` at `x` is evaluating `f` at the point `Fin.cons 1 x`. -/ +lemma affineChart_eval {K : Type*} [CommRing K] {n : ℕ} + (f : MvPolynomial (Fin (n + 1)) K) (x : Fin n → K) : + eval x (affineChart n f) = eval (Fin.cons 1 x) f := by + change eval x (eval₂ C (Fin.cons 1 X) f) = _ + rw [← eval_assoc] + have he : (eval x) ∘ Fin.cons (1 : MvPolynomial (Fin n) K) X = Fin.cons 1 x := by + funext i + refine Fin.cases ?_ (fun j => ?_) i <;> simp + rw [he] + +/-- Evaluating the affine chart of `f` at `a` through `φ` is evaluating `f` at `Fin.cons 1 a` +through `φ`. -/ +lemma eval₂_affineChart {K R : Type*} [CommRing K] [CommRing R] {n : ℕ} (φ : K →+* R) + (a : Fin n → R) (f : MvPolynomial (Fin (n + 1)) K) : + eval₂ φ a (affineChart n f) = eval₂ φ (Fin.cons 1 a) f := by + induction f using MvPolynomial.induction_on with + | C c => simp only [affineChart, eval₂Hom_C, eval₂_C] + | add f g hf hg => simp only [map_add, eval₂_add, hf, hg] + | mul_X f j hf => + rw [map_mul, eval₂_mul, eval₂_mul, hf] + congr 1 + refine Fin.cases ?_ (fun k => ?_) j <;> simp [affineChart] + +/-- Divisibility of affine charts lifts to homogeneous divisibility when the +first equation retains its degree on the chart. -/ +theorem homogeneous_dvd_of_affineChart_dvd {K : Type*} [CommRing K] [IsDomain K] + {n d M : ℕ} {f g : MvPolynomial (Fin (n + 1)) K} + (hf : f.IsHomogeneous d) (hf0 : f ≠ 0) (hg : g.IsHomogeneous M) + (hdchart : (affineChart n f).totalDegree = d) + (hdiv : affineChart n f ∣ affineChart n g) : f ∣ g := by + have hcf : affineChart n f ≠ 0 := by + intro hz + exact hf0 (affineChart_injective_on_homogeneous hf (isHomogeneous_zero _ _ d) + (by simpa only [map_zero] using hz)) + by_cases hcg : affineChart n g = 0 + · have hg0 := affineChart_injective_on_homogeneous hg (isHomogeneous_zero _ _ M) + (by simpa only [map_zero] using hcg) + rw [hg0] + exact dvd_zero f + obtain ⟨q, hq⟩ := hdiv + have hq0 : q ≠ 0 := by intro hz; apply hcg; rw [hq, hz, mul_zero] + have hdeg : d + q.totalDegree ≤ M := by + have hh := (affineChart_totalDegree_le g).trans hg.totalDegree_le + rw [hq, totalDegree_mul_of_isDomain hcf hq0, hdchart] at hh + exact hh + have hdM : d ≤ M := by omega + refine ⟨scaledHomogenize (M - d) 1 q, ?_⟩ + apply affineChart_injective_on_homogeneous hg + · simpa only [Nat.add_sub_of_le hdM] using + hf.mul (scaledHomogenize_homogeneous 1 q (show q.totalDegree ≤ M - d by omega)) + · rw [map_mul, affineChart_scaledHomogenize_one] + exact hq + +/-- Let `f` be a primitive integer homogeneous polynomial of degree `d` whose affine chart +`x₀ = 1` still has degree `d`. If `f` does not divide the homogeneous polynomial `g`, then over +`ℚ` the affine chart of `f` does not divide the affine chart of `g`. -/ +theorem rational_affineChart_not_dvd {n d M : ℕ} {f g : MvPolynomial (Fin (n + 1)) ℤ} + (hf : f.IsHomogeneous d) (hf0 : f ≠ 0) (hg : g.IsHomogeneous M) + (hprim : PrimitiveIntegerPolynomial f) (hdchart : (affineChart n f).totalDegree = d) + (hnd : ¬ f ∣ g) : + ¬ MvPolynomial.map (Int.castRingHom ℚ) (affineChart n f) ∣ + MvPolynomial.map (Int.castRingHom ℚ) (affineChart n g) := by + intro hdiv + rw [← affineChart_map, ← affineChart_map] at hdiv + apply hnd + apply hprim.rational_map_dvd_iff.mp + apply homogeneous_dvd_of_affineChart_dvd (hf.map _) ?_ (hg.map _) ?_ hdiv + · exact fun he => hf0 ((map_injective (Int.castRingHom ℚ) Int.cast_injective) (by simpa using he)) + · rw [affineChart_map] + exact (totalDegree_map_of_injective (RingHom.injective_int _) _).trans hdchart + +end +end CCDN diff --git a/LeanPool/CCDN/Algebra/HomogeneousChartIrreducible.lean b/LeanPool/CCDN/Algebra/HomogeneousChartIrreducible.lean new file mode 100644 index 0000000000..861f566a61 --- /dev/null +++ b/LeanPool/CCDN/Algebra/HomogeneousChartIrreducible.lean @@ -0,0 +1,173 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.Algebra.HomogeneousChart +import LeanPool.CCDN.ForMathlib.MvPolynomial +import Mathlib.Algebra.MvPolynomial.Nilpotent +import Mathlib.Algebra.MvPolynomial.NoZeroDivisors + +/-! Dehomogenization preserves degree and irreducibility when the +homogenizing coordinate does not divide the homogeneous equation. -/ + +@[expose] public section +namespace CCDN +noncomputable section +open MvPolynomial + +/-- A full-degree affine chart detects irreducibility of the original +equation; a factor lost on the chart would force a degree drop. -/ +theorem irreducible_of_affineChart_irreducible {K : Type*} [Field K] {n : ℕ} + (f : MvPolynomial (Fin (n + 1)) K) + (hdeg : (affineChart n f).totalDegree = f.totalDegree) + (hir : Irreducible (affineChart n f)) : Irreducible f := by + have hf0 : f ≠ 0 := by intro h; exact hir.ne_zero (by rw [h, map_zero]) + have hunit (a : MvPolynomial (Fin (n + 1)) K) (ha : a ≠ 0) + (hda : a.totalDegree = (affineChart n a).totalDegree) + (hu : IsUnit (affineChart n a)) : IsUnit a := by + have ha0 : a.totalDegree = 0 := hda.trans (isUnit_iff_totalDegree_of_isReduced.mp hu).2 + have he := totalDegree_eq_zero_iff_eq_C.mp ha0 + rw [he] + apply (isUnit_iff_ne_zero.mpr ?_).map C + intro hc + exact ha (by rw [he, hc, map_zero]) + refine ⟨fun h => hir.not_isUnit (h.map (affineChart n)), ?_⟩ + intro a b hab + have ha : a ≠ 0 := by intro h; exact hf0 (by rw [hab, h, zero_mul]) + have hb : b ≠ 0 := by intro h; exact hf0 (by rw [hab, h, mul_zero]) + have hc : affineChart n f = affineChart n a * affineChart n b := by rw [hab, map_mul] + have hac : affineChart n a ≠ 0 := by + intro h + exact hir.ne_zero (by rw [hc, h, zero_mul]) + have hbc : affineChart n b ≠ 0 := by + intro h + exact hir.ne_zero (by rw [hc, h, mul_zero]) + have hsum := totalDegree_mul_of_isDomain ha hb + have hsumc := totalDegree_mul_of_isDomain hac hbc + rw [← hab] at hsum + rw [← hc, hdeg, hsum] at hsumc + have hla := affineChart_totalDegree_le a + have hlb := affineChart_totalDegree_le b + obtain hau | hbu := hir.isUnit_or_isUnit hc + · exact Or.inl (hunit a ha (by omega) hau) + · exact Or.inr (hunit b hb (by omega) hbu) + +/-- No variable `X j` divides an irreducible homogeneous polynomial of degree at least `2`. -/ +theorem variable_not_dvd_irreducible_homogeneous {K : Type*} [Field K] {n d : ℕ} + (f : MvPolynomial (Fin (n + 1)) K) (hf : f.IsHomogeneous d) + (hir : Irreducible f) (hd : 2 ≤ d) (j : Fin (n + 1)) : ¬ X j ∣ f := by + rintro ⟨g, hg⟩ + have hx : ¬ IsUnit (X j : MvPolynomial (Fin (n + 1)) K) := by + intro hu + have h := (isUnit_iff_totalDegree_of_isReduced.mp hu).2 + simp at h + have hu := (hir.isUnit_or_isUnit hg).resolve_left hx + have h := totalDegree_mul (X j) g + rw [← hg, hf.totalDegree hir.ne_zero, totalDegree_X, + (isUnit_iff_totalDegree_of_isReduced.mp hu).2] at h + omega + +/-- An irreducible homogeneous polynomial of degree at least `2` divides no variable `X j`. -/ +theorem irreducible_homogeneous_not_dvd_variable {K : Type*} [Field K] {n d : ℕ} + (f : MvPolynomial (Fin (n + 1)) K) (hf : f.IsHomogeneous d) + (hir : Irreducible f) (hd : 2 ≤ d) (j : Fin (n + 1)) : ¬ f ∣ X j := by + rintro ⟨g, hg⟩ + have hg0 : g ≠ 0 := by + intro h + exact X_ne_zero j (by simpa only [h, mul_zero] using hg) + have hh := congrArg totalDegree hg + rw [totalDegree_X, totalDegree_mul_of_isDomain hir.ne_zero hg0, + hf.totalDegree hir.ne_zero] at hh + omega + +/-- If `X 0` does not divide the homogeneous polynomial `f` of degree `d`, then the affine chart +`x₀ = 1` of `f` has total degree exactly `d`. -/ +theorem affineChart_degree_of_not_variable_dvd {K : Type*} [Field K] {n d : ℕ} + (f : MvPolynomial (Fin (n + 1)) K) (hf : f.IsHomogeneous d) + (hX : ¬ X 0 ∣ f) : (affineChart n f).totalDegree = d := by + have hle := (affineChart_totalDegree_le f).trans hf.totalDegree_le + by_contra hne + have hd : 0 < d := by omega + have hsmall : (affineChart n f).totalDegree ≤ d - 1 := by omega + let g := scaledHomogenize (d - 1) 1 (affineChart n f) + have hg : g.IsHomogeneous (d - 1) := scaledHomogenize_homogeneous 1 _ hsmall + have hxg : (X 0 * g).IsHomogeneous d := by + convert (isHomogeneous_X K (0 : Fin (n + 1))).mul hg using 1 + omega + have he : f = X 0 * g := by + apply affineChart_injective_on_homogeneous hf hxg + rw [map_mul, show affineChart n (X 0) = 1 by simp [affineChart], + one_mul, affineChart_scaledHomogenize_one] + exact hX ⟨g, he⟩ + +/-- If `X 0` does not divide the irreducible homogeneous polynomial `f`, then the affine chart +`x₀ = 1` of `f` is irreducible. -/ +theorem affineChart_irreducible_of_not_variable_dvd {K : Type*} [Field K] {n d : ℕ} + (f : MvPolynomial (Fin (n + 1)) K) (hf : f.IsHomogeneous d) + (hir : Irreducible f) (hX : ¬ X 0 ∣ f) : + Irreducible (affineChart n f) := by + have hdeg := affineChart_degree_of_not_variable_dvd f hf hX + have hd : 0 < d := by + rw [← hf.totalDegree hir.ne_zero] + exact MvPolynomial.totalDegree_pos_of_irreducible hir + have hc0 : affineChart n f ≠ 0 := by + intro he + have hh := hdeg + rw [he, totalDegree_zero] at hh + omega + refine ⟨?_, ?_⟩ + · intro hu + have hh := (isUnit_iff_totalDegree_of_isReduced.mp hu).2 + rw [hdeg] at hh + omega + · intro a b hab + have ha0 : a ≠ 0 := by + intro ha + exact hc0 (by simpa only [ha, zero_mul] using hab) + have hb0 : b ≠ 0 := by + intro hb + exact hc0 (by simpa only [hb, mul_zero] using hab) + have hsum : a.totalDegree + b.totalDegree = d := by + rw [← totalDegree_mul_of_isDomain ha0 hb0, ← hab, hdeg] + have hh : (scaledHomogenize a.totalDegree 1 a * + scaledHomogenize b.totalDegree 1 b).IsHomogeneous d := by + simpa only [hsum] using (scaledHomogenize_homogeneous 1 a le_rfl).mul + (scaledHomogenize_homogeneous 1 b le_rfl) + have he : f = scaledHomogenize a.totalDegree 1 a * scaledHomogenize b.totalDegree 1 b := by + apply affineChart_injective_on_homogeneous hf hh + rw [map_mul, affineChart_scaledHomogenize_one, affineChart_scaledHomogenize_one] + exact hab + obtain ha | hb := hir.isUnit_or_isUnit he + · left + simpa only [affineChart_scaledHomogenize_one] using ha.map (affineChart n) + · right + simpa only [affineChart_scaledHomogenize_one] using hb.map (affineChart n) + +/-- After mapping coefficients into a field, every pivot chart of an irreducible homogeneous +polynomial of degree at least two remains irreducible and has the original degree. -/ +theorem affineChart_rename_swap_irreducible_degree + {K R : Type*} [CommRing K] [Field R] {n d : ℕ} + (φ : K →+* R) (f : MvPolynomial (Fin (n + 1)) K) + (hf : f.IsHomogeneous d) (hd : 2 ≤ d) + (hir : Irreducible (map φ f)) (k : Fin (n + 1)) : + Irreducible (map φ (affineChart n (rename (Equiv.swap 0 k) f))) ∧ + (map φ (affineChart n (rename (Equiv.swap 0 k) f))).totalDegree = d := by + let q := rename (Equiv.swap 0 k) (map φ f) + have hq : q.IsHomogeneous d := (hf.map _).rename_isHomogeneous + have hqi : Irreducible q := + (MulEquiv.irreducible_iff (f := renameEquiv R (Equiv.swap 0 k))).mpr hir + have hX := variable_not_dvd_irreducible_homogeneous q hq hqi hd 0 + have he : map φ (affineChart n (rename (Equiv.swap 0 k) f)) = affineChart n q := by + rw [← affineChart_map, map_rename] + rw [he] + exact ⟨affineChart_irreducible_of_not_variable_dvd q hq hqi hX, + affineChart_degree_of_not_variable_dvd q hq hX⟩ + +end +end CCDN diff --git a/LeanPool/CCDN/Algebra/HomogeneousEvaluation.lean b/LeanPool/CCDN/Algebra/HomogeneousEvaluation.lean new file mode 100644 index 0000000000..5d9bfa9c05 --- /dev/null +++ b/LeanPool/CCDN/Algebra/HomogeneousEvaluation.lean @@ -0,0 +1,105 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.Algebra.HomogeneousMonomials +import LeanPool.CCDN.Algebra.RowSelection + +/-! Homogeneous equations give nonzero relations on the complete +homogeneous evaluation matrix and hence strictly deficient row rank. -/ + +@[expose] public section +namespace CCDN +noncomputable section +open MvPolynomial Matrix + +/-- A homogeneous polynomial of degree `M` in `n + 1` variables is the sum of its terms over +the degree-`M` monomials enumerated by `homogeneousMonomials n M`. -/ +lemma homogeneous_eq_sum_coeff {K : Type*} [CommSemiring K] {n M : ℕ} + (f : MvPolynomial (Fin (n + 1)) K) (hf : f.IsHomogeneous M) : + f = ∑ j, monomial (homogeneousMonomials n M j) (f.coeff (homogeneousMonomials n M j)) := by + classical + ext p + by_cases hp : p.degree = M + · obtain ⟨j, rfl⟩ := homogeneousMonomials_surjective n M p hp + simp [coeff_monomial, (homogeneousMonomials_injective n M).eq_iff] + · have hc := hf.coeff_eq_zero hp + rw [hc, coeff_sum] + symm + apply Finset.sum_eq_zero + intro j _ + rw [coeff_monomial, ite_eq_right] + intro h + apply hp + rw [← h] + exact homogeneousMonomials_degree n M j + +private lemma homogeneous_coefficients_ne_zero {n M : ℕ} + (f : MvPolynomial (Fin (n + 1)) ℤ) (hf : f.IsHomogeneous M) (hf0 : f ≠ 0) : + (fun j => f.coeff (homogeneousMonomials n M j)) ≠ 0 := by + intro h + apply hf0 + rw [homogeneous_eq_sum_coeff f hf] + have hz : ∀ j, f.coeff (homogeneousMonomials n M j) = 0 := fun j => congrFun h j + simp only [hz, monomial_zero, Finset.sum_const_zero] + +private lemma homogeneous_evaluation_kernel {n M t : ℕ} + (f : MvPolynomial (Fin (n + 1)) ℤ) (hf : f.IsHomogeneous M) + (points : Fin t → (Fin (n + 1) → ℤ)) (hzero : ∀ i, eval (points i) f = 0) : + evaluationMatrix (homogeneousMonomials n M) points *ᵥ + (fun j => f.coeff (homogeneousMonomials n M j)) = 0 := by + have he : polynomialOfCoefficients (homogeneousMonomials n M) + (fun j => f.coeff (homogeneousMonomials n M j)) = f := + (homogeneous_eq_sum_coeff f hf).symm + ext i + rw [← eval_polynomialOfCoefficients, he] + exact hzero i + +/-- Select a spanning family of independent evaluation rows. The +homogeneous equation, padded to degree `M`, proves strict rank deficiency. -/ +theorem homogeneous_independent_evaluation_rows {n d M t : ℕ} + (f : MvPolynomial (Fin (n + 1)) ℤ) (hf : f.IsHomogeneous d) (hf0 : f ≠ 0) (hdM : d ≤ M) + (points : Fin t → (Fin (n + 1) → ℤ)) (hzero : ∀ i, eval (points i) f = 0) : + ∃ (s : ℕ) (a : Fin s → Fin t), Function.Injective a ∧ + LinearIndependent ℚ + ((evaluationMatrix (homogeneousMonomials n M) (points ∘ a)).map ((↑) : ℤ → ℚ)).row ∧ + Submodule.span ℚ (Set.range + ((evaluationMatrix (homogeneousMonomials n M) (points ∘ a)).map ((↑) : ℤ → ℚ)).row) = + Submodule.span ℚ (Set.range + ((evaluationMatrix (homogeneousMonomials n M) points).map ((↑) : ℤ → ℚ)).row) ∧ + s < (n + M).choose M := by + let F := f * X (0 : Fin (n + 1)) ^ (M - d) + have hF : F.IsHomogeneous M := by + simpa only [Nat.add_sub_of_le hdM] using hf.mul (isHomogeneous_X_pow (0 : Fin (n + 1)) (M - d)) + have hF0 : F ≠ 0 := mul_ne_zero hf0 (pow_ne_zero _ (X_ne_zero _)) + have hFzero : ∀ i, eval (points i) F = 0 := by + intro i + simp only [F, eval_mul, hzero i, zero_mul] + let v := fun j => F.coeff (homogeneousMonomials n M j) + have hv : (fun j => (v j : ℚ)) ≠ 0 := by + intro h + apply homogeneous_coefficients_ne_zero F hF hF0 + funext j + have hj := congrFun h j + change (v j : ℚ) = 0 at hj + change v j = 0 + exact_mod_cast hj + have hk := homogeneous_evaluation_kernel F hF points hFzero + have hkQ : ((evaluationMatrix (homogeneousMonomials n M) points).map ((↑) : ℤ → ℚ)) *ᵥ + (fun j => (v j : ℚ)) = 0 := by + ext i + have hi := congrFun hk i + simp only [Matrix.mulVec, dotProduct, Matrix.map_apply, Pi.zero_apply] at hi ⊢ + dsimp only [v] + exact_mod_cast hi + obtain ⟨s, a, ha, hli, hspan, hs⟩ := exists_independent_spanning_rows_lt _ _ hv hkQ + exact ⟨s, a, ha, hli, hspan, hs⟩ + +end +end CCDN diff --git a/LeanPool/CCDN/Algebra/HomogeneousLinearDescent.lean b/LeanPool/CCDN/Algebra/HomogeneousLinearDescent.lean new file mode 100644 index 0000000000..b29feea4ce --- /dev/null +++ b/LeanPool/CCDN/Algebra/HomogeneousLinearDescent.lean @@ -0,0 +1,97 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.Algebra.PolynomialLinearDescent +public import Mathlib.RingTheory.MvPolynomial.Homogeneous +import LeanPool.CCDN.ForMathlib.MvPolynomial +import Mathlib.Algebra.MvPolynomial.NoZeroDivisors +import Mathlib.LinearAlgebra.Dual.Lemmas + +/-! Homogeneous coefficient descent and homogeneous top factors. -/ + +@[expose] public section +namespace CCDN +noncomputable section +open MvPolynomial + +/-- Applying a linear map `φ : K → k` to the coefficients of a homogeneous polynomial of +degree `d` gives a homogeneous polynomial of degree `d`. -/ +lemma polynomialCoefficientLinear_homogeneous {k K : Type*} + [Field k] [Field K] [Algebra k K] {n d : ℕ} + (φ : K →ₗ[k] k) (f : MvPolynomial (Fin n) K) (hf : f.IsHomogeneous d) : + (polynomialCoefficientLinear φ f).IsHomogeneous d := by + intro m hm + apply hf + intro hzero + apply hm + rw [polynomialCoefficientLinear_coeff, hzero, map_zero] + +/-- A nonzero homogeneous polynomial `f` of degree `d` over an extension field `K` of `k` yields +a nonzero homogeneous polynomial `g` of degree `d` over `k` that vanishes at every `k`-point +where `f` vanishes. -/ +theorem exists_nonzero_homogeneous_linear_descent {k K : Type*} + [Field k] [Field K] [Algebra k K] {n d : ℕ} + (f : MvPolynomial (Fin n) K) (hf : f ≠ 0) (hhom : f.IsHomogeneous d) : + ∃ g : MvPolynomial (Fin n) k, g ≠ 0 ∧ g.IsHomogeneous d ∧ + ∀ x : Fin n → k, eval (algebraMap k K ∘ x) f = 0 → eval x g = 0 := by + obtain ⟨m, hm⟩ := exists_coeff_ne_zero hf + obtain ⟨φ, hφ⟩ := Module.Projective.exists_dual_ne_zero k hm + refine ⟨polynomialCoefficientLinear φ f, ?_, + polynomialCoefficientLinear_homogeneous φ f hhom, ?_⟩ + · intro he + have hh := polynomialCoefficientLinear_coeff φ f m + have hz : φ (f.coeff m) = 0 := by simpa [he] using hh.symm + exact hφ hz + · intro x hx + rw [polynomialCoefficientLinear_eval, hx, map_zero] + +/-- The homogeneous component of `g * h` in degree `g.totalDegree + h.totalDegree` is the +product of the top-degree homogeneous components of `g` and `h`. -/ +lemma top_homogeneousComponent_mul {K σ : Type*} [CommRing K] + (g h : MvPolynomial σ K) : + homogeneousComponent (g.totalDegree + h.totalDegree) (g * h) = + homogeneousComponent g.totalDegree g * homogeneousComponent h.totalDegree h := by + classical + calc + _ = homogeneousComponent (g.totalDegree + h.totalDegree) + ((∑ i ∈ Finset.range (g.totalDegree + 1), homogeneousComponent i g) * h) := by + rw [sum_homogeneousComponent] + _ = ∑ i ∈ Finset.range (g.totalDegree + 1), + homogeneousComponent (g.totalDegree + h.totalDegree) (homogeneousComponent i g * h) := by + rw [Finset.sum_mul, map_sum] + _ = homogeneousComponent (g.totalDegree + h.totalDegree) + (homogeneousComponent g.totalDegree g * h) := by + apply Finset.sum_eq_single g.totalDegree + · intro i hi hne + have hi' : i < g.totalDegree := by have := Finset.mem_range.mp hi; omega + apply homogeneousComponent_eq_zero + have hh := totalDegree_mul (homogeneousComponent i g) h + have hh' := (homogeneousComponent_isHomogeneous i g).totalDegree_le + omega + · intro hn + exact (hn (Finset.mem_range.mpr (Nat.lt_succ_self _))).elim + _ = _ := homogeneousComponent_mul_of_isHomogeneous_left + (homogeneousComponent_isHomogeneous _ _) h _ + +/-- A factorization of a homogeneous equation can be replaced by homogeneous +factors of the same respective total degrees. -/ +theorem homogeneous_factorization_top {K σ : Type*} [Field K] + {f g h : MvPolynomial σ K} {d : ℕ} + (hf : f.IsHomogeneous d) (hf0 : f ≠ 0) (hg : g ≠ 0) (hh : h ≠ 0) + (he : f = g * h) : + g.totalDegree + h.totalDegree = d ∧ + f = homogeneousComponent g.totalDegree g * homogeneousComponent h.totalDegree h := by + have hd : g.totalDegree + h.totalDegree = d := by + rw [← totalDegree_mul_of_isDomain hg hh, ← he, hf.totalDegree hf0] + refine ⟨hd, ?_⟩ + rw [← top_homogeneousComponent_mul, ← he, hd, homogeneousComponent_eq_self hf] + +end +end CCDN diff --git a/LeanPool/CCDN/Algebra/HomogeneousMonomials.lean b/LeanPool/CCDN/Algebra/HomogeneousMonomials.lean new file mode 100644 index 0000000000..930cf38141 --- /dev/null +++ b/LeanPool/CCDN/Algebra/HomogeneousMonomials.lean @@ -0,0 +1,80 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.Algebra.MonomialEvaluationMatrix +public import Mathlib.RingTheory.MvPolynomial.Homogeneous +import Mathlib.Data.Sym.Card + +/-! Complete homogeneous monomial lists, their exact cardinalities, and the +corresponding polynomial-space dimensions. -/ + +@[expose] public section +namespace CCDN +noncomputable section +open MvPolynomial + +/-- Exponents of total degree exactly `M` are multisets of cardinality `M`. -/ +def degreeEqEquivSym (σ : Type*) [Fintype σ] [DecidableEq σ] (M : ℕ) : + {p : σ →₀ ℕ // p.degree = M} ≃ Sym σ M := + (Finsupp.equivFunOnFinite.subtypeEquiv (fun p => by simp [Finsupp.degree_eq_sum])).trans + (Sym.equivNatSumOfFintype σ M).symm + +/-- There are exactly `(n + M).choose M` exponent vectors of total degree `M` in `n + 1` +variables. -/ +theorem card_degree_eq (n M : ℕ) : + Nat.card {p : Fin (n + 1) →₀ ℕ // p.degree = M} = (n + M).choose M := by + rw [Nat.card_congr (degreeEqEquivSym (Fin (n + 1)) M), Nat.card_eq_fintype_card, + Sym.card_sym_eq_choose] + simp only [Fintype.card_fin] + congr 1 + omega + +/-- A canonical finite indexing of all degree-`M` monomials in `n+1` variables. -/ +def homogeneousExponentEquiv (n M : ℕ) : + Fin ((n + M).choose M) ≃ {p : Fin (n + 1) →₀ ℕ // p.degree = M} := by + let e := degreeEqEquivSym (Fin (n + 1)) M + let _ := Fintype.ofEquiv (Sym (Fin (n + 1)) M) e.symm + have hc : Fintype.card {p : Fin (n + 1) →₀ ℕ // p.degree = M} = + (n + M).choose M := by rw [← Nat.card_eq_fintype_card, card_degree_eq] + exact (finCongr hc.symm).trans (Fintype.equivFin _).symm + +/-- The list of all exponent vectors of total degree `M` in `n + 1` variables, indexed by +`Fin ((n + M).choose M)` via `homogeneousExponentEquiv`. -/ +def homogeneousMonomials (n M : ℕ) : Fin ((n + M).choose M) → (Fin (n + 1) →₀ ℕ) := + fun j => (homogeneousExponentEquiv n M j).val + +/-- Distinct indices give distinct monomials in `homogeneousMonomials n M`. -/ +lemma homogeneousMonomials_injective (n M : ℕ) : Function.Injective (homogeneousMonomials n M) := + Subtype.val_injective.comp (homogeneousExponentEquiv n M).injective + +/-- Every monomial in `homogeneousMonomials n M` has total degree `M`. -/ +lemma homogeneousMonomials_degree (n M : ℕ) (j : Fin ((n + M).choose M)) : + (homogeneousMonomials n M j).degree = M := + (homogeneousExponentEquiv n M j).property + +/-- Every exponent vector of total degree `M` in `n + 1` variables occurs in +`homogeneousMonomials n M`. -/ +lemma homogeneousMonomials_surjective (n M : ℕ) (p : Fin (n + 1) →₀ ℕ) + (hp : p.degree = M) : ∃ j, homogeneousMonomials n M j = p := by + obtain ⟨j, hj⟩ := (homogeneousExponentEquiv n M).surjective ⟨p, hp⟩ + exact ⟨j, congrArg Subtype.val hj⟩ + +/-- An integer combination of the degree-`M` monomials `homogeneousMonomials n M` is a +homogeneous polynomial of degree `M`. -/ +theorem polynomialOfCoefficients_homogeneous (n M : ℕ) + (v : Fin ((n + M).choose M) → ℤ) : + (polynomialOfCoefficients (homogeneousMonomials n M) v).IsHomogeneous M := by + apply IsHomogeneous.sum + intro j _ + apply isHomogeneous_monomial + exact homogeneousMonomials_degree n M j + +end +end CCDN diff --git a/LeanPool/CCDN/Algebra/HomogeneousRank.lean b/LeanPool/CCDN/Algebra/HomogeneousRank.lean new file mode 100644 index 0000000000..6af6d67bb1 --- /dev/null +++ b/LeanPool/CCDN/Algebra/HomogeneousRank.lean @@ -0,0 +1,144 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.Algebra.HomogeneousMonomials +public import Mathlib.LinearAlgebra.Matrix.Rank +public import Mathlib.RingTheory.MvPolynomial.IrreducibleQuadratic +import LeanPool.CCDN.Algebra.HomogeneousEvaluation +import LeanPool.CCDN.ForMathlib.MvPolynomial +import LeanPool.CCDN.Hilbert.FilteredIntersection +import LeanPool.CCDN.Hilbert.HomogeneousHilbert + +/-! The homogeneous Hilbert count bounds evaluation-matrix ranks. +The deficit includes every degree-allowed multiple of the defining equation. -/ + +@[expose] public section +namespace CCDN +noncomputable section +open MvPolynomial Matrix + +/-- The `K`-linear map sending a polynomial in `n + 1` variables to its vector of coefficients +at the degree-`M` monomials, enumerated by `homogeneousMonomials n M`. -/ +def homogeneousCoefficientMap (K : Type*) [Field K] (n M : ℕ) : + MvPolynomial (Fin (n + 1)) K →ₗ[K] (Fin ((n + M).choose M) → K) := + LinearMap.pi fun j => lcoeff K (homogeneousMonomials n M j) + +/-- The matrix of values of the degree-`M` monomials in `n + 1` variables at the points +`points i`: its `(i, j)` entry is the `j`-th monomial of `homogeneousMonomials n M` evaluated +at `points i`. -/ +def homogeneousEvaluationMatrix (K : Type*) [Field K] (n M : ℕ) {t : ℕ} + (points : Fin t → (Fin (n + 1) → K)) : Matrix (Fin t) (Fin ((n + M).choose M)) K := + fun i j => (homogeneousMonomials n M j).prod fun k e => points i k ^ e + +/-- For a homogeneous polynomial `f` of degree `M`, multiplying the homogeneous evaluation matrix +by the coefficient vector of `f` gives the vector of values of `f` at the points. -/ +lemma homogeneous_evaluation_mulVec {K : Type*} [Field K] {n M t : ℕ} + (f : MvPolynomial (Fin (n + 1)) K) (hf : f.IsHomogeneous M) + (points : Fin t → (Fin (n + 1) → K)) (i : Fin t) : + (homogeneousEvaluationMatrix K n M points *ᵥ homogeneousCoefficientMap K n M f) i = + eval (points i) f := by + symm + calc + eval (points i) f = eval (points i) + (∑ j, monomial (homogeneousMonomials n M j) (f.coeff (homogeneousMonomials n M j))) := + congrArg (eval (points i)) (homogeneous_eq_sum_coeff f hf) + _ = _ := by + simp [homogeneousEvaluationMatrix, homogeneousCoefficientMap, mulVec, dotProduct, + eval_monomial, mul_comm] + +/-- For finitely many zeros of a nonzero homogeneous polynomial `f` of degree `d ≤ M`, the rank +of their degree-`M` evaluation matrix plus the dimension of the degree-`M` multiples of `f` is at +most the number of degree-`M` monomials. -/ +private theorem homogeneous_evaluation_rank_bound {K : Type*} [Field K] {n d M t : ℕ} + (f : MvPolynomial (Fin (n + 1)) K) (hf : f.IsHomogeneous d) (hf0 : f ≠ 0) (hdM : d ≤ M) + (points : Fin t → (Fin (n + 1) → K)) (hzero : ∀ i, eval (points i) f = 0) : + (homogeneousEvaluationMatrix K n M points).rank + (n + (M - d)).choose (M - d) ≤ + (n + M).choose M := by + let A := homogeneousEvaluationMatrix K n M points + let φ := homogeneousCoefficientMap K n M + let U := homogeneousMultiples f d M + let _ : Module.Finite K (homogeneousSubmodule (Fin (n + 1)) K (M - d)) := + Module.Finite.of_fg (homogeneousSubmodule_fg _ _ _) + let _ : Module.Finite K U := Module.Finite.of_fg + ((homogeneousSubmodule_fg _ _ _).map (LinearMap.mulLeft K f)) + have hhom : ∀ u ∈ U, u.IsHomogeneous M := by + intro u hu + obtain ⟨v, hv, rfl⟩ := Submodule.mem_map.mp hu + change (f * v).IsHomogeneous M + simpa only [Nat.add_sub_of_le hdM] using hf.mul hv + have hinj : Function.Injective (φ.domRestrict U) := by + intro u v huv + apply Subtype.ext + have he : ∀ j, u.val.coeff (homogeneousMonomials n M j) = + v.val.coeff (homogeneousMonomials n M j) := fun j => congrFun huv j + rw [homogeneous_eq_sum_coeff u.val (hhom _ u.property), + homogeneous_eq_sum_coeff v.val (hhom _ v.property)] + simp only [he] + have hdim := LinearMap.finrank_range_of_inj hinj + rw [LinearMap.range_domRestrict] at hdim + have hker : U.map φ ≤ A.mulVecLin.ker := by + intro v hv + obtain ⟨u, hu, rfl⟩ := Submodule.mem_map.mp hv + change A *ᵥ φ u = 0 + ext i + rw [homogeneous_evaluation_mulVec u (hhom _ hu)] + obtain ⟨w, _, rfl⟩ := Submodule.mem_map.mp hu + simp only [LinearMap.mulLeft_apply, eval_mul, hzero i, zero_mul, Pi.zero_apply] + have hbound := finrank_map_add_kernel_subspace_le A.mulVecLin ⊤ (U.map φ) le_top hker + rw [Submodule.map_top, hdim] at hbound + change A.rank + Module.finrank K U ≤ Module.finrank K ↥(⊤ : Submodule K _) at hbound + simpa only [U, homogeneousMultiples_finrank f hf0, finrank_homogeneousSubmodule_fin, + finrank_top, Module.finrank_pi, Module.finrank_self, mul_one, Fintype.card_fin] using hbound + +/-- For finitely many zeros of a nonzero homogeneous polynomial `f` of degree `d ≤ M` with +linearly independent degree-`M` evaluation rows, the number of points plus the dimension of the +degree-`M` multiples of `f` is at most the number of degree-`M` monomials. -/ +private theorem homogeneous_independent_rows_hilbert_bound {K : Type*} [Field K] {n d M t : ℕ} + (f : MvPolynomial (Fin (n + 1)) K) (hf : f.IsHomogeneous d) (hf0 : f ≠ 0) (hdM : d ≤ M) + (points : Fin t → (Fin (n + 1) → K)) (hzero : ∀ i, eval (points i) f = 0) + (hli : LinearIndependent K (homogeneousEvaluationMatrix K n M points).row) : + t + (n + (M - d)).choose (M - d) ≤ (n + M).choose M := by + have h := homogeneous_evaluation_rank_bound f hf hf0 hdM points hzero + simpa only [hli.rank_matrix, Fintype.card_fin] using h + +/-- The homogeneous evaluation matrix of integer points, computed over `ℚ`, is the cast of the +integer evaluation matrix of the degree-`M` monomials. -/ +lemma homogeneousEvaluationMatrix_intCast {n M t : ℕ} + (points : Fin t → (Fin (n + 1) → ℤ)) : + homogeneousEvaluationMatrix ℚ n M (fun i j => (points i j : ℚ)) = + (evaluationMatrix (homogeneousMonomials n M) points).map ((↑) : ℤ → ℚ) := by + ext i j + simp [homogeneousEvaluationMatrix, evaluationMatrix] + +/-- For `t` integer zeros of a nonzero homogeneous integer polynomial `f` of degree `d ≤ M` +whose degree-`M` evaluation rows are linearly independent over `ℚ`, the count `t` plus the +dimension `(n + (M - d)).choose (M - d)` of the degree-`M` multiples of `f` is at most the +number `(n + M).choose M` of degree-`M` monomials in `n + 1` variables. -/ +theorem integer_homogeneous_rows_hilbert_bound {n d M t : ℕ} + (f : MvPolynomial (Fin (n + 1)) ℤ) (hf : f.IsHomogeneous d) (hf0 : f ≠ 0) (hdM : d ≤ M) + (points : Fin t → (Fin (n + 1) → ℤ)) (hzero : ∀ i, eval (points i) f = 0) + (hli : LinearIndependent ℚ + ((evaluationMatrix (homogeneousMonomials n M) points).map ((↑) : ℤ → ℚ)).row) : + t + (n + (M - d)).choose (M - d) ≤ (n + M).choose M := by + have hfQ : map (Int.castRingHom ℚ) f ≠ 0 := by + intro h + apply hf0 + exact (map_injective (Int.castRingHom ℚ) Int.cast_injective) (h.trans (map_zero _).symm) + have hzQ : ∀ i, eval (fun j => (points i j : ℚ)) (map (Int.castRingHom ℚ) f) = 0 := by + intro i + rw [eval_map] + simpa only [Function.comp_def, Int.coe_castRingHom, hzero i, map_zero] using + (eval₂_comp (Int.castRingHom ℚ) (points i) f).symm + apply homogeneous_independent_rows_hilbert_bound _ (hf.map (Int.castRingHom ℚ)) hfQ hdM + (fun i j => (points i j : ℚ)) hzQ + simpa only [homogeneousEvaluationMatrix_intCast] using hli + +end +end CCDN diff --git a/LeanPool/CCDN/Algebra/HomogeneousRankDichotomy.lean b/LeanPool/CCDN/Algebra/HomogeneousRankDichotomy.lean new file mode 100644 index 0000000000..35006d0934 --- /dev/null +++ b/LeanPool/CCDN/Algebra/HomogeneousRankDichotomy.lean @@ -0,0 +1,217 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.Algebra.HomogeneousRank +public import LeanPool.CCDN.Hilbert.HomogeneousHilbert +import LeanPool.CCDN.Algebra.HomogeneousEvaluation +import LeanPool.CCDN.ForMathlib.MvPolynomial + +import Mathlib.RingTheory.Localization.Integer + +/-! Exact evaluation rank when all homogeneous vanishing relations are multiples +of the defining equation, and the resulting auxiliary-polynomial dichotomy. -/ + +@[expose] public section +namespace CCDN +noncomputable section +open MvPolynomial Matrix + +private lemma homogeneousCoefficientMap_injective {K : Type*} [Field K] (n M : ℕ) : + Function.Injective ((homogeneousCoefficientMap K n M).domRestrict + (homogeneousSubmodule (Fin (n + 1)) K M)) := by + intro u v huv + apply Subtype.ext + have he : ∀ j, u.val.coeff (homogeneousMonomials n M j) = + v.val.coeff (homogeneousMonomials n M j) := fun j => congrFun huv j + rw [homogeneous_eq_sum_coeff u.val u.property, homogeneous_eq_sum_coeff v.val v.property] + simp only [he] + +private lemma homogeneousCoefficientMap_surjective {K : Type*} [Field K] (n M : ℕ) : + Function.Surjective ((homogeneousCoefficientMap K n M).domRestrict + (homogeneousSubmodule (Fin (n + 1)) K M)) := by + classical + intro v + let f : MvPolynomial (Fin (n + 1)) K := ∑ j, monomial (homogeneousMonomials n M j) (v j) + have hf : f.IsHomogeneous M := by + apply IsHomogeneous.sum + intro j _ + exact isHomogeneous_monomial _ (homogeneousMonomials_degree n M j) + refine ⟨⟨f, hf⟩, ?_⟩ + ext j + simp [homogeneousCoefficientMap, f, coeff_monomial, + (homogeneousMonomials_injective n M).eq_iff] + +/-- When all degree-M relations are multiples of f, the evaluation +kernel is exactly the image of the degree-allowed multiples. -/ +private theorem homogeneous_evaluation_kernel_eq_multiples {K : Type*} [Field K] + {n d M t : ℕ} (f : MvPolynomial (Fin (n + 1)) K) + (hf : f.IsHomogeneous d) (hf0 : f ≠ 0) (hdM : d ≤ M) + (points : Fin t → Fin (n + 1) → K) (hzero : ∀ i, eval (points i) f = 0) + (hrel : ∀ g : MvPolynomial (Fin (n + 1)) K, g.IsHomogeneous M → + (∀ i, eval (points i) g = 0) → f ∣ g) : + (homogeneousEvaluationMatrix K n M points).mulVecLin.ker = + (homogeneousMultiples f d M).map (homogeneousCoefficientMap K n M) := by + ext v + constructor + · intro hv + obtain ⟨g, hg⟩ := homogeneousCoefficientMap_surjective n M v + have hvan : ∀ i, eval (points i) g.val = 0 := by + intro i + rw [← homogeneous_evaluation_mulVec g.val g.property] + have he := congrFun (LinearMap.mem_ker.mp hv) i + simpa only [← hg, LinearMap.domRestrict_apply, Matrix.mulVecLin_apply, Pi.zero_apply] using he + obtain ⟨u, hu⟩ := hrel g.val g.property hvan + have hhom : u.IsHomogeneous (M - d) := + homogeneous_multiplier hf hf0 (hu ▸ g.property) hdM + apply Submodule.mem_map.mpr + refine ⟨g.val, ?_, hg⟩ + exact Submodule.mem_map.mpr ⟨u, hhom, hu.symm⟩ + · intro hv + obtain ⟨g, hg, rfl⟩ := Submodule.mem_map.mp hv + obtain ⟨u, hu, rfl⟩ := Submodule.mem_map.mp hg + have hhom : (f * u).IsHomogeneous M := by + simpa only [Nat.add_sub_of_le hdM] using hf.mul hu + apply LinearMap.mem_ker.mpr + ext i + rw [Matrix.mulVecLin_apply, LinearMap.mulLeft_apply, homogeneous_evaluation_mulVec _ hhom] + simp only [eval_mul, hzero i, zero_mul, Pi.zero_apply] + +/-- The exact Hilbert rank in the absence of an auxiliary polynomial. -/ +private theorem homogeneous_evaluation_rank_eq {K : Type*} [Field K] + {n d M t : ℕ} (f : MvPolynomial (Fin (n + 1)) K) + (hf : f.IsHomogeneous d) (hf0 : f ≠ 0) (hdM : d ≤ M) + (points : Fin t → Fin (n + 1) → K) (hzero : ∀ i, eval (points i) f = 0) + (hrel : ∀ g : MvPolynomial (Fin (n + 1)) K, g.IsHomogeneous M → + (∀ i, eval (points i) g = 0) → f ∣ g) : + (homogeneousEvaluationMatrix K n M points).rank + + (n + (M - d)).choose (M - d) = (n + M).choose M := by + let U := homogeneousMultiples f d M + let φ := homogeneousCoefficientMap K n M + have hhom : ∀ u ∈ U, u.IsHomogeneous M := by + intro u hu + obtain ⟨w, hw, rfl⟩ := Submodule.mem_map.mp hu + simpa only [Nat.add_sub_of_le hdM, LinearMap.mulLeft_apply] using hf.mul hw + have hinj : Function.Injective (φ.domRestrict U) := by + intro u v huv + have h := homogeneousCoefficientMap_injective (K := K) n M + (a₁ := ⟨u.val, hhom _ u.property⟩) (a₂ := ⟨v.val, hhom _ v.property⟩) huv + exact Subtype.ext (Subtype.mk.inj h) + have hdim := LinearMap.finrank_range_of_inj hinj + rw [LinearMap.range_domRestrict, homogeneousMultiples_finrank f hf0, + finrank_homogeneousSubmodule_fin] at hdim + have h := (homogeneousEvaluationMatrix K n M points).mulVecLin.finrank_range_add_finrank_ker + rw [homogeneous_evaluation_kernel_eq_multiples f hf hf0 hdM points hzero hrel, hdim] at h + simpa only [Matrix.rank, Module.finrank_pi, Module.finrank_self, mul_one, + Fintype.card_fin] using h + +/-- Clearing denominators preserves a prescribed homogeneous degree. -/ +private theorem homogeneous_integer_multiple {n M : ℕ} + (g : MvPolynomial (Fin (n + 1)) ℚ) (hg : g.IsHomogeneous M) : + ∃ (b : ℤ) (u : MvPolynomial (Fin (n + 1)) ℤ), b ≠ 0 ∧ + u.IsHomogeneous M ∧ map (Int.castRingHom ℚ) u = C (b : ℚ) * g := by + classical + obtain ⟨b, hb⟩ := IsLocalization.exist_integer_multiples_of_finite + (nonZeroDivisors ℤ) (fun j => g.coeff (homogeneousMonomials n M j)) + choose v hv using hb + refine ⟨b.val, polynomialOfCoefficients (homogeneousMonomials n M) v, + nonZeroDivisors.coe_ne_zero b, polynomialOfCoefficients_homogeneous n M v, ?_⟩ + rw [homogeneous_eq_sum_coeff g hg, Finset.mul_sum] + simp only [polynomialOfCoefficients, map_sum, map_monomial, C_mul_monomial] + congr 1 + funext j + congr 1 + simpa [Algebra.smul_def] using hv j + +/-- Absence of an integral auxiliary polynomial also rules out rational +homogeneous relations outside the principal ideal. -/ +private theorem rational_relations_of_integer_relations {n M t : ℕ} + (f : MvPolynomial (Fin (n + 1)) ℤ) + (points : Fin t → Fin (n + 1) → ℤ) + (hrel : ∀ u : MvPolynomial (Fin (n + 1)) ℤ, u.IsHomogeneous M → + (∀ i, eval (points i) u = 0) → f ∣ u) + (g : MvPolynomial (Fin (n + 1)) ℚ) (hg : g.IsHomogeneous M) + (hzero : ∀ i, eval (fun j => (points i j : ℚ)) g = 0) : + map (Int.castRingHom ℚ) f ∣ g := by + obtain ⟨b, u, hb, hu, he⟩ := homogeneous_integer_multiple g hg + have huv : ∀ i, eval (points i) u = 0 := by + intro i + apply Int.cast_injective (α := ℚ) + have hh := congrArg (eval (fun j => (points i j : ℚ))) he + rw [eval_mul, hzero i, mul_zero] at hh + change (Int.castRingHom ℚ) (eval (points i) u) = 0 + rw [map_eval] + exact hh + obtain ⟨v, hv⟩ := hrel u hu huv + have hm : C (b : ℚ) * g = map (Int.castRingHom ℚ) f * map (Int.castRingHom ℚ) v := by + rw [← he, hv, map_mul] + refine ⟨C ((b : ℚ)⁻¹) * map (Int.castRingHom ℚ) v, ?_⟩ + have hbq : (b : ℚ) ≠ 0 := by exact_mod_cast hb + calc + g = C ((b : ℚ)⁻¹) * (C (b : ℚ) * g) := by + rw [← mul_assoc, ← C_mul, inv_mul_cancel₀ hbq, C_1, one_mul] + _ = _ := by rw [hm]; ring + +/-- The no-auxiliary hypothesis over the integers forces the exact rational +rank of the integer evaluation matrix. -/ +theorem integer_homogeneous_evaluation_rank_eq {n d M t : ℕ} + (f : MvPolynomial (Fin (n + 1)) ℤ) (hf : f.IsHomogeneous d) + (hf0 : f ≠ 0) (hdM : d ≤ M) + (points : Fin t → Fin (n + 1) → ℤ) (hzero : ∀ i, eval (points i) f = 0) + (hrel : ∀ g : MvPolynomial (Fin (n + 1)) ℤ, g.IsHomogeneous M → + (∀ i, eval (points i) g = 0) → f ∣ g) : + ((evaluationMatrix (homogeneousMonomials n M) points).map ((↑) : ℤ → ℚ)).rank + + (n + (M - d)).choose (M - d) = (n + M).choose M := by + have hfQ : map (Int.castRingHom ℚ) f ≠ 0 := by + intro h + apply hf0 + exact (map_injective (Int.castRingHom ℚ) Int.cast_injective) (by simpa using h) + have hzQ : ∀ i, eval (fun j => (points i j : ℚ)) (map (Int.castRingHom ℚ) f) = 0 := by + intro i + have h := congrArg (Int.castRingHom ℚ) (hzero i) + rw [map_eval, map_zero] at h + exact h + have hr := homogeneous_evaluation_rank_eq (map (Int.castRingHom ℚ) f) (hf.map _) + hfQ hdM (fun i j => (points i j : ℚ)) hzQ + (rational_relations_of_integer_relations f points hrel) + rw [homogeneousEvaluationMatrix_intCast] at hr + exact hr + +/-- Twice the dimension of ternary forms, without natural-number division. -/ +lemma ternary_homogeneous_dimension (M : ℕ) : + 2 * (2 + M).choose M = (M + 2) * (M + 1) := by + have h := Nat.add_one_mul_choose_eq (M + 1) 1 + simp only [Nat.choose_one_right] at h + rw [← Nat.choose_symm_add] + simpa only [show M + 1 + 1 = M + 2 by omega, show 1 + 1 = 2 from rfl, + Nat.add_comm 2 M, Nat.mul_comm] using h.symm + +/-- Closed form of the exact rank for plane projective curves. -/ +lemma plane_homogeneous_rank_identity {s d M : ℕ} (hdM : d ≤ M) + (hr : s + (2 + (M - d)).choose (M - d) = (2 + M).choose M) : + 2 * s + d ^ 2 = d * (2 * M + 3) := by + have hM := ternary_homogeneous_dimension M + have hMd := ternary_homogeneous_dimension (M - d) + have hsub : M - d + d = M := Nat.sub_add_cancel hdM + nlinarith + +/-- Elementary rank bounds sufficient to compare the plane rank with d*M. -/ +lemma plane_homogeneous_rank_bounds {s d M : ℕ} (hd : 0 < d) (hdM : d ≤ M) + (hr : s + (2 + (M - d)).choose (M - d) = (2 + M).choose M) : + 0 < s ∧ d * M ≤ 2 * s ∧ s ≤ d * (M + 1) := by + have he := plane_homogeneous_rank_identity hdM hr + have hm : 0 < M := lt_of_lt_of_le hd hdM + have hd2 : d ^ 2 ≤ d * M := by nlinarith + have hd1 : d ≤ d ^ 2 := by nlinarith + constructor + · nlinarith + constructor <;> nlinarith + +end +end CCDN diff --git a/LeanPool/CCDN/Algebra/HomogeneousSlice.lean b/LeanPool/CCDN/Algebra/HomogeneousSlice.lean new file mode 100644 index 0000000000..0d011fc6f8 --- /dev/null +++ b/LeanPool/CCDN/Algebra/HomogeneousSlice.lean @@ -0,0 +1,191 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.Algebra.ScaledHomogenization +import LeanPool.CCDN.ForMathlib.MvPolynomial + +/-! Restriction to a nonzero constant homogenizing coordinate. Proper affine +sections follow from homogeneous nonmultiples also on x₀=h, not just x₀=1. -/ + +@[expose] public section +namespace CCDN +noncomputable section +open MvPolynomial + +/-- The `R`-algebra endomorphism of `MvPolynomial (Fin (n + 1)) R` that substitutes `C h * X 0` for +the first variable and fixes the other variables. -/ +def firstCoordinateScaleHom (R : Type*) [CommRing R] {n : ℕ} (h : R) : + MvPolynomial (Fin (n + 1)) R →ₐ[R] MvPolynomial (Fin (n + 1)) R := + aeval (Fin.cons (C h * X 0) (fun j => X j.succ)) + +/-- Composing the scalings of the first variable by `h` and by `k` gives the scaling by `k * h`. -/ +lemma firstCoordinateScaleHom_comp {R : Type*} [CommRing R] {n : ℕ} (h k : R) : + (firstCoordinateScaleHom R (n := n) h).comp (firstCoordinateScaleHom R k) = + firstCoordinateScaleHom R (k * h) := by + apply MvPolynomial.algHom_ext + intro i + refine Fin.cases ?_ (fun j => ?_) i + · simp [firstCoordinateScaleHom, mul_assoc] + · simp [firstCoordinateScaleHom] + +/-- Scaling the first variable by `1` is the identity. -/ +@[simp] lemma firstCoordinateScaleHom_one (R : Type*) [CommRing R] (n : ℕ) : + firstCoordinateScaleHom R (n := n) 1 = AlgHom.id R _ := by + apply MvPolynomial.algHom_ext + intro i + refine Fin.cases ?_ (fun j => ?_) i <;> simp [firstCoordinateScaleHom] + +/-- For nonzero `h` in a field `K`, the `K`-algebra automorphism of `MvPolynomial (Fin (n + 1)) K` +substituting `C h * X 0` for the first variable; its inverse scales by `h⁻¹`. -/ +def firstCoordinateScale (K : Type*) [Field K] {n : ℕ} (h : K) (hh : h ≠ 0) : + MvPolynomial (Fin (n + 1)) K ≃ₐ[K] MvPolynomial (Fin (n + 1)) K := + AlgEquiv.ofAlgHom (firstCoordinateScaleHom K h) (firstCoordinateScaleHom K h⁻¹) + (by rw [firstCoordinateScaleHom_comp, inv_mul_cancel₀ hh, firstCoordinateScaleHom_one]) + (by rw [firstCoordinateScaleHom_comp, mul_inv_cancel₀ hh, firstCoordinateScaleHom_one]) + +/-- Scaling the first variable preserves homogeneity of degree `d`. -/ +lemma firstCoordinateScaleHom_homogeneous {R : Type*} [CommRing R] {n d : ℕ} + (h : R) {f : MvPolynomial (Fin (n + 1)) R} (hf : f.IsHomogeneous d) : + (firstCoordinateScaleHom R h f).IsHomogeneous d := by + let v : Fin (n + 1) → MvPolynomial (Fin (n + 1)) R := + Fin.cons (C h * X 0) (fun j => X j.succ) + have hv : ∀ i, (v i).IsHomogeneous 1 := by + intro i + refine Fin.cases ?_ (fun j => ?_) i + · exact (isHomogeneous_X R (0 : Fin (n + 1))).C_mul h + · exact isHomogeneous_X R j.succ + change (aeval v f).IsHomogeneous d + simpa only [one_mul] using hf.aeval v hv + +/-- Restriction of a polynomial in `n + 1` variables to the slice `x₀ = h`: the ring homomorphism +sending `X 0` to `C h` and `X i.succ` to `X i`. -/ +def homogeneousSlice {R : Type*} [CommRing R] (n : ℕ) (h : R) : + MvPolynomial (Fin (n + 1)) R →+* MvPolynomial (Fin n) R := + eval₂Hom C (Fin.cons (C h) X) + +/-- The restriction of `f` to the slice `x₀ = h` is the affine chart `x₀ = 1` of `f` with its first +variable scaled by `h`. -/ +lemma homogeneousSlice_eq_chart_scale {R : Type*} [CommRing R] {n : ℕ} + (h : R) (f : MvPolynomial (Fin (n + 1)) R) : + homogeneousSlice n h f = affineChart n (firstCoordinateScaleHom R h f) := by + have he : homogeneousSlice n h = + (affineChart n).comp (firstCoordinateScaleHom R h).toRingHom := by + apply MvPolynomial.ringHom_ext + · intro c + simp [homogeneousSlice, affineChart, firstCoordinateScaleHom] + · intro i + refine Fin.cases ?_ (fun j => ?_) i <;> + simp [homogeneousSlice, affineChart, firstCoordinateScaleHom] + exact RingHom.congr_fun he f + +/-- Evaluating the restriction of `f` to the slice `x₀ = h` at `x` is evaluating `f` at the point +`Fin.cons h x`. -/ +lemma homogeneousSlice_eval {R : Type*} [CommRing R] {n : ℕ} + (h : R) (f : MvPolynomial (Fin (n + 1)) R) (x : Fin n → R) : + eval x (homogeneousSlice n h f) = eval (Fin.cons h x) f := by + change eval x (eval₂ C (Fin.cons (C h) X) f) = _ + rw [← eval_assoc] + congr 2 + funext i + refine Fin.cases ?_ (fun j => ?_) i <;> simp + +/-- The restriction of a homogeneous polynomial of degree `d` to the slice `x₀ = h` has total degree +at most `d`. -/ +lemma homogeneousSlice_degree_le {R : Type*} [CommRing R] {n d : ℕ} + (h : R) {f : MvPolynomial (Fin (n + 1)) R} (hf : f.IsHomogeneous d) : + (homogeneousSlice n h f).totalDegree ≤ d := by + rw [homogeneousSlice_eq_chart_scale] + exact (affineChart_totalDegree_le _).trans + (firstCoordinateScaleHom_homogeneous h hf).totalDegree_le + +/-- The restriction of the monomial `monomial m c` to the slice `x₀ = h` is the monomial with +exponent `chartExponent m` and coefficient `c * h ^ m 0`. -/ +lemma homogeneousSlice_monomial {R : Type*} [CommRing R] {n : ℕ} + (h : R) (m : Fin (n + 1) →₀ ℕ) (c : R) : + homogeneousSlice n h (monomial m c) = monomial (chartExponent m) (c * h ^ m 0) := by + change eval₂ C (Fin.cons (C h) X) (monomial m c) = _ + rw [eval₂_monomial, monomial_eq, + Finsupp.prod_fintype _ _ (by simp), Finsupp.prod_fintype _ _ (by simp), Fin.prod_univ_succ] + simp only [Fin.cons_zero, Fin.cons_succ, map_mul, map_pow, chartExponent_apply] + ring + +/-- If `f` has total degree at most `d`, then restricting the scaled homogenization +`scaledHomogenize d h f` to the slice `x₀ = h` gives `C (h ^ d) * f`. -/ +lemma homogeneousSlice_scaledHomogenize {R : Type*} [CommRing R] {n d : ℕ} + (h : R) (f : MvPolynomial (Fin n) R) (hf : f.totalDegree ≤ d) : + homogeneousSlice n h (scaledHomogenize d h f) = C (h ^ d) * f := by + classical + rw [scaledHomogenize, map_sum] + conv_rhs => rw [f.as_sum, Finset.mul_sum] + apply Finset.sum_congr rfl + intro m hm + rw [homogeneousSlice_monomial, chartExponent_homogenizedExponent, C_mul_monomial] + congr 1 + change (h ^ m.sum (fun _ e => e) * f.coeff m) * h ^ (d - m.sum (fun _ e => e)) = h ^ d * f.coeff m + calc + _ = (h ^ m.sum (fun _ e => e) * h ^ (d - m.sum (fun _ e => e))) * f.coeff m := by ring + _ = _ := by rw [← pow_add, Nat.add_sub_of_le ((le_totalDegree hm).trans hf)] + +/-- Nonzero constant slices reflect divisibility when the equation's degree +is preserved. This is the proper-section transfer for the scaled models. -/ +private theorem homogeneous_dvd_of_slice_dvd {K : Type*} [Field K] {n d M : ℕ} + {f g : MvPolynomial (Fin (n + 1)) K} {h : K} (hh : h ≠ 0) + (hf : f.IsHomogeneous d) (hf0 : f ≠ 0) (hg : g.IsHomogeneous M) + (hd : (homogeneousSlice n h f).totalDegree = d) + (hdiv : homogeneousSlice n h f ∣ homogeneousSlice n h g) : f ∣ g := by + rw [homogeneousSlice_eq_chart_scale, homogeneousSlice_eq_chart_scale] at hdiv + rw [homogeneousSlice_eq_chart_scale] at hd + have hf0' : firstCoordinateScaleHom K h f ≠ 0 := by + exact (map_ne_zero_iff _ (firstCoordinateScale K h hh).injective).mpr hf0 + have hdiv' := homogeneous_dvd_of_affineChart_dvd (firstCoordinateScaleHom_homogeneous h hf) hf0' + (firstCoordinateScaleHom_homogeneous h hg) hd hdiv + have he := map_dvd (firstCoordinateScale K h hh).symm.toRingHom hdiv' + change (firstCoordinateScale K h hh).symm (firstCoordinateScale K h hh f) ∣ + (firstCoordinateScale K h hh).symm (firstCoordinateScale K h hh g) at he + simpa only [AlgEquiv.symm_apply_apply] using he + +private lemma homogeneousSlice_map {R S : Type*} [CommRing R] [CommRing S] {n : ℕ} + (φ : R →+* S) (h : R) (f : MvPolynomial (Fin (n + 1)) R) : + MvPolynomial.map φ (homogeneousSlice n h f) = + homogeneousSlice n (φ h) (MvPolynomial.map φ f) := by + have he : (MvPolynomial.map φ).comp (homogeneousSlice n h) = + (homogeneousSlice n (φ h)).comp (MvPolynomial.map φ) := by + apply MvPolynomial.ringHom_ext + · intro c + simp [homogeneousSlice] + · intro i + refine Fin.cases ?_ (fun j => ?_) i <;> simp [homogeneousSlice] + exact RingHom.congr_fun he f + +/-- Integer nonmultiples of a primitive homogeneous equation stay proper on +a nonzero rational slice preserving the degree. -/ +theorem rational_homogeneousSlice_not_dvd {n d M : ℕ} + {f g : MvPolynomial (Fin (n + 1)) ℤ} {h : ℤ} (hh : h ≠ 0) + (hf : f.IsHomogeneous d) (hf0 : f ≠ 0) (hg : g.IsHomogeneous M) + (hprim : PrimitiveIntegerPolynomial f) (hd : (homogeneousSlice n h f).totalDegree = d) + (hnd : ¬ f ∣ g) : + ¬ MvPolynomial.map (Int.castRingHom ℚ) (homogeneousSlice n h f) ∣ + MvPolynomial.map (Int.castRingHom ℚ) (homogeneousSlice n h g) := by + intro hdiv + rw [homogeneousSlice_map, homogeneousSlice_map] at hdiv + apply hnd + apply hprim.rational_map_dvd_iff.mp + have hhQ : (h : ℚ) ≠ 0 := by exact_mod_cast hh + apply homogeneous_dvd_of_slice_dvd hhQ (hf.map _) ?_ (hg.map _) ?_ hdiv + · exact fun he => hf0 ((map_injective (Int.castRingHom ℚ) Int.cast_injective) (by simpa using he)) + · change (homogeneousSlice n ((Int.castRingHom ℚ) h) + (MvPolynomial.map (Int.castRingHom ℚ) f)).totalDegree = d + rw [← homogeneousSlice_map] + have he : (MvPolynomial.map (Int.castRingHom ℚ) (homogeneousSlice n h f)).totalDegree = + (homogeneousSlice n h f).totalDegree := totalDegree_map_of_injective Int.cast_injective _ + exact he.trans hd + +end +end CCDN diff --git a/LeanPool/CCDN/Algebra/HomogeneousTranslation.lean b/LeanPool/CCDN/Algebra/HomogeneousTranslation.lean new file mode 100644 index 0000000000..72d8c8646a --- /dev/null +++ b/LeanPool/CCDN/Algebra/HomogeneousTranslation.lean @@ -0,0 +1,124 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.Height.Box +public import LeanPool.CCDN.Height.PrimitiveIntegerPolynomial +public import Mathlib.RingTheory.MvPolynomial.Homogeneous + +/-! Integral translations of the affine chart, lifted to homogeneous linear +coordinate changes, preserve primitive equations and homogeneous degree. -/ + +@[expose] public section +namespace CCDN +noncomputable section +open MvPolynomial + +/-- The homogeneous linear substitution fixing `X 0` and sending `X (j + 1)` to +`X (j + 1) + a j * X 0`; on the affine chart `X 0 = 1` it is the translation by `a`. -/ +def homogeneousTranslationHom (R : Type*) [CommRing R] {n : ℕ} (a : Fin n → R) : + MvPolynomial (Fin (n + 1)) R →ₐ[R] MvPolynomial (Fin (n + 1)) R := + aeval (Fin.cons (X 0) (fun j => X j.succ + C (a j) * X 0)) + +/-- Composing the homogeneous translations by `a` and by `b` gives the homogeneous translation +by `a + b`. -/ +lemma homogeneousTranslationHom_comp {R : Type*} [CommRing R] {n : ℕ} (a b : Fin n → R) : + (homogeneousTranslationHom R a).comp (homogeneousTranslationHom R b) = + homogeneousTranslationHom R (a + b) := by + apply MvPolynomial.algHom_ext + intro i + refine Fin.cases ?_ (fun j => ?_) i + · simp [homogeneousTranslationHom] + · simp only [homogeneousTranslationHom, AlgHom.comp_apply, aeval_X, Fin.cons_succ, + map_add, map_mul, aeval_C, Fin.cons_zero, Pi.add_apply, algebraMap_eq] + ring + +/-- The homogeneous translation by zero is the identity. -/ +@[simp] lemma homogeneousTranslationHom_zero (R : Type*) [CommRing R] (n : ℕ) : + homogeneousTranslationHom R (0 : Fin n → R) = AlgHom.id R _ := by + apply MvPolynomial.algHom_ext + intro i + refine Fin.cases ?_ (fun j => ?_) i <;> simp [homogeneousTranslationHom] + +/-- The homogeneous translation by `a` (fixing `X 0` and sending `X (j + 1)` to +`X (j + 1) + a j * X 0`) as an algebra automorphism, with inverse the homogeneous translation +by `-a`. -/ +def homogeneousTranslation (R : Type*) [CommRing R] {n : ℕ} (a : Fin n → R) : + MvPolynomial (Fin (n + 1)) R ≃ₐ[R] MvPolynomial (Fin (n + 1)) R := + AlgEquiv.ofAlgHom (homogeneousTranslationHom R a) (homogeneousTranslationHom R (-a)) + (by rw [homogeneousTranslationHom_comp, add_neg_cancel, homogeneousTranslationHom_zero]) + (by rw [homogeneousTranslationHom_comp, neg_add_cancel, homogeneousTranslationHom_zero]) + +/-- The homogeneous translation of a homogeneous polynomial of degree `d` is homogeneous of +degree `d`. -/ +lemma homogeneousTranslation_homogeneous {R : Type*} [CommRing R] {n d : ℕ} + (a : Fin n → R) {f : MvPolynomial (Fin (n + 1)) R} (hf : f.IsHomogeneous d) : + (homogeneousTranslation R a f).IsHomogeneous d := by + change (aeval (Fin.cons (X 0) (fun j => X j.succ + C (a j) * X 0)) f).IsHomogeneous d + let v : Fin (n + 1) → MvPolynomial (Fin (n + 1)) R := + Fin.cons (X 0) (fun j => X j.succ + C (a j) * X 0) + have hv : ∀ i, (v i).IsHomogeneous 1 := by + intro i + refine Fin.cases ?_ (fun j => ?_) i + · simpa only [v, Fin.cons_zero] using isHomogeneous_X R (0 : Fin (n + 1)) + · simpa only [v, Fin.cons_succ] using + (isHomogeneous_X R j.succ).add ((isHomogeneous_X R (0 : Fin (n + 1))).C_mul (a j)) + simpa only [one_mul] using hf.aeval v hv + +/-- The homogeneous translation by an integer vector of a primitive integer polynomial is +primitive. -/ +lemma PrimitiveIntegerPolynomial.translate_homogeneous {n : ℕ} (a : Fin n → ℤ) + {f : MvPolynomial (Fin (n + 1)) ℤ} (hf : PrimitiveIntegerPolynomial f) : + PrimitiveIntegerPolynomial (homogeneousTranslation ℤ a f) := by + intro c hc + have hd := (C_dvd_iff_dvd_coeff c _).mpr hc + have he := map_dvd (homogeneousTranslation ℤ a).symm.toRingHom hd + have hC : (homogeneousTranslation ℤ a).symm (C c) = C c := + (homogeneousTranslation ℤ a).symm.commutes c + change (homogeneousTranslation ℤ a).symm (C c) ∣ + (homogeneousTranslation ℤ a).symm (homogeneousTranslation ℤ a f) at he + rw [hC, AlgEquiv.symm_apply_apply] at he + exact hf c ((C_dvd_iff_dvd_coeff c f).mp he) + +/-- The homogeneous translation of `f` by `a` evaluates at `x` to the value of `f` at the point +with coordinates `x 0` and `x (j + 1) + a j * x 0`. -/ +lemma homogeneousTranslation_eval {R : Type*} [CommRing R] {n : ℕ} + (a : Fin n → R) (f : MvPolynomial (Fin (n + 1)) R) (x : Fin (n + 1) → R) : + eval x (homogeneousTranslation R a f) = + eval (Fin.cons (x 0) (fun j => x j.succ + a j * x 0)) f := by + change eval x (eval₂ C (Fin.cons (X 0) (fun j => X j.succ + C (a j) * X 0)) f) = _ + rw [← eval_assoc] + congr 2 + funext i + refine Fin.cases ?_ (fun j => ?_) i <;> simp + +/-- On the affine chart `X 0 = 1`, the homogeneous translation of `f` by `a` evaluates at the +point `(1, z)` to the value of `f` at `(1, z + a)`. -/ +lemma homogeneousTranslation_eval_chart {R : Type*} [CommRing R] {n : ℕ} + (a z : Fin n → R) (f : MvPolynomial (Fin (n + 1)) R) : + eval (Fin.cons 1 z) (homogeneousTranslation R a f) = eval (Fin.cons 1 (z + a)) f := by + rw [homogeneousTranslation_eval] + simp only [Fin.cons_zero, Fin.cons_succ, mul_one] + rfl + +/-- If `z` lies in the box of radius `B ≥ 1` and every coordinate of `a` lies in `[0, d]`, then +every coordinate of `z - a` has absolute value at most `(d + 1) * B`. -/ +lemma homogeneousTranslation_integer_point_box {n : ℕ} {B : ℝ} {d : ℕ} + (hB : 1 ≤ B) (a z : Fin n → ℤ) (ha : ∀ j, 0 ≤ a j ∧ a j ≤ d) + (hz : InBox z B) (j : Fin n) : + |((z j - a j : ℤ) : ℝ)| ≤ ((d : ℝ) + 1) * B := by + rw [Int.cast_sub] + have ha0 : (0 : ℝ) ≤ a j := by exact_mod_cast (ha j).1 + have had : (a j : ℝ) ≤ d := by exact_mod_cast (ha j).2 + have h := abs_sub_le (z j : ℝ) 0 (a j : ℝ) + simp only [sub_zero, zero_sub, abs_neg, abs_of_nonneg ha0] at h + nlinarith [hz j, show (0 : ℝ) ≤ d by positivity] + +end +end CCDN diff --git a/LeanPool/CCDN/Algebra/IdealScalarDescent.lean b/LeanPool/CCDN/Algebra/IdealScalarDescent.lean new file mode 100644 index 0000000000..cd4b151574 --- /dev/null +++ b/LeanPool/CCDN/Algebra/IdealScalarDescent.lean @@ -0,0 +1,83 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.Algebra.PolynomialLinearDescent +import LeanPool.CCDN.Hilbert.ScalarExtension +import Mathlib.LinearAlgebra.Dual.Lemmas + +/-! Coefficient-linear descent detects nonmembership in scalar-extended +ideals, and descends degree-bounded separating equations. -/ + +@[expose] public section +namespace CCDN +noncomputable section +open MvPolynomial + +private lemma polynomialCoefficientLinear_map_of_one {K L : Type*} + [Field K] [Field L] [Algebra K L] {n : ℕ} + (φ : L →ₗ[K] K) (hφ : φ 1 = 1) (f : MvPolynomial (Fin n) K) : + polynomialCoefficientLinear φ (map (algebraMap K L) f) = f := by + ext a + rw [polynomialCoefficientLinear_coeff, coeff_map] + have hh := φ.map_smul (f.coeff a) (1 : L) + simpa only [Algebra.smul_def, mul_one, hφ, Algebra.algebraMap_self, + RingHom.id_apply] using hh + +/-- For a field extension `L` of `K`, a polynomial `f` over `K` lies in the extension of the +ideal `I` to polynomials over `L` if and only if `f` lies in `I`. -/ +theorem mem_ideal_scalar_extension_iff + {K L : Type*} [Field K] [Field L] [Algebra K L] {n : ℕ} + (I : Ideal (MvPolynomial (Fin n) K)) (f : MvPolynomial (Fin n) K) : + map (algebraMap K L) f ∈ I.map (map (algebraMap K L)) ↔ f ∈ I := by + constructor + · intro h + obtain ⟨φ, hφ⟩ := Module.Projective.exists_dual_eq_one K (one_ne_zero : (1 : L) ≠ 0) + have hh := polynomialCoefficientLinear_mem_scalar_extension I _ h φ + rwa [polynomialCoefficientLinear_map_of_one φ hφ] at hh + · exact Ideal.mem_map_of_mem _ + +/-- Some coefficient functional detects nonmembership in an extended ideal. -/ +theorem exists_polynomialCoefficientLinear_not_mem + {K L : Type*} [Field K] [Field L] [Algebra K L] {n : ℕ} + (J : Ideal (MvPolynomial (Fin n) K)) (g : MvPolynomial (Fin n) L) + (hg : g ∉ J.map (map (algebraMap K L))) : + ∃ φ : L →ₗ[K] K, polynomialCoefficientLinear φ g ∉ J := by + classical + let J' := J.map (map (algebraMap K L)) + obtain ⟨r, hrstd, _, hrJ⟩ := Ideal.exists_standard_representative_degLex J' g + have hr0 : r ≠ 0 := by intro h; exact hg (by simpa only [h, sub_zero] using hrJ) + obtain ⟨a, ha⟩ := exists_coeff_ne_zero hr0 + obtain ⟨φ, hφ⟩ := Module.Projective.exists_dual_ne_zero K ha + refine ⟨φ, ?_⟩ + intro hgJ + have hdJ := polynomialCoefficientLinear_mem_scalar_extension J (g - r) hrJ φ + rw [map_sub] at hdJ + have hrmem : polynomialCoefficientLinear φ r ∈ J := by + have hh := J.sub_mem hgJ hdJ + simpa only [sub_sub_cancel] using hh + have hrstdK : polynomialCoefficientLinear φ r ∈ + restrictSupport K (Ideal.standardExponents J MonomialOrder.degLex) := by + intro b hb + have hbr : b ∈ r.support := by + change b ∈ (polynomialCoefficientLinear φ r).support at hb + rw [mem_support_iff] at hb ⊢ + intro hzero + apply hb + rw [polynomialCoefficientLinear_coeff, hzero, map_zero] + have hh := hrstd hbr + simpa only [J', standardExponents_scalar_extension] using hh + have hrzero := + Ideal.eq_zero_of_mem_restrictSupport_standardExponents J MonomialOrder.degLex hrstdK hrmem + have hc := polynomialCoefficientLinear_coeff φ r a + have hz : φ (r.coeff a) = 0 := by simpa [hrzero] using hc.symm + exact hφ hz + +end +end CCDN diff --git a/LeanPool/CCDN/Algebra/IdealZeroLocus.lean b/LeanPool/CCDN/Algebra/IdealZeroLocus.lean new file mode 100644 index 0000000000..75e7e3825f --- /dev/null +++ b/LeanPool/CCDN/Algebra/IdealZeroLocus.lean @@ -0,0 +1,29 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import Mathlib.Algebra.MvPolynomial.Eval +public import Mathlib.RingTheory.Ideal.Defs + +/-! The zero locus of an ideal of multivariate polynomials, with coordinates in the +coefficient ring. Over a field `K` this is Mathlib's `MvPolynomial.zeroLocus K I`, +stated with `eval` rather than `aeval`; that definition is reserved for points with +coordinates in an extension field. -/ + +@[expose] public section +namespace CCDN +noncomputable section +open MvPolynomial + +/-- The common zeros, with coordinates in the coefficient ring, of all polynomials in `I`. -/ +def idealZeroLocus {K σ : Type*} [CommRing K] (I : Ideal (MvPolynomial σ K)) : Set (σ → K) := + {x | ∀ f ∈ I, eval x f = 0} + +end +end CCDN diff --git a/LeanPool/CCDN/Algebra/IntegerLinearForms.lean b/LeanPool/CCDN/Algebra/IntegerLinearForms.lean new file mode 100644 index 0000000000..758622f50f --- /dev/null +++ b/LeanPool/CCDN/Algebra/IntegerLinearForms.lean @@ -0,0 +1,63 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.Algebra.LinearForm +import Mathlib.Analysis.SpecialFunctions.Pow.Real + +/-! Integer matrices give linear forms. Composition is matrix +multiplication, with an explicit coefficient-height bound. -/ + +@[expose] public section +namespace CCDN +noncomputable section +open MvPolynomial + +/-- The rational linear form `∑ i, a i * X i` with integer coefficient vector `a`. -/ +abbrev integerLinearForm {σ : Type*} [Fintype σ] (a : σ → ℤ) : MvPolynomial σ ℚ := + linearForm fun i => (a i : ℚ) + +/-- The integer linear form with the `i`-th standard basis vector as coefficients is `X i`. -/ +lemma integerLinearForm_identity {σ : Type*} [Fintype σ] [DecidableEq σ] (i : σ) : + integerLinearForm (fun j => if j = i then 1 else 0) = X i := by + simp [linearForm] + +/-- Substituting the integer linear forms with coefficient rows `b i` into the integer linear +form with coefficients `a` gives the integer linear form with coefficients `∑ i, a i * b i j`, +the product of the row vector `a` with the matrix `b`. -/ +lemma integerLinearForm_comp {σ τ : Type*} [Fintype σ] [Fintype τ] + (a : σ → ℤ) (b : σ → τ → ℤ) : + aeval (fun i => integerLinearForm (b i)) (integerLinearForm a) = + integerLinearForm (fun j => ∑ i, a i * b i j) := by + simp only [integerLinearForm, linearForm, map_sum, map_mul, aeval_C, aeval_X, + Finset.mul_sum, Int.cast_sum, Int.cast_mul, Finset.sum_mul] + rw [Finset.sum_comm] + apply Finset.sum_congr rfl + intro j _ + apply Finset.sum_congr rfl + intro i _ + rw [algebraMap_eq, mul_assoc] + +/-- If the entries of `a` are bounded by `H` and those of `b` by `T` in absolute value, then +each entry of the product `∑ i, a i * b i j` is bounded by `Fintype.card σ * H * T`. -/ +lemma integerLinearForm_matrix_height {σ τ : Type*} [Fintype σ] + (a : σ → ℤ) (b : σ → τ → ℤ) (H T : ℕ) + (ha : ∀ i, |a i| ≤ H) (hb : ∀ i j, |b i j| ≤ T) (j : τ) : + |∑ i, a i * b i j| ≤ Fintype.card σ * H * T := by + calc + _ ≤ ∑ i, |a i * b i j| := Finset.abs_sum_le_sum_abs _ _ + _ ≤ ∑ _i : σ, (H : ℤ) * T := by + apply Finset.sum_le_sum + intro i _ + rw [abs_mul] + exact mul_le_mul (ha i) (hb i j) (abs_nonneg _) (by positivity) + _ = _ := by simp [mul_assoc] + +end +end CCDN diff --git a/LeanPool/CCDN/Algebra/IntegerPolynomialAvoidance.lean b/LeanPool/CCDN/Algebra/IntegerPolynomialAvoidance.lean new file mode 100644 index 0000000000..3ffb6f052c --- /dev/null +++ b/LeanPool/CCDN/Algebra/IntegerPolynomialAvoidance.lean @@ -0,0 +1,59 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import Mathlib.Algebra.MvPolynomial.Degrees +import Mathlib.Combinatorics.Nullstellensatz + +/-! A nonzero polynomial over a characteristic-zero domain has a nonzero value at an integer +point with every coordinate between 0 and its degree. This is the combinatorial +Nullstellensatz `MvPolynomial.eq_zero_of_eval_zero_at_prod_finset` on the grid {0,...,D}. -/ + +@[expose] public section +namespace CCDN +noncomputable section +open MvPolynomial + +/-- A nonzero polynomial with coordinate degrees at most `D` has a nonzero value on the +integer grid {0,...,D}, in any finite number of variables. Unlike the total-degree version, +the side length does not grow with the number of variables. -/ +theorem exists_integer_grid_eval_ne_zero_of_degreeOf + {R σ : Type*} [CommRing R] [IsDomain R] [CharZero R] [Finite σ] + (f : MvPolynomial σ R) (hf : f ≠ 0) (D : ℕ) (hdeg : ∀ i, f.degreeOf i ≤ D) : + ∃ v : σ → ℤ, (∀ i, 0 ≤ v i ∧ v i ≤ D) ∧ eval (fun i => (v i : R)) f ≠ 0 := by + classical + by_contra! hn + let S : Finset R := (Finset.range (D + 1)).image Nat.cast + have hS : S.card = D + 1 := by + rw [Finset.card_image_of_injective _ Nat.cast_injective, Finset.card_range] + refine hf (eq_zero_of_eval_zero_at_prod_finset f (fun _ => S) + (fun i => hS ▸ Nat.lt_succ_of_le (hdeg i)) fun x hx => ?_) + choose a ha he using fun i => Finset.mem_image.mp (hx i) + have hv := hn (fun i => (a i : ℤ)) fun i => + ⟨Int.natCast_nonneg _, by exact_mod_cast Finset.mem_range_succ_iff.mp (ha i)⟩ + simpa only [Int.cast_natCast, he] using hv + +/-- A nonzero polynomial of total degree at most `A` over a characteristic-zero domain has a +nonzero value on the integer grid {0,...,A}. -/ +theorem exists_integer_grid_eval_ne_zero_of_totalDegree + {R σ : Type*} [CommRing R] [IsDomain R] [CharZero R] [Finite σ] + (f : MvPolynomial σ R) (hf : f ≠ 0) (A : ℕ) (hd : f.totalDegree ≤ A) : + ∃ v : σ → ℤ, (∀ i, 0 ≤ v i ∧ v i ≤ A) ∧ eval (fun i => (v i : R)) f ≠ 0 := + exists_integer_grid_eval_ne_zero_of_degreeOf f hf A fun i => + (degreeOf_le_totalDegree f i).trans hd + +/-- The integer case of `exists_integer_grid_eval_ne_zero_of_totalDegree`. The bound is +independent of the number and names of the coordinates. -/ +theorem exists_integer_grid_eval_ne_zero {σ : Type*} [Finite σ] + (f : MvPolynomial σ ℤ) (hf : f ≠ 0) (A : ℕ) (hd : f.totalDegree ≤ A) : + ∃ v : σ → ℤ, (∀ i, 0 ≤ v i ∧ v i ≤ A) ∧ eval v f ≠ 0 := by + simpa only [Int.cast_id] using exists_integer_grid_eval_ne_zero_of_totalDegree f hf A hd + +end +end CCDN diff --git a/LeanPool/CCDN/Algebra/IntegerRationalIrreducibility.lean b/LeanPool/CCDN/Algebra/IntegerRationalIrreducibility.lean new file mode 100644 index 0000000000..f6dcc5fa37 --- /dev/null +++ b/LeanPool/CCDN/Algebra/IntegerRationalIrreducibility.lean @@ -0,0 +1,71 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import Mathlib.Algebra.MvPolynomial.Degrees +import LeanPool.CCDN.Algebra.PrimitiveNormalization +import LeanPool.CCDN.ForMathlib.MvPolynomial +import Mathlib.Algebra.MvPolynomial.Nilpotent +import Mathlib.Algebra.MvPolynomial.NoZeroDivisors + +/-! Positive-degree irreducible integer equations remain irreducible over Q. +Primitive denominator-cleared divisors give an elementary Gauss argument. -/ + +@[expose] public section +namespace CCDN +noncomputable section +open MvPolynomial + +/-- An irreducible integer polynomial of positive total degree remains irreducible over `ℚ`. -/ +theorem rational_map_irreducible_of_integer {n : ℕ} + (f : MvPolynomial (Fin n) ℤ) (hir : Irreducible f) (hd : 0 < f.totalDegree) : + Irreducible (map (Int.castRingHom ℚ) f) := by + have hdegree : (map (Int.castRingHom ℚ) f).totalDegree = f.totalDegree := + totalDegree_map_of_injective Int.cast_injective _ + have hf0 : map (Int.castRingHom ℚ) f ≠ 0 := by + intro he + exact hir.ne_zero ((map_injective (Int.castRingHom ℚ) Int.cast_injective) (by simpa using he)) + have hunit : ¬ IsUnit (map (Int.castRingHom ℚ) f) := by + intro hu + have he := (isUnit_iff_totalDegree_of_isReduced.mp hu).2 + rw [hdegree] at he + omega + refine ⟨hunit, ?_⟩ + intro q r he + by_cases hqu : IsUnit q + · exact Or.inl hqu + by_cases hru : IsUnit r + · exact Or.inr hru + have hq0 : q ≠ 0 := fun hh => hf0 (by simp [he, hh]) + have hr0 : r ≠ 0 := fun hh => hf0 (by simp [he, hh]) + have hqd := totalDegree_pos_of_ne_zero_of_not_isUnit hq0 hqu + have hrd := totalDegree_pos_of_ne_zero_of_not_isUnit hr0 hru + have hsum : q.totalDegree + r.totalDegree = f.totalDegree := by + rw [← totalDegree_mul_of_isDomain hq0 hr0, ← he, hdegree] + obtain ⟨g, a, hgprim, ha, hmodel⟩ := exists_primitive_integer_model q hq0 + have hgdegree := rational_scalar_model_degree ha hq0 hmodel + have hdivQ : map (Int.castRingHom ℚ) g ∣ map (Int.castRingHom ℚ) f := by + refine ⟨C a⁻¹ * r, ?_⟩ + rw [hmodel, mul_mul_mul_comm, ← C_mul, mul_inv_cancel₀ ha, C_1, one_mul] + exact he + obtain ⟨u, hu⟩ := hgprim.rational_map_dvd_iff.mp hdivQ + have hg0 : g ≠ 0 := fun hh => hir.ne_zero (by simp [hu, hh]) + have hu0 : u ≠ 0 := fun hh => hir.ne_zero (by simp [hu, hh]) + rcases hir.isUnit_or_isUnit hu with hgu | huu + · have hh := (isUnit_iff_totalDegree_of_isReduced.mp hgu).2 + rw [hgdegree] at hh + omega + · have hh := (isUnit_iff_totalDegree_of_isReduced.mp huu).2 + have hedeg : f.totalDegree = g.totalDegree + u.totalDegree := by + rw [hu, totalDegree_mul_of_isDomain hg0 hu0] + rw [hgdegree, hh, add_zero] at hedeg + omega + +end +end CCDN diff --git a/LeanPool/CCDN/Algebra/IntegralMinpolyDegree.lean b/LeanPool/CCDN/Algebra/IntegralMinpolyDegree.lean new file mode 100644 index 0000000000..3d8b7e484f --- /dev/null +++ b/LeanPool/CCDN/Algebra/IntegralMinpolyDegree.lean @@ -0,0 +1,41 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import Mathlib.FieldTheory.Minpoly.Basic +public import Mathlib.RingTheory.IntegralClosure.IntegrallyClosed +import Mathlib.FieldTheory.Minpoly.Finite +import Mathlib.FieldTheory.Minpoly.IsIntegrallyClosed + +/-! Degree bounds for integral elements: over an integrally closed base ring, the minimal +polynomial of an integral element has degree at most the degree of the fraction-field +extension. -/ + +@[expose] public section +namespace CCDN +noncomputable section +open Polynomial + +/-- Let `R` be an integrally closed domain with fraction field `F`, and let `E` be a finite +extension of `F` receiving compatible maps from `R`, `A` and `F`. The minimal polynomial over +`R` of an integral element `a` of the domain `A` has degree at most `[E : F]`. -/ +theorem integral_minpoly_natDegree_le_fraction_finrank + (R A F E : Type*) [CommRing R] [IsDomain R] [IsIntegrallyClosed R] + [CommRing A] [IsDomain A] [Field F] [Field E] + [Algebra R A] [Algebra R F] [IsFractionRing R F] + [Algebra R E] [Algebra A E] [Algebra F E] + [IsScalarTower R A E] [IsScalarTower R F E] + [FiniteDimensional F E] (a : A) (ha : IsIntegral R a) : + (minpoly R a).natDegree ≤ Module.finrank F E := by + rw [← (minpoly.monic ha).natDegree_map (algebraMap R F), + ← minpoly.isIntegrallyClosed_eq_field_fractions F E ha] + exact minpoly.natDegree_le (algebraMap A E a) + +end +end CCDN diff --git a/LeanPool/CCDN/Algebra/LinearForm.lean b/LeanPool/CCDN/Algebra/LinearForm.lean new file mode 100644 index 0000000000..0ca6f1e4a7 --- /dev/null +++ b/LeanPool/CCDN/Algebra/LinearForm.lean @@ -0,0 +1,97 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import Mathlib.RingTheory.MvPolynomial.Homogeneous + +/-! The homogeneous linear form `∑ i, a i * X i` with a given coefficient vector, +and its basic API: homogeneity, degree, evaluation, coefficient maps, renaming and +linearity in the coefficient vector. -/ + +@[expose] public section +namespace CCDN +noncomputable section +open MvPolynomial + +variable {R S σ τ : Type*} + +/-- The linear form `∑ i, a i * X i` with coefficient vector `a`. -/ +def linearForm [CommSemiring R] [Fintype σ] (a : σ → R) : MvPolynomial σ R := + ∑ i, C (a i) * X i + +section CommSemiring + +variable [CommSemiring R] [Fintype σ] + +/-- A linear form is homogeneous of degree one. -/ +lemma linearForm_isHomogeneous (a : σ → R) : (linearForm a).IsHomogeneous 1 := + IsHomogeneous.sum _ _ _ (fun i _ => isHomogeneous_C_mul_X _ i) + +/-- A linear form has total degree at most one. -/ +lemma linearForm_totalDegree_le (a : σ → R) : (linearForm a).totalDegree ≤ 1 := + (linearForm_isHomogeneous a).totalDegree_le + +/-- The linear form with coefficients `a` evaluates at `x` to `∑ i, a i * x i`. -/ +@[simp] lemma eval_linearForm (a x : σ → R) : eval x (linearForm a) = ∑ i, a i * x i := by + simp [linearForm] + +/-- The linear form with coefficients `a` evaluates at a point `x` of an `R`-algebra `S` to +`∑ i, algebraMap R S (a i) * x i`. -/ +@[simp] lemma aeval_linearForm [CommSemiring S] [Algebra R S] (a : σ → R) (x : σ → S) : + aeval x (linearForm a) = ∑ i, algebraMap R S (a i) * x i := by + simp [linearForm] + +/-- Evaluating the linear form with coefficients `a` along `φ` at `x` gives +`∑ i, φ (a i) * x i`. -/ +@[simp] lemma eval₂_linearForm [CommSemiring S] (φ : R →+* S) (a : σ → R) (x : σ → S) : + eval₂ φ x (linearForm a) = ∑ i, φ (a i) * x i := by + simp [linearForm] + +/-- Renaming the variables of a linear form along a bijection `e` gives the linear form whose +coefficient vector is reindexed by `e.symm`. -/ +lemma rename_linearForm [Fintype τ] (e : σ ≃ τ) (a : σ → R) : + rename e (linearForm a) = linearForm (a ∘ e.symm) := by + simp only [linearForm, map_sum, map_mul, rename_C, rename_X] + simpa only [Equiv.symm_apply_apply, Function.comp_apply] using + e.sum_comp (fun j => C (a (e.symm j)) * X j) + +/-- The linear form with zero coefficient vector is zero. -/ +@[simp] lemma linearForm_zero : linearForm (0 : σ → R) = 0 := by + simp [linearForm] + +/-- The linear form is additive in its coefficient vector. -/ +lemma linearForm_add (a b : σ → R) : linearForm (a + b) = linearForm a + linearForm b := by + simp [linearForm, add_mul, Finset.sum_add_distrib] + +/-- The linear form commutes with scalar multiplication of its coefficient vector. -/ +lemma linearForm_smul (c : R) (a : σ → R) : linearForm (c • a) = c • linearForm a := by + simp [linearForm, Finset.smul_sum, smul_eq_C_mul, mul_assoc] + +variable (R σ) in +/-- `linearForm` as a linear map from coefficient vectors to polynomials. -/ +def linearFormLinearMap : (σ → R) →ₗ[R] MvPolynomial σ R where + toFun := linearForm + map_add' := linearForm_add + map_smul' := linearForm_smul + +/-- The linear map `linearFormLinearMap R σ` sends a coefficient vector `a` to `linearForm a`. -/ +@[simp] lemma linearFormLinearMap_apply (a : σ → R) : + linearFormLinearMap R σ a = linearForm a := + rfl + +end CommSemiring + +section CommRing + +variable [CommRing R] [Fintype σ] + +end CommRing + +end +end CCDN diff --git a/LeanPool/CCDN/Algebra/ModularPolynomialLift.lean b/LeanPool/CCDN/Algebra/ModularPolynomialLift.lean new file mode 100644 index 0000000000..15cc8209ba --- /dev/null +++ b/LeanPool/CCDN/Algebra/ModularPolynomialLift.lean @@ -0,0 +1,67 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import Mathlib.Data.ZMod.Basic +public import Mathlib.RingTheory.MvPolynomial.Ideal + +/-! Coefficientwise integer lifts preserve homogeneous weights and origin-ideal +powers. -/ + +@[expose] public section +namespace CCDN +noncomputable section +open MvPolynomial + +/-- The integer polynomial lifting a polynomial over `ZMod p` coefficientwise, using the +representatives `ZMod.val` in `[0, p)`. -/ +def modularPolynomialLift {σ : Type*} {p : ℕ} (f : MvPolynomial σ (ZMod p)) : + MvPolynomial σ ℤ := ∑ e ∈ f.support, monomial e (f.coeff e).val + +private lemma coeff_modularPolynomialLift {σ : Type*} {p : ℕ} + (f : MvPolynomial σ (ZMod p)) (e : σ →₀ ℕ) : + (modularPolynomialLift f).coeff e = (f.coeff e).val := by + classical + simp only [modularPolynomialLift, coeff_sum, coeff_monomial] + rw [Finset.sum_ite_eq'] + split_ifs with he + · rfl + · have hc : f.coeff e = 0 := by simpa only [mem_support_iff, not_not] using he + rw [hc, ZMod.val_zero, Nat.cast_zero] + +/-- Reducing the coefficientwise lift of `f` modulo `p` recovers `f`. -/ +lemma map_modularPolynomialLift {σ : Type*} {p : ℕ} [NeZero p] + (f : MvPolynomial σ (ZMod p)) : + map (Int.castRingHom (ZMod p)) (modularPolynomialLift f) = f := by + ext e + simp only [coeff_map, coeff_modularPolynomialLift, Int.coe_castRingHom, + Int.cast_natCast, ZMod.natCast_zmod_val] + +/-- The coefficientwise lift of a homogeneous polynomial of degree `k` over `ZMod p` is +homogeneous of degree `k`. -/ +lemma modularPolynomialLift_homogeneous {σ : Type*} {p k : ℕ} + (f : MvPolynomial σ (ZMod p)) (hf : f.IsHomogeneous k) : + (modularPolynomialLift f).IsHomogeneous k := by + intro e he + have hfe : f.coeff e ≠ 0 := by + intro h + exact he (by simp only [coeff_modularPolynomialLift, h, ZMod.val_zero, Nat.cast_zero]) + exact hf hfe + +/-- If `f` lies in the `k`-th power of the ideal of the origin over `ZMod p`, then so does its +coefficientwise lift over `ℤ`. -/ +lemma modularPolynomialLift_mem_origin_power {σ : Type*} {p k : ℕ} + (f : MvPolynomial σ (ZMod p)) (hf : f ∈ idealOfVars σ (ZMod p) ^ k) : + modularPolynomialLift f ∈ idealOfVars σ ℤ ^ k := by + rw [mem_pow_idealOfVars_iff'] at hf ⊢ + intro e he + rw [coeff_modularPolynomialLift, hf e he, ZMod.val_zero, Nat.cast_zero] + +end +end CCDN diff --git a/LeanPool/CCDN/Algebra/MonomialEvaluationMatrix.lean b/LeanPool/CCDN/Algebra/MonomialEvaluationMatrix.lean new file mode 100644 index 0000000000..9dcdb323a6 --- /dev/null +++ b/LeanPool/CCDN/Algebra/MonomialEvaluationMatrix.lean @@ -0,0 +1,105 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.Height.Box +public import Mathlib.Algebra.MvPolynomial.Degrees +public import Mathlib.Data.Finsupp.Weight +public import Mathlib.Data.Matrix.Mul +import Mathlib.Analysis.SpecialFunctions.Pow.Real +import Mathlib.Tactic.Positivity + +/-! +# Evaluation matrices and auxiliary polynomials + +A coefficient vector in the kernel of the monomial evaluation matrix produces a polynomial +vanishing at every specified point. Injectivity of the list of monomials ensures that a +nonzero vector produces a nonzero polynomial. +-/ + +@[expose] public section + +namespace CCDN + +open Matrix + +/-- The integer polynomial `∑ j, v j * X ^ mon j` with coefficient vector `v` on the list of +monomials `mon`. -/ +noncomputable def polynomialOfCoefficients {n N : ℕ} (mon : Fin N → (Fin n →₀ ℕ)) + (v : Fin N → ℤ) : MvPolynomial (Fin n) ℤ := + ∑ j, MvPolynomial.monomial (mon j) (v j) + +/-- For an injective list of monomials `mon`, the coefficient of `mon j` in +`polynomialOfCoefficients mon v` is `v j`. -/ +theorem coefficient_polynomialOfCoefficients {n N : ℕ} (mon : Fin N → (Fin n →₀ ℕ)) + (hmon : Function.Injective mon) (v : Fin N → ℤ) (j : Fin N) : + (polynomialOfCoefficients mon v).coeff (mon j) = v j := by + classical + simp [polynomialOfCoefficients, MvPolynomial.coeff_monomial, + hmon.eq_iff] + +/-- For an injective list of monomials, a nonzero coefficient vector gives a nonzero +polynomial. -/ +theorem polynomialOfCoefficients_ne_zero {n N : ℕ} (mon : Fin N → (Fin n →₀ ℕ)) + (hmon : Function.Injective mon) {v : Fin N → ℤ} (hv : v ≠ 0) : + polynomialOfCoefficients mon v ≠ 0 := by + intro h + apply hv + funext j + have hc := coefficient_polynomialOfCoefficients mon hmon v j + rw [h] at hc + simpa using hc.symm + +/-- If every monomial in the list `mon` has degree at most `M`, then +`polynomialOfCoefficients mon v` has total degree at most `M`. -/ +theorem totalDegree_polynomialOfCoefficients_le {n N M : ℕ} + (mon : Fin N → (Fin n →₀ ℕ)) (v : Fin N → ℤ) + (hdeg : ∀ j, (mon j).degree ≤ M) : + (polynomialOfCoefficients mon v).totalDegree ≤ M := by + apply (MvPolynomial.totalDegree_finsetSum _ _).trans + apply Finset.sup_le + intro j _ + exact (MvPolynomial.totalDegree_monomial_le _ _).trans (hdeg j) + +/-- The monomial evaluation matrix, whose entry in row `i` and column `j` is the value of the +monomial `mon j` at the integer point `points i`. -/ +noncomputable def evaluationMatrix {n N s : ℕ} (mon : Fin N → (Fin n →₀ ℕ)) + (points : Fin s → (Fin n → ℤ)) : Matrix (Fin s) (Fin N) ℤ := + fun i j => (mon j).prod fun k e => points i k ^ e + +/-- The value of `polynomialOfCoefficients mon v` at `points i` is the `i`-th entry of the +product of the monomial evaluation matrix with `v`. -/ +theorem eval_polynomialOfCoefficients {n N s : ℕ} (mon : Fin N → (Fin n →₀ ℕ)) + (points : Fin s → (Fin n → ℤ)) (v : Fin N → ℤ) (i : Fin s) : + MvPolynomial.eval (points i) (polynomialOfCoefficients mon v) = + (evaluationMatrix mon points *ᵥ v) i := by + simp [polynomialOfCoefficients, evaluationMatrix, Matrix.mulVec, dotProduct, + MvPolynomial.eval_monomial, mul_comm] + +/-- If the integer point `x` lies in the box of radius `B ≥ 1` and the exponent `e` has degree +at most `M`, then the monomial `x ^ e` has absolute value at most `B ^ M`. -/ +theorem abs_monomial_value_le {n M : ℕ} (e : Fin n →₀ ℕ) (x : Fin n → ℤ) + {B : ℝ} (hB : 1 ≤ B) (hx : InBox x B) + (he : e.degree ≤ M) : + |((e.prod (fun i k => x i ^ k) : ℤ) : ℝ)| ≤ B ^ M := by + have hsum : ∑ i, e i ≤ M := by rwa [Finsupp.degree_eq_sum] at he + rw [Finsupp.prod_fintype e (fun i k => x i ^ k) (fun _ => pow_zero _)] + push_cast + rw [Finset.abs_prod] + calc + _ ≤ ∏ i : Fin n, B ^ e i := by + apply Finset.prod_le_prod₀ + · intro i _; positivity + · intro i _ + rw [abs_pow] + exact pow_le_pow_left₀ (abs_nonneg _) (hx i) _ + _ = B ^ (∑ i, e i) := Finset.prod_pow_eq_pow_sum _ _ _ + _ ≤ B ^ M := pow_le_pow_right₀ hB hsum + +end CCDN diff --git a/LeanPool/CCDN/Algebra/MonomialOrthants.lean b/LeanPool/CCDN/Algebra/MonomialOrthants.lean new file mode 100644 index 0000000000..c637776925 --- /dev/null +++ b/LeanPool/CCDN/Algebra/MonomialOrthants.lean @@ -0,0 +1,162 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import Mathlib.Algebra.BigOperators.Group.Finset.Defs +public import Mathlib.Algebra.Group.End +public import Mathlib.Order.UpperLower.Basic +import Mathlib.Analysis.SpecialFunctions.Pow.Real + +/-! A lower set of exponent vectors is a finite disjoint union of translated +coordinate orthants. Dickson's lemma supplies a threshold above which each +coordinate is either fixed or unrestricted. -/ + +@[expose] public section +namespace CCDN +noncomputable section + +/-- Truncate every coordinate of the exponent vector `a` at `B`, giving `i ↦ min (a i) B`. -/ +def clampExponent {σ : Type*} (B : ℕ) (a : σ → ℕ) : σ → ℕ := + fun i => min (a i) B + +/-- Membership in a lower exponent set is determined by a finite box. -/ +theorem lowerSet_clamp_stabilizes {σ : Type*} [Finite σ] + (S : Set (σ → ℕ)) (hS : IsLowerSet S) : + ∃ B : ℕ, 0 < B ∧ ∀ a, a ∈ S ↔ clampExponent B a ∈ S := by + classical + let := Fintype.ofFinite σ + let T : Set (σ → ℕ) := {a | Minimal (fun a => a ∉ S) a} + have hT : T.Finite := + WellQuasiOrderedLE.finite_of_isAntichain (setOfPred_minimal_antichain _) + let B := hT.toFinset.sup (fun a => Finset.univ.sup a) + 1 + have hbound (a : σ → ℕ) (ha : a ∈ T) (i : σ) : a i ≤ B := by + have h₁ := Finset.le_sup (f := a) (Finset.mem_univ i) + have h₂ := Finset.le_sup (f := fun a : σ → ℕ => Finset.univ.sup a) + (hT.mem_toFinset.mpr ha) + dsimp [B] + omega + refine ⟨B, by dsimp [B]; omega, ?_⟩ + intro a + constructor + · exact fun ha => hS (fun i => min_le_left _ _) ha + · intro ha + by_contra hn + have hp : (Sᶜ).IsPWO := by + intro f + exact wellQuasiOrdered_le (fun n => (f n).val) + obtain ⟨b, hba, hb⟩ := hp.exists_le_minimal hn + apply hb.1 + apply hS (a := clampExponent B a) _ ha + intro i + exact le_min (hba i) (hbound b hb i) + +/-- A truncated coordinate pattern, with value B marking its free coordinates. -/ +abbrev orthantFree {σ : Type*} {B : ℕ} (b : σ → Fin (B + 1)) := + {i : σ // (b i).val = B} + +/-- The total degree `∑ i, b i` of the base point of the orthant with truncated coordinate +pattern `b`. -/ +def orthantBaseDegree {σ : Type*} [Fintype σ] {B : ℕ} + (b : σ → Fin (B + 1)) : ℕ := ∑ i, (b i).val + +/-- The point of the orthant with truncated pattern `b` obtained by adding `c` to the base +point `b` in the free coordinates (those where `b i = B`). -/ +def orthantExpand {σ : Type*} {B : ℕ} (b : σ → Fin (B + 1)) + (c : orthantFree b → ℕ) : σ → ℕ := + fun i => (b i).val + if h : (b i).val = B then c ⟨i, h⟩ else 0 + +/-- Every point of the orthant with truncated pattern `b` truncates back to `b`. -/ +lemma clamp_orthantExpand {σ : Type*} {B : ℕ} + (b : σ → Fin (B + 1)) (c : orthantFree b → ℕ) : + clampExponent B (orthantExpand b c) = fun i => (b i).val := by + funext i + simp only [clampExponent, orthantExpand] + split_ifs with h + · rw [h] + omega + · have hi := (b i).isLt + omega + +/-- The total degree of a point of the orthant with pattern `b` is the base degree of `b` +plus the total of the added free coordinates. -/ +lemma orthantExpand_sum {σ : Type*} [Fintype σ] {B : ℕ} + (b : σ → Fin (B + 1)) (c : orthantFree b → ℕ) : + (∑ i, orthantExpand b c i) = orthantBaseDegree b + ∑ j, c j := by + classical + simp only [orthantExpand, Finset.sum_add_distrib, orthantBaseDegree] + congr 1 + have hh := Finset.sum_subtype (p := fun i => (b i).val = B) (F := inferInstance) + (Finset.univ.filter (fun i => (b i).val = B)) + (by simp) (fun i => if h : (b i).val = B then c ⟨i, h⟩ else 0) + calc + _ = ∑ i ∈ Finset.univ.filter (fun i => (b i).val = B), + if h : (b i).val = B then c ⟨i, h⟩ else 0 := by + rw [Finset.sum_filter] + apply Finset.sum_congr rfl + intro i _ + by_cases hi : (b i).val = B <;> simp [hi] + _ = _ := hh + _ = ∑ j, c j := Finset.sum_congr rfl (fun j _ => by simp [j.property]) + +/-- Every cell of the truncation map is a coordinate orthant. -/ +def orthantExpandEquiv {σ : Type*} {B : ℕ} (b : σ → Fin (B + 1)) : + (orthantFree b → ℕ) ≃ + {a : σ → ℕ // clampExponent B a = fun i => (b i).val} where + toFun c := ⟨orthantExpand b c, clamp_orthantExpand b c⟩ + invFun a := fun i => a.val i.val - B + left_inv c := by + funext i + simp [orthantExpand, i.property] + right_inv a := by + apply Subtype.ext + funext i + have hi := congrFun a.property i + change min (a.val i) B = (b i).val at hi + simp only [orthantExpand] + split_ifs with h + · rw [h] + omega + · have hb := (b i).isLt + simp only [add_zero] + omega + +/-- Truncation gives the unique cell containing any exponent vector. -/ +def exponentPattern {σ : Type*} (B : ℕ) (a : σ → ℕ) : σ → Fin (B + 1) := + fun i => ⟨min (a i) B, Nat.lt_succ_of_le (min_le_right _ _)⟩ + +/-- A bounded slice of an orthant is a bounded exponent simplex in its free +coordinates. -/ +def boundedOrthantEquiv {σ : Type*} [Fintype σ] {B N : ℕ} + (b : σ → Fin (B + 1)) (hN : orthantBaseDegree b ≤ N) : + {c : orthantFree b → ℕ // ∑ i, c i ≤ N - orthantBaseDegree b} ≃ + {a : σ → ℕ // clampExponent B a = (fun i => (b i).val) ∧ ∑ i, a i ≤ N} := by + let e := orthantExpandEquiv b + refine Equiv.ofBijective + (fun c => ⟨orthantExpand b c.val, clamp_orthantExpand b c.val, ?_⟩) ⟨?_, ?_⟩ + · rw [orthantExpand_sum] + omega + · intro c d h + have hv : orthantExpand b c.val = orthantExpand b d.val := + congrArg (fun a : {a : σ → ℕ // + clampExponent B a = (fun i => (b i).val) ∧ ∑ i, a i ≤ N} => a.val) h + apply Subtype.ext + apply e.injective + exact Subtype.ext hv + · intro a + let c := e.symm ⟨a.val, a.property.1⟩ + have he : orthantExpand b c = a.val := + congrArg Subtype.val (e.apply_symm_apply ⟨a.val, a.property.1⟩) + have hc : ∑ i, c i ≤ N - orthantBaseDegree b := by + have hs := orthantExpand_sum b c + rw [he] at hs + omega + exact ⟨⟨c, hc⟩, Subtype.ext he⟩ + +end +end CCDN diff --git a/LeanPool/CCDN/Algebra/MonomialRays.lean b/LeanPool/CCDN/Algebra/MonomialRays.lean new file mode 100644 index 0000000000..fcbb361abb --- /dev/null +++ b/LeanPool/CCDN/Algebra/MonomialRays.lean @@ -0,0 +1,139 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.ForMathlib.StandardExponents +public import LeanPool.CCDN.Hilbert.FilteredIntersection +import LeanPool.CCDN.ForMathlib.MvPolynomial +import LeanPool.CCDN.Hilbert.StandardMonomials + +/-! +# Linear-growth monomial sets + +A downward-closed exponent set with a linear counting bound cannot contain a +large square. Consequently its unbounded portions lie in coordinate rays. +-/ + +@[expose] public section +namespace CCDN +noncomputable section +open MvPolynomial + +/-- Bounded exponent sets are finite. -/ +instance finite_boundedStandardExponents {K σ : Type*} [Field K] [Finite σ] + (I : Ideal (MvPolynomial σ K)) (m : MonomialOrder σ) (N : ℕ) : + Finite (Ideal.boundedStandardExponents I m N) := by + classical + let := Fintype.ofFinite σ + let e := Finsupp.degreeLeEquivSym σ N + let f : Ideal.boundedStandardExponents I m N → Sym (Option σ) N := + fun a => e ⟨a.val, a.property.2⟩ + apply Finite.of_injective f + intro a b h + have he := e.injective h + apply Subtype.ext + exact congrArg (fun p : {p : σ →₀ ℕ // p.degree ≤ N} => p.val) he + +/-- A standard monomial with two large exponents supplies an entire square of +independent quotient classes in degree at most `2B`. -/ +private theorem standard_square_le_filtered {K σ : Type*} [Field K] [Finite σ] + [LinearOrder σ] (I : Ideal (MvPolynomial σ K)) (B : ℕ) + {a : σ →₀ ℕ} (ha : a ∈ Ideal.standardExponents I MonomialOrder.degLex) + {i j : σ} (hij : i ≠ j) (hi : B ≤ a i) (hj : B ≤ a j) : + (B + 1) ^ 2 ≤ Module.finrank K (filteredCoordinateSpace I (2 * B)) := by + classical + let := Fintype.ofFinite σ + let f : Fin (B + 1) × Fin (B + 1) → + Ideal.boundedStandardExponents I MonomialOrder.degLex (2 * B) := fun t => + ⟨Finsupp.single i t.1.val + Finsupp.single j t.2.val, by + constructor + · apply Ideal.isLowerSet_standardExponents I MonomialOrder.degLex _ ha + intro k + by_cases hki : k = i + · subst k + simpa [hij, hij.symm] using (show t.1.val ≤ a i from by omega) + by_cases hkj : k = j + · subst k + simpa [hij, hij.symm] using (show t.2.val ≤ a j from by omega) + simp [hki, hkj] + · change Finsupp.degree _ ≤ 2 * B + rw [map_add, Finsupp.degree_single, Finsupp.degree_single] + omega⟩ + have hinj : Function.Injective f := by + intro s t h + have hval := congrArg Subtype.val h + have h1 := congrArg (fun a : σ →₀ ℕ => a i) hval + have h2 := congrArg (fun a : σ →₀ ℕ => a j) hval + simp only [f, Finsupp.coe_add, Pi.add_apply, Finsupp.single_eq_same, + Finsupp.single_eq_of_ne hij, Finsupp.single_eq_of_ne hij.symm, add_zero, zero_add] at h1 h2 + exact Prod.ext (Fin.ext h1) (Fin.ext h2) + have hc := Nat.card_le_card_of_injective f hinj + rw [← filteredCoordinateSpace_finrank_eq_standard_card I (2 * B)] at hc + simpa [Nat.card_prod, Nat.card_fin, pow_two] using hc + +/-- Under a linear Hilbert bound, at most one exponent can exceed a fixed +threshold. This excludes every two-dimensional monomial quadrant. -/ +theorem standard_at_most_one_large {K σ : Type*} [Field K] [Finite σ] + [LinearOrder σ] (I : Ideal (MvPolynomial σ K)) (C : ℕ) + (hbound : ∀ N ≥ 14, Module.finrank K (filteredCoordinateSpace I N) ≤ C * (N + 2)) + {a : σ →₀ ℕ} (ha : a ∈ Ideal.standardExponents I MonomialOrder.degLex) + {i j : σ} (hi : 2 * C + 14 ≤ a i) (hj : 2 * C + 14 ≤ a j) : i = j := by + classical + let := Fintype.ofFinite σ + by_contra hij + have hs := standard_square_le_filtered I (2 * C + 14) ha hij hi hj + have hu := hbound (2 * (2 * C + 14)) (by omega) + nlinarith + +/-- A coordinate ray whose other coordinates lie in a finite box. -/ +def exponentRay {σ : Type*} [Fintype σ] (B : ℕ) + (j : σ) (b : σ → Fin B) (n : ℕ) : σ →₀ ℕ := + (Finsupp.equivFunOnFinite.symm (fun k => (b k).val)).update j n + +/-- The ray exponent `exponentRay B j b n` has coordinate `n` at `j` and coordinate `b k` at +every other `k`. -/ +@[simp] lemma exponentRay_apply {σ : Type*} [Fintype σ] [DecidableEq σ] + (B : ℕ) (j : σ) (b : σ → Fin B) (n : ℕ) (k : σ) : + exponentRay B j b n k = if k = j then n else (b k).val := by + simp [exponentRay, Finsupp.update_apply, eq_comm] + +/-- Uniformly over all rays based in a finite box, membership in a lower set +stabilizes to either the whole ray or the empty tail. -/ +theorem lowerSet_rays_eventually_full {σ : Type*} [Fintype σ] + (S : Set (σ →₀ ℕ)) (hS : IsLowerSet S) (B : ℕ) : + ∃ M ≥ B, ∀ (j : σ) (b : σ → Fin B) (n : ℕ), M ≤ n → + (exponentRay B j b n ∈ S ↔ ∀ k, exponentRay B j b k ∈ S) := by + classical + have hex (p : σ × (σ → Fin B)) : + ∃ m, (∀ k, exponentRay B p.1 p.2 k ∈ S) ∨ exponentRay B p.1 p.2 m ∉ S := by + by_cases h : ∀ k, exponentRay B p.1 p.2 k ∈ S + · exact ⟨0, Or.inl h⟩ + · push Not at h + obtain ⟨m, hm⟩ := h + exact ⟨m, Or.inr hm⟩ + choose m hm using hex + refine ⟨Finset.univ.sup m + B, by omega, ?_⟩ + intro j b n hn + constructor + · intro hmem + rcases hm (j, b) with hall | hnot + · exact hall + · exfalso + apply hnot + apply hS _ hmem + intro k + simp only [exponentRay_apply] + split_ifs + · have hle := Finset.le_sup (f := m) (Finset.mem_univ (j, b)) + omega + · exact le_rfl + · exact fun h => h n + +end +end CCDN diff --git a/LeanPool/CCDN/Algebra/OriginShift.lean b/LeanPool/CCDN/Algebra/OriginShift.lean new file mode 100644 index 0000000000..fe8c2598d2 --- /dev/null +++ b/LeanPool/CCDN/Algebra/OriginShift.lean @@ -0,0 +1,118 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import Mathlib.Algebra.MvPolynomial.PDeriv + +/-! Translation of multivariate polynomials by a point: `originShift a` substitutes +`X j + a j` for every variable, moving the point `a` to the origin. `originShiftEquiv a` is +the same substitution as an algebra automorphism, with inverse the shift by `-a`. -/ + +@[expose] public section +namespace CCDN +noncomputable section +open MvPolynomial + +/-- The substitution `X j ↦ X j + a j`, which moves the point `a` to the origin. -/ +def originShift {R σ : Type*} [CommRing R] (a : σ → R) : + MvPolynomial σ R →+* MvPolynomial σ R := eval₂Hom C (fun j => X j + C (a j)) + +/-- The origin shift fixes constant polynomials. -/ +@[simp] lemma originShift_C {R σ : Type*} [CommRing R] (a : σ → R) (c : R) : + originShift a (C c) = C c := by + simp [originShift] + +/-- The origin shift by `a` sends the variable `X j` to `X j + a j`. -/ +@[simp] lemma originShift_X {R σ : Type*} [CommRing R] (a : σ → R) (j : σ) : + originShift a (X j) = X j + C (a j) := by + simp [originShift] + +private lemma originShift_neg {R σ : Type*} [CommRing R] (a : σ → R) (f : MvPolynomial σ R) : + originShift (-a) (originShift a f) = f := by + induction f using MvPolynomial.induction_on with + | C c => simp + | add f g hf hg => simp only [map_add, hf, hg] + | mul_X f j hf => simp [hf] + +/-- The substitution `X j ↦ X j + a j` as an algebra automorphism, with inverse the +substitution `X j ↦ X j - a j`. -/ +def originShiftEquiv {R σ : Type*} [CommRing R] (a : σ → R) : + MvPolynomial σ R ≃ₐ[R] MvPolynomial σ R := + AlgEquiv.ofAlgHom (aeval fun j => X j + C (a j)) (aeval fun j => X j + C ((-a) j)) + (by apply MvPolynomial.algHom_ext; intro j; simp) + (by apply MvPolynomial.algHom_ext; intro j; simp) + +/-- The automorphism `originShiftEquiv a` acts as the origin shift `originShift a`. -/ +@[simp] lemma originShiftEquiv_apply {R σ : Type*} [CommRing R] (a : σ → R) + (f : MvPolynomial σ R) : originShiftEquiv a f = originShift a f := + rfl + +/-- The inverse of `originShiftEquiv a` acts as the origin shift by `-a`. -/ +@[simp] lemma originShiftEquiv_symm_apply {R σ : Type*} [CommRing R] (a : σ → R) + (f : MvPolynomial σ R) : (originShiftEquiv a).symm f = originShift (-a) f := + rfl + +/-- The origin shift is injective. -/ +lemma originShift_injective {R σ : Type*} [CommRing R] (a : σ → R) : + Function.Injective (originShift a) := + Function.LeftInverse.injective (originShift_neg a) + +/-- The origin shift of a nonzero polynomial is nonzero. -/ +lemma originShift_ne_zero {R σ : Type*} [CommRing R] (a : σ → R) + {f : MvPolynomial σ R} (hf : f ≠ 0) : originShift a f ≠ 0 := + (map_ne_zero_iff _ (originShift_injective a)).2 hf + +/-- The origin shift commutes with coefficient maps: mapping the coefficients of the shift of `f` +by `a` along `φ` gives the shift of the mapped polynomial by the mapped point `φ ∘ a`. -/ +lemma originShift_map {R S σ : Type*} [CommRing R] [CommRing S] + (φ : R →+* S) (a : σ → R) (f : MvPolynomial σ R) : + map φ (originShift a f) = originShift (fun j => φ (a j)) (map φ f) := by + induction f using MvPolynomial.induction_on with + | C c => simp + | add f g hf hg => simp only [map_add, hf, hg] + | mul_X f j hf => simp [hf] + +private lemma eval_originShift {R σ : Type*} [CommRing R] (a x : σ → R) + (f : MvPolynomial σ R) : + eval x (originShift a f) = eval (fun j => x j + a j) f := by + induction f using MvPolynomial.induction_on with + | C c => simp + | add f g hf hg => simp only [map_add, hf, hg] + | mul_X f j hf => simp [hf] + +/-- Evaluating the origin shift of `f` by `a` at `x - a` gives the value of `f` at `x`. -/ +lemma eval_sub_originShift {R σ : Type*} [CommRing R] (a x : σ → R) + (f : MvPolynomial σ R) : + eval (fun j => x j - a j) (originShift a f) = eval x f := by + rw [eval_originShift] + simp only [sub_add_cancel] + +/-- The origin shift of `f` by `a` takes at the origin the value of `f` at `a`. -/ +lemma eval_zero_originShift {R σ : Type*} [CommRing R] (a : σ → R) + (f : MvPolynomial σ R) : eval (0 : σ → R) (originShift a f) = eval a f := by + rw [eval_originShift] + simp only [Pi.zero_apply, zero_add] + +/-- The origin shift commutes with partial derivatives. -/ +lemma pderiv_originShift {R σ : Type*} [CommRing R] (a : σ → R) + (f : MvPolynomial σ R) (j : σ) : + pderiv j (originShift a f) = originShift a (pderiv j f) := by + classical + induction f using MvPolynomial.induction_on with + | C c => simp + | add f g hf hg => simp only [map_add, hf, hg] + | mul_X f k hf => + simp only [map_mul, pderiv_mul, map_add, hf] + by_cases h : k = j + · subst k + simp + · simp [pderiv_X_of_ne h] + +end +end CCDN diff --git a/LeanPool/CCDN/Algebra/ParameterPowerSpan.lean b/LeanPool/CCDN/Algebra/ParameterPowerSpan.lean new file mode 100644 index 0000000000..fd18a32fa3 --- /dev/null +++ b/LeanPool/CCDN/Algebra/ParameterPowerSpan.lean @@ -0,0 +1,88 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import Mathlib.Algebra.Polynomial.AlgebraMap +public import Mathlib.LinearAlgebra.Dimension.Finrank +public import Mathlib.RingTheory.Finiteness.Defs +import Mathlib.LinearAlgebra.Dimension.Constructions +import Mathlib.LinearAlgebra.FreeModule.StrongRankCondition + +/-! Finite power blocks for a linear parameter. Their dimension grows with +slope at most the number of field-basis vectors, giving the upper Hilbert +growth bound needed to recognize a parameter of full degree. -/ + +@[expose] public section +namespace CCDN +noncomputable section + +/-- Powers through N of a parameter, multiplied by a fixed finite family. -/ +def parameterPowerSpan (K : Type*) {F ι : Type*} [Field K] [Field F] [Algebra K F] + (u : F) (v : ι → F) (N : ℕ) : Submodule K F := + Submodule.span K (Set.range (fun t : ι × Fin (N + 1) => u ^ t.2.val * v t.1)) + +instance parameterPowerSpan_finite {K F ι : Type*} [Field K] [Field F] [Algebra K F] + [Finite ι] (u : F) (v : ι → F) (N : ℕ) : Module.Finite K (parameterPowerSpan K u v N) := + Module.Finite.span_of_finite K (Set.finite_range _) + +/-- The span of the products `u ^ j * v i` with `j ≤ N` has dimension at most +`Fintype.card ι * (N + 1)`. -/ +lemma parameterPowerSpan_finrank_le {K F ι : Type*} [Field K] [Field F] [Algebra K F] + [Fintype ι] (u : F) (v : ι → F) (N : ℕ) : + Module.finrank K (parameterPowerSpan K u v N) ≤ Fintype.card ι * (N + 1) := by + unfold parameterPowerSpan + have h := finrank_range_le_card (R := K) (fun t : ι × Fin (N + 1) => u ^ t.2.val * v t.1) + change Module.finrank K (Submodule.span K (Set.range + (fun t : ι × Fin (N + 1) => u ^ t.2.val * v t.1))) ≤ Fintype.card (ι × Fin (N + 1)) at h + rw [Fintype.card_prod, Fintype.card_fin] at h + exact h + +/-- The parameter power spans increase with the power bound. -/ +lemma parameterPowerSpan_mono {K F ι : Type*} [Field K] [Field F] [Algebra K F] + (u : F) (v : ι → F) {M N : ℕ} (hMN : M ≤ N) : + parameterPowerSpan K u v M ≤ parameterPowerSpan K u v N := by + apply Submodule.span_le.mpr + rintro _ ⟨⟨i,j⟩, rfl⟩ + exact Submodule.subset_span ⟨(i, ⟨j.val, by omega⟩), rfl⟩ + +/-- Multiplication by the parameter `u` maps the span of powers up to `N` into the span of +powers up to `N + 1`. -/ +lemma parameterPowerSpan_mul_mem {K F ι : Type*} [Field K] [Field F] [Algebra K F] + (u : F) (v : ι → F) (N : ℕ) {x : F} (hx : x ∈ parameterPowerSpan K u v N) : + u * x ∈ parameterPowerSpan K u v (N + 1) := by + induction hx using Submodule.span_induction with + | mem x hx => + obtain ⟨⟨i,j⟩, rfl⟩ := hx + have he : u * (u ^ j.val * v i) = u ^ (j.val + 1) * v i := by ring + rw [he] + exact Submodule.subset_span ⟨(i, ⟨j.val + 1, by omega⟩), rfl⟩ + | zero => simpa only [mul_zero] using (parameterPowerSpan K u v (N + 1)).zero_mem + | add x y hx hy hxi hyi => + simpa only [mul_add] using (parameterPowerSpan K u v (N + 1)).add_mem hxi hyi + | smul c x hx hxi => + simpa only [mul_smul_comm] using (parameterPowerSpan K u v (N + 1)).smul_mem c hxi + +/-- Polynomial parameter coefficients of degree at most N lie in the power block. -/ +lemma polynomial_parameter_mul_mem {K F ι : Type*} [Field K] [Field F] [Algebra K F] + (u : F) (v : ι → F) (i : ι) (p : Polynomial K) (N : ℕ) (hp : p.natDegree ≤ N) : + Polynomial.aeval u p * v i ∈ parameterPowerSpan K u v N := by + classical + rw [p.as_sum_support, map_sum, Finset.sum_mul] + apply (parameterPowerSpan K u v N).sum_mem + intro j hj + rw [Polynomial.aeval_monomial] + have hjN : j ≤ N := + (Polynomial.le_natDegree_of_ne_zero (Polynomial.mem_support_iff.mp hj)).trans hp + have hm : u ^ j * v i ∈ parameterPowerSpan K u v N := + Submodule.subset_span ⟨(i, ⟨j, by omega⟩), rfl⟩ + simpa only [Algebra.smul_def, mul_assoc] using + (parameterPowerSpan K u v N).smul_mem (p.coeff j) hm + +end +end CCDN diff --git a/LeanPool/CCDN/Algebra/ParameterRecurrenceGrowth.lean b/LeanPool/CCDN/Algebra/ParameterRecurrenceGrowth.lean new file mode 100644 index 0000000000..1ca58c1f86 --- /dev/null +++ b/LeanPool/CCDN/Algebra/ParameterRecurrenceGrowth.lean @@ -0,0 +1,172 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.Algebra.ParameterPowerSpan +public import Mathlib.FieldTheory.RatFunc.AsPolynomial +public import Mathlib.RingTheory.SimpleRing.Principal +import LeanPool.CCDN.Hilbert.AffineCurveDegree + +/-! +If a filtration is eventually generated one step at a time by a parameter, +its linear growth has slope at most the function-field degree over K(t). +Common denominators affect only the fixed additive offset in the bound. + +Any finite family in a finite extension of K(t) fits in a bounded power +block after multiplication by one nonzero rational-function denominator. +-/ + +@[expose] public section +namespace CCDN +noncomputable section + +private lemma ratFunc_polynomial_eval {K F : Type*} [Field K] [Field F] + [Algebra K F] [Algebra (RatFunc K) F] [IsScalarTower K (RatFunc K) F] + (p : Polynomial K) : + algebraMap (RatFunc K) F (algebraMap (Polynomial K) (RatFunc K) p) = + Polynomial.aeval (algebraMap (RatFunc K) F RatFunc.X) p := by + let φ := (IsScalarTower.toAlgHom K (RatFunc K) F).comp + (IsScalarTower.toAlgHom K (Polynomial K) (RatFunc K)) + have he : φ = Polynomial.aeval (algebraMap (RatFunc K) F RatFunc.X) := by + apply Polynomial.algHom_ext + simp [φ, RatFunc.algebraMap_X] + exact congrArg (fun f => f p) he + +/-- A common parameter denominator makes every supplied vector a bounded +polynomial combination of a fixed function-field basis. -/ +private theorem finite_family_parameter_power_span {K F ι : Type*} [Field K] [Field F] + [Algebra K F] [Algebra (RatFunc K) F] [IsScalarTower K (RatFunc K) F] + [Module.Finite (RatFunc K) F] [Finite ι] + (w : ι → F) : + ∃ q : F, q ≠ 0 ∧ ∃ D : ℕ, ∀ j, + q * w j ∈ parameterPowerSpan K (algebraMap (RatFunc K) F RatFunc.X) + (Module.finBasis (RatFunc K) F) D := by + classical + let := Fintype.ofFinite ι + let b := Module.finBasis (RatFunc K) F + let r := Module.finrank (RatFunc K) F + obtain ⟨q, hq⟩ := IsLocalization.exist_integer_multiples_of_finite + (nonZeroDivisors (Polynomial K)) (fun t : ι × Fin r => b.repr (w t.1) t.2) + choose p hp using hq + let D := Finset.univ.sup (fun t : ι × Fin r => (p t).natDegree) + let qF := algebraMap (RatFunc K) F (algebraMap (Polynomial K) (RatFunc K) q.val) + have hqn : q.val ≠ 0 := mem_nonZeroDivisors_iff_ne_zero.mp q.property + have hqF : qF ≠ 0 := (map_ne_zero_iff (algebraMap (RatFunc K) F) + (algebraMap (RatFunc K) F).injective).mpr (RatFunc.algebraMap_ne_zero hqn) + refine ⟨qF, hqF, D, ?_⟩ + intro j + rw [← b.sum_repr (w j), Finset.mul_sum] + apply (parameterPowerSpan K (algebraMap (RatFunc K) F RatFunc.X) b D).sum_mem + intro i _ + have he : qF * (b.repr (w j) i • b i) = + Polynomial.aeval (algebraMap (RatFunc K) F RatFunc.X) (p (j,i)) * b i := by + have hp' : algebraMap (Polynomial K) (RatFunc K) (p (j,i)) = + algebraMap (Polynomial K) (RatFunc K) q.val * b.repr (w j) i := + (hp (j,i)).trans (Algebra.smul_def _ _) + rw [Algebra.smul_def, ← mul_assoc] + change (algebraMap (RatFunc K) F _ * algebraMap (RatFunc K) F _) * _ = _ + rw [← map_mul, ← hp', ratFunc_polynomial_eval] + rw [he] + exact polynomial_parameter_mul_mem _ b i (p (j,i)) D + (Finset.le_sup (f := fun t : ι × Fin r => (p t).natDegree) (Finset.mem_univ (j,i))) + +end +end CCDN + +namespace CCDN +noncomputable section + +/-- Eventual generation by the parameter bounds filtered dimensions with +slope equal to the rank over its rational-function field. -/ +private theorem parameter_recurrence_growth_bound {K A F : Type*} [Field K] + [AddCommGroup A] [Module K A] [Field F] [Algebra K F] + [Algebra (RatFunc K) F] [IsScalarTower K (RatFunc K) F] + [Module.Finite (RatFunc K) F] + (V : ℕ → Submodule K A) [∀ n, Module.Finite K (V n)] + (L : A →ₗ[K] A) (φ : A →ₗ[K] F) (hφ : Function.Injective φ) + (hL : ∀ x, φ (L x) = algebraMap (RatFunc K) F RatFunc.X * φ x) + (N : ℕ) (hrec : ∀ n ≥ N, V (n + 1) = V n ⊔ (V n).map L) : + ∃ D : ℕ, ∀ m, Module.finrank K (V (N + m)) ≤ + Module.finrank (RatFunc K) F * (D + m + 1) := by + classical + let b := Module.finBasis K (V N) + let u := algebraMap (RatFunc K) F RatFunc.X + let v := Module.finBasis (RatFunc K) F + obtain ⟨q, hq, D, hD⟩ := finite_family_parameter_power_span (K := K) (fun i => φ (b i).val) + let T := (LinearMap.mulLeft K q).comp φ + have hT (x : A) : T x = q * φ x := rfl + have hTi : Function.Injective T := fun x y h => hφ (mul_left_cancel₀ hq h) + have hTL (x : A) : T (L x) = u * T x := by rw [hT, hL, hT]; ring + have hbase {x : A} (hx : x ∈ V N) : T x ∈ parameterPowerSpan K u v D := by + have he : T x = ∑ i, b.repr ⟨x, hx⟩ i • (q * φ (b i).val) := by + have hh := congrArg (fun z : V N => T z.val) (b.sum_repr ⟨x, hx⟩) + simpa only [Submodule.coe_sum, Submodule.coe_smul, map_sum, map_smul, hT, Finset.mul_sum, + mul_smul_comm] using hh.symm + rw [he] + apply (parameterPowerSpan K u v D).sum_mem + intro i _ + exact (parameterPowerSpan K u v D).smul_mem _ (hD i) + have hbound (m : ℕ) : ∀ x ∈ V (N + m), T x ∈ parameterPowerSpan K u v (D + m) := by + induction m with + | zero => + intro x hx + exact hbase hx + | succ m ih => + intro x hx + rw [show N + (m + 1) = (N + m) + 1 by omega, hrec (N + m) (by omega)] at hx + obtain ⟨y, hy, z, hz, rfl⟩ := Submodule.mem_sup.mp hx + obtain ⟨z, hz, rfl⟩ := hz + rw [map_add, hTL] + apply (parameterPowerSpan K u v (D + (m + 1))).add_mem + · exact parameterPowerSpan_mono u v (by omega) (ih y hy) + · simpa only [Nat.add_assoc] using parameterPowerSpan_mul_mem u v (D + m) (ih z hz) + refine ⟨D, ?_⟩ + intro m + let Tm : V (N + m) →ₗ[K] parameterPowerSpan K u v (D + m) := + (T.comp (V (N + m)).subtype).codRestrict _ (fun x => hbound m x.val x.property) + have hi : Function.Injective Tm := by + intro x y h + apply Subtype.ext + apply hTi + exact congrArg Subtype.val h + have hd := LinearMap.finrank_le_finrank_of_injective hi + exact hd.trans (by simpa only [Fintype.card_fin, Nat.add_assoc] using + parameterPowerSpan_finrank_le u v (D + m)) + +/-- The Hilbert slope is at most the parameter extension degree whenever the +coordinate filtration has the parameter-generation recurrence. -/ +theorem parameter_recurrence_slope_le {K A F : Type*} [Field K] + [AddCommGroup A] [Module K A] [Field F] [Algebra K F] + [Algebra (RatFunc K) F] [IsScalarTower K (RatFunc K) F] + [Module.Finite (RatFunc K) F] + (V : ℕ → Submodule K A) [∀ n, Module.Finite K (V n)] + (L : A →ₗ[K] A) (φ : A →ₗ[K] F) (hφ : Function.Injective φ) + (hL : ∀ x, φ (L x) = algebraMap (RatFunc K) F RatFunc.X * φ x) + (N : ℕ) (hrec : ∀ n ≥ N, V (n + 1) = V n ⊔ (V n).map L) + (d : ℕ) (hdegree : ∃ M : ℕ, ∃ c : ℤ, ∀ n ≥ M, + (Module.finrank K (V n) : ℤ) = (d : ℤ) * n + c) : + d ≤ Module.finrank (RatFunc K) F := by + obtain ⟨D, hD⟩ := parameter_recurrence_growth_bound V L φ hφ hL N hrec + obtain ⟨M, c, hc⟩ := hdegree + apply eventual_linear_slope_le + (fun m => Module.finrank K (V (N + m))) + (fun m => Module.finrank (RatFunc K) F * (D + m + 1)) d (Module.finrank (RatFunc K) F) + · refine ⟨M, (d : ℤ) * N + c, ?_⟩ + intro m hm + rw [hc (N + m) (by omega)] + push_cast + ring + · refine ⟨0, (Module.finrank (RatFunc K) F : ℤ) * (D + 1), ?_⟩ + intro m _ + push_cast + ring + · exact hD + +end +end CCDN diff --git a/LeanPool/CCDN/Algebra/PointSeparation.lean b/LeanPool/CCDN/Algebra/PointSeparation.lean new file mode 100644 index 0000000000..0966df6360 --- /dev/null +++ b/LeanPool/CCDN/Algebra/PointSeparation.lean @@ -0,0 +1,127 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import Mathlib.Algebra.Algebra.Pi +public import Mathlib.Algebra.MvPolynomial.Eval +public import Mathlib.LinearAlgebra.LinearIndependent.Defs +import Mathlib.Algebra.BigOperators.Fin +import Mathlib.Algebra.MvPolynomial.CommRing +import Mathlib.Tactic.Ring + +/-! +# Point separation for polynomial sections + +Powers of any nonzero element vanishing at the selected points give +independent classes. +-/ + +@[expose] public section +namespace CCDN +noncomputable section + +/-- Powers of a nonzero element killed by an algebra homomorphism, times elements with +independent images, form an independent family. The target algebra may be trivial. -/ +lemma section_powers_independent {K A B ι : Type*} [Field K] + [CommRing A] [IsDomain A] [Algebra K A] [CommRing B] [Algebra K B] + [Finite ι] (φ : A →ₐ[K] B) (h : ι → A) + (hlin : LinearIndependent K (fun i => φ (h i))) (l : A) (hl : l ≠ 0) + (hzero : φ l = 0) (N : ℕ) : + LinearIndependent K (fun t : ι × Fin N => h t.1 * l ^ t.2.val) := by + classical + let := Fintype.ofFinite ι + induction N with + | zero => exact linearIndependent_empty_type + | succ N ih => + rw [Fintype.linearIndependent_iff] + intro a ha t + have he := congrArg φ ha + have he' : ∑ i, a (i, 0) • φ (h i) = 0 := by + simpa [Fintype.sum_prod_type, Fin.sum_univ_succ, hzero] using he + have hconst : ∀ i, a (i, 0) = 0 := Fintype.linearIndependent_iff.mp hlin _ he' + have htail : ∑ t : ι × Fin N, a (t.1, t.2.succ) • (h t.1 * l ^ t.2.val) = 0 := by + apply mul_left_cancel₀ hl + rw [mul_zero, Finset.mul_sum] + have hsum : (∑ t : ι × Fin (N + 1), a t • (h t.1 * l ^ t.2.val)) = + ∑ t : ι × Fin N, l * (a (t.1, t.2.succ) • (h t.1 * l ^ t.2.val)) := by + simp only [Fintype.sum_prod_type, Fin.sum_univ_succ, hconst, zero_smul, zero_add] + apply Finset.sum_congr rfl + intro i _ + apply Finset.sum_congr rfl + intro k _ + simp only [Fin.val_succ, pow_succ, Algebra.smul_def] + ring + rw [← hsum] + exact ha + have hc := Fintype.linearIndependent_iff.mp ih (fun t => a (t.1, t.2.succ)) htail + obtain ⟨i, k⟩ := t + exact Fin.cases (hconst i) (fun k => hc (i, k)) k + +/-- Elements separated by algebra homomorphisms have independent simultaneous images. -/ +lemma separator_images_independent {K A ι : Type*} [Field K] [CommRing A] [Algebra K A] + [Finite ι] [DecidableEq ι] (φ : ι → A →ₐ[K] K) (h : ι → A) + (hsep : ∀ i j, φ i (h j) = if i = j then 1 else 0) : + LinearIndependent K (fun j => AlgHom.pi φ (h j)) := by + classical + let := Fintype.ofFinite ι + rw [Fintype.linearIndependent_iff] + intro c hc i + have he := congrFun hc i + simpa [AlgHom.pi_apply, Finset.sum_apply, Pi.smul_apply, hsep, eq_comm] using he + +/-- Powers of a nonzero element times point separators are jointly independent +when that element vanishes under all point evaluations. -/ +theorem separator_powers_independent {K A ι : Type*} [Field K] [CommRing A] + [IsDomain A] [Algebra K A] [Finite ι] [DecidableEq ι] + (φ : ι → A →ₐ[K] K) (h : ι → A) (l : A) (hl : l ≠ 0) + (hzero : ∀ i, φ i l = 0) + (hsep : ∀ i j, φ i (h j) = if i = j then 1 else 0) (N : ℕ) : + LinearIndependent K (fun t : ι × Fin N => h t.1 * l ^ t.2.val) := by + apply section_powers_independent (AlgHom.pi φ) h + (separator_images_independent φ h hsep) l hl + · ext i + exact hzero i + +/-- Explicit polynomial interpolation at a finite family of distinct affine points. -/ +theorem exists_point_separators {K σ ι : Type*} [Field K] [Finite ι] [DecidableEq ι] + (z : ι → σ → K) (hz : Function.Injective z) : + ∃ h : ι → MvPolynomial σ K, ∀ i j, + MvPolynomial.eval (z i) (h j) = if i = j then 1 else 0 := by + classical + let := Fintype.ofFinite ι + have hex (i : ι) : ∃ h : MvPolynomial σ K, + MvPolynomial.eval (z i) h = 1 ∧ + ∀ j, j ≠ i → MvPolynomial.eval (z j) h = 0 := by + have hcoords (j : {j : ι // j ≠ i}) : ∃ k, z i k ≠ z j.val k := + Function.ne_iff.mp (fun he => j.property (hz he).symm) + choose k hk using hcoords + let factor (j : {j : ι // j ≠ i}) : MvPolynomial σ K := + MvPolynomial.C ((z i (k j) - z j.val (k j))⁻¹) * + (MvPolynomial.X (k j) - MvPolynomial.C (z j.val (k j))) + refine ⟨∏ j, factor j, ?_, ?_⟩ + · rw [MvPolynomial.eval_prod] + apply Finset.prod_eq_one + intro j _ + simp only [factor, MvPolynomial.eval_mul, MvPolynomial.eval_C, + MvPolynomial.eval_sub, MvPolynomial.eval_X] + exact inv_mul_cancel₀ (sub_ne_zero.mpr (hk j)) + · intro j hji + rw [MvPolynomial.eval_prod] + apply Finset.prod_eq_zero (Finset.mem_univ (⟨j, hji⟩ : {j : ι // j ≠ i})) + simp [factor] + choose h hon hoff using hex + refine ⟨h, ?_⟩ + intro i j + by_cases he : i = j + · subst j + simpa using hon i + · simpa [he] using hoff j i he + +end +end CCDN diff --git a/LeanPool/CCDN/Algebra/PolynomialFamilyRelations.lean b/LeanPool/CCDN/Algebra/PolynomialFamilyRelations.lean new file mode 100644 index 0000000000..ac835f48d0 --- /dev/null +++ b/LeanPool/CCDN/Algebra/PolynomialFamilyRelations.lean @@ -0,0 +1,164 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import Mathlib.Algebra.Polynomial.Eval.Defs +public import Mathlib.LinearAlgebra.Dimension.Finrank +public import Mathlib.RingTheory.Finiteness.Defs +import LeanPool.CCDN.Algebra.PolynomialKernelOrigin +import Mathlib.LinearAlgebra.FreeModule.PID + +/-! +Relations among a finite family of algebra-valued polynomials can be +encoded by a finite scalar polynomial matrix. Persistent dependence therefore +gives a polynomial relation nonzero at the origin, even when the target +algebra is infinite-dimensional over the scalar field. + +Apply a linear functional to the coefficients of a univariate polynomial. +This commutes with evaluation over the scalar field and multiplication by +scalar polynomials, allowing polynomial relations to be encoded by matrices. +-/ + +@[expose] public section +namespace CCDN +noncomputable section +open Polynomial + +private def coefficientLinearUnivariate {K A : Type*} [Field K] [CommRing A] [Algebra K A] + (φ : A →ₗ[K] K) (p : Polynomial A) : Polynomial K := + ∑ n ∈ p.support, monomial n (φ (p.coeff n)) + +private lemma coefficientLinearUnivariate_coeff {K A : Type*} [Field K] [CommRing A] [Algebra K A] + (φ : A →ₗ[K] K) (p : Polynomial A) (n : ℕ) : + (coefficientLinearUnivariate φ p).coeff n = φ (p.coeff n) := by + classical + by_cases hn : n ∈ p.support + · simp [coefficientLinearUnivariate, coeff_monomial, hn] + · have hz := notMem_support_iff.mp hn + simp [coefficientLinearUnivariate, coeff_monomial, hn, hz] + +/-- Coefficientwise application of a linear functional. -/ +private def coefficientLinearUnivariateMap {K A : Type*} [Field K] [CommRing A] [Algebra K A] + (φ : A →ₗ[K] K) : Polynomial A →ₗ[K] Polynomial K where + toFun := coefficientLinearUnivariate φ + map_add' p q := by ext n; simp [coefficientLinearUnivariate_coeff] + map_smul' c p := by ext n; simp [coefficientLinearUnivariate_coeff] + +private lemma coefficientLinearUnivariate_scalar_mul {K A : Type*} [Field K] [CommRing A] + [Algebra K A] (φ : A →ₗ[K] K) (p : Polynomial K) (q : Polynomial A) : + coefficientLinearUnivariate φ (p.map (algebraMap K A) * q) = + p * coefficientLinearUnivariate φ q := by + ext n + rw [coefficientLinearUnivariate_coeff, coeff_mul, coeff_mul, map_sum] + apply Finset.sum_congr rfl + intro t _ + rw [coeff_map, coefficientLinearUnivariate_coeff] + simpa [Algebra.smul_def] using φ.map_smul (p.coeff t.1) (q.coeff t.2) + +private lemma coefficientLinearUnivariate_eval {K A : Type*} [Field K] [CommRing A] [Algebra K A] + (φ : A →ₗ[K] K) (p : Polynomial A) (t : K) : + (coefficientLinearUnivariate φ p).eval t = φ (p.eval (algebraMap K A t)) := by + classical + rw [coefficientLinearUnivariate, Polynomial.eval_finsetSum] + conv_rhs => rw [p.as_sum_support, Polynomial.eval_finsetSum, map_sum] + apply Finset.sum_congr rfl + intro n _ + rw [eval_monomial, eval_monomial, ← map_pow] + have hh := φ.map_smul (t ^ n) (p.coeff n) + simpa [Algebra.smul_def, mul_comm] using hh.symm + +end +end CCDN + +namespace CCDN +noncomputable section +open Polynomial Matrix + +private lemma exists_finite_detecting_functionals {K V : Type*} [Field K] + [AddCommGroup V] [Module K V] (S : Submodule K V) [Module.Finite K S] : + ∃ φ : Fin (Module.finrank K S) → V →ₗ[K] K, + ∀ x ∈ S, (∀ i, φ i x = 0) → x = 0 := by + obtain ⟨T, hT⟩ := S.subtype.exists_leftInverse_of_injective S.ker_subtype + let b := Module.finBasis K S + refine ⟨fun i => (b.coord i).comp T, ?_⟩ + intro x hx hzero + have hTx : T x = (⟨x, hx⟩ : S) := congrArg (fun f : S →ₗ[K] S => f ⟨x, hx⟩) hT + have ht : T x = 0 := by + apply b.repr.injective + ext i + simpa only [map_zero, Finsupp.zero_apply, LinearMap.comp_apply, + Module.Basis.coord_apply] using hzero i + have hz : (⟨x, hx⟩ : S) = 0 := hTx.symm.trans ht + exact congrArg Subtype.val hz + +/-- If the evaluations of a polynomial family are dependent over K at every +integer parameter, the family has a polynomial relation whose coefficient +vector does not vanish at zero. -/ +theorem polynomial_family_relation_nonzero_at_zero {K A κ : Type*} + [Field K] [CharZero K] [CommRing A] [Algebra K A] [Fintype κ] + (g : κ → Polynomial A) + (hdep : ∀ k : ℕ, ∃ c : κ → K, c ≠ 0 ∧ ∑ j, c j • (g j).eval (k : A) = 0) : + ∃ v : κ → Polynomial K, + (∑ j, (v j).map (algebraMap K A) * g j) = 0 ∧ ∃ j, (v j).eval 0 ≠ 0 := by + classical + let s := Finset.univ.biUnion (fun j => (g j).support.image (fun n => (g j).coeff n)) + let S := Submodule.span K (s : Set A) + let : Module.Finite K S := Module.Finite.span_of_finite K s.finite_toSet + have hmem (j : κ) (n : ℕ) : (g j).coeff n ∈ S := by + by_cases hn : n ∈ (g j).support + · apply Submodule.subset_span + exact Finset.mem_biUnion.mpr ⟨j, Finset.mem_univ j, Finset.mem_image.mpr ⟨n, hn, rfl⟩⟩ + · rw [Polynomial.notMem_support_iff.mp hn] + exact S.zero_mem + obtain ⟨φ, hφ⟩ := exists_finite_detecting_functionals S + let M : Matrix (Fin (Module.finrank K S)) κ (Polynomial K) := + fun i j => coefficientLinearUnivariate (φ i) (g j) + have hM (k : ℕ) : ¬ Function.Injective (M.map (Polynomial.evalRingHom (k : K))).mulVecLin := by + intro hi + obtain ⟨c, hc, hrel⟩ := hdep k + apply hc + apply hi + rw [map_zero] + funext i + change ∑ j, (coefficientLinearUnivariate (φ i) (g j)).eval (k : K) * c j = 0 + simp only [coefficientLinearUnivariate_eval, map_natCast] + calc + _ = φ i (∑ j, c j • (g j).eval (k : A)) := by + rw [map_sum] + apply Finset.sum_congr rfl + intro j _ + simp only [map_smul, smul_eq_mul, mul_comm] + _ = 0 := by rw [hrel, map_zero] + obtain ⟨v, hv, hv0⟩ := polynomial_matrix_kernel_nonzero_at_zero M hM + refine ⟨v, ?_, hv0⟩ + let P := ∑ j, (v j).map (algebraMap K A) * g j + have hcoeff (n : ℕ) : P.coeff n ∈ S := by + simp only [P, Polynomial.finsetSum_coeff, coeff_mul, coeff_map] + apply S.sum_mem + intro j _ + apply S.sum_mem + intro t _ + simpa only [Algebra.smul_def] using S.smul_mem ((v j).coeff t.1) (hmem j t.2) + have hfun (i : Fin (Module.finrank K S)) : coefficientLinearUnivariate (φ i) P = 0 := by + change coefficientLinearUnivariateMap (φ i) (∑ j, (v j).map (algebraMap K A) * g j) = 0 + rw [map_sum] + change ∑ j, coefficientLinearUnivariate (φ i) ((v j).map (algebraMap K A) * g j) = 0 + simp only [coefficientLinearUnivariate_scalar_mul] + have hh := congrFun hv i + change ∑ j, coefficientLinearUnivariate (φ i) (g j) * v j = 0 at hh + simpa only [mul_comm] using hh + change P = 0 + ext n + apply hφ _ (hcoeff n) + intro i + have hh := congrArg (fun p : Polynomial K => p.coeff n) (hfun i) + simpa only [coefficientLinearUnivariate_coeff, coeff_zero] using hh + +end +end CCDN diff --git a/LeanPool/CCDN/Algebra/PolynomialKernelOrigin.lean b/LeanPool/CCDN/Algebra/PolynomialKernelOrigin.lean new file mode 100644 index 0000000000..c037c58dff --- /dev/null +++ b/LeanPool/CCDN/Algebra/PolynomialKernelOrigin.lean @@ -0,0 +1,128 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import Mathlib.FieldTheory.RatFunc.Basic +public import Mathlib.RingTheory.SimpleRing.Principal +import LeanPool.CCDN.Algebra.PolynomialMatrixSpecialization + +/-! A polynomial matrix with dependent columns at every integer value has a +polynomial kernel vector that remains nonzero at zero. A common power of X is +removed from a kernel vector after its rational denominators are cleared. -/ + +@[expose] public section +namespace CCDN +noncomputable section +open Polynomial Matrix + +/-- Remove the common order of vanishing of a nonzero polynomial vector. -/ +private lemma polynomial_vector_remove_common_X {K ι : Type*} [Field K] + (w : ι → Polynomial K) (hw : w ≠ 0) : + ∃ N : ℕ, ∃ v : ι → Polynomial K, + (∀ i, w i = X ^ N * v i) ∧ ∃ i, (v i).eval 0 ≠ 0 := by + classical + have hex : ∃ N : ℕ, ∃ i, (w i).coeff N ≠ 0 := by + by_contra! h + apply hw + funext i + ext N + exact h N i + let N := Nat.find hex + have hdiv (i : ι) : X ^ N ∣ w i := by + apply Polynomial.X_pow_dvd_iff.mpr + intro m hm + by_contra hn + exact Nat.find_min hex hm ⟨i, hn⟩ + choose v hv using hdiv + refine ⟨N, v, hv, ?_⟩ + obtain ⟨i, hi⟩ := Nat.find_spec hex + refine ⟨i, ?_⟩ + have hh : (w i).coeff N = (v i).coeff 0 := by + rw [hv i] + simpa only [zero_add] using Polynomial.coeff_X_pow_mul (v i) N 0 + rw [← Polynomial.coeff_zero_eq_eval_zero] + exact hh ▸ hi + +/-- Rational-function dependence of columns gives a nonzero polynomial +kernel vector by clearing one common denominator. -/ +private theorem polynomial_matrix_kernel_of_not_injective {K ι κ : Type*} [Field K] + [Finite ι] [Fintype κ] + (A : Matrix ι κ (Polynomial K)) + (hA : ¬ Function.Injective (A.map (algebraMap (Polynomial K) (RatFunc K))).mulVecLin) : + ∃ w : κ → Polynomial K, w ≠ 0 ∧ A *ᵥ w = 0 := by + classical + let := Fintype.ofFinite ι + let f := algebraMap (Polynomial K) (RatFunc K) + let M := A.map f + obtain ⟨x, y, hxy, hne⟩ := Function.not_injective_iff.mp hA + let v := x - y + have hv : v ≠ 0 := sub_ne_zero.mpr hne + have hMv : M *ᵥ v = 0 := by + change M.mulVecLin (x - y) = 0 + rw [map_sub, hxy, sub_self] + obtain ⟨q, C, hq, hC⟩ := ratFunc_matrix_common_denominator + (fun i (_ : Unit) => v i) + let w := fun i => C i () + have hw (i : κ) : f (w i) = f q * v i := hC i () + have hqf : f q ≠ 0 := RatFunc.algebraMap_ne_zero hq + refine ⟨w, ?_, ?_⟩ + · intro hw0 + apply hv + funext i + have hh := hw i + rw [hw0, Pi.zero_apply, map_zero] at hh + exact (mul_eq_zero.mp hh.symm).resolve_left hqf + · funext i + apply RatFunc.algebraMap_injective K + change f (∑ j, A i j * w j) = f 0 + rw [map_sum, map_zero] + simp only [map_mul, hw] + calc + ∑ j, f (A i j) * (f q * v j) = f q * ∑ j, f (A i j) * v j := by + rw [Finset.mul_sum] + apply Finset.sum_congr rfl + intro j _ + ring + _ = 0 := by + have hh := congrFun hMv i + change ∑ j, f (A i j) * v j = 0 at hh + rw [hh, mul_zero] + +/-- If every integer specialization has dependent columns, there is a +polynomial relation whose constant coefficient vector is nonzero. -/ +theorem polynomial_matrix_kernel_nonzero_at_zero {K ι κ : Type*} [Field K] [CharZero K] + [Finite ι] [Fintype κ] + (A : Matrix ι κ (Polynomial K)) + (hA : ∀ k : ℕ, ¬ Function.Injective (A.map (Polynomial.evalRingHom (k : K))).mulVecLin) : + ∃ v : κ → Polynomial K, A *ᵥ v = 0 ∧ ∃ j, (v j).eval 0 ≠ 0 := by + classical + let := Fintype.ofFinite ι + have hgen : ¬ Function.Injective + (A.map (algebraMap (Polynomial K) (RatFunc K))).mulVecLin := by + intro hi + obtain ⟨k, hk⟩ := polynomial_matrix_injective_specialization A hi + exact hA k hk + obtain ⟨w, hw, hAw⟩ := polynomial_matrix_kernel_of_not_injective A hgen + obtain ⟨N, v, hv, hv0⟩ := polynomial_vector_remove_common_X w hw + refine ⟨v, ?_, hv0⟩ + funext i + apply mul_left_cancel₀ (pow_ne_zero N (Polynomial.X_ne_zero : (X : Polynomial K) ≠ 0)) + simp only [Pi.zero_apply, mul_zero] + change X ^ N * (∑ j, A i j * v j) = 0 + rw [Finset.mul_sum] + have hh := congrFun hAw i + change ∑ j, A i j * w j = 0 at hh + rw [← hh] + apply Finset.sum_congr rfl + intro j _ + rw [hv j] + ring + +end +end CCDN diff --git a/LeanPool/CCDN/Algebra/PolynomialLinearDescent.lean b/LeanPool/CCDN/Algebra/PolynomialLinearDescent.lean new file mode 100644 index 0000000000..5747ffda5d --- /dev/null +++ b/LeanPool/CCDN/Algebra/PolynomialLinearDescent.lean @@ -0,0 +1,155 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import Mathlib.Algebra.MvPolynomial.Degrees +public import Mathlib.RingTheory.Ideal.Maps +import Mathlib.LinearAlgebra.Dual.Lemmas + +/-! Coefficientwise application of a linear map to multivariate polynomials, and +linear coefficient descent of a polynomial over a field extension: a nonzero +polynomial vanishing at base-field points has a nonzero base-field polynomial of no +larger degree vanishing at every one of those points. -/ + +@[expose] public section +namespace CCDN +noncomputable section +open MvPolynomial Module + +/-- Apply a linear coefficient map without changing any monomial. -/ +def polynomialCoefficientLinear {R A B σ : Type*} [CommSemiring R] + [CommRing A] [CommRing B] [Algebra R A] [Algebra R B] + (φ : A →ₗ[R] B) : MvPolynomial σ A →ₗ[R] MvPolynomial σ B := + (AddMonoidAlgebra.coeffLinearEquiv R).symm.toLinearMap ∘ₗ + Finsupp.mapRange.linearMap φ ∘ₗ (AddMonoidAlgebra.coeffLinearEquiv R).toLinearMap + +/-- Each coefficient of `polynomialCoefficientLinear φ f` is the image under `φ` of the +corresponding coefficient of `f`. -/ +@[simp] lemma polynomialCoefficientLinear_coeff {R A B σ : Type*} + [CommSemiring R] [CommRing A] [CommRing B] [Algebra R A] [Algebra R B] + (φ : A →ₗ[R] B) (f : MvPolynomial σ A) (e : σ →₀ ℕ) : + (polynomialCoefficientLinear φ f).coeff e = φ (f.coeff e) := rfl + +/-- Applying the linear map `φ` coefficientwise sends the constant `C a` to `C (φ a)`. -/ +@[simp] lemma polynomialCoefficientLinear_C {R A B σ : Type*} + [CommSemiring R] [CommRing A] [CommRing B] [Algebra R A] [Algebra R B] + (φ : A →ₗ[R] B) (a : A) : + polynomialCoefficientLinear (σ := σ) φ (C a) = C (φ a) := by + classical + ext e + simp only [polynomialCoefficientLinear_coeff, coeff_C] + split_ifs <;> simp + +/-- If `φ (a * θ b) = φ a * b` for a ring map `θ : B →+* A`, then applying `φ` coefficientwise +is linear over polynomials with coefficients in `B`: it sends `f * map θ g` to +`polynomialCoefficientLinear φ f * g`. -/ +lemma polynomialCoefficientLinear_mul_compatible {R A B σ : Type*} + [CommSemiring R] [CommRing A] [CommRing B] [Algebra R A] [Algebra R B] + (φ : A →ₗ[R] B) (θ : B →+* A) + (hφ : ∀ a b, φ (a * θ b) = φ a * b) + (f : MvPolynomial σ A) (g : MvPolynomial σ B) : + polynomialCoefficientLinear φ (f * map θ g) = + polynomialCoefficientLinear φ f * g := by + classical + ext e + rw [polynomialCoefficientLinear_coeff, coeff_mul, coeff_mul, map_sum] + apply Finset.sum_congr rfl + intro p _ + rw [coeff_map, polynomialCoefficientLinear_coeff, hφ] + +/-- For a field extension `L / K` and a `K`-linear functional `φ : L → K`, applying `φ` +coefficientwise is linear over polynomials with coefficients in `K`: it sends +`f * map (algebraMap K L) g` to `polynomialCoefficientLinear φ f * g`. -/ +lemma polynomialCoefficientLinear_mul_map {K L σ : Type*} + [Field K] [Field L] [Algebra K L] + (φ : L →ₗ[K] K) (f : MvPolynomial σ L) (g : MvPolynomial σ K) : + polynomialCoefficientLinear φ (f * map (algebraMap K L) g) = + polynomialCoefficientLinear φ f * g := by + apply polynomialCoefficientLinear_mul_compatible + intro a b + simpa only [Algebra.smul_def, smul_eq_mul, mul_comm, Algebra.algebraMap_self, + RingHom.id_apply] using φ.map_smul b a + +/-- Coefficient functionals send elements of an extended ideal into the +original ideal, for any variable type. -/ +theorem polynomialCoefficientLinear_mem_scalar_extension {K L σ : Type*} + [Field K] [Field L] [Algebra K L] + (I : Ideal (MvPolynomial σ K)) (g : MvPolynomial σ L) + (hg : g ∈ I.map (map (algebraMap K L))) (φ : L →ₗ[K] K) : + polynomialCoefficientLinear φ g ∈ I := by + have hh : ∀ (ψ : L →ₗ[K] K) (r : MvPolynomial σ L), + polynomialCoefficientLinear ψ (r * g) ∈ I := by + induction hg using Submodule.span_induction with + | mem x hx => + obtain ⟨f, hf, rfl⟩ := hx + intro ψ r + rw [polynomialCoefficientLinear_mul_map] + exact I.mul_mem_left _ hf + | zero => + intro ψ r + rw [mul_zero, map_zero] + exact I.zero_mem + | add x y _ _ hx hy => + intro ψ r + rw [mul_add, map_add] + exact I.add_mem (hx ψ r) (hy ψ r) + | smul a x _ hx => + intro ψ r + simpa only [smul_eq_mul, mul_assoc] using hx ψ (r * a) + simpa only [one_mul] using hh φ 1 + +/-- Applying a linear map coefficientwise does not increase the total degree. -/ +lemma polynomialCoefficientLinear_totalDegree_le {R A B σ : Type*} + [CommSemiring R] [CommRing A] [CommRing B] [Algebra R A] [Algebra R B] + (φ : A →ₗ[R] B) (f : MvPolynomial σ A) : + (polynomialCoefficientLinear φ f).totalDegree ≤ f.totalDegree := by + classical + apply Finset.sup_le + intro m hm + apply le_totalDegree + rw [mem_support_iff, polynomialCoefficientLinear_coeff] at hm + rw [mem_support_iff] + intro hz + exact hm (by rw [hz, map_zero]) + +/-- For a field extension `K / k` and a `k`-linear functional `φ : K → k`, evaluating the +coefficientwise image of `f` under `φ` at a `k`-point `x` gives `φ` of the value of `f` at `x`. -/ +lemma polynomialCoefficientLinear_eval {k K σ : Type*} [Field k] [Field K] [Algebra k K] + (φ : K →ₗ[k] k) (f : MvPolynomial σ K) (x : σ → k) : + eval x (polynomialCoefficientLinear φ f) = φ (eval (algebraMap k K ∘ x) f) := by + induction f using MvPolynomial.induction_on with + | C c => simp + | add f g hf hg => simp only [map_add, hf, hg] + | mul_X f j hf => + have hX : (X j : MvPolynomial σ K) = map (algebraMap k K) (X j) := by simp + rw [hX, polynomialCoefficientLinear_mul_map, map_X] + simp only [map_mul, eval_X, hf, Function.comp_apply] + rw [mul_comm _ (algebraMap k K (x j)), ← Algebra.smul_def, φ.map_smul, smul_eq_mul, mul_comm] + +/-- Linear coefficient descent: for a field extension `K / k`, a nonzero polynomial `f` over `K` +has a nonzero polynomial over `k` of total degree at most that of `f` vanishing at every +`k`-point where `f` vanishes. -/ +theorem exists_nonzero_polynomial_linear_descent {k K : Type*} + [Field k] [Field K] [Algebra k K] {n : ℕ} + (f : MvPolynomial (Fin n) K) (hf : f ≠ 0) : + ∃ g : MvPolynomial (Fin n) k, g ≠ 0 ∧ g.totalDegree ≤ f.totalDegree ∧ + ∀ x : Fin n → k, eval (algebraMap k K ∘ x) f = 0 → eval x g = 0 := by + obtain ⟨m, hm⟩ := exists_coeff_ne_zero hf + obtain ⟨φ, hφ⟩ := Module.Projective.exists_dual_ne_zero k hm + refine ⟨polynomialCoefficientLinear φ f, ?_, + polynomialCoefficientLinear_totalDegree_le φ f, ?_⟩ + · intro he + have hh := polynomialCoefficientLinear_coeff φ f m + have hz : φ (f.coeff m) = 0 := by simpa [he] using hh.symm + exact hφ hz + · intro x hx + rw [polynomialCoefficientLinear_eval, hx, map_zero] + +end +end CCDN diff --git a/LeanPool/CCDN/Algebra/PolynomialMatrixSpecialization.lean b/LeanPool/CCDN/Algebra/PolynomialMatrixSpecialization.lean new file mode 100644 index 0000000000..3468d8a170 --- /dev/null +++ b/LeanPool/CCDN/Algebra/PolynomialMatrixSpecialization.lean @@ -0,0 +1,95 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import Mathlib.FieldTheory.RatFunc.Basic +public import Mathlib.RingTheory.SimpleRing.Principal +import LeanPool.CCDN.Algebra.BoundedLinearSeparation + +/-! An injective polynomial matrix over K(t) remains injective at some integer +specialization. Clearing a left inverse's denominators gives the assertion +without selecting minors or assuming a generic-rank principle. -/ + +@[expose] public section +namespace CCDN +noncomputable section +open Matrix Polynomial + +/-- Clear all entries of a rational-function matrix with one nonzero +polynomial denominator. -/ +lemma ratFunc_matrix_common_denominator {K ι κ : Type*} [Field K] + [Finite ι] [Finite κ] (B : Matrix ι κ (RatFunc K)) : + ∃ q : Polynomial K, ∃ C : Matrix ι κ (Polynomial K), q ≠ 0 ∧ + ∀ i j, algebraMap (Polynomial K) (RatFunc K) (C i j) = + algebraMap (Polynomial K) (RatFunc K) q * B i j := by + classical + let := Fintype.ofFinite ι + let := Fintype.ofFinite κ + obtain ⟨q, hq⟩ := IsLocalization.exist_integer_multiples_of_finite + (nonZeroDivisors (Polynomial K)) (fun t : ι × κ => B t.1 t.2) + choose C hC using hq + refine ⟨q.val, fun i j => C (i,j), mem_nonZeroDivisors_iff_ne_zero.mp q.property, ?_⟩ + intro i j + exact (hC (i,j)).trans (Algebra.smul_def _ _) + +/-- An injective polynomial matrix has a polynomial left inverse up to one +nonzero scalar polynomial. -/ +private theorem polynomial_matrix_scaled_left_inverse {K ι κ : Type*} [Field K] + [Fintype ι] [Fintype κ] [DecidableEq κ] + (A : Matrix ι κ (Polynomial K)) + (hA : Function.Injective (A.map (algebraMap (Polynomial K) (RatFunc K))).mulVecLin) : + ∃ q : Polynomial K, ∃ C : Matrix κ ι (Polynomial K), q ≠ 0 ∧ C * A = q • 1 := by + classical + let f := algebraMap (Polynomial K) (RatFunc K) + let M := A.map f + obtain ⟨T, hT⟩ := M.mulVecLin.exists_leftInverse_of_injective (LinearMap.ker_eq_bot.mpr hA) + let B := LinearMap.toMatrix' T + have hBA : B * M = 1 := by + have hh := congrArg LinearMap.toMatrix' hT + simpa only [LinearMap.toMatrix'_comp, ← Matrix.toLin'_apply', + LinearMap.toMatrix'_toLin', LinearMap.toMatrix'_id] using hh + obtain ⟨q, C, hq, hC⟩ := ratFunc_matrix_common_denominator B + refine ⟨q, C, hq, ?_⟩ + have hCm : C.map f = f q • B := by ext i j; exact hC i j + have hh : (C * A).map f = f q • (1 : Matrix κ κ (RatFunc K)) := by + rw [Matrix.map_mul, hCm, Matrix.smul_mul, hBA] + apply Matrix.ext + intro i j + apply RatFunc.algebraMap_injective K + change f ((C * A) i j) = f ((q • (1 : Matrix κ κ (Polynomial K))) i j) + have he := congrArg (fun D : Matrix κ κ (RatFunc K) => D i j) hh + simpa only [Matrix.map_apply, Matrix.smul_apply, smul_eq_mul, map_mul, + Matrix.one_apply, apply_ite f, map_one, map_zero] using he + +/-- Full column rank over K(t) occurs at an integer specialization. -/ +theorem polynomial_matrix_injective_specialization {K ι κ : Type*} [Field K] [CharZero K] + [Finite ι] [Fintype κ] + (A : Matrix ι κ (Polynomial K)) + (hA : Function.Injective (A.map (algebraMap (Polynomial K) (RatFunc K))).mulVecLin) : + ∃ k : ℕ, Function.Injective (A.map (Polynomial.evalRingHom (k : K))).mulVecLin := by + classical + let := Fintype.ofFinite ι + obtain ⟨q, C, hq, hCA⟩ := polynomial_matrix_scaled_left_inverse A hA + obtain ⟨k, _, hk⟩ := polynomial_exists_small_nat_eval_ne_zero q hq + refine ⟨k, ?_⟩ + let e := Polynomial.evalRingHom (k : K) + have he : C.map e * A.map e = e q • (1 : Matrix κ κ K) := by + rw [← Matrix.map_mul, hCA] + ext i j + simp only [Matrix.map_apply, Matrix.smul_apply, smul_eq_mul, map_mul, + Matrix.one_apply, apply_ite e, map_one, map_zero] + intro x y hxy + have hh := congrArg (fun v => (C.map e) *ᵥ v) hxy + change (C.map e) *ᵥ ((A.map e) *ᵥ x) = (C.map e) *ᵥ ((A.map e) *ᵥ y) at hh + rw [Matrix.mulVec_mulVec, Matrix.mulVec_mulVec, he, Matrix.smul_mulVec, + Matrix.smul_mulVec, Matrix.one_mulVec, Matrix.one_mulVec] at hh + exact (smul_right_injective _ hk) hh + +end +end CCDN diff --git a/LeanPool/CCDN/Algebra/PolynomialPredicates.lean b/LeanPool/CCDN/Algebra/PolynomialPredicates.lean new file mode 100644 index 0000000000..864fa36140 --- /dev/null +++ b/LeanPool/CCDN/Algebra/PolynomialPredicates.lean @@ -0,0 +1,62 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import Mathlib.FieldTheory.IsAlgClosed.AlgebraicClosure +public import Mathlib.RingTheory.MvPolynomial.Homogeneous + +/-! +# Predicates on polynomials and ideals + +Named forms of hypotheses that recur throughout the library: homogeneity of an ideal of a +polynomial ring, absolute irreducibility of a polynomial (irreducibility over `the algebraic closure +of ℚ`), and +absolute irreducibility of its top-degree homogeneous part (the hypothesis of CCDN Theorem 4). +All are abbreviations, so proofs may use them as their unfolded statements. +-/ + +@[expose] public section + +namespace CCDN + +open MvPolynomial + +variable {σ K : Type*} [CommSemiring K] + +/-- An ideal of a polynomial ring is homogeneous when it contains every homogeneous +component of each of its elements. -/ +abbrev IsHomogeneousIdeal (I : Ideal (MvPolynomial σ K)) : Prop := + ∀ f ∈ I, ∀ N, homogeneousComponent N f ∈ I + +/-- An integer polynomial is absolutely irreducible when it is irreducible over `the algebraic +closure of ℚ`. -/ +abbrev AbsolutelyIrreducible (f : MvPolynomial σ ℤ) : Prop := + Irreducible (map (Int.castRingHom (AlgebraicClosure ℚ)) f) + +/-- A rational polynomial is absolutely irreducible when it is irreducible over `the algebraic +closure of ℚ`. -/ +abbrev AbsolutelyIrreducibleRat (f : MvPolynomial σ ℚ) : Prop := + Irreducible (map (algebraMap ℚ (AlgebraicClosure ℚ)) f) + +/-- An integer polynomial is absolutely irreducible exactly when its image over `ℚ` is. -/ +theorem absolutelyIrreducibleRat_map_iff (f : MvPolynomial σ ℤ) : + AbsolutelyIrreducibleRat (map (Int.castRingHom ℚ) f) ↔ AbsolutelyIrreducible f := by + rw [AbsolutelyIrreducibleRat, map_map, RingHom.ext_int ((algebraMap ℚ _).comp _)] + +/-- The top-degree homogeneous part of an integer polynomial is irreducible over `the algebraic +closure of ℚ`. -/ +abbrev HasAbsIrreducibleTopForm (f : MvPolynomial σ ℤ) : Prop := + AbsolutelyIrreducible (homogeneousComponent f.totalDegree f) + +/-- The top-degree homogeneous part of a rational polynomial is irreducible over `the algebraic +closure of ℚ`. -/ +abbrev HasAbsIrreducibleTopFormRat (f : MvPolynomial σ ℚ) : Prop := + AbsolutelyIrreducibleRat (homogeneousComponent f.totalDegree f) + +end CCDN diff --git a/LeanPool/CCDN/Algebra/PolynomialPrimeLocalization.lean b/LeanPool/CCDN/Algebra/PolynomialPrimeLocalization.lean new file mode 100644 index 0000000000..d9c82e2022 --- /dev/null +++ b/LeanPool/CCDN/Algebra/PolynomialPrimeLocalization.lean @@ -0,0 +1,89 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import Mathlib.Algebra.MvPolynomial.Degrees +public import Mathlib.RingTheory.Localization.FractionRing +import LeanPool.CCDN.ForMathlib.MvPolynomial +import Mathlib.Algebra.MvPolynomial.NoZeroDivisors +import Mathlib.RingTheory.Localization.Ideal +import Mathlib.RingTheory.MvPolynomial.Localization +import Mathlib.RingTheory.Polynomial.UniqueFactorization + +/-! Nonconstant prime polynomials remain prime when coefficients are localized. +This also proves preservation of irreducibility under purely transcendental +coefficient extension, using a swap of the two polynomial variable families. -/ + +@[expose] public section +namespace CCDN +noncomputable section +open MvPolynomial +attribute [local instance] MvPolynomial.algebraMvPolynomial + +/-- A nonconstant polynomial over a domain cannot divide a nonzero constant. -/ +private theorem mvPolynomial_not_dvd_nonzero_constant + {R σ : Type*} [CommRing R] [IsDomain R] + (f : MvPolynomial σ R) (hf : f ≠ 0) (hd : 0 < f.totalDegree) + (a : R) (ha : a ≠ 0) : ¬ f ∣ C a := by + rintro ⟨g, hg⟩ + have hg0 : g ≠ 0 := by + intro h + rw [h, mul_zero] at hg + exact ha ((C_injective σ R) (by simpa using hg)) + have hdeg := totalDegree_mul_of_isDomain hf hg0 + rw [← hg, totalDegree_C] at hdeg + omega + +/-- Prime polynomials of positive variable degree survive coefficient +localization at nonzero elements. -/ +theorem prime_mvPolynomial_map_localization + {R S σ : Type*} [CommRing R] [IsDomain R] [CommRing S] + [Algebra R S] (M : Submonoid R) [IsLocalization M S] + (hM : M ≤ nonZeroDivisors R) + (f : MvPolynomial σ R) (hf : Prime f) (hd : 0 < f.totalDegree) : + Prime (MvPolynomial.map (algebraMap R S) f) := by + let N : Submonoid (MvPolynomial σ R) := M.map C + let I : Ideal (MvPolynomial σ R) := Ideal.span {f} + have hI : I.IsPrime := Ideal.isPrime_span_singleton_of_prime hf + have hdis : Disjoint (N : Set (MvPolynomial σ R)) (I : Set (MvPolynomial σ R)) := by + rw [Set.disjoint_left] + intro q hq hqi + obtain ⟨a, ha, rfl⟩ := hq + exact mvPolynomial_not_dvd_nonzero_constant f hf.ne_zero hd a + (mem_nonZeroDivisors_iff_ne_zero.mp (hM ha)) (Ideal.mem_span_singleton.mp hqi) + have hp := IsLocalization.isPrime_of_isPrime_disjoint N (MvPolynomial σ S) I hI hdis + have hinj : Function.Injective (algebraMap R S) := IsLocalization.injective S hM + have hne : MvPolynomial.map (algebraMap R S) f ≠ 0 := + (map_ne_zero_iff _ (MvPolynomial.map_injective _ hinj)).mpr hf.ne_zero + apply (Ideal.span_singleton_prime hne).mp + simpa only [I, Ideal.map_span, Set.image_singleton, MvPolynomial.algebraMap_def] using hp + +/-- Irreducibility survives adjoining algebraically independent parameters +and then passing to their rational function field. -/ +theorem irreducible_mvPolynomial_map_fraction_parameters + {K F σ τ : Type*} [Field K] [Field F] + [Algebra (MvPolynomial τ K) F] [IsFractionRing (MvPolynomial τ K) F] + (f : MvPolynomial σ K) (hf : Irreducible f) (hd : 0 < f.totalDegree) : + Irreducible (MvPolynomial.map ((algebraMap (MvPolynomial τ K) F).comp C) f) := by + let g : MvPolynomial σ (MvPolynomial τ K) := MvPolynomial.map C f + have hg : Prime g := by + have hC : Prime (C f : MvPolynomial τ (MvPolynomial σ K)) := + (MvPolynomial.prime_C_iff τ).mpr hf.prime + have h := (MulEquiv.prime_iff (MvPolynomial.commAlgEquiv K τ σ)).mpr hC + simpa only [MvPolynomial.commAlgEquiv_C, g] using h + have hdeg : 0 < g.totalDegree := by + have he : g.totalDegree = f.totalDegree := totalDegree_map_of_injective (C_injective τ K) _ + rw [he] + exact hd + have h := prime_mvPolynomial_map_localization (S := F) + (nonZeroDivisors (MvPolynomial τ K)) le_rfl g hg hdeg + simpa only [g, MvPolynomial.map_map] using h.irreducible + +end +end CCDN diff --git a/LeanPool/CCDN/Algebra/PolynomialSection.lean b/LeanPool/CCDN/Algebra/PolynomialSection.lean new file mode 100644 index 0000000000..5ddceb3cc5 --- /dev/null +++ b/LeanPool/CCDN/Algebra/PolynomialSection.lean @@ -0,0 +1,50 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.Algebra.IdealZeroLocus +public import LeanPool.CCDN.Hilbert.FilteredIntersection +import LeanPool.CCDN.Algebra.PointSeparation +import LeanPool.CCDN.Algebra.SectionImageRank + +/-! A sharp affine curve intersection bound from the Hilbert function. +Bounds intersections with arbitrary section polynomials. No projective intersection theorem or +point-counting axiom is used. -/ + +@[expose] public section +namespace CCDN +noncomputable section + +/-- A proper polynomial section of a prime curve contains at most the product +of the curve's affine Hilbert degree and the section degree many distinct points. -/ +theorem polynomial_section_card_le_degree {K σ ι : Type*} [Field K] [Finite σ] + [Fintype ι] + (p : Ideal (MvPolynomial σ K)) [p.IsPrime] (d : ℕ) + (hdegree : HasFilteredCurveDegree p d) + (l : MvPolynomial σ K) (hl : l ∉ p) (m : ℕ) (hld : l.totalDegree ≤ m) + (z : ι → σ → K) (hz : Function.Injective z) + (hzp : ∀ i, z i ∈ idealZeroLocus p) (hzl : ∀ i, MvPolynomial.eval (z i) l = 0) : + Fintype.card ι ≤ d * m := by + classical + obtain ⟨h, hsep⟩ := exists_point_separators z hz + let φ : ι → (MvPolynomial σ K ⧸ p) →ₐ[K] K := + fun i => Ideal.Quotient.liftₐ p (MvPolynomial.aeval (z i)) (by + intro f hf + simpa only [MvPolynomial.aeval_eq_eval] using hzp i f hf) + have hφ (i : ι) (f : MvPolynomial σ K) : + φ i ((Ideal.Quotient.mk p) f) = MvPolynomial.eval (z i) f := by + simp [φ, Ideal.Quotient.liftₐ_apply] + have hlin := separator_images_independent φ (fun i => (Ideal.Quotient.mk p) (h i)) + (fun i j => by rw [hφ]; exact hsep i j) + apply section_image_rank_le p d hdegree (AlgHom.pi φ) h hlin l hl m hld + ext i + exact (hφ i l).trans (hzl i) + +end +end CCDN diff --git a/LeanPool/CCDN/Algebra/PrimitiveNormalization.lean b/LeanPool/CCDN/Algebra/PrimitiveNormalization.lean new file mode 100644 index 0000000000..373276f385 --- /dev/null +++ b/LeanPool/CCDN/Algebra/PrimitiveNormalization.lean @@ -0,0 +1,98 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.Height.PrimitiveIntegerPolynomial +public import Mathlib.Basic.Real.Basic +public import Mathlib.RingTheory.MvPolynomial.Homogeneous +import LeanPool.CCDN.ForMathlib.MvPolynomial +import Mathlib.Algebra.MvPolynomial.NoZeroDivisors + +/-! Primitive models preserve the equation's degree and homogeneous grading, +and integer content removal does not increase coefficient height. -/ + +@[expose] public section +namespace CCDN +noncomputable section +open MvPolynomial + +private lemma integer_scalar_factor_ne_zero {σ : Type*} {f g : MvPolynomial σ ℤ} {c : ℤ} + (hf : f ≠ 0) (he : f = C c * g) : g ≠ 0 := by + intro hg + apply hf + rw [he, hg, mul_zero] + +/-- If a nonzero integer polynomial factors as `f = C c * g` with `c ≠ 0`, then `g` has the +same total degree as `f`. -/ +lemma integer_scalar_factor_degree {σ : Type*} {f g : MvPolynomial σ ℤ} {c : ℤ} + (hf : f ≠ 0) (hc : c ≠ 0) (he : f = C c * g) : g.totalDegree = f.totalDegree := by + have hg := integer_scalar_factor_ne_zero hf he + rw [he, totalDegree_mul_of_isDomain (C_eq_zero.not.mpr hc) hg, totalDegree_C, zero_add] + +/-- If `f = C c * g` with a nonzero integer `c`, then every coefficient of `g` is at most the +corresponding coefficient of `f` in absolute value. -/ +lemma integer_scalar_factor_coefficient_bound {σ : Type*} + {f g : MvPolynomial σ ℤ} {c : ℤ} (hc : c ≠ 0) (he : f = C c * g) (m : σ →₀ ℕ) : + |(g.coeff m : ℝ)| ≤ |(f.coeff m : ℝ)| := by + have hc1 : (1 : ℝ) ≤ |(c : ℝ)| := by exact_mod_cast Int.one_le_abs hc + rw [he, coeff_C_mul, Int.cast_mul, abs_mul] + nlinarith [abs_nonneg (g.coeff m : ℝ)] + +/-- Every nonzero rational multivariate polynomial has a primitive integer +model differing by a nonzero rational scalar. -/ +theorem exists_primitive_integer_model {σ : Type*} (f : MvPolynomial σ ℚ) (hf : f ≠ 0) : + ∃ (g : MvPolynomial σ ℤ) (a : ℚ), PrimitiveIntegerPolynomial g ∧ a ≠ 0 ∧ + MvPolynomial.map (Int.castRingHom ℚ) g = C a * f := by + obtain ⟨b, u, hb, hu⟩ := mvPolynomial_integer_multiple f + have hbQ : (b : ℚ) ≠ 0 := by exact_mod_cast hb + have hu0 : u ≠ 0 := by + intro h + have hz : C (b : ℚ) * f = 0 := by simpa only [h, map_zero] using hu.symm + exact (mul_ne_zero (C_eq_zero.not.mpr hbQ) hf) hz + obtain ⟨c, g, hc, hg, he⟩ := exists_primitive_integer_factor u hu0 + have hcQ : (c : ℚ) ≠ 0 := by exact_mod_cast hc + refine ⟨g, (b : ℚ) / c, hg, div_ne_zero hbQ hcQ, ?_⟩ + have hh := congrArg (MvPolynomial.map (Int.castRingHom ℚ)) he + rw [hu, map_mul, map_C] at hh + simp only [Int.coe_castRingHom] at hh + apply mul_left_cancel₀ (C_eq_zero.not.mpr hcQ) + rw [← mul_assoc, ← C_mul, mul_div_cancel₀ _ hcQ] + exact hh.symm + +/-- An integer polynomial `g` whose image over `ℚ` is a nonzero rational multiple `C a * f` of +a nonzero polynomial `f` has the same total degree as `f`. -/ +lemma rational_scalar_model_degree {σ : Type*} {f : MvPolynomial σ ℚ} + {g : MvPolynomial σ ℤ} {a : ℚ} (ha : a ≠ 0) (hf : f ≠ 0) + (he : MvPolynomial.map (Int.castRingHom ℚ) g = C a * f) : g.totalDegree = f.totalDegree := by + have hd : (MvPolynomial.map (Int.castRingHom ℚ) g).totalDegree = g.totalDegree := + totalDegree_map_of_injective Int.cast_injective _ + rw [he, totalDegree_mul_of_isDomain (C_eq_zero.not.mpr ha) hf, totalDegree_C, zero_add] at hd + exact hd.symm + +/-- If the image over `ℚ` of an integer polynomial `g` is `C a * f` for a homogeneous +polynomial `f` of degree `d`, then `g` is homogeneous of degree `d`. -/ +lemma rational_scalar_model_homogeneous {σ : Type*} {f : MvPolynomial σ ℚ} + {g : MvPolynomial σ ℤ} {a : ℚ} {d : ℕ} + (he : MvPolynomial.map (Int.castRingHom ℚ) g = C a * f) (hf : f.IsHomogeneous d) : + g.IsHomogeneous d := by + apply IsHomogeneous.of_map (f := Int.castRingHom ℚ) Int.cast_injective + rw [he] + exact hf.C_mul a + +/-- If the image over `ℚ` of an integer polynomial `g` is `C a * f` with `a ≠ 0`, then `g` and +`f` have the same rational zeros. -/ +lemma rational_scalar_model_zero_iff {σ : Type*} {f : MvPolynomial σ ℚ} + {g : MvPolynomial σ ℤ} {a : ℚ} (ha : a ≠ 0) + (he : MvPolynomial.map (Int.castRingHom ℚ) g = C a * f) (x : σ → ℚ) : + eval x (MvPolynomial.map (Int.castRingHom ℚ) g) = 0 ↔ eval x f = 0 := by + rw [he, eval_mul, eval_C] + exact mul_eq_zero_iff_left ha + +end +end CCDN diff --git a/LeanPool/CCDN/Algebra/ProjectionScalarExtension.lean b/LeanPool/CCDN/Algebra/ProjectionScalarExtension.lean new file mode 100644 index 0000000000..704ef7629a --- /dev/null +++ b/LeanPool/CCDN/Algebra/ProjectionScalarExtension.lean @@ -0,0 +1,116 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.Algebra.PolynomialLinearDescent +public import LeanPool.CCDN.Hilbert.LinearProjectionFiltration +import LeanPool.CCDN.Algebra.IdealScalarDescent + +/-! Scalar extension commutes with a polynomial image ideal. +Consequently a polynomial projection of a geometrically integral source +has a geometrically integral image. -/ + +@[expose] public section +namespace CCDN +noncomputable section +open MvPolynomial + +/-- Coefficient functionals commute with substitution of polynomials defined +over the base field, even though they need not preserve multiplication. -/ +theorem polynomialCoefficientLinear_aeval + {K L : Type*} [Field K] [Field L] [Algebra K L] {m n : ℕ} + (φ : L →ₗ[K] K) (a : Fin m → MvPolynomial (Fin n) K) + (g : MvPolynomial (Fin m) L) : + polynomialCoefficientLinear φ + (aeval (fun i => map (algebraMap K L) (a i)) g) = + aeval a (polynomialCoefficientLinear φ g) := by + induction g using MvPolynomial.induction_on with + | C c => simp only [aeval_C, algebraMap_eq, polynomialCoefficientLinear_C] + | add g h hg hh => + simp only [map_add, map_add, hg, hh] + | mul_X g i hg => + rw [map_mul, aeval_X, polynomialCoefficientLinear_mul_map, hg] + have hX : (X i : MvPolynomial (Fin m) L) = + map (algebraMap K L) (X i) := by simp + rw [hX, polynomialCoefficientLinear_mul_map, map_mul, aeval_X] + +private lemma map_aeval_polynomial_substitution + {K L : Type*} [Field K] [Field L] [Algebra K L] {m n : ℕ} + (a : Fin m → MvPolynomial (Fin n) K) (f : MvPolynomial (Fin m) K) : + map (algebraMap K L) (aeval a f) = + aeval (fun i => map (algebraMap K L) (a i)) (map (algebraMap K L) f) := by + induction f using MvPolynomial.induction_on with + | C c => simp + | add f g hf hg => simp only [map_add, hf, hg] + | mul_X f i hf => simp only [map_mul, aeval_X, map_X, hf] + +/-- A polynomial `f` lies in the kernel of the coordinate map `Y i ↦ a i` into +`MvPolynomial (Fin n) K ⧸ I` if and only if the substitution `aeval a f` lies in `I`. -/ +lemma mem_projectionCoordinateMap_ker + {K : Type*} [Field K] {m n : ℕ} + (I : Ideal (MvPolynomial (Fin n) K)) + (a : Fin m → MvPolynomial (Fin n) K) (f : MvPolynomial (Fin m) K) : + f ∈ RingHom.ker (projectionCoordinateMap I a) ↔ aeval a f ∈ I := by + exact Ideal.Quotient.eq_zero_iff_mem + +/-- The scalar-extended image ideal is the image ideal of the scalar-extended +source. The substitution need not be linear, finite or birational. -/ +theorem projection_kernel_scalar_extension + {K L : Type*} [Field K] [Field L] [Algebra K L] {m n : ℕ} + (I : Ideal (MvPolynomial (Fin n) K)) + (a : Fin m → MvPolynomial (Fin n) K) : + (RingHom.ker (projectionCoordinateMap I a)).map (map (algebraMap K L)) = + RingHom.ker (projectionCoordinateMap (I.map (map (algebraMap K L))) + (fun i => map (algebraMap K L) (a i))) := by + apply le_antisymm + · rw [Ideal.map_le_iff_le_comap] + intro f hf + rw [Ideal.mem_comap, mem_projectionCoordinateMap_ker, + ← map_aeval_polynomial_substitution] + exact Ideal.mem_map_of_mem _ ((mem_projectionCoordinateMap_ker I a f).mp hf) + · intro g hg + by_contra hnot + obtain ⟨φ, hφ⟩ := exists_polynomialCoefficientLinear_not_mem + (RingHom.ker (projectionCoordinateMap I a)) g hnot + apply hφ + rw [mem_projectionCoordinateMap_ker, ← polynomialCoefficientLinear_aeval] + exact polynomialCoefficientLinear_mem_scalar_extension I _ + ((mem_projectionCoordinateMap_ker _ _ g).mp hg) φ + +/-- If the ideal `I` stays prime after extending scalars from `K` to `L`, then so does the +kernel of the coordinate map `Y i ↦ a i` into `MvPolynomial (Fin n) K ⧸ I`, that is, the ideal +of the image of the polynomial map `a`. -/ +theorem projection_kernel_geometrically_prime + {K L : Type*} [Field K] [Field L] [Algebra K L] {m n : ℕ} + (I : Ideal (MvPolynomial (Fin n) K)) + [(I.map (map (algebraMap K L))).IsPrime] + (a : Fin m → MvPolynomial (Fin n) K) : + ((RingHom.ker (projectionCoordinateMap I a)).map + (map (algebraMap K L))).IsPrime := by + rw [projection_kernel_scalar_extension] + exact RingHom.ker_isPrime _ + +/-- A nonzero equation generating the image ideal remains +irreducible over every field extension where the source ideal stays prime. -/ +theorem projection_equation_irreducible_map + {K L : Type*} [Field K] [Field L] [Algebra K L] {m n : ℕ} + (I : Ideal (MvPolynomial (Fin n) K)) + [(I.map (map (algebraMap K L))).IsPrime] + (a : Fin m → MvPolynomial (Fin n) K) + (f : MvPolynomial (Fin m) K) (hf : f ≠ 0) + (hker : RingHom.ker (projectionCoordinateMap I a) = Ideal.span {f}) : + Irreducible (map (algebraMap K L) f) := by + have hp := projection_kernel_geometrically_prime (L := L) I a + rw [hker, Ideal.map_span, Set.image_singleton] at hp + have hne : map (algebraMap K L) f ≠ 0 := + (map_ne_zero_iff _ (map_injective _ (algebraMap K L).injective)).mpr hf + exact ((Ideal.span_singleton_prime hne).mp hp).irreducible + +end +end CCDN diff --git a/LeanPool/CCDN/Algebra/ProjectiveAffineChart.lean b/LeanPool/CCDN/Algebra/ProjectiveAffineChart.lean new file mode 100644 index 0000000000..f3cf926fc6 --- /dev/null +++ b/LeanPool/CCDN/Algebra/ProjectiveAffineChart.lean @@ -0,0 +1,94 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.Algebra.HomogeneousChart +public import LeanPool.CCDN.Height.ProjectiveZeroLocus + +/-! Affine coordinates on the standard open subset of projective space, +with injectivity and compatibility with homogeneous equations. -/ + +@[expose] public section +namespace CCDN +noncomputable section +open MvPolynomial +variable {K : Type*} [Field K] + +/-- The affine coordinates `(x₁ / x₀, …, xₙ / x₀)` of a projective point `x`, computed from +its chosen representative; meaningful on the standard chart `x₀ ≠ 0`. -/ +def projectiveAffineCoordinates {n : ℕ} (x : Projectivization K (Fin (n + 1) → K)) : + Fin n → K := fun j => x.rep j.succ / x.rep 0 + +private lemma projectiveAffineCoordinates_normalization {n : ℕ} + (x : Projectivization K (Fin (n + 1) → K)) (hx : x.rep 0 ≠ 0) : + Fin.cons 1 (projectiveAffineCoordinates x) = (x.rep 0)⁻¹ • x.rep := by + funext j + refine Fin.cases ?_ (fun k => ?_) j + · simp [hx] + · simp [projectiveAffineCoordinates, div_eq_mul_inv, mul_comm] + +/-- The vector `(1, a₁, …, aₙ)` is nonzero. -/ +lemma affineCoordinateVector_ne_zero {n : ℕ} (a : Fin n → K) : + (Fin.cons 1 a : Fin (n + 1) → K) ≠ 0 := by + intro h + have hh := congrFun h 0 + simp at hh + +private lemma projectiveAffineCoordinates_mk {n : ℕ} + (x : Projectivization K (Fin (n + 1) → K)) (hx : x.rep 0 ≠ 0) : + Projectivization.mk K (Fin.cons 1 (projectiveAffineCoordinates x)) + (affineCoordinateVector_ne_zero _) = x := by + calc + _ = Projectivization.mk K x.rep x.rep_nonzero := by + rw [Projectivization.mk_eq_mk_iff'] + exact ⟨_, (projectiveAffineCoordinates_normalization x hx).symm⟩ + _ = x := x.mk_rep + +/-- Projective points with nonzero first coordinate are determined by their affine +coordinates. -/ +theorem projectiveAffineCoordinates_injective {n : ℕ} + {x y : Projectivization K (Fin (n + 1) → K)} + (hx : x.rep 0 ≠ 0) (hy : y.rep 0 ≠ 0) + (hxy : projectiveAffineCoordinates x = projectiveAffineCoordinates y) : x = y := by + have h := congrArg (fun a : Fin n → K => Projectivization.mk K (Fin.cons 1 a : Fin (n + 1) → K) + (affineCoordinateVector_ne_zero a)) hxy + rwa [projectiveAffineCoordinates_mk x hx, projectiveAffineCoordinates_mk y hy] at h + +/-- If a projective point `x` with nonzero first coordinate lies on the zero locus of a +homogeneous polynomial `f`, then the dehomogenization of `f` vanishes at the affine coordinates +of `x`. -/ +theorem projectiveAffineCoordinates_zero {n d : ℕ} + (f : MvPolynomial (Fin (n + 1)) K) (hf : f.IsHomogeneous d) + (x : Projectivization K (Fin (n + 1) → K)) (hx : x.rep 0 ≠ 0) + (hxf : x ∈ projectiveZeroLocus f) : + eval (projectiveAffineCoordinates x) (affineChart n f) = 0 := by + rw [affineChart_eval, projectiveAffineCoordinates_normalization x hx, + homogeneous_eval_smul hf, show eval x.rep f = 0 from hxf, mul_zero] + +/-- The point `[1 : a]` of projective space on the standard chart. -/ +def affineProjectivePoint {K : Type*} [Field K] {n : ℕ} (a : Fin n → K) : + Projectivization K (Fin (n + 1) → K) := + Projectivization.mk K (Fin.cons 1 a) (affineCoordinateVector_ne_zero a) + +/-- The map `a ↦ [1 : a]` from affine space to projective space is injective. -/ +theorem affineProjectivePoint_injective {K : Type*} [Field K] (n : ℕ) : + Function.Injective (@affineProjectivePoint K _ n) := by + intro a b he + obtain ⟨c, hc⟩ := (Projectivization.mk_eq_mk_iff K _ _ _ _).mp he + have hc1 : (c : K) = 1 := by + have h := congrFun hc 0 + change (c : K) * 1 = 1 at h + simpa using h + funext j + have h := congrFun hc j.succ + change (c : K) * b j = a j at h + simpa [hc1] using h.symm + +end +end CCDN diff --git a/LeanPool/CCDN/Algebra/ProjectiveChartDegree.lean b/LeanPool/CCDN/Algebra/ProjectiveChartDegree.lean new file mode 100644 index 0000000000..e3234c5de4 --- /dev/null +++ b/LeanPool/CCDN/Algebra/ProjectiveChartDegree.lean @@ -0,0 +1,149 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.Algebra.HomogeneousChart +public import LeanPool.CCDN.Algebra.ProjectiveSection +public import LeanPool.CCDN.Hilbert.AffineCurveDegree +public import Mathlib.Analysis.SpecialFunctions.Pow.Real +import LeanPool.CCDN.Height.ProjectiveZeroLocus + +/-! The affine chart of a projective coordinate domain, constructed from +coordinate ratios in a field. Its filtered Hilbert function agrees exactly +with the homogeneous Hilbert function before dehomogenization. -/ + +@[expose] public section +namespace CCDN +noncomputable section +open MvPolynomial + +private lemma aeval_affineChart {K F : Type*} [Field K] [Field F] [Algebra K F] {n : ℕ} + (f : MvPolynomial (Fin (n + 1)) K) (x : Fin n → F) : + aeval x (affineChart n f) = aeval (Fin.cons 1 x) f := by + change eval₂ (algebraMap K F) x (affineChart n f) = + eval₂ (algebraMap K F) (Fin.cons 1 x) f + rw [← eval_map, ← affineChart_map, affineChart_eval, eval_map] + +/-- A homogeneous polynomial of degree `d` satisfies `f(a • v) = a ^ d * f(v)` for evaluations in +any field `F` that is a `K`-algebra. -/ +lemma homogeneous_aeval_smul {K F σ : Type*} [Field K] [Field F] [Algebra K F] + {f : MvPolynomial σ K} {d : ℕ} (hf : f.IsHomogeneous d) (a : F) (v : σ → F) : + aeval (a • v) f = a ^ d * aeval v f := by + change eval₂ (algebraMap K F) (a • v) f = a ^ d * eval₂ (algebraMap K F) v f + rw [← eval_map, ← eval_map] + exact homogeneous_eval_smul (hf.map _) a v + +/-- Given a `K`-algebra map `q` from the coordinate ring of `I` to a field `F`, the +`K`-algebra map from polynomials in the affine coordinates to `F` sending `Xⱼ` to the ratio +`q Xⱼ₊₁ / q X₀`. -/ +def projectiveChartCoordinateMap {K F : Type*} [Field K] [Field F] [Algebra K F] {n : ℕ} + (I : Ideal (MvPolynomial (Fin (n + 1)) K)) + (q : (MvPolynomial (Fin (n + 1)) K ⧸ I) →ₐ[K] F) : + MvPolynomial (Fin n) K →ₐ[K] F := + aeval (fun j => q ((Ideal.Quotient.mk I) (X j.succ)) / q ((Ideal.Quotient.mk I) (X 0))) + +/-- When `q X₀ ≠ 0`, the coordinate-ratio map sends the dehomogenization of a homogeneous +polynomial `f` of degree `M` to `(q X₀)⁻¹ ^ M * q f`. -/ +lemma projectiveChartCoordinateMap_chart {K F : Type*} [Field K] [Field F] [Algebra K F] + {n M : ℕ} (I : Ideal (MvPolynomial (Fin (n + 1)) K)) + (q : (MvPolynomial (Fin (n + 1)) K ⧸ I) →ₐ[K] F) + (h0 : q ((Ideal.Quotient.mk I) (X 0)) ≠ 0) + (f : MvPolynomial (Fin (n + 1)) K) (hf : f.IsHomogeneous M) : + projectiveChartCoordinateMap I q (affineChart n f) = + (q ((Ideal.Quotient.mk I) (X 0)))⁻¹ ^ M * q ((Ideal.Quotient.mk I) f) := by + let v : Fin (n + 1) → F := fun j => q ((Ideal.Quotient.mk I) (X j)) + have he : aeval v = q.comp (Ideal.Quotient.mkₐ K I) := by + apply MvPolynomial.algHom_ext + intro j + simp [v] + have hnorm : Fin.cons 1 (fun j => v j.succ / v 0) = (v 0)⁻¹ • v := by + funext j + refine Fin.cases ?_ (fun k => ?_) j + · simp [v, h0] + · simp [div_eq_mul_inv, mul_comm] + change aeval (fun j => v j.succ / v 0) (affineChart n f) = _ + rw [aeval_affineChart, hnorm, homogeneous_aeval_smul hf, he] + rfl + +private lemma projectiveChartCoordinateMap_homogenize {K F : Type*} [Field K] [Field F] + [Algebra K F] {n M : ℕ} (I : Ideal (MvPolynomial (Fin (n + 1)) K)) + (q : (MvPolynomial (Fin (n + 1)) K ⧸ I) →ₐ[K] F) + (h0 : q ((Ideal.Quotient.mk I) (X 0)) ≠ 0) + (f : MvPolynomial (Fin n) K) (hf : f.totalDegree ≤ M) : + projectiveChartCoordinateMap I q f = + (q ((Ideal.Quotient.mk I) (X 0)))⁻¹ ^ M * + q ((Ideal.Quotient.mk I) (scaledHomogenize M 1 f)) := by + have h := projectiveChartCoordinateMap_chart I q h0 (scaledHomogenize M 1 f) + (scaledHomogenize_homogeneous 1 f hf) + rwa [affineChart_scaledHomogenize_one] at h + +/-- Let `q` embed the coordinate ring of `I` into a field with `q X₀ ≠ 0`. In every degree `M`, +the filtered Hilbert function of the kernel of the coordinate-ratio map +`projectiveChartCoordinateMap I q` equals the homogeneous Hilbert function of `I`. -/ +theorem projective_chart_filtered_finrank {K F : Type*} [Field K] [Field F] [Algebra K F] + {n : ℕ} (I : Ideal (MvPolynomial (Fin (n + 1)) K)) + (q : (MvPolynomial (Fin (n + 1)) K ⧸ I) →ₐ[K] F) (hq : Function.Injective q) + (h0 : q ((Ideal.Quotient.mk I) (X 0)) ≠ 0) (M : ℕ) : + Module.finrank K (filteredCoordinateSpace (RingHom.ker (projectiveChartCoordinateMap I q)) M) = + projectiveHilbertFunction I M := by + let ψ := projectiveChartCoordinateMap I q + let η := Ideal.kerLiftAlg ψ + let V := homogeneousCoordinateSpace I M + let W := filteredCoordinateSpace (RingHom.ker ψ) M + let c := (q ((Ideal.Quotient.mk I) (X 0)))⁻¹ ^ M + let φ : (MvPolynomial (Fin (n + 1)) K ⧸ I) →ₗ[K] F := + (LinearMap.mulLeft K c).comp q.toLinearMap + have hφ : Function.Injective φ := by + intro x y h + exact hq (mul_left_cancel₀ (pow_ne_zero _ (inv_ne_zero h0)) h) + have himage : V.map φ = W.map η.toLinearMap := by + ext x + constructor + · intro hx + obtain ⟨u, hu, rfl⟩ := Submodule.mem_map.mp hx + obtain ⟨f, hf, rfl⟩ := Submodule.mem_map.mp hu + refine Submodule.mem_map.mpr ⟨(Ideal.Quotient.mk (RingHom.ker ψ)) (affineChart n f), ?_, ?_⟩ + · exact Submodule.mem_map.mpr ⟨affineChart n f, + (mem_restrictTotalDegree _ _ _).mpr + ((affineChart_totalDegree_le f).trans hf.totalDegree_le), + rfl⟩ + · exact projectiveChartCoordinateMap_chart I q h0 f hf + · intro hx + obtain ⟨u, hu, rfl⟩ := Submodule.mem_map.mp hx + obtain ⟨f, hf, rfl⟩ := Submodule.mem_map.mp hu + refine Submodule.mem_map.mpr ⟨(Ideal.Quotient.mk I) (scaledHomogenize M 1 f), ?_, ?_⟩ + · exact Submodule.mem_map.mpr ⟨scaledHomogenize M 1 f, + scaledHomogenize_homogeneous 1 f ((mem_restrictTotalDegree _ _ _).mp hf), rfl⟩ + · exact (projectiveChartCoordinateMap_homogenize I q h0 f + ((mem_restrictTotalDegree _ _ _).mp hf)).symm + have hV := (Submodule.equivMapOfInjective φ hφ V).finrank_eq + have hW := + (Submodule.equivMapOfInjective η.toLinearMap (Ideal.kerLiftAlg_injective ψ) W).finrank_eq + rw [himage] at hV + exact hW.trans hV.symm + +/-- Let `q` embed the coordinate ring of `I` into a field with `q X₀ ≠ 0`. If `I` defines a +projective curve of degree `d`, then the kernel of the coordinate-ratio map +`projectiveChartCoordinateMap I q` defines an affine curve of degree `d`. -/ +theorem projective_chart_degree {F : Type*} [Field F] [Algebra ℚ F] {n d : ℕ} + (I : Ideal (MvPolynomial (Fin (n + 1)) ℚ)) + (q : (MvPolynomial (Fin (n + 1)) ℚ ⧸ I) →ₐ[ℚ] F) (hq : Function.Injective q) + (h0 : q ((Ideal.Quotient.mk I) (X 0)) ≠ 0) + (hdegree : HasProjectiveCurveDegree I d) : + HasAffineCurveDegree (RingHom.ker (projectiveChartCoordinateMap I q)) d := by + obtain ⟨N, c, hN⟩ := hdegree + refine ⟨N, c, ?_⟩ + intro M hM + have he := projective_chart_filtered_finrank I q hq h0 M + change (Module.finrank ℚ (filteredCoordinateSpace _ M) : ℤ) = _ + rw [he] + exact hN M hM + +end +end CCDN diff --git a/LeanPool/CCDN/Algebra/ProjectiveChartIdeal.lean b/LeanPool/CCDN/Algebra/ProjectiveChartIdeal.lean new file mode 100644 index 0000000000..cd99cc3fae --- /dev/null +++ b/LeanPool/CCDN/Algebra/ProjectiveChartIdeal.lean @@ -0,0 +1,101 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.Algebra.ProjectiveAffineChart +public import LeanPool.CCDN.Algebra.ProjectiveChartDegree + +/-! The prime affine-chart ideal and the transport of projective +points and Hilbert degree to that ideal. -/ + +@[expose] public section +namespace CCDN +noncomputable section +open MvPolynomial + +/-- Let `q` embed the coordinate ring of `I` into a field with `q X₀ ≠ 0`. A homogeneous +polynomial `f` lies in `I` if and only if its dehomogenization `affineChart n f` lies in the +kernel of the coordinate-ratio map `projectiveChartCoordinateMap I q`. -/ +theorem affineChart_mem_coordinate_kernel_iff {K F : Type*} [Field K] [Field F] + [Algebra K F] {n M : ℕ} (I : Ideal (MvPolynomial (Fin (n + 1)) K)) + (q : (MvPolynomial (Fin (n + 1)) K ⧸ I) →ₐ[K] F) (hq : Function.Injective q) + (h0 : q ((Ideal.Quotient.mk I) (X 0)) ≠ 0) + (f : MvPolynomial (Fin (n + 1)) K) (hf : f.IsHomogeneous M) : + affineChart n f ∈ RingHom.ker (projectiveChartCoordinateMap I q) ↔ f ∈ I := by + rw [RingHom.mem_ker, projectiveChartCoordinateMap_chart I q h0 f hf, + mul_eq_zero_iff_left (pow_ne_zero _ (inv_ne_zero h0))] + rw [show q ((Ideal.Quotient.mk I) f) = 0 ↔ (Ideal.Quotient.mk I) f = 0 from + ⟨fun h => hq (by simpa using h), fun h => by rw [h, map_zero]⟩] + exact Ideal.Quotient.eq_zero_iff_mem + +/-- Let `q` embed the coordinate ring of `I` into a field with `q X₀ ≠ 0`. A polynomial `f` of +total degree at most `M` lies in the kernel of the coordinate-ratio map +`projectiveChartCoordinateMap I q` if and only if its degree-`M` homogenization lies in `I`. -/ +theorem coordinate_kernel_mem_iff_homogenize {K F : Type*} [Field K] [Field F] + [Algebra K F] {n M : ℕ} (I : Ideal (MvPolynomial (Fin (n + 1)) K)) + (q : (MvPolynomial (Fin (n + 1)) K ⧸ I) →ₐ[K] F) (hq : Function.Injective q) + (h0 : q ((Ideal.Quotient.mk I) (X 0)) ≠ 0) + (f : MvPolynomial (Fin n) K) (hf : f.totalDegree ≤ M) : + f ∈ RingHom.ker (projectiveChartCoordinateMap I q) ↔ scaledHomogenize M 1 f ∈ I := by + have h := affineChart_mem_coordinate_kernel_iff I q hq h0 (scaledHomogenize M 1 f) + (scaledHomogenize_homogeneous 1 f hf) + rwa [affineChart_scaledHomogenize_one] at h + +private theorem projective_point_chart_vanishes {K F : Type*} [Field K] [Field F] + [Algebra K F] {n : ℕ} (I : Ideal (MvPolynomial (Fin (n + 1)) K)) + (q : (MvPolynomial (Fin (n + 1)) K ⧸ I) →ₐ[K] F) (hq : Function.Injective q) + (h0 : q ((Ideal.Quotient.mk I) (X 0)) ≠ 0) + (x : Projectivization K (Fin (n + 1) → K)) + (hx : x ∈ projectiveIdealZeroLocus I) (hx0 : x.rep 0 ≠ 0) : + ∀ f ∈ RingHom.ker (projectiveChartCoordinateMap I q), + eval (projectiveAffineCoordinates x) f = 0 := by + intro f hf + have hh := (coordinate_kernel_mem_iff_homogenize I q hq h0 f le_rfl).mp hf + have hz := projectiveAffineCoordinates_zero (scaledHomogenize f.totalDegree 1 f) + (scaledHomogenize_homogeneous 1 f le_rfl) x hx0 (projectiveIdealZeroLocus_rep hx _ hh) + rwa [affineChart_scaledHomogenize_one] at hz + +/-- The ideal of the standard affine chart `X₀ ≠ 0` of the projective variety cut out by a +homogeneous prime ideal `I`: the polynomials in the affine coordinates vanishing at the ratios +`Xⱼ / X₀` in the fraction field of the coordinate ring of `I`. -/ +def projectiveChartIdeal {K : Type*} [Field K] {n : ℕ} + (I : Ideal (MvPolynomial (Fin (n + 1)) K)) [I.IsPrime] : + Ideal (MvPolynomial (Fin n) K) := + RingHom.ker (projectiveChartCoordinateMap I + (IsScalarTower.toAlgHom K (MvPolynomial (Fin (n + 1)) K ⧸ I) + (FractionRing (MvPolynomial (Fin (n + 1)) K ⧸ I)))) + +instance projectiveChartIdeal_isPrime {K : Type*} [Field K] {n : ℕ} + (I : Ideal (MvPolynomial (Fin (n + 1)) K)) [I.IsPrime] : + (projectiveChartIdeal I).IsPrime := + RingHom.ker_isPrime _ + +/-- If a prime ideal `I` not containing `X 0` defines a projective curve of degree `d`, then its +standard affine chart ideal defines an affine curve of degree `d`. -/ +theorem projectiveChartIdeal_degree {n d : ℕ} + (I : Ideal (MvPolynomial (Fin (n + 1)) ℚ)) [I.IsPrime] (hX : X 0 ∉ I) + (hdegree : HasProjectiveCurveDegree I d) : + HasAffineCurveDegree (projectiveChartIdeal I) d := by + apply projective_chart_degree I _ (IsFractionRing.injective _ _) _ hdegree + intro h + exact hX (Ideal.Quotient.eq_zero_iff_mem.mp (by simpa using h)) + +/-- If a projective point `x` on the zero locus of a prime ideal `I` (with `X 0 ∉ I`) has +nonzero first coordinate, then its affine coordinates lie on the zero locus of the standard +affine chart ideal of `I`. -/ +theorem projectiveChartIdeal_point {n : ℕ} + (I : Ideal (MvPolynomial (Fin (n + 1)) ℚ)) [I.IsPrime] (hX : X 0 ∉ I) + (x : ProjectivePoint n) (hx : x ∈ projectiveIdealZeroLocus I) (hx0 : x.rep 0 ≠ 0) : + projectiveAffineCoordinates x ∈ idealZeroLocus (projectiveChartIdeal I) := by + apply projective_point_chart_vanishes I _ (IsFractionRing.injective _ _) _ x hx hx0 + intro h + exact hX (Ideal.Quotient.eq_zero_iff_mem.mp (by simpa using h)) + +end +end CCDN diff --git a/LeanPool/CCDN/Algebra/ProjectiveChartScalarExtension.lean b/LeanPool/CCDN/Algebra/ProjectiveChartScalarExtension.lean new file mode 100644 index 0000000000..d6fece086a --- /dev/null +++ b/LeanPool/CCDN/Algebra/ProjectiveChartScalarExtension.lean @@ -0,0 +1,114 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.Algebra.PolynomialLinearDescent +public import LeanPool.CCDN.Algebra.ProjectiveChartIdeal +import LeanPool.CCDN.Algebra.HomogeneousLinearDescent +import LeanPool.CCDN.Algebra.IdealScalarDescent +import LeanPool.CCDN.Algebra.ProjectionScalarExtension +import LeanPool.CCDN.Algebra.ScaledHypersurfaceModel +import LeanPool.CCDN.ForMathlib.MvPolynomial + +/-! Projective affine charts commute with extension of coefficients. +In particular the standard chart of a geometrically integral projective +variety is geometrically integral. -/ + +@[expose] public section +namespace CCDN +noncomputable section +open MvPolynomial + +/-- Applying a `K`-linear map `φ : L → K` to the coefficients commutes with the affine chart +`affineChart n` (setting the first variable to `1`). -/ +lemma polynomialCoefficientLinear_affineChart + {K L : Type*} [Field K] [Field L] [Algebra K L] {n : ℕ} + (φ : L →ₗ[K] K) (f : MvPolynomial (Fin (n + 1)) L) : + polynomialCoefficientLinear φ (affineChart n f) = + affineChart n (polynomialCoefficientLinear φ f) := by + have h := polynomialCoefficientLinear_aeval φ + (Fin.cons (1 : MvPolynomial (Fin n) K) X) f + have he : (fun i : Fin (n + 1) => map (algebraMap K L) + ((Fin.cons (1 : MvPolynomial (Fin n) K) X : + Fin (n + 1) → MvPolynomial (Fin n) K) i)) = + (Fin.cons 1 X : Fin (n + 1) → MvPolynomial (Fin n) L) := by + funext i + refine Fin.cases ?_ (fun j => ?_) i <;> simp + rw [he] at h + exact h + +private lemma polynomialCoefficientLinear_scaledHomogenize_one + {K L : Type*} [Field K] [Field L] [Algebra K L] {n D : ℕ} + (φ : L →ₗ[K] K) (f : MvPolynomial (Fin n) L) (hf : f.totalDegree ≤ D) : + polynomialCoefficientLinear φ (scaledHomogenize D 1 f) = + scaledHomogenize D 1 (polynomialCoefficientLinear φ f) := by + apply affineChart_injective_on_homogeneous + (polynomialCoefficientLinear_homogeneous φ _ (scaledHomogenize_homogeneous 1 f hf)) + (scaledHomogenize_homogeneous 1 _ ((polynomialCoefficientLinear_totalDegree_le φ f).trans hf)) + rw [← polynomialCoefficientLinear_affineChart, + affineChart_scaledHomogenize_one, affineChart_scaledHomogenize_one] + +/-- For a prime ideal `I` not containing `X 0`, a polynomial `f` of total degree at most `D` +lies in the chart ideal `projectiveChartIdeal I` if and only if its homogenization +`scaledHomogenize D 1 f` in degree `D` lies in `I`. -/ +lemma projectiveChartIdeal_mem_iff_scaledHomogenize + {K : Type*} [Field K] {n D : ℕ} + (I : Ideal (MvPolynomial (Fin (n + 1)) K)) [I.IsPrime] + (hX : X 0 ∉ I) (f : MvPolynomial (Fin n) K) (hf : f.totalDegree ≤ D) : + f ∈ projectiveChartIdeal I ↔ scaledHomogenize D 1 f ∈ I := by + apply coordinate_kernel_mem_iff_homogenize I _ (IsFractionRing.injective _ _) _ f hf + intro h + apply hX + apply Ideal.Quotient.eq_zero_iff_mem.mp + exact (IsFractionRing.injective (MvPolynomial (Fin (n + 1)) K ⧸ I) + (FractionRing (MvPolynomial (Fin (n + 1)) K ⧸ I))) (by simpa using h) + +private theorem projectiveChartIdeal_scalar_extension + {K L : Type*} [Field K] [Field L] [Algebra K L] {n : ℕ} + (I : Ideal (MvPolynomial (Fin (n + 1)) K)) [I.IsPrime] + [(I.map (map (algebraMap K L))).IsPrime] (hX : X 0 ∉ I) : + (projectiveChartIdeal I).map (map (algebraMap K L)) = + projectiveChartIdeal (I.map (map (algebraMap K L))) := by + have hXL : X 0 ∉ I.map (map (algebraMap K L)) := by + simpa only [map_X] using + (not_congr (mem_ideal_scalar_extension_iff (L := L) I (X 0))).mpr hX + apply le_antisymm + · rw [Ideal.map_le_iff_le_comap] + intro f hf + rw [Ideal.mem_comap, projectiveChartIdeal_mem_iff_scaledHomogenize + _ hXL _ (totalDegree_map_le _ f)] + have hm : map (algebraMap K L) (scaledHomogenize f.totalDegree 1 f) = + scaledHomogenize f.totalDegree 1 (map (algebraMap K L) f) := by + rw [scaledHomogenize_map, map_one] + rw [← hm] + exact Ideal.mem_map_of_mem _ + ((projectiveChartIdeal_mem_iff_scaledHomogenize I hX f le_rfl).mp hf) + · intro g hg + by_contra hnot + obtain ⟨φ, hφ⟩ := + exists_polynomialCoefficientLinear_not_mem (projectiveChartIdeal I) g hnot + apply hφ + rw [projectiveChartIdeal_mem_iff_scaledHomogenize I hX _ + (polynomialCoefficientLinear_totalDegree_le φ g), + ← polynomialCoefficientLinear_scaledHomogenize_one φ g le_rfl] + exact polynomialCoefficientLinear_mem_scalar_extension I _ + ((projectiveChartIdeal_mem_iff_scaledHomogenize _ hXL g le_rfl).mp hg) φ + +/-- If a prime ideal `I` not containing `X 0` stays prime after extending scalars from `K` to +`L`, then so does its affine chart ideal `projectiveChartIdeal I`. -/ +theorem projectiveChartIdeal_geometrically_prime + {K L : Type*} [Field K] [Field L] [Algebra K L] {n : ℕ} + (I : Ideal (MvPolynomial (Fin (n + 1)) K)) [I.IsPrime] + [(I.map (map (algebraMap K L))).IsPrime] (hX : X 0 ∉ I) : + ((projectiveChartIdeal I).map (map (algebraMap K L))).IsPrime := by + rw [projectiveChartIdeal_scalar_extension I hX] + infer_instance + +end +end CCDN diff --git a/LeanPool/CCDN/Algebra/ProjectiveClosureIdeal.lean b/LeanPool/CCDN/Algebra/ProjectiveClosureIdeal.lean new file mode 100644 index 0000000000..fccd7a5880 --- /dev/null +++ b/LeanPool/CCDN/Algebra/ProjectiveClosureIdeal.lean @@ -0,0 +1,134 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.Algebra.HomogeneousChart +public import LeanPool.CCDN.Algebra.RadialHomogeneousMap +import LeanPool.CCDN.Algebra.IdealScalarDescent +import LeanPool.CCDN.Algebra.ProjectiveChartScalarExtension +import LeanPool.CCDN.Hilbert.HomogeneousPrimeComponents + +/-! The projective closure ideal of an affine coordinate ideal. The +homogenizing parameter is a polynomial variable over the affine quotient, +so primeness and homogeneous components are proved directly. -/ + +@[expose] public section +namespace CCDN +noncomputable section +open MvPolynomial + +/-- Send the homogenizing coordinate to t and every affine coordinate to +its quotient class times t. -/ +def projectiveClosureMap {K : Type*} [Field K] {n : ℕ} + (I : Ideal (MvPolynomial (Fin n) K)) : + MvPolynomial (Fin (n + 1)) K →ₐ[K] Polynomial (MvPolynomial (Fin n) K ⧸ I) where + toRingHom := (Polynomial.mapRingHom (Ideal.Quotient.mk I)).comp + ((Polynomial.mapRingHom (affineChart n)).comp radialMap) + commutes' c := by + change Polynomial.map (Ideal.Quotient.mk I) + (Polynomial.map (affineChart n) (radialMap (MvPolynomial.C c))) = _ + simp only [radialMap_C, Polynomial.map_C] + rw [show affineChart n (C c) = C c by simp [affineChart], Polynomial.algebraMap_apply] + congr 1 + +/-- The coefficient of `t ^ M` in `projectiveClosureMap I f` is the class modulo `I` of the +dehomogenization of the degree-`M` homogeneous component of `f`. -/ +lemma projectiveClosureMap_coeff {K : Type*} [Field K] {n : ℕ} + (I : Ideal (MvPolynomial (Fin n) K)) (f : MvPolynomial (Fin (n + 1)) K) (M : ℕ) : + (projectiveClosureMap I f).coeff M = + Ideal.Quotient.mk I (affineChart n (homogeneousComponent M f)) := by + change ((Polynomial.map (Ideal.Quotient.mk I)) + (Polynomial.map (affineChart n) (radialMap f))).coeff M = _ + simp only [Polynomial.coeff_map, radialMap_coeff] + +/-- The projective closure of the affine coordinate ideal `I`: the homogeneous ideal of +polynomials in `n + 1` variables all of whose homogeneous components dehomogenize (by setting +`X 0 = 1`) into `I`, defined as the kernel of `projectiveClosureMap I`. -/ +def projectiveClosureIdeal {K : Type*} [Field K] {n : ℕ} + (I : Ideal (MvPolynomial (Fin n) K)) : Ideal (MvPolynomial (Fin (n + 1)) K) := + RingHom.ker (projectiveClosureMap I) + +instance projectiveClosureIdeal_isPrime {K : Type*} [Field K] {n : ℕ} + (I : Ideal (MvPolynomial (Fin n) K)) [I.IsPrime] : + (projectiveClosureIdeal I).IsPrime := RingHom.ker_isPrime _ + +/-- A polynomial lies in the projective closure ideal of `I` if and only if the dehomogenization +of each of its homogeneous components lies in `I`. -/ +lemma mem_projectiveClosureIdeal_iff {K : Type*} [Field K] {n : ℕ} + (I : Ideal (MvPolynomial (Fin n) K)) (f : MvPolynomial (Fin (n + 1)) K) : + f ∈ projectiveClosureIdeal I ↔ + ∀ M, affineChart n (homogeneousComponent M f) ∈ I := by + change projectiveClosureMap I f = 0 ↔ _ + rw [Polynomial.ext_iff] + simp only [projectiveClosureMap_coeff, Polynomial.coeff_zero, Ideal.Quotient.eq_zero_iff_mem] + +/-- A homogeneous polynomial lies in the projective closure ideal of `I` if and only if its +dehomogenization lies in `I`. -/ +lemma homogeneous_mem_projectiveClosureIdeal_iff {K : Type*} [Field K] {n M : ℕ} + (I : Ideal (MvPolynomial (Fin n) K)) (f : MvPolynomial (Fin (n + 1)) K) + (hf : f.IsHomogeneous M) : + f ∈ projectiveClosureIdeal I ↔ affineChart n f ∈ I := by + rw [mem_projectiveClosureIdeal_iff] + constructor + · intro h + simpa only [homogeneousComponent_of_mem hf, ite_true] using h M + · intro h N + rw [homogeneousComponent_of_mem hf] + split_ifs <;> simp_all only [map_zero, Ideal.zero_mem] + +/-- The projective closure ideal is homogeneous: every homogeneous component of a member is a +member. -/ +lemma projectiveClosureIdeal_homogeneous {K : Type*} [Field K] {n : ℕ} + (I : Ideal (MvPolynomial (Fin n) K)) (f : MvPolynomial (Fin (n + 1)) K) + (hf : f ∈ projectiveClosureIdeal I) (M : ℕ) : + homogeneousComponent M f ∈ projectiveClosureIdeal I := by + rw [homogeneous_mem_projectiveClosureIdeal_iff I _ (homogeneousComponent_isHomogeneous _ _)] + exact (mem_projectiveClosureIdeal_iff I f).mp hf M + +/-- Closure commutes with arbitrary coefficient-field extension. -/ +theorem projectiveClosureIdeal_scalar_extension {K L : Type*} + [Field K] [Field L] [Algebra K L] {n : ℕ} + (I : Ideal (MvPolynomial (Fin n) K)) : + (projectiveClosureIdeal I).map (map (algebraMap K L)) = + projectiveClosureIdeal (I.map (map (algebraMap K L))) := by + apply le_antisymm + · rw [Ideal.map_le_iff_le_comap] + intro f hf + rw [Ideal.mem_comap, mem_projectiveClosureIdeal_iff] + intro M + have he : homogeneousComponent M (map (algebraMap K L) f) = + map (algebraMap K L) (homogeneousComponent M f) := by + ext a + simp only [coeff_homogeneousComponent, coeff_map] + split_ifs <;> simp only [map_zero] + rw [he, affineChart_map] + exact Ideal.mem_map_of_mem _ ((mem_projectiveClosureIdeal_iff I f).mp hf M) + · intro f hf + by_contra hnot + obtain ⟨φ,hφ⟩ := + exists_polynomialCoefficientLinear_not_mem (projectiveClosureIdeal I) f hnot + apply hφ + rw [mem_projectiveClosureIdeal_iff] + intro M + rw [← polynomialCoefficientLinear_homogeneousComponent, + ← polynomialCoefficientLinear_affineChart] + exact polynomialCoefficientLinear_mem_scalar_extension I _ + ((mem_projectiveClosureIdeal_iff _ f).mp hf M) φ + +/-- If the extension of `I` to a field extension `L` of `K` is prime, then so is the extension of +the projective closure ideal of `I` to `L`. -/ +theorem projectiveClosureIdeal_geometrically_prime {K L : Type*} + [Field K] [Field L] [Algebra K L] {n : ℕ} + (I : Ideal (MvPolynomial (Fin n) K)) [(I.map (map (algebraMap K L))).IsPrime] : + ((projectiveClosureIdeal I).map (map (algebraMap K L))).IsPrime := by + rw [projectiveClosureIdeal_scalar_extension] + infer_instance + +end +end CCDN diff --git a/LeanPool/CCDN/Algebra/ProjectiveFiltration.lean b/LeanPool/CCDN/Algebra/ProjectiveFiltration.lean new file mode 100644 index 0000000000..1546409f4e --- /dev/null +++ b/LeanPool/CCDN/Algebra/ProjectiveFiltration.lean @@ -0,0 +1,84 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.Hilbert.FilteredIntersection +import LeanPool.CCDN.Algebra.CoordinatePowerGeneration + +/-! +# The filtered homogeneous coordinate algebra + +For an affine coordinate ideal `I`, this constructs the subalgebra of +`(K[x]/I)[w]` whose coefficient of `w^n` is represented by a polynomial of degree +at most `n`. Its homogeneous pieces recover the filtered Hilbert +function. Multiplication by the homogenizing coordinate identifies the previous +filtered space inside the next piece. +-/ + +@[expose] public section +namespace CCDN +noncomputable section + +/-- Constants lie in every piece of the degree filtration of the coordinate ring. -/ +lemma filteredCoordinateSpace_algebraMap_mem {K σ : Type*} [Field K] + (I : Ideal (MvPolynomial σ K)) (n : ℕ) (c : K) : + algebraMap K (MvPolynomial σ K ⧸ I) c ∈ filteredCoordinateSpace I n := by + apply Submodule.mem_map.mpr + refine ⟨MvPolynomial.C c, ?_, by simp⟩ + simp [MvPolynomial.mem_restrictTotalDegree] + +/-- Homogeneous coordinate algebra of the filtered affine coordinate ring. -/ +def projectiveCoordinateAlgebra {K σ : Type*} [Field K] + (I : Ideal (MvPolynomial σ K)) : Subalgebra K (Polynomial (MvPolynomial σ K ⧸ I)) where + carrier := {f | ∀ n, f.coeff n ∈ filteredCoordinateSpace I n} + mul_mem' hf hg n := by + rw [Polynomial.coeff_mul] + apply Submodule.sum_mem + rintro ⟨i, j⟩ hij + have := filteredCoordinateSpace_mul_mem I (hf i) (hg j) + rwa [Finset.HasAntidiagonal.mem_antidiagonal.mp hij] at this + one_mem' n := by + by_cases hn : n = 0 + · subst n + simpa using filteredCoordinateSpace_algebraMap_mem I 0 1 + · simp [Polynomial.coeff_one, hn] + add_mem' hf hg n := by + rw [Polynomial.coeff_add] + exact (filteredCoordinateSpace I n).add_mem (hf n) (hg n) + zero_mem' n := by simp + algebraMap_mem' c n := by + by_cases hn : n = 0 + · subst n + simpa using filteredCoordinateSpace_algebraMap_mem I 0 c + · simp [Polynomial.algebraMap_apply, Polynomial.coeff_C, hn] + +/-- The monomial `a * w ^ n` lies in the homogeneous coordinate algebra if and only if `a` lies +in the degree-`n` piece of the filtered coordinate ring. -/ +lemma projective_monomial_mem {K σ : Type*} [Field K] + (I : Ideal (MvPolynomial σ K)) (n : ℕ) (a : MvPolynomial σ K ⧸ I) : + Polynomial.monomial n a ∈ projectiveCoordinateAlgebra I ↔ + a ∈ filteredCoordinateSpace I n := by + constructor + · intro h + simpa using h n + · intro ha i + by_cases hi : n = i + · subst i + simpa using ha + · simp [Polynomial.coeff_monomial, hi] + +/-- The homogenizing coordinate `w` in the homogeneous coordinate algebra. -/ +def projectiveInfinityCoordinate {K σ : Type*} [Field K] + (I : Ideal (MvPolynomial σ K)) : projectiveCoordinateAlgebra I := + ⟨Polynomial.X, by + rw [← Polynomial.monomial_one_one_eq_X, projective_monomial_mem] + simpa using filteredCoordinateSpace_algebraMap_mem I 1 1⟩ + +end +end CCDN diff --git a/LeanPool/CCDN/Algebra/ProjectiveFiniteFibers.lean b/LeanPool/CCDN/Algebra/ProjectiveFiniteFibers.lean new file mode 100644 index 0000000000..e787314ec6 --- /dev/null +++ b/LeanPool/CCDN/Algebra/ProjectiveFiniteFibers.lean @@ -0,0 +1,105 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.Algebra.ProjectiveSection +public import LeanPool.CCDN.Height.ProjectiveBox + +/-! Finite affine fibers of a linear map exclude a projective base point. +Projective fibers inject into affine fibers after choosing a fixed +nonzero representative in the target. -/ + +@[expose] public section +namespace CCDN +noncomputable section +open MvPolynomial + +/-- If the zeros of `I` in `ℚ^(n+1)` killed by the linear map `L` form a set of at most `M` +elements, then `L` does not vanish on the representative of any point of the projective zero +locus of `I`. -/ +theorem projective_linear_image_ne_zero_of_finite_zero_fiber + {n r : ℕ} (I : Ideal (MvPolynomial (Fin (n + 1)) ℚ)) + (L : (Fin (n + 1) → ℚ) →ₗ[ℚ] (Fin r → ℚ)) (M : ℕ) + (hfinite : ∀ s : Finset (Fin (n + 1) → ℚ), + (∀ z ∈ s, z ∈ idealZeroLocus I) → + (∀ z ∈ s, L z = 0) → s.card ≤ M) + (x : ProjectivePoint n) (hx : x ∈ projectiveIdealZeroLocus I) : + L x.rep ≠ 0 := by + classical + intro hzero + let z : Fin (M + 1) → (Fin (n + 1) → ℚ) := fun j => (j.val + 1 : ℚ) • x.rep + have hz (j : Fin (M + 1)) : z j ≠ 0 := by + apply smul_ne_zero _ x.rep_nonzero + positivity + have hmk (j : Fin (M + 1)) : Projectivization.mk ℚ (z j) (hz j) = x := by + rw [← x.mk_rep, Projectivization.mk_eq_mk_iff'] + exact ⟨(j.val + 1 : ℚ), rfl⟩ + have hinj : Function.Injective z := by + intro i j hij + have hh := smul_left_injective ℚ x.rep_nonzero hij + apply Fin.ext + exact_mod_cast (add_right_cancel hh) + have hh := hfinite (Finset.univ.image z) (by + intro w hw f hf + obtain ⟨j, _, rfl⟩ := Finset.mem_image.mp hw + exact hx (z j) (hz j) (hmk j) f hf) (by + intro w hw + obtain ⟨j, _, rfl⟩ := Finset.mem_image.mp hw + simp only [z, map_smul, hzero, smul_zero]) + rw [Finset.card_image_of_injective _ hinj, Finset.card_univ, Fintype.card_fin] at hh + omega + +/-- Let `s` be a finite set of points of the projective zero locus of `I` on which the linear +map `L` is nonzero and which `L` sends to the single projective point `y`. If at most `M` zeros +of `I` in `ℚ^(n+1)` are sent by `L` to the representative of `y`, then `s` has at most `M` +elements. -/ +theorem projective_linear_fiber_card_le + {n m : ℕ} (I : Ideal (MvPolynomial (Fin (n + 1)) ℚ)) + (L : (Fin (n + 1) → ℚ) →ₗ[ℚ] (Fin (m + 1) → ℚ)) + (s : Finset (ProjectivePoint n)) + (hs : ∀ x ∈ s, x ∈ projectiveIdealZeroLocus I) + (hL : ∀ x ∈ s, L x.rep ≠ 0) + (y : ProjectivePoint m) + (he : ∀ (x) (hx : x ∈ s), Projectivization.mk ℚ (L x.rep) (hL x hx) = y) + (M : ℕ) + (hfinite : ∀ t : Finset (Fin (n + 1) → ℚ), + (∀ z ∈ t, z ∈ idealZeroLocus I) → + (∀ z ∈ t, L z = y.rep) → t.card ≤ M) : + s.card ≤ M := by + classical + have hscale (x : s) : ∃ c : ℚˣ, c • y.rep = L x.val.rep := by + exact (Projectivization.mk_eq_mk_iff ℚ _ _ _ _).mp + ((he x.val x.property).trans y.mk_rep.symm) + choose c hc using hscale + let z : s → (Fin (n + 1) → ℚ) := fun x => (c x : ℚ)⁻¹ • x.val.rep + have hz (x : s) : z x ≠ 0 := smul_ne_zero (inv_ne_zero (c x).ne_zero) x.val.rep_nonzero + have hmk (x : s) : Projectivization.mk ℚ (z x) (hz x) = x.val := by + rw [← x.val.mk_rep, Projectivization.mk_eq_mk_iff'] + exact ⟨(c x : ℚ)⁻¹, rfl⟩ + have himage (x : s) : L (z x) = y.rep := by + dsimp only [z] + rw [map_smul, ← hc x] + change (c x : ℚ)⁻¹ • ((c x : ℚ) • y.rep) = y.rep + rw [smul_smul, inv_mul_cancel₀ (c x).ne_zero, one_smul] + have hinj : Function.Injective z := by + intro x x' heq + apply Subtype.ext + rw [← hmk x, ← hmk x'] + exact (Projectivization.mk_eq_mk_iff' ℚ _ _ _ _).mpr ⟨1, by simpa using heq.symm⟩ + have hh := hfinite (Finset.univ.image z) (by + intro w hw f hf + obtain ⟨x, _, rfl⟩ := Finset.mem_image.mp hw + exact hs x.val x.property (z x) (hz x) (hmk x) f hf) (by + intro w hw + obtain ⟨x, _, rfl⟩ := Finset.mem_image.mp hw + exact himage x) + simpa only [Finset.card_image_of_injective _ hinj, Finset.card_univ, Fintype.card_coe] using hh + +end +end CCDN diff --git a/LeanPool/CCDN/Algebra/ProjectivePermutation.lean b/LeanPool/CCDN/Algebra/ProjectivePermutation.lean new file mode 100644 index 0000000000..5f7e98b4eb --- /dev/null +++ b/LeanPool/CCDN/Algebra/ProjectivePermutation.lean @@ -0,0 +1,148 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.Algebra.ProjectiveSection +public import LeanPool.CCDN.Height.ProjectiveBox +public import LeanPool.CCDN.Hilbert.AffineCurveDegree + +/-! Coordinate permutations preserve projective heights, homogeneous +Hilbert functions, and prime coordinate ideals. -/ + +@[expose] public section +namespace CCDN +noncomputable section +open MvPolynomial + +/-- The ideal obtained from `I` by permuting the coordinates by `e`: the polynomials `f` with +`rename e f ∈ I`. -/ +def projectivePermutedIdeal {K : Type*} [Field K] {n : ℕ} + (I : Ideal (MvPolynomial (Fin (n + 1)) K)) (e : Equiv.Perm (Fin (n + 1))) : + Ideal (MvPolynomial (Fin (n + 1)) K) := + RingHom.ker ((Ideal.Quotient.mkₐ K I).comp (renameEquiv K e).toAlgHom) + +/-- A polynomial `f` lies in the permuted ideal `projectivePermutedIdeal I e` if and only if +`rename e f` lies in `I`. -/ +lemma mem_projectivePermutedIdeal {K : Type*} [Field K] {n : ℕ} + (I : Ideal (MvPolynomial (Fin (n + 1)) K)) (e : Equiv.Perm (Fin (n + 1))) + (f : MvPolynomial (Fin (n + 1)) K) : + f ∈ projectivePermutedIdeal I e ↔ rename e f ∈ I := by + change (Ideal.Quotient.mk I) (rename e f) = 0 ↔ _ + exact Ideal.Quotient.eq_zero_iff_mem + +instance projectivePermutedIdeal_isPrime {K : Type*} [Field K] {n : ℕ} + (I : Ideal (MvPolynomial (Fin (n + 1)) K)) [I.IsPrime] + (e : Equiv.Perm (Fin (n + 1))) : (projectivePermutedIdeal I e).IsPrime := + RingHom.ker_isPrime _ + +private theorem projectiveHilbertFunction_permuted {K : Type*} [Field K] {n : ℕ} + (I : Ideal (MvPolynomial (Fin (n + 1)) K)) (e : Equiv.Perm (Fin (n + 1))) (M : ℕ) : + projectiveHilbertFunction (projectivePermutedIdeal I e) M = projectiveHilbertFunction I M := by + let ψ := (Ideal.Quotient.mkₐ K I).comp (renameEquiv K e).toAlgHom + let φ := Ideal.kerLiftAlg ψ + let V := homogeneousCoordinateSpace (RingHom.ker ψ) M + have he : V.map φ.toLinearMap = homogeneousCoordinateSpace I M := by + ext x + constructor + · intro hx + obtain ⟨u, hu, rfl⟩ := Submodule.mem_map.mp hx + obtain ⟨f, hf, rfl⟩ := Submodule.mem_map.mp hu + exact Submodule.mem_map.mpr ⟨rename e f, hf.rename_isHomogeneous, rfl⟩ + · intro hx + obtain ⟨f, hf, rfl⟩ := Submodule.mem_map.mp hx + refine Submodule.mem_map.mpr + ⟨(Ideal.Quotient.mk (RingHom.ker ψ)) (rename e.symm f), + Submodule.mem_map.mpr ⟨rename e.symm f, hf.rename_isHomogeneous, rfl⟩, ?_⟩ + change (Ideal.Quotient.mk I) (rename e (rename e.symm f)) = (Ideal.Quotient.mk I) f + exact congrArg (Ideal.Quotient.mk I) ((renameEquiv K e).apply_symm_apply f) + have h := + (Submodule.equivMapOfInjective φ.toLinearMap (Ideal.kerLiftAlg_injective ψ) V).finrank_eq + rw [he] at h + exact h + +/-- Permuting the coordinates preserves the degree of a projective curve. -/ +theorem projectiveCurveDegree_permuted {K : Type*} [Field K] {n d : ℕ} + (I : Ideal (MvPolynomial (Fin (n + 1)) K)) (e : Equiv.Perm (Fin (n + 1))) + (hdegree : HasProjectiveCurveDegree I d) : + HasProjectiveCurveDegree (projectivePermutedIdeal I e) d := by + obtain ⟨N, c, hN⟩ := hdegree + exact ⟨N, c, fun M hM => by rw [projectiveHilbertFunction_permuted]; exact hN M hM⟩ + +private def coordinatePermutation {K : Type*} [Field K] {n : ℕ} (e : Equiv.Perm (Fin (n + 1))) : + (Fin (n + 1) → K) →ₗ[K] (Fin (n + 1) → K) where + toFun v := v ∘ e + map_add' _ _ := rfl + map_smul' _ _ := rfl + +private lemma coordinatePermutation_injective {K : Type*} [Field K] {n : ℕ} + (e : Equiv.Perm (Fin (n + 1))) : + Function.Injective (coordinatePermutation (K := K) e) := by + intro v w he + funext j + simpa [coordinatePermutation] using congrFun he (e.symm j) + +private def projectivePermutation {K : Type*} [Field K] {n : ℕ} (e : Equiv.Perm (Fin (n + 1))) : + Projectivization K (Fin (n + 1) → K) → Projectivization K (Fin (n + 1) → K) := + Projectivization.map (coordinatePermutation e) (coordinatePermutation_injective e) + +private lemma projectivePermutation_injective {K : Type*} [Field K] {n : ℕ} + (e : Equiv.Perm (Fin (n + 1))) : Function.Injective (projectivePermutation (K := K) e) := + Projectivization.map_injective _ _ + +private lemma projectivePermutation_height {n : ℕ} (e : Equiv.Perm (Fin (n + 1))) + (x : ProjectivePoint n) : + (projectivePermutation e x).mulHeight = x.mulHeight := by + conv_lhs => rw [← x.mk_rep] + rw [projectivePermutation, Projectivization.map_mk, Projectivization.mulHeight_mk] + change Height.mulHeight (x.rep ∘ e) = x.mulHeight + rw [Height.mulHeight_comp_equiv, projective_rep_height] + +private lemma projectivePermutation_zeroLocus {K : Type*} [Field K] {n : ℕ} + (I : Ideal (MvPolynomial (Fin (n + 1)) K)) (e : Equiv.Perm (Fin (n + 1))) + (x : Projectivization K (Fin (n + 1) → K)) (hx : x ∈ projectiveIdealZeroLocus I) : + projectivePermutation e x ∈ projectiveIdealZeroLocus (projectivePermutedIdeal I e) := by + intro v hv hvx f hf + have hxperm : coordinatePermutation e x.rep ≠ 0 := by + intro h + apply x.rep_nonzero + apply coordinatePermutation_injective (K := K) e + simpa only [map_zero] using h + have he : Projectivization.mk K v hv = + Projectivization.mk K (coordinatePermutation e x.rep) hxperm := by + change Projectivization.mk K v hv = + projectivePermutation e (Projectivization.mk K x.rep x.rep_nonzero) + rw [x.mk_rep] + exact hvx + obtain ⟨c, hc⟩ := (Projectivization.mk_eq_mk_iff K _ _ _ _).mp he + have hw : (c : K) • x.rep ≠ 0 := smul_ne_zero c.ne_zero x.rep_nonzero + have hwx : Projectivization.mk K ((c : K) • x.rep) hw = x := by + calc + _ = Projectivization.mk K x.rep x.rep_nonzero := + (Projectivization.mk_eq_mk_iff' K _ _ _ _).mpr ⟨c, rfl⟩ + _ = x := x.mk_rep + have h := hx ((c : K) • x.rep) hw hwx (rename e f) ((mem_projectivePermutedIdeal I e f).mp hf) + rw [eval_rename] at h + have hv' : ((c : K) • x.rep) ∘ e = v := hc + rwa [hv'] at h + +/-- Permuting the coordinates does not decrease the number of rational projective points of +height at most `B` on the zero locus of `I`. -/ +theorem projectiveCount_le_permuted {n : ℕ} + (I : Ideal (CoordinatePolynomial (n + 1))) (e : Equiv.Perm (Fin (n + 1))) (B : ℝ) : + projectiveCount (projectiveIdealZeroLocus I) B ≤ + projectiveCount (projectiveIdealZeroLocus (projectivePermutedIdeal I e)) B := by + apply Finset.card_le_card_of_injOn (projectivePermutation e) + · intro x hx + obtain ⟨hxB, hxI⟩ := mem_projectivePoints.mp hx + exact mem_projectivePoints.mpr ⟨by rwa [projectivePermutation_height], + projectivePermutation_zeroLocus I e x hxI⟩ + · exact (projectivePermutation_injective e).injOn + +end +end CCDN diff --git a/LeanPool/CCDN/Algebra/ProjectiveSection.lean b/LeanPool/CCDN/Algebra/ProjectiveSection.lean new file mode 100644 index 0000000000..bc8f4b4178 --- /dev/null +++ b/LeanPool/CCDN/Algebra/ProjectiveSection.lean @@ -0,0 +1,171 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.Algebra.IdealZeroLocus +public import LeanPool.CCDN.Algebra.PolynomialPredicates +public import LeanPool.CCDN.Hilbert.HomogeneousHilbert +public import Mathlib.LinearAlgebra.Projectivization.Basic +import LeanPool.CCDN.Height.ProjectivePointSeparation +import LeanPool.CCDN.Height.ProjectiveZeroLocus +import LeanPool.CCDN.Hilbert.FilteredIntersection + +/-! Proper homogeneous sections of projective curves. The degree is the +slope of the graded Hilbert function. Homogeneous interpolation and +injective multiplication in the coordinate domain prove the section bound. -/ + +@[expose] public section +namespace CCDN +noncomputable section +open MvPolynomial +variable {K : Type*} [Field K] + +instance homogeneousCoordinateSpace_finite {n : ℕ} + (I : Ideal (MvPolynomial (Fin (n + 1)) K)) (M : ℕ) : + Module.Finite K (homogeneousCoordinateSpace I M) := by + exact Module.Finite.of_fg + ((homogeneousSubmodule_fg _ _ _).map (Ideal.Quotient.mkₐ K I).toLinearMap) + +/-- The graded Hilbert function of `I`: the dimension of the degree-`M` part of the coordinate +ring `MvPolynomial (Fin (n + 1)) K ⧸ I`, that is, of the image of the homogeneous polynomials +of degree `M`. -/ +def projectiveHilbertFunction {n : ℕ} (I : Ideal (MvPolynomial (Fin (n + 1)) K)) + (M : ℕ) : ℕ := Module.finrank K (homogeneousCoordinateSpace I M) + +/-- The ideal `I` has projective curve degree `d`: the graded Hilbert function of +`MvPolynomial (Fin (n + 1)) K ⧸ I` eventually equals `d * M + c` for some constant `c`. For a +homogeneous prime ideal of a projective curve, `d` is the usual degree. -/ +def HasProjectiveCurveDegree {n : ℕ} (I : Ideal (MvPolynomial (Fin (n + 1)) K)) + (d : ℕ) : Prop := + ∃ N : ℕ, ∃ c : ℤ, ∀ M : ℕ, N ≤ M → + (projectiveHilbertFunction I M : ℤ) = (d : ℤ) * M + c + +/-- A projective point belongs to the zero locus when every representative +annihilates the ideal. For homogeneous ideals this is the usual projective zero locus. -/ +def projectiveIdealZeroLocus {n : ℕ} (I : Ideal (MvPolynomial (Fin (n + 1)) K)) : + Set (Projectivization K (Fin (n + 1) → K)) := + {x | ∀ (v : Fin (n + 1) → K) (hv : v ≠ 0), + Projectivization.mk K v hv = x → v ∈ idealZeroLocus I} + +/-- The chosen representative of a point of the projective zero locus of `I` lies in the affine +zero locus of `I`. -/ +lemma projectiveIdealZeroLocus_rep {n : ℕ} + {I : Ideal (MvPolynomial (Fin (n + 1)) K)} {x : Projectivization K (Fin (n + 1) → K)} + (hx : x ∈ projectiveIdealZeroLocus I) : + x.rep ∈ idealZeroLocus I := + hx x.rep x.rep_nonzero x.mk_rep + +private lemma homogeneousCoordinateSpace_mul_mem {n M m : ℕ} + (I : Ideal (MvPolynomial (Fin (n + 1)) K)) + {f : MvPolynomial (Fin (n + 1)) K} (hf : f.IsHomogeneous m) + {a : MvPolynomial (Fin (n + 1)) K ⧸ I} + (ha : a ∈ homogeneousCoordinateSpace I M) : + (Ideal.Quotient.mk I) f * a ∈ homogeneousCoordinateSpace I (m + M) := by + obtain ⟨g, hg, rfl⟩ := Submodule.mem_map.mp ha + exact Submodule.mem_map.mpr ⟨f * g, hf.mul hg, by simp⟩ + +/-- Distinct points on a proper homogeneous section force a drop in the graded +Hilbert function at least as large as their number. -/ +private theorem projective_section_hilbert_drop {n m M : ℕ} {ι : Type*} + [Fintype ι] + (I : Ideal (MvPolynomial (Fin (n + 1)) K)) [I.IsPrime] + (f : MvPolynomial (Fin (n + 1)) K) (hf : f.IsHomogeneous m) (hfI : f ∉ I) + (z : ι → Projectivization K (Fin (n + 1) → K)) (hz : Function.Injective z) + (hzI : ∀ i, z i ∈ projectiveIdealZeroLocus I) + (hzf : ∀ i, eval (z i).rep f = 0) (hM : Fintype.card ι - 1 ≤ m + M) : + Fintype.card ι + projectiveHilbertFunction I M ≤ projectiveHilbertFunction I (m + M) := by + classical + let A := MvPolynomial (Fin (n + 1)) K ⧸ I + let q := Ideal.Quotient.mkₐ K I + let ψ : ι → A →ₐ[K] K := fun i => + Ideal.Quotient.liftₐ I (aeval (z i).rep) (by + intro g hg + simpa only [aeval_eq_eval] using projectiveIdealZeroLocus_rep (hzI i) g hg) + have hψ (i : ι) (g : MvPolynomial (Fin (n + 1)) K) : + ψ i (q g) = eval (z i).rep g := by + rfl + let φ : A →ₗ[K] (ι → K) := LinearMap.pi fun i => (ψ i).toLinearMap + let V := homogeneousCoordinateSpace I (m + M) + let W := homogeneousCoordinateSpace I M + let U := W.map (LinearMap.mulLeft K (q f)) + have hUV : U ≤ V := by + rintro a ha + obtain ⟨b, hb, rfl⟩ := Submodule.mem_map.mp ha + exact homogeneousCoordinateSpace_mul_mem I hf hb + have hUker : U ≤ φ.ker := by + rintro a ha + obtain ⟨b, hb, rfl⟩ := Submodule.mem_map.mp ha + apply LinearMap.mem_ker.mpr + ext i + change ψ i (q f * b) = 0 + rw [map_mul, hψ, hzf, zero_mul] + have hf0 : q f ≠ 0 := by + exact fun he => hfI (Ideal.Quotient.eq_zero_iff_mem.mp he) + have hUdim : Module.finrank K U = projectiveHilbertFunction I M := + (Submodule.equivMapOfInjective (LinearMap.mulLeft K (q f)) + (fun a b h => mul_left_cancel₀ hf0 h) W).finrank_eq.symm + obtain ⟨h, hhom, heval⟩ := exists_projective_point_separators_degree z hz hM + have htop : V.map φ = ⊤ := by + apply top_unique + intro w _ + apply Submodule.mem_map.mpr + refine ⟨∑ j, w j • q (h j), ?_, ?_⟩ + · apply Submodule.sum_mem + intro j _ + apply Submodule.smul_mem + exact Submodule.mem_map.mpr ⟨h j, hhom j, rfl⟩ + · ext i + change ψ i (∑ j, w j • q (h j)) = w i + simp [hψ, heval] + have h := finrank_map_add_kernel_subspace_le φ V U hUV hUker + rw [htop, finrank_top, Module.finrank_pi, hUdim] at h + exact h + +/-- A proper section of degree `m` meets a degree-`d` projective prime curve +in at most `dm` distinct points over the base field. -/ +theorem projective_section_card_le_degree {n d m : ℕ} {ι : Type*} + [Fintype ι] + (I : Ideal (MvPolynomial (Fin (n + 1)) K)) [I.IsPrime] + (hdegree : HasProjectiveCurveDegree I d) + (f : MvPolynomial (Fin (n + 1)) K) (hf : f.IsHomogeneous m) (hfI : f ∉ I) + (z : ι → Projectivization K (Fin (n + 1) → K)) (hz : Function.Injective z) + (hzI : ∀ i, z i ∈ projectiveIdealZeroLocus I) + (hzf : ∀ i, eval (z i).rep f = 0) : + Fintype.card ι ≤ d * m := by + classical + obtain ⟨N, c, hN⟩ := hdegree + let M := max N (Fintype.card ι) + have hlow := projective_section_hilbert_drop I f hf hfI z hz hzI hzf + (show Fintype.card ι - 1 ≤ m + M by dsimp [M]; omega) + have h0 := hN M (le_max_left _ _) + have h1 := hN (m + M) (by dsimp [M]; omega) + have hlowZ : (Fintype.card ι : ℤ) + projectiveHilbertFunction I M ≤ + projectiveHilbertFunction I (m + M) := by exact_mod_cast hlow + rw [h0, h1] at hlowZ + push_cast at hlowZ + have hcard : (Fintype.card ι : ℤ) ≤ (d : ℤ) * m := by linarith + exact_mod_cast hcard + +/-- A point of the zero locus of a homogeneous ideal stays in it after scaling. -/ +lemma homogeneous_ideal_eval_smul + {K σ : Type*} [Field K] (I : Ideal (MvPolynomial σ K)) + (hI : IsHomogeneousIdeal I) + (v : σ → K) (hv : v ∈ idealZeroLocus I) + (a : K) (f : MvPolynomial σ K) (hf : f ∈ I) : + eval (a • v) f = 0 := by + classical + conv_lhs => rw [← sum_homogeneousComponent f] + rw [map_sum] + apply Finset.sum_eq_zero + intro N _ + rw [homogeneous_eval_smul (homogeneousComponent_isHomogeneous _ _), + hv _ (hI f hf N), mul_zero] + +end +end CCDN diff --git a/LeanPool/CCDN/Algebra/ProjectiveTranslation.lean b/LeanPool/CCDN/Algebra/ProjectiveTranslation.lean new file mode 100644 index 0000000000..50376aafa2 --- /dev/null +++ b/LeanPool/CCDN/Algebra/ProjectiveTranslation.lean @@ -0,0 +1,146 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.Algebra.HomogeneousTranslation +public import LeanPool.CCDN.Height.ProjectiveBox + +/-! Bounded integral shears act on all projective representatives, preserve +primitivity, and preserve distinctness of their projective classes. -/ + +@[expose] public section +namespace CCDN +noncomputable section +open MvPolynomial + +/-- The shear of a point `x` by `a`, keeping `x 0` and sending `x (j + 1)` to +`x (j + 1) + a j * x 0`; on the affine chart `x 0 = 1` it is the translation by `a`. -/ +def homogeneousPointTranslation {R : Type*} [CommRing R] {n : ℕ} + (a : Fin n → R) (x : Fin (n + 1) → R) : Fin (n + 1) → R := + Fin.cons (x 0) (fun j => x j.succ + a j * x 0) + +/-- The shear by `-a` undoes the shear by `a`. -/ +lemma homogeneousPointTranslation_inverse {R : Type*} [CommRing R] {n : ℕ} + (a : Fin n → R) (x : Fin (n + 1) → R) : + homogeneousPointTranslation (-a) (homogeneousPointTranslation a x) = x := by + funext i + refine Fin.cases ?_ (fun j => ?_) i + · rfl + · simp [homogeneousPointTranslation] + +private lemma homogeneousPointTranslation_injective {R : Type*} [CommRing R] {n : ℕ} + (a : Fin n → R) : Function.Injective (homogeneousPointTranslation a) := + Function.LeftInverse.injective (homogeneousPointTranslation_inverse a) + +/-- The shear of a nonzero point is nonzero. -/ +lemma homogeneousPointTranslation_ne_zero {R : Type*} [CommRing R] {n : ℕ} + (a : Fin n → R) {x : Fin (n + 1) → R} (hx : x ≠ 0) : + homogeneousPointTranslation a x ≠ 0 := by + intro h + apply hx + apply homogeneousPointTranslation_injective a + have hzero : homogeneousPointTranslation a 0 = 0 := by + funext i + refine Fin.cases ?_ (fun j => ?_) i <;> simp [homogeneousPointTranslation] + exact h.trans hzero.symm + +private lemma homogeneousPointTranslation_smul {K : Type*} [Field K] {n : ℕ} + (a : Fin n → K) (c : K) (x : Fin (n + 1) → K) : + homogeneousPointTranslation a (c • x) = c • homogeneousPointTranslation a x := by + funext i + refine Fin.cases ?_ (fun j => ?_) i + · rfl + · simp only [homogeneousPointTranslation, Fin.cons_succ, Pi.smul_apply, smul_eq_mul] + ring + +private lemma homogeneousPointTranslation_map {R S : Type*} [CommRing R] [CommRing S] + {n : ℕ} (φ : R →+* S) (a : Fin n → R) (x : Fin (n + 1) → R) : + φ ∘ homogeneousPointTranslation a x = homogeneousPointTranslation (φ ∘ a) (φ ∘ x) := by + funext i + refine Fin.cases ?_ (fun j => ?_) i + · rfl + · simp [homogeneousPointTranslation] + +/-- An integral shear preserves the greatest common divisor of the coordinates of an integer +point; in particular it preserves primitivity. -/ +lemma homogeneousPointTranslation_gcd {n : ℕ} (a : Fin n → ℤ) + (x : Fin (n + 1) → ℤ) : + Finset.univ.gcd (homogeneousPointTranslation a x) = Finset.univ.gcd x := by + have hdiv (b : Fin n → ℤ) (y : Fin (n + 1) → ℤ) : + Finset.univ.gcd y ∣ Finset.univ.gcd (homogeneousPointTranslation b y) := by + apply Finset.dvd_gcd + intro i _ + refine Fin.cases ?_ (fun j => ?_) i + · exact Finset.gcd_dvd (Finset.mem_univ 0) + · exact dvd_add (Finset.gcd_dvd (Finset.mem_univ j.succ)) + (dvd_mul_of_dvd_right (Finset.gcd_dvd (Finset.mem_univ 0)) (b j)) + have hback := hdiv (-a) (homogeneousPointTranslation a x) + rw [homogeneousPointTranslation_inverse] at hback + apply dvd_antisymm_of_normalize_eq Finset.normalize_gcd Finset.normalize_gcd + hback (hdiv a x) + +/-- An integral shear preserves distinctness of projective classes: if the nonzero integer points +`z i` represent pairwise distinct points of projective space over `ℚ`, so do their shears. -/ +lemma homogeneousPointTranslation_projective_injective {n t : ℕ} + (a : Fin n → ℤ) (z : Fin t → IntPoint (n + 1)) (hz0 : ∀ i, z i ≠ 0) + (hinj : Function.Injective (fun i => Projectivization.mk ℚ (rationalPoint (z i)) + (rationalPoint_ne_zero (hz0 i)))) : + Function.Injective (fun i => Projectivization.mk ℚ + (rationalPoint (homogeneousPointTranslation a (z i))) + (rationalPoint_ne_zero (homogeneousPointTranslation_ne_zero a (hz0 i)))) := by + intro i j he + apply hinj + obtain ⟨c, hc⟩ := (Projectivization.mk_eq_mk_iff ℚ _ _ _ _).mp he + apply (Projectivization.mk_eq_mk_iff ℚ _ _ _ _).mpr + refine ⟨c, ?_⟩ + change (c : ℚ) • ((Int.castRingHom ℚ) ∘ homogeneousPointTranslation a (z j)) = + (Int.castRingHom ℚ) ∘ homogeneousPointTranslation a (z i) at hc + rw [homogeneousPointTranslation_map, homogeneousPointTranslation_map, + ← homogeneousPointTranslation_smul] at hc + exact homogeneousPointTranslation_injective _ hc + +/-- If `z` lies in the box of radius `B ≥ 1` and every coordinate of `a` lies in `[0, d]`, then +the shear of `z` by `-a` lies in the box of radius `(d + 1) * B`. -/ +lemma homogeneousPointTranslation_box {n d : ℕ} {B : ℝ} (hB : 1 ≤ B) + (a : Fin n → ℤ) (ha : ∀ j, 0 ≤ a j ∧ a j ≤ d) + (z : IntPoint (n + 1)) (hz : InBox z B) (j : Fin (n + 1)) : + |((homogeneousPointTranslation (-a) z j : ℤ) : ℝ)| ≤ ((d : ℝ) + 1) * B := by + have hd0 : (0 : ℝ) ≤ d := by positivity + refine Fin.cases ?_ (fun k => ?_) j + · change |(z 0 : ℝ)| ≤ _ + nlinarith [hz 0] + · change |((z k.succ + -a k * z 0 : ℤ) : ℝ)| ≤ _ + rw [Int.cast_add, Int.cast_mul, Int.cast_neg] + have hak : |(a k : ℝ)| ≤ d := by + rw [abs_of_nonneg (by exact_mod_cast (ha k).1)] + exact_mod_cast (ha k).2 + calc + _ ≤ |(z k.succ : ℝ)| + |-(a k : ℝ) * (z 0 : ℝ)| := abs_add_le _ _ + _ = |(z k.succ : ℝ)| + |(a k : ℝ)| * |(z 0 : ℝ)| := by rw [abs_mul, abs_neg] + _ ≤ B + (d : ℝ) * B := by + gcongr + · exact hz _ + · exact hz _ + _ = _ := by ring + +/-- The homogeneous translation commutes with coefficient maps: mapping the coefficients of the +homogeneous translation of `f` by `a` along `φ` gives the homogeneous translation of the mapped +polynomial by `φ ∘ a`. -/ +lemma homogeneousTranslation_map {R S : Type*} [CommRing R] [CommRing S] {n : ℕ} + (φ : R →+* S) (a : Fin n → R) (f : MvPolynomial (Fin (n + 1)) R) : + map φ (homogeneousTranslation R a f) = + homogeneousTranslation S (φ ∘ a) (map φ f) := by + change map φ (eval₂ C _ f) = eval₂ C _ (map φ f) + rw [map_eval₂] + congr 1 + funext i + refine Fin.cases ?_ (fun j => ?_) i <;> simp + +end +end CCDN diff --git a/LeanPool/CCDN/Algebra/RadialHomogeneousMap.lean b/LeanPool/CCDN/Algebra/RadialHomogeneousMap.lean new file mode 100644 index 0000000000..08009f0c29 --- /dev/null +++ b/LeanPool/CCDN/Algebra/RadialHomogeneousMap.lean @@ -0,0 +1,67 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.Algebra.RadialMap +public import LeanPool.CCDN.Hilbert.InfinitySection + +/-! Radial scaling records the homogeneous components as coefficients of a +single polynomial. -/ + +@[expose] public section +namespace CCDN +noncomputable section +open MvPolynomial + +/-- The `K`-algebra map sending `f` to `∑ n, [f_n] T ^ n`, where `f_n` is the degree-`n` +homogeneous component of `f` and `[·]` its class modulo `I`; it lands in the homogeneous +coordinate algebra `projectiveCoordinateAlgebra I`. -/ +def quotientRadialMap {K σ : Type*} [Field K] (I : Ideal (MvPolynomial σ K)) : + MvPolynomial σ K →ₐ[K] projectiveCoordinateAlgebra I where + toFun f := ⟨Polynomial.map (Ideal.Quotient.mk I) (radialMap f), by + intro n + rw [Polynomial.coeff_map, radialMap_coeff] + exact ⟨homogeneousComponent n f, + (mem_restrictTotalDegree _ _ _).mpr + (homogeneousComponent_isHomogeneous n f).totalDegree_le, rfl⟩⟩ + map_zero' := by apply Subtype.ext; simp + map_one' := by apply Subtype.ext; simp + map_add' f g := by apply Subtype.ext; simp + map_mul' f g := by apply Subtype.ext; simp + commutes' c := by + apply Subtype.ext + simp only [algebraMap_eq, radialMap_C, Polynomial.map_C, SubalgebraClass.coe_algebraMap, + Polynomial.algebraMap_apply, Polynomial.C_inj] + exact (Ideal.Quotient.mkₐ K I).commutes c + +/-- The coefficient of `T ^ n` in the radial image of `f` is the class modulo `I` of the +degree-`n` homogeneous component of `f`. -/ +lemma quotientRadialMap_coeff {K σ : Type*} [Field K] + (I : Ideal (MvPolynomial σ K)) (f : MvPolynomial σ K) (n : ℕ) : + ((quotientRadialMap I f : projectiveCoordinateAlgebra I) : + Polynomial (MvPolynomial σ K ⧸ I)).coeff n = + (Ideal.Quotient.mk I) (homogeneousComponent n f) := by + change (Polynomial.map (Ideal.Quotient.mk I) (radialMap f)).coeff n = _ + rw [Polynomial.coeff_map, radialMap_coeff] + +/-- The radial image of a homogeneous polynomial `f` of degree `n` is the class of `f` +placed in degree `n` of the homogeneous coordinate algebra. -/ +lemma quotientRadialMap_homogeneous {K σ : Type*} [Field K] + (I : Ideal (MvPolynomial σ K)) (f : MvPolynomial σ K) (n : ℕ) (hf : f.IsHomogeneous n) : + quotientRadialMap I f = projectiveMonomialMap I n + ⟨(Ideal.Quotient.mk I) f, + ⟨f, (mem_restrictTotalDegree _ _ _).mpr hf.totalDegree_le, rfl⟩⟩ := by + apply Subtype.ext + ext m + rw [quotientRadialMap_coeff, projectiveMonomialMap_val, Polynomial.coeff_monomial, + homogeneousComponent_of_mem hf] + split_ifs <;> simp_all + +end +end CCDN diff --git a/LeanPool/CCDN/Algebra/RadialMap.lean b/LeanPool/CCDN/Algebra/RadialMap.lean new file mode 100644 index 0000000000..65053af6c1 --- /dev/null +++ b/LeanPool/CCDN/Algebra/RadialMap.lean @@ -0,0 +1,76 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import Mathlib.RingTheory.MvPolynomial.Homogeneous + +/-! Radial scaling `X j ↦ X j * T` of a multivariate polynomial; the coefficient of +`T ^ n` is the homogeneous component of degree `n`. -/ + +@[expose] public section +namespace CCDN +noncomputable section +open MvPolynomial + +/-- Replace every coordinate by itself times the same scalar parameter. -/ +def radialMap {K σ : Type*} [CommRing K] : + MvPolynomial σ K →+* Polynomial (MvPolynomial σ K) := + MvPolynomial.eval₂Hom (Polynomial.C.comp MvPolynomial.C) + (fun j => Polynomial.C (MvPolynomial.X j) * Polynomial.X) + +/-- The radial map sends a constant `C c` to the constant polynomial `C (C c)`. -/ +@[simp] lemma radialMap_C {K σ : Type*} [CommRing K] (c : K) : + radialMap (MvPolynomial.C c : MvPolynomial σ K) = Polynomial.C (MvPolynomial.C c) := by + simp [radialMap] + +/-- The radial map sends the variable `X j` to `X j * T`. -/ +@[simp] lemma radialMap_X {K σ : Type*} [CommRing K] (j : σ) : + radialMap (MvPolynomial.X j : MvPolynomial σ K) = + Polynomial.C (MvPolynomial.X j) * Polynomial.X := by simp [radialMap] + +private lemma radialMap_monomial {K σ : Type*} [CommRing K] (e : σ →₀ ℕ) (c : K) : + radialMap (monomial e c) = Polynomial.C (monomial e c) * Polynomial.X ^ e.degree := by + classical + rw [monomial_eq, map_mul, radialMap_C, map_finsuppProd] + simp only [map_pow, radialMap_X, mul_pow] + rw [Finsupp.prod_mul] + simp only [← map_pow, Finsupp.prod] + rw [← map_prod, Finset.prod_pow_eq_pow_sum] + rw [← mul_assoc, ← Polynomial.C_mul] + rfl + +/-- The coefficient of `T ^ n` in the radial image of `f` is the homogeneous component of +`f` of degree `n`. -/ +lemma radialMap_coeff {K σ : Type*} [CommRing K] (f : MvPolynomial σ K) (n : ℕ) : + (radialMap f).coeff n = homogeneousComponent n f := by + classical + induction f using MvPolynomial.induction_on' with + | add f g hf hg => simp only [map_add, Polynomial.coeff_add, hf, hg] + | monomial e c => + rw [radialMap_monomial, Polynomial.coeff_C_mul, Polynomial.coeff_X_pow] + rw [homogeneousComponent_of_mem (isHomogeneous_monomial c rfl)] + split_ifs <;> simp_all + +/-- The radial map is injective, since evaluating at `T = 1` recovers the polynomial. -/ +lemma radialMap_injective {K σ : Type*} [CommRing K] : + Function.Injective (radialMap : MvPolynomial σ K →+* Polynomial (MvPolynomial σ K)) := by + have he : (Polynomial.evalRingHom 1).comp + (radialMap : MvPolynomial σ K →+* Polynomial (MvPolynomial σ K)) = RingHom.id _ := by + apply MvPolynomial.ringHom_ext <;> intro <;> simp + exact Function.LeftInverse.injective (fun p => RingHom.congr_fun he p) + +/-- The constant coefficient of the radial image of `f` is the constant `f(0)`. -/ +lemma constantCoeff_comp_radialMap {K σ : Type*} [CommRing K] : + Polynomial.constantCoeff.comp + (radialMap : MvPolynomial σ K →+* Polynomial (MvPolynomial σ K)) = + C.comp (eval (fun _ => (0 : K))) := by + apply MvPolynomial.ringHom_ext <;> intro <;> simp + +end +end CCDN diff --git a/LeanPool/CCDN/Algebra/RowSelection.lean b/LeanPool/CCDN/Algebra/RowSelection.lean new file mode 100644 index 0000000000..4ef42e6524 --- /dev/null +++ b/LeanPool/CCDN/Algebra/RowSelection.lean @@ -0,0 +1,120 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.Algebra.MonomialEvaluationMatrix +public import Mathlib.LinearAlgebra.LinearIndependent.Defs +import LeanPool.CCDN.ForMathlib.Matrix + +/-! +Independent rows can be selected from the finite evaluation matrix. +A nonzero annihilator gives the strict rank deficiency required by Bombieri--Vaaler. + +The determinant argument selects independent evaluation rows. This module justifies why a +coefficient vector annihilating those rows vanishes on every point whose row they span. +-/ + +@[expose] public section + +namespace CCDN + +open Matrix + +private theorem dotProduct_eq_zero_of_mem_span {s N : ℕ} + (A : Matrix (Fin s) (Fin N) ℚ) (v : Fin N → ℚ) (hAv : A *ᵥ v = 0) + {r : Fin N → ℚ} (hr : r ∈ Submodule.span ℚ (Set.range A.row)) : r ⬝ᵥ v = 0 := by + induction hr using Submodule.span_induction with + | mem r hr => + obtain ⟨i, rfl⟩ := hr + exact congrFun hAv i + | zero => simp + | add x y hx hy ihx ihy => simp [add_dotProduct, ihx, ihy] + | smul a x hx ih => simp [smul_dotProduct, ih] + +private theorem integer_dotProduct_eq_zero_of_mem_span {s N : ℕ} + (A : Matrix (Fin s) (Fin N) ℤ) (v : Fin N → ℤ) (hAv : A *ᵥ v = 0) + {r : Fin N → ℤ} + (hr : (fun j => (r j : ℚ)) ∈ Submodule.span ℚ (Set.range (A.map ((↑) : ℤ → ℚ)).row)) : + r ⬝ᵥ v = 0 := by + have hAvQ : A.map ((↑) : ℤ → ℚ) *ᵥ (fun j => (v j : ℚ)) = 0 := by + ext i + have hi := congrFun hAv i + change ∑ j, (A i j : ℚ) * (v j : ℚ) = 0 + exact_mod_cast hi + have h := dotProduct_eq_zero_of_mem_span _ _ hAvQ hr + simp only [dotProduct] at h ⊢ + exact_mod_cast h + +/-- If an integer coefficient vector `v` annihilates the evaluation matrix of the monomials +`mon` at the points `points`, then the polynomial with coefficients `v` vanishes at every +integer point `x` whose monomial evaluation row lies in the `ℚ`-span of the matrix rows. -/ +theorem vanishes_of_evaluation_row_mem_span {n N s : ℕ} + (mon : Fin N → (Fin n →₀ ℕ)) (points : Fin s → (Fin n → ℤ)) + (v : Fin N → ℤ) (hAv : evaluationMatrix mon points *ᵥ v = 0) + (x : Fin n → ℤ) + (hx : (fun j => (((mon j).prod (fun k e => x k ^ e) : ℤ) : ℚ)) ∈ + Submodule.span ℚ (Set.range ((evaluationMatrix mon points).map ((↑) : ℤ → ℚ)).row)) : + MvPolynomial.eval x (polynomialOfCoefficients mon v) = 0 := by + have h := integer_dotProduct_eq_zero_of_mem_span _ _ hAv hx + simpa [polynomialOfCoefficients, MvPolynomial.eval_monomial, dotProduct, mul_comm] using h + +end CCDN + +namespace CCDN +open Matrix + +/-- Every matrix with `N` columns has an injectively indexed family of `s ≤ N` linearly +independent rows spanning its row space. -/ +theorem exists_independent_spanning_rows {F ι : Type*} [Field F] {N : ℕ} + (A : Matrix ι (Fin N) F) : + ∃ (s : ℕ) (a : Fin s → ι), Function.Injective a ∧ + LinearIndependent F (A.submatrix a id).row ∧ + Submodule.span F (Set.range (A.submatrix a id).row) = + Submodule.span F (Set.range A.row) ∧ s ≤ N := by + obtain ⟨s, a, ha, hli, hs⟩ := Matrix.exists_fin_linearIndependent_row_span_eq A + exact ⟨s, a, ha, hli, hs, by simpa using hli.fintype_card_le_finrank⟩ + +/-- A nonzero relation on all columns forces every independent row family +to contain strictly fewer rows than columns. -/ +private theorem independent_rows_lt_of_kernel {s N : ℕ} (A : Matrix (Fin s) (Fin N) ℚ) + (hA : LinearIndependent ℚ A.row) (v : Fin N → ℚ) (hv : v ≠ 0) + (hker : A *ᵥ v = 0) : s < N := by + have hle : s ≤ N := by simpa using hA.fintype_card_le_finrank + by_contra h + have he : s = N := by omega + by_cases hs : s = 0 + · have hN : N = 0 := by omega + apply hv + funext j + have hj := j.isLt + omega + let : Nonempty (Fin s) := ⟨⟨0, by omega⟩⟩ + have hspan : Submodule.span ℚ (Set.range A.row) = ⊤ := + hA.span_eq_top_of_card_eq_finrank (by simpa using he) + apply hv + funext j + have hz := dotProduct_eq_zero_of_mem_span A v hker + (r := Pi.single j 1) (by rw [hspan]; trivial) + simpa using hz + +/-- If a rational matrix with `N` columns has a nonzero kernel vector, then some injectively +indexed family of `s < N` linearly independent rows spans its row space. -/ +theorem exists_independent_spanning_rows_lt {t N : ℕ} + (A : Matrix (Fin t) (Fin N) ℚ) (v : Fin N → ℚ) (hv : v ≠ 0) + (hker : A *ᵥ v = 0) : + ∃ (s : ℕ) (a : Fin s → Fin t), Function.Injective a ∧ + LinearIndependent ℚ (A.submatrix a id).row ∧ + Submodule.span ℚ (Set.range (A.submatrix a id).row) = + Submodule.span ℚ (Set.range A.row) ∧ s < N := by + obtain ⟨s, a, ha, hi, hs, _⟩ := exists_independent_spanning_rows A + refine ⟨s, a, ha, hi, hs, independent_rows_lt_of_kernel _ hi v hv ?_⟩ + ext i + exact congrFun hker (a i) + +end CCDN diff --git a/LeanPool/CCDN/Algebra/ScaledHomogenization.lean b/LeanPool/CCDN/Algebra/ScaledHomogenization.lean new file mode 100644 index 0000000000..72f074691c --- /dev/null +++ b/LeanPool/CCDN/Algebra/ScaledHomogenization.lean @@ -0,0 +1,87 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.Algebra.HomogeneousChart + +/-! Integral scaled homogenization. The point (h,x) on the homogeneous model +corresponds to the original integer affine point x, without dividing coordinates. -/ + +@[expose] public section +namespace CCDN +noncomputable section +open MvPolynomial + +/-- The monomial with exponent `homogenizedExponent d m` evaluated at `(h, x)` equals +`h ^ (d - |m|)` times the monomial `x ^ m`. -/ +lemma homogenizedExponent_value {R : Type*} [CommRing R] {n : ℕ} + (d : ℕ) (h : R) (x : Fin n → R) (m : Fin n →₀ ℕ) : + (homogenizedExponent d m).prod (fun j e => ((Fin.cons h x : Fin (n + 1) → R) j) ^ e) = + h ^ (d - m.sum (fun _ e => e)) * m.prod (fun j e => x j ^ e) := by + rw [Finsupp.prod_fintype _ _ (by simp), Fin.prod_univ_succ, + Finsupp.prod_fintype _ _ (by simp)] + rfl + +/-- If `f` has total degree at most `d`, then evaluating `scaledHomogenize d h f` at the point +`(h, x)` gives `h ^ d` times the value of `f` at `x`. -/ +theorem scaledHomogenize_eval {R : Type*} [CommRing R] {n d : ℕ} + (h : R) (f : MvPolynomial (Fin n) R) (hf : f.totalDegree ≤ d) (x : Fin n → R) : + eval (Fin.cons h x) (scaledHomogenize d h f) = h ^ d * eval x f := by + classical + rw [scaledHomogenize, map_sum] + conv_rhs => rw [f.as_sum, map_sum, Finset.mul_sum] + apply Finset.sum_congr rfl + intro m hm + rw [eval_monomial, eval_monomial, homogenizedExponent_value] + calc + _ = (h ^ m.sum (fun _ e => e) * h ^ (d - m.sum (fun _ e => e))) * + (f.coeff m * m.prod (fun j e => x j ^ e)) := by ring + _ = _ := by rw [← pow_add, Nat.add_sub_of_le ((le_totalDegree hm).trans hf)] + +/-- Over a domain, the scaled homogenization of a nonzero polynomial with a nonzero scaling +factor `h` is nonzero. -/ +lemma scaledHomogenize_ne_zero {R : Type*} [CommRing R] [IsDomain R] {n : ℕ} + (d : ℕ) {h : R} (hh : h ≠ 0) {f : MvPolynomial (Fin n) R} (hf : f ≠ 0) : + scaledHomogenize d h f ≠ 0 := by + obtain ⟨m, hm⟩ := exists_coeff_ne_zero hf + intro hz + have hc := scaledHomogenize_coefficient d h f m + have he : h ^ m.sum (fun _ e => e) * f.coeff m = 0 := by simpa [hz] using hc.symm + exact (mul_ne_zero (pow_ne_zero _ hh) hm) he + +/-- A scaling factor at least one does not decrease any original coefficient. -/ +lemma scaledHomogenize_integer_coefficient_lower {n : ℕ} (d : ℕ) {h : ℤ} (hh : 1 ≤ h) + (f : MvPolynomial (Fin n) ℤ) (m : Fin n →₀ ℕ) : + |(f.coeff m : ℝ)| ≤ |((scaledHomogenize d h f).coeff (homogenizedExponent d m) : ℝ)| := by + have hhR : (1 : ℝ) ≤ h := by exact_mod_cast hh + rw [scaledHomogenize_coefficient, Int.cast_mul, Int.cast_pow, abs_mul, abs_pow, + abs_of_nonneg (by linarith : (0 : ℝ) ≤ h)] + have hp := one_le_pow₀ (n := m.sum (fun _ e => e)) hhR + nlinarith [abs_nonneg (f.coeff m : ℝ)] + +/-- A highest-degree nonzero coefficient gains the full factor h^d. -/ +theorem exists_scaledHomogenize_top_coefficient {n : ℕ} (f : MvPolynomial (Fin n) ℤ) + (hf : f ≠ 0) {h : ℤ} (hh : 1 ≤ h) : + ∃ m : Fin n →₀ ℕ, m.sum (fun _ e => e) = f.totalDegree ∧ + (h : ℝ) ^ f.totalDegree ≤ + |((scaledHomogenize f.totalDegree h f).coeff + (homogenizedExponent f.totalDegree m) : ℝ)| := by + obtain ⟨m, hm, hdeg⟩ := f.support.exists_mem_eq_sup (support_nonempty.mpr hf) + (fun m => m.sum (fun _ e => e)) + have hm0 : f.coeff m ≠ 0 := mem_support_iff.mp hm + have hdegree : m.sum (fun _ e => e) = f.totalDegree := hdeg.symm + refine ⟨m, hdegree, ?_⟩ + have hcoef : (1 : ℝ) ≤ |(f.coeff m : ℝ)| := by exact_mod_cast Int.one_le_abs hm0 + have hhR : (1 : ℝ) ≤ h := by exact_mod_cast hh + rw [scaledHomogenize_coefficient, hdegree, Int.cast_mul, Int.cast_pow, abs_mul, abs_pow, + abs_of_nonneg (by linarith : (0 : ℝ) ≤ h)] + nlinarith [show 0 ≤ (h : ℝ) ^ f.totalDegree by positivity] + +end +end CCDN diff --git a/LeanPool/CCDN/Algebra/ScaledHomogenizationPrimitive.lean b/LeanPool/CCDN/Algebra/ScaledHomogenizationPrimitive.lean new file mode 100644 index 0000000000..e599015351 --- /dev/null +++ b/LeanPool/CCDN/Algebra/ScaledHomogenizationPrimitive.lean @@ -0,0 +1,56 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.Algebra.ScaledHomogenization +import Mathlib.Data.Nat.Prime.Int + +/-! A prime scaling preserves primitive integer equations when it does not +divide the constant coefficient. This is the arithmetic input for affine scaling. -/ + +@[expose] public section +namespace CCDN +noncomputable section +open MvPolynomial + +/-- If `f` is a primitive integer polynomial and the prime `p` does not divide its constant +coefficient, then the scaled homogenization `scaledHomogenize d p f` is primitive. -/ +theorem PrimitiveIntegerPolynomial.scaledHomogenize {n : ℕ} {f : MvPolynomial (Fin n) ℤ} + (hf : PrimitiveIntegerPolynomial f) (d : ℕ) {p : ℕ} (hp : p.Prime) + (hp0 : ¬ (p : ℤ) ∣ f.coeff 0) : + PrimitiveIntegerPolynomial (CCDN.scaledHomogenize d (p : ℤ) f) := by + intro c hc + apply Int.isUnit_iff_natAbs_eq.mpr + by_contra hunit + obtain ⟨l, hl, hlc⟩ := Nat.exists_prime_and_dvd hunit + have hlZ : Prime (l : ℤ) := Nat.prime_iff_prime_int.mp hl + have hld : ∀ m, (l : ℤ) ∣ (CCDN.scaledHomogenize d (p : ℤ) f).coeff m := + fun m => (Int.natCast_dvd.mpr hlc).trans (hc m) + have hlp : l ≠ p := by + intro he + subst l + have h := hld (homogenizedExponent d 0) + rw [scaledHomogenize_coefficient] at h + simp only [Finsupp.sum_zero_index, pow_zero, one_mul] at h + exact hp0 h + have hn : ¬ (l : ℤ) ∣ (p : ℤ) := by + intro h + have h' : l ∣ p := by exact_mod_cast h + exact hlp ((Nat.prime_dvd_prime_iff_eq hl hp).mp h') + have hcoeff : ∀ m, (l : ℤ) ∣ f.coeff m := by + intro m + have h := hld (homogenizedExponent d m) + rw [scaledHomogenize_coefficient] at h + rcases hlZ.dvd_or_dvd h with h | h + · exact False.elim (hn (hlZ.dvd_of_dvd_pow h)) + · exact h + exact hlZ.not_isUnit (hf l hcoeff) + +end +end CCDN diff --git a/LeanPool/CCDN/Algebra/ScaledHypersurfaceModel.lean b/LeanPool/CCDN/Algebra/ScaledHypersurfaceModel.lean new file mode 100644 index 0000000000..f204a50392 --- /dev/null +++ b/LeanPool/CCDN/Algebra/ScaledHypersurfaceModel.lean @@ -0,0 +1,83 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.Algebra.ScaledHomogenization +import LeanPool.CCDN.Algebra.HomogeneousChartIrreducible +import LeanPool.CCDN.Algebra.HomogeneousSlice +import LeanPool.CCDN.Algebra.IntegerRationalIrreducibility +import LeanPool.CCDN.Algebra.ScaledHomogenizationPrimitive +import LeanPool.CCDN.ForMathlib.MvPolynomial +import Mathlib.Algebra.MvPolynomial.NoZeroDivisors + +/-! Irreducibility and coefficient maps of scaled homogeneous models in +arbitrary dimension, including reduction modulo primes. -/ + +@[expose] public section +namespace CCDN +noncomputable section +open MvPolynomial + +/-- Scaled homogenization commutes with mapping the coefficients along a ring homomorphism +`φ`, the scaling factor `h` being replaced by `φ h`. -/ +lemma scaledHomogenize_map {R S : Type*} [CommRing R] [CommRing S] {n : ℕ} + (φ : R →+* S) (d : ℕ) (h : R) (f : MvPolynomial (Fin n) R) : + map φ (scaledHomogenize d h f) = scaledHomogenize d (φ h) (map φ f) := by + classical + simp only [scaledHomogenize, map_sum, map_monomial, map_mul, map_pow, coeff_map] + symm + apply Finset.sum_subset (support_map_subset _ _) + intro m _ hm + have hz : φ (f.coeff m) = 0 := by simpa only [coeff_map] using notMem_support_iff.mp hm + simp only [hz, mul_zero, monomial_zero] + +/-- Over a field, every nonzero scaling of an irreducible affine equation +has an irreducible homogeneous model. -/ +theorem scaledHomogenize_irreducible {K : Type*} [Field K] {n : ℕ} + (f : MvPolynomial (Fin n) K) (hir : Irreducible f) {h : K} (hh : h ≠ 0) : + Irreducible (scaledHomogenize f.totalDegree h f) := by + let F := scaledHomogenize f.totalDegree h f + have hF0 : F ≠ 0 := scaledHomogenize_ne_zero _ hh hir.ne_zero + have hFhom : F.IsHomogeneous f.totalDegree := scaledHomogenize_homogeneous h f le_rfl + let G := firstCoordinateScale K h hh F + have hG0 : G ≠ 0 := (firstCoordinateScale K h hh).injective.ne (by simpa using hF0) + have hGhom : G.IsHomogeneous f.totalDegree := firstCoordinateScaleHom_homogeneous h hFhom + have hchart : affineChart n G = C (h ^ f.totalDegree) * f := by + rw [show affineChart n G = homogeneousSlice n h F from + (homogeneousSlice_eq_chart_scale h F).symm] + exact homogeneousSlice_scaledHomogenize h f le_rfl + have hunit : IsUnit (C (h ^ f.totalDegree) : MvPolynomial (Fin n) K) := + (isUnit_iff_ne_zero.mpr (pow_ne_zero _ hh)).map C + have hchartir : Irreducible (affineChart n G) := by + rw [hchart] + exact (irreducible_isUnit_mul hunit).mpr hir + have hchartdeg : (affineChart n G).totalDegree = G.totalDegree := by + rw [hchart, totalDegree_mul_of_isDomain (C_eq_zero.not.mpr (pow_ne_zero _ hh)) hir.ne_zero, + totalDegree_C, zero_add, hGhom.totalDegree hG0] + exact (MulEquiv.irreducible_iff (f := firstCoordinateScale K h hh)).mp + (irreducible_of_affineChart_irreducible G hchartdeg hchartir) + +/-- Prime scaling preserves integer irreducibility whenever it preserves +primitivity; avoiding the constant coefficient discharges that condition. -/ +theorem scaledHomogenize_integer_irreducible {n : ℕ} + (f : MvPolynomial (Fin n) ℤ) (hprim : PrimitiveIntegerPolynomial f) + (hir : Irreducible f) (hd : 0 < f.totalDegree) + {p : ℕ} (hp : p.Prime) (hp0 : ¬ (p : ℤ) ∣ f.coeff 0) : + Irreducible (scaledHomogenize f.totalDegree (p : ℤ) f) := by + apply (hprim.scaledHomogenize f.totalDegree hp hp0).irreducible_of_rational_map + rw [scaledHomogenize_map] + have hdeg : (map (Int.castRingHom ℚ) f).totalDegree = f.totalDegree := + totalDegree_map_of_injective Int.cast_injective _ + rw [← hdeg] + exact scaledHomogenize_irreducible _ (rational_map_irreducible_of_integer f hir hd) + (by simpa only [Int.coe_castRingHom, Int.cast_natCast] using + (show (p : ℚ) ≠ 0 by exact_mod_cast hp.ne_zero)) + +end +end CCDN diff --git a/LeanPool/CCDN/Algebra/SectionImageRank.lean b/LeanPool/CCDN/Algebra/SectionImageRank.lean new file mode 100644 index 0000000000..aa0e3641be --- /dev/null +++ b/LeanPool/CCDN/Algebra/SectionImageRank.lean @@ -0,0 +1,88 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.Hilbert.FilteredIntersection +import LeanPool.CCDN.Algebra.PointSeparation +import Mathlib.LinearAlgebra.Dimension.Finite +import Mathlib.LinearAlgebra.FreeModule.StrongRankCondition + +/-! A proper section bounds independent classes in any algebra-valued image, +not only distinct point evaluations. This is the multiplicity-ready form of +the Hilbert-function argument. -/ + +@[expose] public section +namespace CCDN +noncomputable section + +private lemma section_image_filtered_lower {K B σ ι : Type*} [Field K] + [CommRing B] [Algebra K B] [Finite σ] [Fintype ι] + (p : Ideal (MvPolynomial σ K)) [p.IsPrime] + (φ : (MvPolynomial σ K ⧸ p) →ₐ[K] B) + (h : ι → MvPolynomial σ K) + (hlin : LinearIndependent K (fun i => φ ((Ideal.Quotient.mk p) (h i)))) + (l : MvPolynomial σ K) (hl : l ∉ p) (m : ℕ) (hld : l.totalDegree ≤ m) + (hzero : φ ((Ideal.Quotient.mk p) l) = 0) : + ∃ D : ℕ, ∀ N : ℕ, + Fintype.card ι * (N + 1) ≤ Module.finrank K (filteredCoordinateSpace p (D + m * N)) := by + classical + let := Fintype.ofFinite σ + let D := Finset.univ.sup (fun i => (h i).totalDegree) + refine ⟨D, ?_⟩ + intro N + have hli := section_powers_independent φ (fun i => (Ideal.Quotient.mk p) (h i)) hlin + ((Ideal.Quotient.mk p) l) (fun hzero => hl (Ideal.Quotient.eq_zero_iff_mem.mp hzero)) + hzero (N + 1) + have hmem (t : ι × Fin (N + 1)) : + (Ideal.Quotient.mk p) (h t.1) * ((Ideal.Quotient.mk p) l) ^ t.2.val ∈ + filteredCoordinateSpace p (D + m * N) := by + apply Submodule.mem_map.mpr + refine ⟨h t.1 * l ^ t.2.val, ?_, by simp⟩ + rw [MvPolynomial.mem_restrictTotalDegree] + have hh : (h t.1).totalDegree ≤ D := + Finset.le_sup (f := fun i => (h i).totalDegree) (Finset.mem_univ t.1) + have hp : (l ^ t.2.val).totalDegree ≤ m * N := + (MvPolynomial.totalDegree_pow _ _).trans + ((Nat.mul_le_mul_left _ hld).trans (by nlinarith [t.2.isLt])) + exact (MvPolynomial.totalDegree_mul _ _).trans (Nat.add_le_add hh hp) + let v : ι × Fin (N + 1) → filteredCoordinateSpace p (D + m * N) := + fun t => ⟨(Ideal.Quotient.mk p) (h t.1) * ((Ideal.Quotient.mk p) l) ^ t.2.val, hmem t⟩ + have hv : LinearIndependent K v := + LinearIndependent.of_comp (filteredCoordinateSpace p (D + m * N)).subtype hli + simpa using hv.fintype_card_le_finrank + +/-- Independent polynomial images in a proper section number at most d*m. +This applies to truncated local expansions as well as to point values. -/ +theorem section_image_rank_le {K B σ ι : Type*} [Field K] + [CommRing B] [Algebra K B] [Finite σ] [Fintype ι] + (p : Ideal (MvPolynomial σ K)) [p.IsPrime] + (d : ℕ) (hdegree : HasFilteredCurveDegree p d) + (φ : (MvPolynomial σ K ⧸ p) →ₐ[K] B) (h : ι → MvPolynomial σ K) + (hlin : LinearIndependent K (fun i => φ ((Ideal.Quotient.mk p) (h i)))) + (l : MvPolynomial σ K) (hl : l ∉ p) (m : ℕ) (hld : l.totalDegree ≤ m) + (hzero : φ ((Ideal.Quotient.mk p) l) = 0) : Fintype.card ι ≤ d * m := by + classical + let := Fintype.ofFinite σ + by_cases hm : m = 0 + · have hlC := MvPolynomial.totalDegree_eq_zero_iff_eq_C.mp (show l.totalDegree = 0 by omega) + have hc : l.coeff 0 ≠ 0 := by + intro he + apply hl + rw [hlC, he, map_zero] + exact p.zero_mem + have hu : IsUnit l := hlC ▸ (isUnit_iff_ne_zero.mpr hc).map MvPolynomial.C + have hz : IsUnit (0 : B) := hzero ▸ ((hu.map (Ideal.Quotient.mk p)).map φ.toRingHom) + let : Subsingleton B := subsingleton_of_zero_eq_one (isUnit_zero_iff.mp hz) + have hn := hlin.fintype_card_le_finrank + simpa only [Module.finrank_zero_of_subsingleton, hm, Nat.mul_zero] using hn + obtain ⟨D, hD⟩ := section_image_filtered_lower p φ h hlin l hl m hld hzero + exact hdegree.le_mul_of_growth (Nat.pos_of_ne_zero hm) hD + +end +end CCDN diff --git a/LeanPool/CCDN/Analysis/PrimeCutoff.lean b/LeanPool/CCDN/Analysis/PrimeCutoff.lean new file mode 100644 index 0000000000..27ff4c6de7 --- /dev/null +++ b/LeanPool/CCDN/Analysis/PrimeCutoff.lean @@ -0,0 +1,259 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.Mertens + +/-! +Uniform prime-weighted reciprocal tails and degree cutoffs. + +The Mertens lower bound for the prime sum after excluding a finite +bad-prime set. +-/ + +@[expose] public section +namespace CCDN +open Finset Real + +/-- The explicit constant `log 4 + 6 + E₁` in Mertens' first theorem: for every `x ≥ 1`, +`|∑_{p ≤ x} log p / p - log x| ≤ mertensError` (`Mertens.prime_sum_bounded_error`). -/ +noncomputable def mertensError : ℝ := Real.log 4 + 6 + Mertens.E₁ + +/-- The Mertens error constant `mertensError` is positive. -/ +lemma mertensError_pos : 0 < mertensError := by + have hE : 0 ≤ Mertens.E₁ := tsum_nonneg Mertens.E₁.summand_nonneg + have hl : 0 ≤ Real.log 4 := Real.log_nonneg (by norm_num) + unfold mertensError + linarith + +/-- The first Mertens estimate retains an explicit charge for excluded primes. -/ +theorem prime_sum_lower_sdiff {x : ℝ} (hx : 1 ≤ x) (B : Finset ℕ) : + Real.log x - mertensError - (∑ p ∈ B, Real.log p / p) ≤ + ∑ p ∈ ((Ioc 0 ⌊x⌋₊).filter Nat.Prime) \ B, Real.log p / p := by + let S := (Ioc 0 ⌊x⌋₊).filter Nat.Prime + have hm := (abs_le.mp (Mertens.prime_sum_bounded_error hx)).1 + have he := sum_inter_add_sum_sdiff S B (fun p : ℕ => Real.log p / p) + have hB : (∑ p ∈ S ∩ B, Real.log p / p) ≤ ∑ p ∈ B, Real.log p / p := by + apply sum_le_sum_of_subset_of_nonneg inter_subset_right + intro p _ _ + exact div_nonneg (Real.log_natCast_nonneg p) (Nat.cast_nonneg p) + change Real.log x - mertensError - _ ≤ ∑ p ∈ S \ B, Real.log p / p + change -mertensError ≤ (∑ p ∈ S, Real.log p / p) - Real.log x at hm + linarith + +end CCDN + +namespace CCDN +noncomputable section +open Finset Real + +/-- For `t ≥ 0`, a prime `p` lies in the set of primes in `(0, ⌊t⌋]` if and only if +`p ≤ t`. -/ +lemma prime_mem_upto_iff {t : ℝ} (ht : 0 ≤ t) {p : ℕ} (hp : p.Prime) : + p ∈ ((Ioc 0 ⌊t⌋₊).filter Nat.Prime) ↔ (p : ℝ) ≤ t := by + simp only [mem_filter, mem_Ioc, hp, and_true, hp.pos, true_and] + exact Nat.le_floor_iff ht + +/-- An explicit constant bounding the prime weight `∑ log p / p` over the primes in any +interval `(L, 4 L]` with `L ≥ 1`. -/ +def primeBlockMass : ℝ := Real.log 4 + 2 * mertensError + +/-- The prime block constant `primeBlockMass` is positive. -/ +lemma primeBlockMass_pos : 0 < primeBlockMass := by + have h := mertensError_pos + have hl : 0 ≤ Real.log 4 := Real.log_nonneg (by norm_num) + dsimp [primeBlockMass] + linarith + +/-- Mertens bounds an arbitrary prime interval, with its logarithmic width +and two absolute error terms. -/ +lemma prime_interval_weight_le {L T : ℝ} (hL : 1 ≤ L) (hLT : L ≤ T) + (P : Finset ℕ) (hP : ∀ p ∈ P, p.Prime) + (hlo : ∀ p ∈ P, L < (p : ℝ)) (hhi : ∀ p ∈ P, (p : ℝ) ≤ T) : + (∑ p ∈ P, Real.log p / p) ≤ Real.log T - Real.log L + 2 * mertensError := by + let U := (Ioc 0 ⌊L⌋₊).filter Nat.Prime + let V := (Ioc 0 ⌊T⌋₊).filter Nat.Prime + have hUV : U ⊆ V := by + intro p hp + have hp' := (mem_filter.mp hp).2 + exact (prime_mem_upto_iff (by linarith) hp').mpr + (((prime_mem_upto_iff (by linarith) hp').mp hp).trans hLT) + have hPV : P ⊆ V \ U := by + intro p hp + refine mem_sdiff.mpr ⟨(prime_mem_upto_iff (by linarith) (hP p hp)).mpr (hhi p hp), ?_⟩ + intro hu + exact (not_le_of_gt (hlo p hp)) ((prime_mem_upto_iff (by linarith) (hP p hp)).mp hu) + have hsum : (∑ p ∈ P, Real.log p / p) ≤ ∑ p ∈ V \ U, Real.log p / p := + sum_le_sum_of_subset_of_nonneg hPV (fun _ _ _ => by positivity) + have hsplit := sum_sdiff hUV (f := fun p : ℕ => Real.log p / p) + have hl := (abs_le.mp (Mertens.prime_sum_bounded_error hL)).1 + have ht := (abs_le.mp (Mertens.prime_sum_bounded_error (hL.trans hLT))).2 + change -mertensError ≤ (∑ p ∈ U, Real.log p / p) - Real.log L at hl + change (∑ p ∈ V, Real.log p / p) - Real.log T ≤ mertensError at ht + linarith + +/-- The Mertens bounded error bounds every factor-four prime interval by a +constant independent of its location. -/ +private lemma prime_block_weight_le {L : ℝ} (hL : 1 ≤ L) (S : Finset ℕ) + (hprime : ∀ p ∈ S, p.Prime) (hlo : ∀ p ∈ S, L < (p : ℝ)) + (hhi : ∀ p ∈ S, (p : ℝ) ≤ 4 * L) : + ∑ p ∈ S, Real.log p / p ≤ primeBlockMass := by + have h := prime_interval_weight_le hL (by linarith : L ≤ 4 * L) S hprime hlo hhi + rw [Real.log_mul (by norm_num) (by linarith : L ≠ 0)] at h + dsimp only [primeBlockMass] + linarith + +/-- A prime interval above L has weighted reciprocal-square-root mass +bounded by its Mertens weight divided by sqrt(L). -/ +private lemma prime_block_sqrt_weight_le {L : ℝ} (hL : 1 ≤ L) (S : Finset ℕ) + (hprime : ∀ p ∈ S, p.Prime) (hlo : ∀ p ∈ S, L < (p : ℝ)) + (hhi : ∀ p ∈ S, (p : ℝ) ≤ 4 * L) : + ∑ p ∈ S, Real.log p / p / Real.sqrt p ≤ primeBlockMass / Real.sqrt L := by + have hs : (∑ p ∈ S, Real.log p / p / Real.sqrt p) ≤ + (∑ p ∈ S, Real.log p / p) / Real.sqrt L := by + rw [sum_div] + apply sum_le_sum + intro p hp + exact div_le_div_of_nonneg_left (by positivity) + (Real.sqrt_pos.mpr (by linarith)) (Real.sqrt_le_sqrt (hlo p hp).le) + exact hs.trans (div_le_div_of_nonneg_right (prime_block_weight_le hL S hprime hlo hhi) + (Real.sqrt_nonneg L)) + +/-- Finite dyadic assembly retains a constant times L^(-1/2), rather than +summing over every integer and losing a logarithm. -/ +private lemma prime_sqrt_tail_le_of_pow_bound (n : ℕ) {L : ℝ} (hL : 1 ≤ L) + (S : Finset ℕ) (hprime : ∀ p ∈ S, p.Prime) (hlo : ∀ p ∈ S, L < (p : ℝ)) + (hhi : ∀ p ∈ S, (p : ℝ) ≤ 4 ^ n * L) : + ∑ p ∈ S, Real.log p / p / Real.sqrt p ≤ 2 * primeBlockMass / Real.sqrt L := by + induction n generalizing L S with + | zero => + have hS : S = ∅ := by + apply eq_empty_iff_forall_notMem.mpr + intro p hp + have h1 := hlo p hp + have h2 := hhi p hp + simp only [pow_zero, one_mul] at h2 + linarith + rw [hS, sum_empty] + exact div_nonneg (mul_nonneg (by norm_num) primeBlockMass_pos.le) (Real.sqrt_nonneg _) + | succ n ih => + let A := S.filter (fun p : ℕ => (p : ℝ) ≤ 4 * L) + let B := S \ A + have hA := prime_block_sqrt_weight_le hL A + (fun p hp => hprime p (mem_filter.mp hp).1) + (fun p hp => hlo p (mem_filter.mp hp).1) (fun _ hp => (mem_filter.mp hp).2) + have hB := ih (L := 4 * L) (by linarith) B + (fun p hp => hprime p (mem_sdiff.mp hp).1) + (fun p hp => by + have hm := mem_sdiff.mp hp + have hn : ¬(p : ℝ) ≤ 4 * L := by simpa only [A, mem_filter, hm.1, true_and] using hm.2 + exact lt_of_not_ge hn) + (fun p hp => by + have h := hhi p (mem_sdiff.mp hp).1 + simpa only [pow_succ, mul_assoc] using h) + have he : ∑ p ∈ S, Real.log p / p / Real.sqrt p = + (∑ p ∈ A, Real.log p / p / Real.sqrt p) + + ∑ p ∈ B, Real.log p / p / Real.sqrt p := by + simpa only [add_comm] using + (sum_sdiff (filter_subset (fun p : ℕ => (p : ℝ) ≤ 4 * L) S) + (f := fun p : ℕ => Real.log p / p / Real.sqrt p)).symm + have hsqrt : Real.sqrt (4 * L) = 2 * Real.sqrt L := by + have hfour : Real.sqrt (4 : ℝ) = 2 := + (Real.sqrt_eq_iff_eq_sq (by norm_num) (by norm_num)).mpr (by norm_num) + rw [Real.sqrt_mul (by norm_num), hfour] + rw [he] + rw [hsqrt] at hB + have hL0 : Real.sqrt L ≠ 0 := ne_of_gt (Real.sqrt_pos.mpr (by linarith)) + calc _ ≤ primeBlockMass / Real.sqrt L + 2 * primeBlockMass / (2 * Real.sqrt L) := + add_le_add hA hB + _ = _ := by field_simp; ring + +/-- The reciprocal-square-root tail over any finite set of primes is uniform +in its upper endpoint. -/ +theorem prime_sqrt_tail_le {L : ℝ} (hL : 1 ≤ L) (S : Finset ℕ) + (hprime : ∀ p ∈ S, p.Prime) (hlo : ∀ p ∈ S, L < (p : ℝ)) : + ∑ p ∈ S, Real.log p / p / Real.sqrt p ≤ 2 * primeBlockMass / Real.sqrt L := by + obtain ⟨n, hn⟩ := pow_unbounded_of_one_lt (((S.sup id : ℕ) : ℝ) / L) + (by norm_num : (1 : ℝ) < 4) + apply prime_sqrt_tail_le_of_pow_bound n hL S hprime hlo + intro p hp + have hsup : (p : ℝ) ≤ ((S.sup id : ℕ) : ℝ) := by + exact_mod_cast (le_sup (f := id) hp : p ≤ S.sup id) + exact hsup.trans ((div_lt_iff₀ (by linarith : 0 < L)).mp hn).le + +/-- The exact reciprocal perturbation error, retaining the prime weight +needed for the sharp tail estimate. -/ +private lemma log_div_sqrt_perturbation_exact {p C N : ℝ} (hp : 1 ≤ p) (hC : 0 ≤ C) + (hN : 0 < N) (hupper : N ≤ p + C * Real.sqrt p) : + Real.log p / p - C * (Real.log p / p / Real.sqrt p) ≤ Real.log p / N := by + have hp0 : 0 < p := by linarith + have hs0 : 0 < Real.sqrt p := Real.sqrt_pos.mpr hp0 + have hs : Real.sqrt p ^ 2 = p := Real.sq_sqrt hp0.le + have hrec : 1 / p - C / (p * Real.sqrt p) ≤ 1 / (p + C * Real.sqrt p) := by + apply (le_div_iff₀ (by positivity : 0 < p + C * Real.sqrt p)).mpr + field_simp + nlinarith [sq_nonneg C] + have hlog := mul_le_mul_of_nonneg_left hrec (Real.log_nonneg hp) + calc _ = Real.log p * (1 / p - C / (p * Real.sqrt p)) := by ring + _ ≤ Real.log p * (1 / (p + C * Real.sqrt p)) := hlog + _ ≤ _ := by + rw [mul_one_div] + exact div_le_div_of_nonneg_left (Real.log_nonneg hp) hN hupper + +/-- Good-prime reciprocal sum with an explicit lower cutoff. Its error is +O(C/sqrt(L)), with absolute constants, and its small-prime cost is log(L). -/ +theorem weighted_prime_sum_lower_cutoff {x L C : ℝ} (hx : 1 ≤ x) (hL : 1 ≤ L) + (hC : 0 ≤ C) (B : Finset ℕ) (N : ℕ → ℝ) + (hN : ∀ p ∈ ((Ioc 0 ⌊x⌋₊).filter Nat.Prime) \ B, L < (p : ℝ) → 0 < N p) + (hupper : ∀ p ∈ ((Ioc 0 ⌊x⌋₊).filter Nat.Prime) \ B, L < (p : ℝ) → + N p ≤ p + C * Real.sqrt p) : + Real.log x - Real.log L - 2 * mertensError - (∑ p ∈ B, Real.log p / p) - + C * (2 * primeBlockMass / Real.sqrt L) ≤ + ∑ p ∈ (((Ioc 0 ⌊x⌋₊).filter Nat.Prime) \ B).filter (fun p : ℕ => L < (p : ℝ)), + Real.log p / N p := by + classical + let T := (Ioc 0 ⌊x⌋₊).filter Nat.Prime + let U := (Ioc 0 ⌊L⌋₊).filter Nat.Prime + let S := (T \ B).filter (fun p : ℕ => L < (p : ℝ)) + have hS : S = T \ (B ∪ U) := by + ext p + by_cases hp : p ∈ T + · have hprime := (mem_filter.mp hp).2 + have hU : p ∈ U ↔ (p : ℝ) ≤ L := prime_mem_upto_iff (by linarith) hprime + simp only [S, mem_filter, mem_sdiff, mem_union, hp, true_and, + hU, not_or, not_le] + · simp only [S, mem_filter, mem_sdiff, hp, false_and] + have hcharge : (∑ p ∈ B ∪ U, Real.log p / p) ≤ + (∑ p ∈ B, Real.log p / p) + (∑ p ∈ U, Real.log p / p) := by + have he := sum_union_inter (s₁ := B) (s₂ := U) (f := fun p : ℕ => Real.log p / p) + have hn : 0 ≤ ∑ p ∈ B ∩ U, Real.log p / p := sum_nonneg (fun _ _ => by positivity) + linarith + have hu := (abs_le.mp (Mertens.prime_sum_bounded_error hL)).2 + change (∑ p ∈ U, Real.log p / p) - Real.log L ≤ mertensError at hu + have hm := prime_sum_lower_sdiff hx (B ∪ U) + change Real.log x - mertensError - _ ≤ ∑ p ∈ T \ (B ∪ U), Real.log p / p at hm + rw [← hS] at hm + have hbase : Real.log x - Real.log L - 2 * mertensError - + (∑ p ∈ B, Real.log p / p) ≤ ∑ p ∈ S, Real.log p / p := by linarith + have hprime (p : ℕ) (hp : p ∈ S) : p.Prime := + (mem_filter.mp (mem_sdiff.mp (mem_filter.mp hp).1).1).2 + have htail := prime_sqrt_tail_le hL S hprime (fun _ hp => (mem_filter.mp hp).2) + have hpoint : ∀ p ∈ S, Real.log p / p - C * (Real.log p / p / Real.sqrt p) ≤ + Real.log p / N p := by + intro p hp + have hmem := mem_filter.mp hp + exact log_div_sqrt_perturbation_exact (by exact_mod_cast (hprime p hp).one_le) hC + (hN p hmem.1 hmem.2) (hupper p hmem.1 hmem.2) + have hs := sum_le_sum hpoint + rw [sum_sub_distrib, ← mul_sum] at hs + change _ ≤ ∑ p ∈ S, Real.log p / N p + nlinarith + +end +end CCDN diff --git a/LeanPool/CCDN/Analysis/PrimeProduct.lean b/LeanPool/CCDN/Analysis/PrimeProduct.lean new file mode 100644 index 0000000000..1b1e9c508c --- /dev/null +++ b/LeanPool/CCDN/Analysis/PrimeProduct.lean @@ -0,0 +1,53 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import Mathlib.Analysis.SpecialFunctions.Log.Basic + +/-! +# Combining local divisibility at distinct primes + +Local powers of different primes multiply, and their weighted logarithms give a lower +bound on the logarithm of a positive determinant gcd. Estimating these prime sums uniformly +for curve reductions is a separate part of CCDN Proposition 3.2.6. +-/ + +@[expose] public section + +namespace CCDN + +private theorem prime_power_product_dvd (P : Finset ℕ) (e : ℕ → ℕ) (D : ℤ) + (hP : ∀ p ∈ P, Nat.Prime p) (hdiv : ∀ p ∈ P, (p : ℤ) ^ e p ∣ D) : + (∏ p ∈ P, (p : ℤ) ^ e p) ∣ D := by + apply Finset.prod_dvd_of_coprime ?_ hdiv + intro p hp q hq hpq + have hcop : IsCoprime (p : ℤ) (q : ℤ) := + Nat.isCoprime_iff_coprime.mpr ((Nat.coprime_primes (hP p hp) (hP q hq)).mpr hpq) + exact hcop.pow + +/-- If each prime `p` in the finite set `P` has `p ^ e p` dividing the positive integer `D`, +then `∑ p ∈ P, e p * log p ≤ log D`. -/ +theorem sum_log_prime_powers_le_log (P : Finset ℕ) (e : ℕ → ℕ) {D : ℤ} + (hD : 0 < D) (hP : ∀ p ∈ P, Nat.Prime p) + (hdiv : ∀ p ∈ P, (p : ℤ) ^ e p ∣ D) : + (∑ p ∈ P, (e p : ℝ) * Real.log p) ≤ Real.log D := by + have hle := Int.le_of_dvd hD (prime_power_product_dvd P e D hP hdiv) + have hleR : (∏ p ∈ P, (p : ℝ) ^ e p) ≤ (D : ℝ) := by exact_mod_cast hle + have hpR : ∀ p ∈ P, 0 < (p : ℝ) := by + intro p hp + exact_mod_cast (hP p hp).pos + have hpos : 0 < ∏ p ∈ P, (p : ℝ) ^ e p := by + apply Finset.prod_pos + intro p hp + exact pow_pos (hpR p hp) _ + have hlog := Real.log_le_log hpos hleR + rw [Real.log_prod (fun p hp => (pow_pos (hpR p hp) (e p)).ne')] at hlog + simpa only [Real.log_pow] using hlog + +end CCDN diff --git a/LeanPool/CCDN/Analysis/SmallDegreeSurfaceNumerics.lean b/LeanPool/CCDN/Analysis/SmallDegreeSurfaceNumerics.lean new file mode 100644 index 0000000000..28405c4714 --- /dev/null +++ b/LeanPool/CCDN/Analysis/SmallDegreeSurfaceNumerics.lean @@ -0,0 +1,79 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import Mathlib.Analysis.SpecialFunctions.Pow.Real + +/-! Explicit exponent margins for the small-degree surface argument. +The estimates hold uniformly for every degree at least five and absorb any +fixed power of the logarithm; geometric covering hypotheses are separate. -/ + +@[expose] public section +namespace CCDN +noncomputable section + +/-- For every natural number `d ≥ 5`, `1 / √d ≤ 9 / 20`. -/ +theorem surface_inverse_sqrt_le_nine_twentieths {d : ℕ} (hd : 5 ≤ d) : + 1 / Real.sqrt (d : ℝ) ≤ (9 / 20 : ℝ) := by + have hdR : (5 : ℝ) ≤ d := by exact_mod_cast hd + have hs : (20 / 9 : ℝ) ≤ Real.sqrt (d : ℝ) := + Real.le_sqrt_of_sq_le (by nlinarith) + have hh := one_div_le_one_div_of_le (by norm_num : (0 : ℝ) < 20 / 9) hs + norm_num at hh + simpa only [one_div] using hh + +/-- A fixed logarithmic power costs at most any prescribed positive power, +with an explicit constant independent of the box radius. -/ +theorem logarithm_add_one_pow_le_rpow (k : ℕ) {ε B : ℝ} + (hε : 0 < ε) (hB : 1 ≤ B) : + (Real.log B + 1) ^ k ≤ + ((k.factorial : ℝ) / ε ^ k * Real.exp ε) * B ^ ε := by + have hB0 : 0 < B := by linarith + have hlog : 0 ≤ Real.log B := Real.log_nonneg hB + have hfac : (0 : ℝ) < k.factorial := by exact_mod_cast Nat.factorial_pos k + have hh := Real.pow_div_factorial_le_exp (ε * (Real.log B + 1)) + (show 0 ≤ ε * (Real.log B + 1) by positivity) k + have he : Real.exp (ε * (Real.log B + 1)) = Real.exp ε * B ^ ε := by + rw [Real.rpow_def_of_pos hB0, ← Real.exp_add] + congr 1 + ring + rw [he, mul_pow] at hh + have hm := (div_le_iff₀ hfac).mp hh + calc + _ ≤ (Real.exp ε * B ^ ε * (k.factorial : ℝ)) / ε ^ k := by + apply (le_div_iff₀ (pow_pos hε k)).mpr + simpa only [mul_comm] using hm + _ = _ := by ring + +/-- Retained nonlinear curves with square-root box count and any fixed +polylogarithmic loss contribute O(B) once their number has the +B^(1/sqrt(d)) scale, for every d>=5. -/ +theorem surface_nonlinear_polylog_weight {d : ℕ} (hd : 5 ≤ d) (k : ℕ) + {B : ℝ} (hB : 1 ≤ B) : + B ^ (1 / Real.sqrt (d : ℝ) + 1 / 2) * (Real.log B + 1) ^ k ≤ + ((k.factorial : ℝ) / (1 / 20 : ℝ) ^ k * Real.exp (1 / 20)) * B := by + have hB0 : 0 < B := by linarith + have hlog := logarithm_add_one_pow_le_rpow k (by norm_num : (0 : ℝ) < 1 / 20) hB + let C : ℝ := (k.factorial : ℝ) / (1 / 20 : ℝ) ^ k * Real.exp (1 / 20) + have hC : 0 ≤ C := by positivity + have he : 1 / Real.sqrt (d : ℝ) + 1 / 2 + 1 / 20 ≤ (1 : ℝ) := by + have := surface_inverse_sqrt_le_nine_twentieths hd + linarith + calc + _ ≤ B ^ (1 / Real.sqrt (d : ℝ) + 1 / 2) * (C * B ^ (1 / 20 : ℝ)) := + mul_le_mul_of_nonneg_left hlog (by positivity) + _ = C * B ^ (1 / Real.sqrt (d : ℝ) + 1 / 2 + 1 / 20) := by + rw [Real.rpow_add hB0 (1 / Real.sqrt (d : ℝ) + 1 / 2) (1 / 20)] + ring + _ ≤ C * B := by + exact mul_le_mul_of_nonneg_left + (by simpa only [Real.rpow_one] using Real.rpow_le_rpow_of_exponent_le hB he) hC + +end +end CCDN diff --git a/LeanPool/CCDN/Analysis/SurfaceMonomialWeightNumerics.lean b/LeanPool/CCDN/Analysis/SurfaceMonomialWeightNumerics.lean new file mode 100644 index 0000000000..8d100bf65c --- /dev/null +++ b/LeanPool/CCDN/Analysis/SurfaceMonomialWeightNumerics.lean @@ -0,0 +1,96 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import Mathlib.Algebra.BigOperators.Group.Finset.Defs +public import Mathlib.Basic.Real.Basic +import Mathlib.Algebra.CharP.Defs +import Mathlib.Algebra.Order.Star.Real + +/-! Sharp leading constant for sums of surface standard-monomial degrees. +The numerical argument retains 2/3 of the cutoff in the weighted rank. -/ + +@[expose] public section +namespace CCDN +noncomputable section +open Finset + +private lemma sum_successor_squares_lower (M : ℕ) : + (M : ℝ) ^ 3 / 3 ≤ ∑ i ∈ range M, ((i : ℝ) + 1) ^ 2 := by + induction M with + | zero => simp + | succ M ih => + rw [sum_range_succ] + push_cast + nlinarith [sq_nonneg (M : ℝ), Nat.cast_nonneg (α := ℝ) M] + +/-- If `H k ≥ d * (k - d + 1) ^ 2 / 2` for all `k ≥ d`, then for `M ≥ d` the prefix sum +`∑ k < M, H k` is at least `d * (M - d) ^ 3 / 6`. -/ +theorem surface_rank_prefix_sum_lower (H : ℕ → ℕ) {d M : ℕ} (hdM : d ≤ M) + (hlower : ∀ k, d ≤ k → + (d : ℝ) * ((k : ℝ) - d + 1) ^ 2 / 2 ≤ H k) : + (d : ℝ) * ((M : ℝ) - d) ^ 3 / 6 ≤ ∑ k ∈ range M, (H k : ℝ) := by + have hsum : (d : ℝ) / 2 * ∑ i ∈ range (M - d), ((i : ℝ) + 1) ^ 2 ≤ + ∑ i ∈ range (M - d), (H (d + i) : ℝ) := by + rw [mul_sum] + apply sum_le_sum + intro i _ + have h := hlower (d + i) (by omega) + push_cast at h + convert h using 1 + ring + have hnum := mul_le_mul_of_nonneg_left (sum_successor_squares_lower (M - d)) + (show 0 ≤ (d : ℝ) / 2 by positivity) + rw [Nat.cast_sub hdM] at hnum + have hprefix : (∑ i ∈ range (M - d), (H (d + i) : ℝ)) ≤ + ∑ k ∈ range M, (H k : ℝ) := by + conv_rhs => rw [show M = d + (M - d) by omega, sum_range_add] + exact le_add_of_nonneg_left (sum_nonneg (fun _ _ => Nat.cast_nonneg _)) + convert (hnum.trans hsum).trans hprefix using 1 + ring + +/-- Let `R ≥ 0` satisfy `d * R ^ 2 = 2 * s` and `M - d ≤ R ≤ M + 2`, where `0 < d ≤ M` and +`s ≥ 0`. If `W + d * (M - d) ^ 3 / 6 ≤ M * s`, then `W ≤ (2 / 3) * s * R + (2 * d + 2) * s`. -/ +theorem surface_weight_root_bound {d M s W : ℝ} + (hd : 0 < d) (hM : d ≤ M) (hs : 0 ≤ s) (R : ℝ) (hR : 0 ≤ R) + (hroot : d * R ^ 2 = 2 * s) + (hlo : M - d ≤ R) (hhi : R ≤ M + 2) + (hweight : W + d * (M - d) ^ 3 / 6 ≤ M * s) : + W ≤ (2 / 3 : ℝ) * s * R + (2 * d + 2) * s := by + have ht : 0 ≤ M - d := sub_nonneg.mpr hM + have htR : (M - d) ^ 2 ≤ R ^ 2 := pow_le_pow_left₀ ht hlo 2 + have hprod : R * (M - d) ≤ R ^ 2 := by + simpa only [pow_two] using mul_le_mul_of_nonneg_left hlo hR + have hdiff : 0 ≤ R - (M - d) := sub_nonneg.mpr hlo + have hfactor : R ^ 2 + R * (M - d) + (M - d) ^ 2 ≤ 3 * R ^ 2 := by + linarith only [htR, hprod] + have hcube : R ^ 3 - (M - d) ^ 3 ≤ 3 * (d + 2) * R ^ 2 := by + calc + _ = (R - (M - d)) * (R ^ 2 + R * (M - d) + (M - d) ^ 2) := by ring + _ ≤ (R - (M - d)) * (3 * R ^ 2) := + mul_le_mul_of_nonneg_left hfactor hdiff + _ ≤ (d + 2) * (3 * R ^ 2) := + mul_le_mul_of_nonneg_right (by linarith) (by positivity) + _ = _ := by ring + have hc := mul_le_mul_of_nonneg_left hcube hd.le + have hr3 : d * R ^ 3 = 2 * s * R := by + calc + _ = (d * R ^ 2) * R := by ring + _ = _ := congrArg (fun x : ℝ => x * R) hroot + have hr2 : 3 * d * (d + 2) * R ^ 2 = 6 * (d + 2) * s := by + calc + _ = 3 * (d + 2) * (d * R ^ 2) := by ring + _ = 3 * (d + 2) * (2 * s) := congrArg (fun x : ℝ => 3 * (d + 2) * x) hroot + _ = _ := by ring + have hMs : M * s ≤ (R + d) * s := + mul_le_mul_of_nonneg_right (by linarith) hs + nlinarith only [hweight, hc, hr3, hr2, hMs] + +end +end CCDN diff --git a/LeanPool/CCDN/ArithmeticHeights/Absolute.lean b/LeanPool/CCDN/ArithmeticHeights/Absolute.lean new file mode 100644 index 0000000000..d1891748a9 --- /dev/null +++ b/LeanPool/CCDN/ArithmeticHeights/Absolute.lean @@ -0,0 +1,783 @@ +/- +Copyright (c) 2026 Ralf Stephan. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Ralf Stephan +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.ArithmeticHeights.Extension +public import Mathlib.NumberTheory.Height.Projectivization + +/-! +# The absolute height of a tuple of algebraic numbers + +Mathlib's `Height.mulHeight` is the height **relative** to the field it is computed over: over a +number field `K` it is the `[K : ℚ]`-th power of the classical absolute height. This file defines +the absolute height of a tuple of algebraic numbers, + +`absMulHeight x = mulHeight_F x ^ (1 / [F : ℚ])` with `F = ℚ(x₀, x₁, …)`, + +and proves that the field of definition does not matter: any number field containing the +coordinates computes the same number. It is the tuple analogue of Mathlib's +`NumberField.absMulHeight₁`, which handles a single element through `ℚ⟮x⟯`, and restricts to it. + +## Main definitions + +* `NumberField.absMulHeight x` for `x : ι → K` over a field `K` of characteristic zero, and + `NumberField.absLogHeight x`, its logarithm. A tuple with a coordinate that is not algebraic + gets the junk value `1`, as `NumberField.absMulHeight₁` does. +* `Projectivization.absMulHeight` and `Projectivization.absLogHeight`: the descent to + `Projectivization K (ι → K)` over a field all of whose elements are algebraic. + +## Main results + +* `NumberField.absMulHeight_eq`: over a number field `K`, `absMulHeight x` is + `mulHeight x ^ (finrank ℚ K)⁻¹`. With `NumberField.mulHeight_pow_finrank` of Layer 0.3 this is + the classical statement that the absolute height does not depend on the field of definition. +* `NumberField.absMulHeight_eq_of_mem`: the same, computed in an intermediate field containing + the coordinates rather than in `K` itself. This is the form the other results consume. +* `NumberField.absMulHeight_comp`: invariance under an embedding of fields of characteristic + zero — the absolute height belongs to the algebraic numbers themselves, not to any field + holding them. It is what lets a value computed over `ℚ` be read off in `ℝ` or `ℂ`. +* `NumberField.absMulHeight_eq_absMulHeight₁`: the absolute height of `![x, 1]` is Mathlib's + absolute height of `x`, so the tuple definition extends the one-variable one. With it, + `NumberField.absMulHeight₁_comp` transports Mathlib's `absMulHeight₁` along an embedding — + an algebraic number and each of its conjugates have the same absolute height. +* `NumberField.absMulHeight_smul_eq`: scaling invariance, for an **algebraic** scalar. +* `NumberField.absMulHeight_comp_le`: reindexing does not raise the height, and + `NumberField.absMulHeight₁_le_absMulHeight`, the bound on the coordinates of an affine chart + that Northcott's theorem on projective space runs on. +* `NumberField.absMulHeight_pow` and `NumberField.absMulHeight₁_pow`: the height of a + coordinatewise power, together with `NumberField.absMulHeight₁_zero` and + `NumberField.absMulHeight₁_one`. Kronecker's theorem of Layer 1.4 runs on these. + +## Implementation notes + +⚠ The algebraicity hypothesis on the scalar in `absMulHeight_smul_eq` is not decorative. For `c` +transcendental and `x` a nonzero algebraic tuple, `c • x` has a transcendental coordinate, so its +absolute height is the junk value `1` while that of `x` need not be; `absMulHeight` is then not +constant on the line through `x`. This is refuted among the examples at the end of the file. The +descent to `Projectivization K (ι → K)` is therefore stated over a field with +`[Algebra.IsAlgebraic ℚ K]` — a number field, or `AlgebraicClosure ℚ` — and not over a general +field of characteristic zero, even though `absMulHeight` itself is defined there. + +Two facts about `Height.mulHeight` do the work. Passing from the field `ℚ(x₀, x₁, …)` generated by +the coordinates to a larger number field is `NumberField.mulHeight_pow_finrank` of Layer 0.3, and +the normalizing exponent is what cancels the degree it introduces. Passing between two *isomorphic* +number fields is the degree-one case of the same theorem, recorded here as +`NumberField.mulHeight_comp_ringEquiv`; it is what `absMulHeight_comp` needs, since an embedding +carries `ℚ(x₀, x₁, …)` to the field generated by the image coordinates, not into a fixed field. + +The index type carries `Finite`, as `Height.mulHeight` does, where the roadmap's pinned signature +has `Fintype`; nothing here sums over `ι`. + +## References + +E. Bombieri and W. Gubler, *Heights in Diophantine Geometry*, Cambridge University Press (2006), +§1.5. Their `h(P)` is Definition 1.5.4, the absolute *logarithmic* height; the independence of the +field of definition is Lemma 1.5.2, the invariance under coordinate scaling that makes the height +projective is Lemma 1.5.3, and the affine case — the height of `(x₁, …, xₙ)` read as the height of +`(1 : x₁ : ⋯ : xₙ)` — is 1.5.6. `NumberField.absMulHeight_comp` generalizes their Proposition +1.5.17, which states the same invariance for an element of the absolute Galois group of `ℚ`. + +This is Layer 0.4 of the `ArithmeticHeights` roadmap. +-/ + +public section + +namespace NumberField + +open Function IntermediateField Module + +/-! +### The definition + +The absolute height is computed over `IntermediateField.adjoin ℚ (Set.range x)`, the field the +coordinates generate, and normalized by the inverse of its degree. +-/ + +section Def + +variable {K : Type*} [Field K] [CharZero K] {ι : Type*} [Finite ι] + +/-- The field a tuple of algebraic numbers generates is a number field. Every proof below enters +through this; the definition itself repeats it inline, its exposed body being public. -/ +private lemma numberField_adjoin_range {x : ι → K} (hx : ∀ i, IsIntegral ℚ (x i)) : + NumberField (adjoin ℚ (Set.range x)) := + have : FiniteDimensional ℚ (adjoin ℚ (Set.range x)) := + finiteDimensional_adjoin fun y hy ↦ by obtain ⟨i, rfl⟩ := hy; exact hx i + {} + +open scoped Classical in +/-- The **absolute multiplicative height** of a tuple of algebraic numbers: the height relative to +the field the coordinates generate, taken to the power `1 / [ℚ(x₀, x₁, …) : ℚ]`. A tuple with a +coordinate that is not algebraic gets the junk value `1`. + +This is the tuple analogue of Mathlib's `NumberField.absMulHeight₁`, which takes this route +through `ℚ⟮x⟯`; `absMulHeight_eq_absMulHeight₁` says the two agree where both apply. -/ +@[expose] noncomputable def absMulHeight (x : ι → K) : ℝ := + if hx : ∀ i, IsIntegral ℚ (x i) then + haveI : FiniteDimensional ℚ (adjoin ℚ (Set.range x)) := + finiteDimensional_adjoin fun y hy ↦ by obtain ⟨i, rfl⟩ := hy; exact hx i + haveI : NumberField (adjoin ℚ (Set.range x)) := {} + Height.mulHeight (fun i ↦ (⟨x i, subset_adjoin ℚ _ ⟨i, rfl⟩⟩ : adjoin ℚ (Set.range x))) + ^ ((finrank ℚ (adjoin ℚ (Set.range x)) : ℝ))⁻¹ + else 1 + +/-- The **absolute logarithmic height** of a tuple of algebraic numbers. -/ +@[expose] noncomputable def absLogHeight (x : ι → K) : ℝ := Real.log (absMulHeight x) + +lemma absLogHeight_eq_log_absMulHeight (x : ι → K) : + absLogHeight x = Real.log (absMulHeight x) := + rfl + +/-- The definition, with the `NumberField` instance on the generated field taken from the context +instead of rebuilt; every proof below enters through this. -/ +private lemma absMulHeight_def {x : ι → K} (hx : ∀ i, IsIntegral ℚ (x i)) + [NumberField (adjoin ℚ (Set.range x))] : + absMulHeight x = + Height.mulHeight (fun i ↦ (⟨x i, subset_adjoin ℚ _ ⟨i, rfl⟩⟩ : adjoin ℚ (Set.range x))) + ^ ((finrank ℚ (adjoin ℚ (Set.range x)) : ℝ))⁻¹ := by + rw [absMulHeight, dite_eq_left_of_eq_true (eq_true hx)] + +open scoped Classical in +/-- The junk value: a tuple with a coordinate that is not algebraic has absolute height `1`. -/ +lemma absMulHeight_eq_one_of_not_isIntegral {x : ι → K} (hx : ¬ ∀ i, IsIntegral ℚ (x i)) : + absMulHeight x = 1 := by + rw [absMulHeight, dite_eq_right_of_eq_false (eq_false hx)] + +lemma absLogHeight_eq_zero_of_not_isIntegral {x : ι → K} (hx : ¬ ∀ i, IsIntegral ℚ (x i)) : + absLogHeight x = 0 := by + rw [absLogHeight, absMulHeight_eq_one_of_not_isIntegral hx, Real.log_one] + +end Def + +/-! +### Heights of isomorphic number fields + +`NumberField.mulHeight_pow_finrank` at degree one: an isomorphism of number fields preserves the +relative height. This is the second ingredient of `absMulHeight_comp`, where the two fields +generated by a tuple and by its image are isomorphic but not equal. +-/ + +section RingEquiv + +variable {K L : Type*} [Field K] [Field L] [NumberField K] [NumberField L] {ι : Type*} [Finite ι] + +/-- The relative height is invariant under an isomorphism of number fields, being the degree-one +case of `NumberField.mulHeight_pow_finrank`. -/ +theorem mulHeight_comp_ringEquiv (e : K ≃+* L) (x : ι → K) : + Height.mulHeight (e ∘ x) = Height.mulHeight x := by + let : Algebra K L := e.toRingHom.toAlgebra + have hb : Function.Bijective (Algebra.linearMap K L) := e.bijective + have h1 : finrank K L = 1 := by + rw [← (LinearEquiv.ofBijective (Algebra.linearMap K L) hb).finrank_eq, finrank_self] + simpa [h1, RingHom.algebraMap_toAlgebra] using (mulHeight_pow_finrank (K := K) (L := L) x).symm + +end RingEquiv + +/-! +### The field of definition does not matter + +The two statements of the layer. Both come from `NumberField.mulHeight_pow_finrank`: passing from +`ℚ(x₀, x₁, …)` to a larger field raises the relative height to the degree of the step, which the +normalizing exponent divides out again. +-/ + +section FieldOfDefinition + +variable {K : Type*} [Field K] [CharZero K] {ι : Type*} [Finite ι] + +/-- The arithmetic behind the normalization: raising to the `n`-th power and normalizing by +`1 / (m * n)` is normalizing by `1 / m`. -/ +private lemma rpow_inv_natCast_mul {a : ℝ} (ha : 0 ≤ a) {m n : ℕ} (hm : m ≠ 0) (hn : n ≠ 0) : + (a ^ n) ^ (((m * n : ℕ) : ℝ))⁻¹ = a ^ ((m : ℝ))⁻¹ := by + rw [← Real.rpow_natCast a n, ← Real.rpow_mul ha] + congr 1 + have hm' : (m : ℝ) ≠ 0 := Nat.cast_ne_zero.mpr hm + have hn' : (n : ℝ) ≠ 0 := Nat.cast_ne_zero.mpr hn + push_cast + field_simp + +/-- **The absolute height computed in a field of definition.** Any number field between +`ℚ(x₀, x₁, …)` and `K` gives the same value. -/ +theorem absMulHeight_eq_of_mem {E : IntermediateField ℚ K} [NumberField E] {x : ι → K} + (hx : ∀ i, x i ∈ E) : + absMulHeight x = Height.mulHeight (fun i ↦ (⟨x i, hx i⟩ : E)) ^ ((finrank ℚ E : ℝ))⁻¹ := by + have hint : ∀ i, IsIntegral ℚ (x i) := fun i ↦ + (Algebra.IsIntegral.isIntegral (⟨x i, hx i⟩ : E)).map E.val + have hFE : adjoin ℚ (Set.range x) ≤ E := adjoin_le_iff.mpr (by rintro _ ⟨i, rfl⟩; exact hx i) + have := numberField_adjoin_range hint + let : Algebra (adjoin ℚ (Set.range x)) E := (inclusion hFE).toAlgebra + have : IsScalarTower ℚ (adjoin ℚ (Set.range x)) E := + IsScalarTower.of_algebraMap_eq fun r ↦ ((inclusion hFE).commutes r).symm + have : Module.Finite (adjoin ℚ (Set.range x)) E := + Module.Finite.of_restrictScalars_finite ℚ _ E + let : Module.Free (adjoin ℚ (Set.range x)) E := Module.Free.of_divisionRing _ _ + have hcomp : (algebraMap (adjoin ℚ (Set.range x)) E) ∘ + (fun i ↦ (⟨x i, subset_adjoin ℚ _ ⟨i, rfl⟩⟩ : adjoin ℚ (Set.range x))) + = fun i ↦ (⟨x i, hx i⟩ : E) := rfl + rw [absMulHeight_def hint, ← hcomp, ← mulHeight_pow_finrank, + ← Module.finrank_mul_finrank ℚ (adjoin ℚ (Set.range x)) E, + rpow_inv_natCast_mul (Height.mulHeight_pos _).le + (Module.finrank_pos (R := ℚ) (M := adjoin ℚ (Set.range x))).ne' + (Module.finrank_pos (R := adjoin ℚ (Set.range x)) (M := E)).ne'] + +end FieldOfDefinition + +section NumberFieldBase + +variable {K : Type*} [Field K] [NumberField K] {ι : Type*} [Finite ι] + +/-- **The absolute height over a number field is the relative height normalized by the degree.** +With `NumberField.mulHeight_pow_finrank`, this says that the absolute height does not depend on +the field the coordinates are read in. -/ +theorem absMulHeight_eq (x : ι → K) : + absMulHeight x = Height.mulHeight x ^ ((finrank ℚ K : ℝ))⁻¹ := by + have hint : ∀ i, IsIntegral ℚ (x i) := fun i ↦ Algebra.IsIntegral.isIntegral _ + have := numberField_adjoin_range hint + have : Module.Finite (adjoin ℚ (Set.range x)) K := + Module.Finite.of_restrictScalars_finite ℚ _ K + have hcomp : (algebraMap (adjoin ℚ (Set.range x)) K) ∘ + (fun i ↦ (⟨x i, subset_adjoin ℚ _ ⟨i, rfl⟩⟩ : adjoin ℚ (Set.range x))) = x := rfl + rw [absMulHeight_def hint] + conv_rhs => rw [← hcomp] + rw [← mulHeight_pow_finrank, ← Module.finrank_mul_finrank ℚ (adjoin ℚ (Set.range x)) K, + rpow_inv_natCast_mul (Height.mulHeight_pos _).le + (Module.finrank_pos (R := ℚ) (M := adjoin ℚ (Set.range x))).ne' + (Module.finrank_pos (R := adjoin ℚ (Set.range x)) (M := K)).ne'] + +/-- The logarithmic form: the absolute logarithmic height is the relative one divided by the +degree. -/ +theorem absLogHeight_eq (x : ι → K) : + absLogHeight x = ((finrank ℚ K : ℝ))⁻¹ * Height.logHeight x := by + rw [absLogHeight, absMulHeight_eq, Real.log_rpow (Height.mulHeight_pos x), + Height.logHeight_eq_log_mulHeight] + +end NumberFieldBase + +/-! +### Invariance under an embedding + +The absolute height is attached to the algebraic numbers themselves: an embedding of fields of +characteristic zero leaves it unchanged. This is what makes a value computed over `ℚ` readable +inside `ℝ` or `ℂ`, where `absMulHeight_eq` does not apply because the ambient field is not a +number field. +-/ + +section Comp + +variable {K L : Type*} [Field K] [CharZero K] [Field L] [CharZero L] {ι : Type*} [Finite ι] + +/-- **The absolute height is invariant under an embedding.** The height of `x` is a property of +the algebraic numbers `x i`, not of the field they are read in. -/ +theorem absMulHeight_comp (f : K →ₐ[ℚ] L) (x : ι → K) : + absMulHeight (f ∘ x) = absMulHeight x := by + by_cases hx : ∀ i, IsIntegral ℚ (x i) + · have hfx : ∀ i, IsIntegral ℚ ((f ∘ x) i) := fun i ↦ (hx i).map f + have := numberField_adjoin_range hx + have := numberField_adjoin_range hfx + have hmap : (adjoin ℚ (Set.range x)).map f = adjoin ℚ (Set.range (f ∘ x)) := by + rw [adjoin_map, Set.range_comp] + let e : adjoin ℚ (Set.range x) ≃ₐ[ℚ] adjoin ℚ (Set.range (f ∘ x)) := + ((adjoin ℚ (Set.range x)).equivMap f).trans (equivOfEq hmap) + have hcomp : (e.toRingEquiv : _ → _) ∘ + (fun i ↦ (⟨x i, subset_adjoin ℚ _ ⟨i, rfl⟩⟩ : adjoin ℚ (Set.range x))) + = fun i ↦ (⟨(f ∘ x) i, subset_adjoin ℚ _ ⟨i, rfl⟩⟩ : adjoin ℚ (Set.range (f ∘ x))) := by + funext i + exact Subtype.ext rfl + rw [absMulHeight_def hfx, absMulHeight_def hx, ← hcomp, mulHeight_comp_ringEquiv, + e.toLinearEquiv.finrank_eq] + · rw [absMulHeight_eq_one_of_not_isIntegral hx, absMulHeight_eq_one_of_not_isIntegral] + intro h + exact hx fun i ↦ + ((isAlgebraic_algHom_iff f f.injective).mp (h i).isAlgebraic).isIntegral + +/-- The logarithmic form of `NumberField.absMulHeight_comp`. -/ +theorem absLogHeight_comp (f : K →ₐ[ℚ] L) (x : ι → K) : + absLogHeight (f ∘ x) = absLogHeight x := + congrArg Real.log (absMulHeight_comp f x) + +end Comp + +/-! +### The absolute Arakelov height is invariant under an embedding + +The Arakelov normalization has no absolute form of its own in this development — it is written +`arakelovMulHeight x ^ (finrank ℚ K : ℝ)⁻¹` wherever it is needed — so the statement is that that +expression does not change along a homomorphism of number fields. It is +`NumberField.arakelovMulHeight_pow_finrank` of Layer 0.3 read through the degree, and it is what +lets a conjugate of a tuple be compared with the tuple itself. +-/ + +section ArakelovComp + +variable {F L : Type*} [Field F] [NumberField F] [Field L] [NumberField L] +variable {κ : Type*} [Fintype κ] + +/-- **The absolute Arakelov height is invariant under an embedding of number fields.** Every ring +homomorphism between fields of characteristic zero is a `ℚ`-algebra map, so no compatibility +hypothesis is needed; the degree of the extension it exhibits is exactly what the normalizing +exponent divides out. -/ +theorem arakelovMulHeight_rpow_comp (f : F →+* L) (x : κ → F) : + arakelovMulHeight (f ∘ x) ^ ((finrank ℚ L : ℝ))⁻¹ + = arakelovMulHeight x ^ ((finrank ℚ F : ℝ))⁻¹ := by + let : Algebra F L := f.toAlgebra + have : IsScalarTower ℚ F L := + IsScalarTower.of_algebraMap_eq' (Subsingleton.elim _ _) + have : Module.Finite F L := Module.Finite.of_restrictScalars_finite ℚ F L + have hmul : finrank ℚ F * finrank F L = finrank ℚ L := Module.finrank_mul_finrank ℚ F L + have hkey : arakelovMulHeight x ^ finrank F L = arakelovMulHeight (f ∘ x) := by + simpa [RingHom.algebraMap_toAlgebra] using + arakelovMulHeight_pow_finrank (K := F) (L := L) x + rw [← hkey, ← hmul, rpow_inv_natCast_mul (arakelovMulHeight_pos x).le + (Module.finrank_pos (R := ℚ) (M := F)).ne' (Module.finrank_pos (R := F) (M := L)).ne'] + +end ArakelovComp + +/-! +### Basic API, the affine case, and scaling +-/ + +section Basic + +variable {K : Type*} [Field K] [CharZero K] {ι : Type*} [Finite ι] + +lemma one_le_absMulHeight (x : ι → K) : 1 ≤ absMulHeight x := by + by_cases hx : ∀ i, IsIntegral ℚ (x i) + · have := numberField_adjoin_range hx + rw [absMulHeight_def hx] + exact Real.one_le_rpow (Height.one_le_mulHeight _) (by positivity) + · rw [absMulHeight_eq_one_of_not_isIntegral hx] + +lemma absMulHeight_pos (x : ι → K) : 0 < absMulHeight x := + zero_lt_one.trans_le <| one_le_absMulHeight x + +lemma absMulHeight_ne_zero (x : ι → K) : absMulHeight x ≠ 0 := + (absMulHeight_pos x).ne' + +lemma absLogHeight_nonneg (x : ι → K) : 0 ≤ absLogHeight x := + Real.log_nonneg <| one_le_absMulHeight x +/-- On a subsingleton index type every tuple has absolute height `1`: the height is computed in +the field the single coordinate generates, where `Height.mulHeight_eq_one_of_subsingleton` is the +product formula. -/ +@[simp] +lemma absMulHeight_eq_one_of_subsingleton [Subsingleton ι] (x : ι → K) : absMulHeight x = 1 := by + by_cases hx : ∀ i, IsIntegral ℚ (x i) + · have := numberField_adjoin_range hx + rw [absMulHeight_def hx, Height.mulHeight_eq_one_of_subsingleton, Real.one_rpow] + · rw [absMulHeight_eq_one_of_not_isIntegral hx] + +@[simp] +lemma absLogHeight_eq_zero_of_subsingleton [Subsingleton ι] (x : ι → K) : absLogHeight x = 0 := by + simp [absLogHeight_eq_log_absMulHeight] + +/-- **The absolute height does not increase under reindexing.** Dropping or repeating coordinates +of an algebraic tuple cannot raise its height; this is `Height.mulHeight_comp_le` read in the +field the coordinates generate. + +⚠ The algebraicity hypothesis is not decorative. Without it the statement is false: if some +coordinate of `x` is transcendental then `absMulHeight x` is the junk value `1`, while `x ∘ f` +may omit that coordinate and have a larger height. -/ +theorem absMulHeight_comp_le {ι' : Type*} [Finite ι'] (f : ι → ι') {x : ι' → K} + (hx : ∀ i, IsIntegral ℚ (x i)) : absMulHeight (x ∘ f) ≤ absMulHeight x := by + have := numberField_adjoin_range hx + have hmem : ∀ i, x i ∈ adjoin ℚ (Set.range x) := fun i ↦ subset_adjoin ℚ _ ⟨i, rfl⟩ + rw [absMulHeight_eq_of_mem hmem, absMulHeight_eq_of_mem (x := x ∘ f) fun i ↦ hmem (f i)] + exact Real.rpow_le_rpow (Height.mulHeight_pos _).le + (Height.mulHeight_comp_le f fun i ↦ (⟨x i, hmem i⟩ : adjoin ℚ (Set.range x))) + (by positivity) + +/-- The logarithmic form of `NumberField.absMulHeight_comp_le`. -/ +theorem absLogHeight_comp_le {ι' : Type*} [Finite ι'] (f : ι → ι') {x : ι' → K} + (hx : ∀ i, IsIntegral ℚ (x i)) : absLogHeight (x ∘ f) ≤ absLogHeight x := + Real.log_le_log (absMulHeight_pos _) (absMulHeight_comp_le f hx) +/-- **The absolute height is invariant under re-indexing.** Unlike +`NumberField.absMulHeight_comp_le`, of which it is the two-sided form, this needs no algebraicity +hypothesis: an equivalence permutes the transcendental coordinates rather than dropping them, so +the two sides take the junk value together. -/ +theorem absMulHeight_comp_equiv {ι' : Type*} [Finite ι'] (e : ι ≃ ι') (x : ι' → K) : + absMulHeight (x ∘ e) = absMulHeight x := by + by_cases hx : ∀ i, IsIntegral ℚ (x i) + · refine le_antisymm (absMulHeight_comp_le e hx) ?_ + have hcomp : (x ∘ e) ∘ e.symm = x := by + funext i + simp + simpa [hcomp] using absMulHeight_comp_le e.symm (x := x ∘ e) fun i ↦ hx (e i) + · rw [absMulHeight_eq_one_of_not_isIntegral hx, + absMulHeight_eq_one_of_not_isIntegral fun h ↦ hx fun i ↦ by simpa using h (e.symm i)] + +/-- The logarithmic form of `NumberField.absMulHeight_comp_equiv`. -/ +theorem absLogHeight_comp_equiv {ι' : Type*} [Finite ι'] (e : ι ≃ ι') (x : ι' → K) : + absLogHeight (x ∘ e) = absLogHeight x := + congrArg Real.log (absMulHeight_comp_equiv e x) + +/-- **The absolute height of a coordinatewise power.** Raising every coordinate of an algebraic +tuple to the `n`-th power raises the height to the `n`-th power. This is `Height.mulHeight_pow` +read in the field the coordinates generate, which the power does not enlarge, so the normalizing +exponent is the same on both sides. -/ +theorem absMulHeight_pow {x : ι → K} (hx : ∀ i, IsIntegral ℚ (x i)) (n : ℕ) : + absMulHeight (x ^ n) = absMulHeight x ^ n := by + have hmem : ∀ i, x i ∈ adjoin ℚ (Set.range x) := fun i ↦ subset_adjoin ℚ _ ⟨i, rfl⟩ + have hmem' : ∀ i, (x ^ n) i ∈ adjoin ℚ (Set.range x) := fun i ↦ pow_mem (hmem i) n + have := numberField_adjoin_range hx + rw [absMulHeight_eq_of_mem hmem, absMulHeight_eq_of_mem hmem', + show (fun i ↦ (⟨(x ^ n) i, hmem' i⟩ : adjoin ℚ (Set.range x))) + = (fun i ↦ (⟨x i, hmem i⟩ : adjoin ℚ (Set.range x))) ^ n from funext fun i ↦ Subtype.ext rfl, + Height.mulHeight_pow, Real.rpow_pow_comm (Height.mulHeight_pos _).le] + +/-- The logarithmic form of `NumberField.absMulHeight_pow`. -/ +theorem absLogHeight_pow {x : ι → K} (hx : ∀ i, IsIntegral ℚ (x i)) (n : ℕ) : + absLogHeight (x ^ n) = n * absLogHeight x := by + rw [absLogHeight, absMulHeight_pow hx, Real.log_pow, absLogHeight] + +open scoped Classical in +/-- **The tuple definition extends Mathlib's one-variable one.** Both sides take the junk value +`1` when `x` is not algebraic. -/ +theorem absMulHeight_eq_absMulHeight₁ (x : K) : absMulHeight ![x, 1] = absMulHeight₁ x := by + by_cases hx : IsIntegral ℚ x + · have : FiniteDimensional ℚ ℚ⟮x⟯ := adjoin.finiteDimensional hx + have : NumberField ℚ⟮x⟯ := {} + have hmem : ∀ i, (![x, 1] : Fin 2 → K) i ∈ ℚ⟮x⟯ := by + intro i + fin_cases i + · exact mem_adjoin_simple_self ℚ x + · exact one_mem _ + have heq : (fun i ↦ (⟨(![x, 1] : Fin 2 → K) i, hmem i⟩ : ℚ⟮x⟯)) + = ![AdjoinSimple.gen ℚ x, 1] := by + funext i + fin_cases i <;> exact Subtype.ext rfl + rw [absMulHeight_eq_of_mem hmem, heq, absMulHeight₁, dite_eq_left_of_eq_true (eq_true hx), + Height.mulHeight₁_eq_mulHeight] + · have hne : ¬ ∀ i, IsIntegral ℚ ((![x, 1] : Fin 2 → K) i) := fun h ↦ hx (by simpa using h 0) + rw [absMulHeight_eq_one_of_not_isIntegral hne, absMulHeight₁, + dite_eq_right_of_eq_false (eq_false hx)] + +/-- The logarithmic form of `NumberField.absMulHeight_eq_absMulHeight₁`. -/ +theorem absLogHeight_eq_absLogHeight₁ (x : K) : absLogHeight ![x, 1] = absLogHeight₁ x := + congrArg Real.log (absMulHeight_eq_absMulHeight₁ x) + +/-- Mathlib's absolute height of an element is at least `1`, the junk value included. -/ +lemma one_le_absMulHeight₁ (x : K) : 1 ≤ absMulHeight₁ x := + absMulHeight_eq_absMulHeight₁ x ▸ one_le_absMulHeight ![x, 1] + +/-- The logarithmic form of `NumberField.one_le_absMulHeight₁`. -/ +lemma absLogHeight₁_nonneg (x : K) : 0 ≤ absLogHeight₁ x := + Real.log_nonneg (one_le_absMulHeight₁ x) + +/-- **Mathlib's absolute height of a power**: the one-variable form of +`NumberField.absMulHeight_pow`. It is what makes the easy half of Kronecker's theorem immediate — +a root of unity has a power of height `1`, hence height `1`. -/ +theorem absMulHeight₁_pow {x : K} (hx : IsIntegral ℚ x) (n : ℕ) : + absMulHeight₁ (x ^ n) = absMulHeight₁ x ^ n := by + rw [← absMulHeight_eq_absMulHeight₁, ← absMulHeight_eq_absMulHeight₁, + show (![x ^ n, 1] : Fin 2 → K) = ![x, 1] ^ n from by funext i; fin_cases i <;> simp, + absMulHeight_pow (fun i ↦ by fin_cases i; exacts [hx, isIntegral_one])] + +/-- The logarithmic form of `NumberField.absMulHeight₁_pow`. -/ +theorem absLogHeight₁_pow {x : K} (hx : IsIntegral ℚ x) (n : ℕ) : + absLogHeight₁ (x ^ n) = n * absLogHeight₁ x := by + rw [absLogHeight₁, absMulHeight₁_pow hx, Real.log_pow, absLogHeight₁] + +/-- **Zero has absolute height `1`**, the value Mathlib's `Height.mulHeight₁_zero` gives for the +relative height. Both this and `NumberField.absMulHeight₁_one` come from `absMulHeight₁_pow`: +`0` and `1` are the idempotents of `K`, and a real number at least `1` that is its own square +is `1`. -/ +lemma absMulHeight₁_zero : absMulHeight₁ (0 : K) = 1 := by + have ha : (0 : ℝ) < absMulHeight₁ (0 : K) := zero_lt_one.trans_le (one_le_absMulHeight₁ _) + have h := absMulHeight₁_pow (isIntegral_zero (R := ℚ) (B := K)) 2 + rw [zero_pow two_ne_zero, pow_two] at h + exact (mul_left_cancel₀ ha.ne' ((mul_one _).trans h)).symm + +/-- The logarithmic form of `NumberField.absMulHeight₁_zero`. -/ +lemma absLogHeight₁_zero : absLogHeight₁ (0 : K) = 0 := by + rw [absLogHeight₁, absMulHeight₁_zero, Real.log_one] + +/-- **One has absolute height `1`**; see `NumberField.absMulHeight₁_zero`. -/ +lemma absMulHeight₁_one : absMulHeight₁ (1 : K) = 1 := by + have ha : (0 : ℝ) < absMulHeight₁ (1 : K) := zero_lt_one.trans_le (one_le_absMulHeight₁ _) + have h := absMulHeight₁_pow (isIntegral_one (R := ℚ) (B := K)) 2 + rw [one_pow, pow_two] at h + exact (mul_left_cancel₀ ha.ne' ((mul_one _).trans h)).symm + +/-- The logarithmic form of `NumberField.absMulHeight₁_one`. -/ +lemma absLogHeight₁_one : absLogHeight₁ (1 : K) = 0 := by + rw [absLogHeight₁, absMulHeight₁_one, Real.log_one] + +/-- **In an affine chart, every coordinate has absolute height at most that of the tuple.** A +tuple with a coordinate equal to `1` is the affine normalization of the projective point it +represents, and this is the bound its coordinates then satisfy. + +It is the absolute counterpart of `Height.mulHeight₁_div_le_mulHeight`, and the one arithmetic +step in Northcott's theorem on projective space. -/ +theorem absMulHeight₁_le_absMulHeight {x : ι → K} (hx : ∀ i, IsIntegral ℚ (x i)) {j : ι} + (hj : x j = 1) (i : ι) : absMulHeight₁ (x i) ≤ absMulHeight x := by + rw [← absMulHeight_eq_absMulHeight₁, show ![x i, 1] = x ∘ ![i, j] from by + funext k; fin_cases k <;> simp [hj]] + exact absMulHeight_comp_le _ hx + +/-- The logarithmic form of `NumberField.absMulHeight₁_le_absMulHeight`. -/ +theorem absLogHeight₁_le_absLogHeight {x : ι → K} (hx : ∀ i, IsIntegral ℚ (x i)) {j : ι} + (hj : x j = 1) (i : ι) : absLogHeight₁ (x i) ≤ absLogHeight x := + Real.log_le_log (zero_lt_one.trans_le (one_le_absMulHeight₁ (x i))) + (absMulHeight₁_le_absMulHeight hx hj i) + +/-- **Mathlib's absolute height of an element is invariant under an embedding.** This is the +one-variable form of `NumberField.absMulHeight_comp`, obtained from it through +`absMulHeight_eq_absMulHeight₁`; it is the statement that an algebraic number and each of its +conjugates have the same absolute height. -/ +theorem absMulHeight₁_comp {L : Type*} [Field L] [CharZero L] (f : K →ₐ[ℚ] L) (x : K) : + absMulHeight₁ (f x) = absMulHeight₁ x := by + rw [← absMulHeight_eq_absMulHeight₁, ← absMulHeight_eq_absMulHeight₁, + show ![f x, 1] = ⇑f ∘ ![x, 1] from by funext i; fin_cases i <;> simp, absMulHeight_comp] + +/-- The logarithmic form of `NumberField.absMulHeight₁_comp`. -/ +theorem absLogHeight₁_comp {L : Type*} [Field L] [CharZero L] (f : K →ₐ[ℚ] L) (x : K) : + absLogHeight₁ (f x) = absLogHeight₁ x := + congrArg Real.log (absMulHeight₁_comp f x) + +/-- **Scaling invariance, for an algebraic scalar.** ⚠ The algebraicity hypothesis is not +decorative: for `c` transcendental and `x` a nonzero algebraic tuple, `c • x` has a transcendental +coordinate and `absMulHeight (c • x)` is the junk value `1`. See the examples at the end of the +file. -/ +theorem absMulHeight_smul_eq (x : ι → K) {c : K} (hc : c ≠ 0) (hc' : IsAlgebraic ℚ c) : + absMulHeight (c • x) = absMulHeight x := by + by_cases hx : ∀ i, IsIntegral ℚ (x i) + · have hfin : (insert c (Set.range x)).Finite := (Set.finite_range x).insert c + have := hfin.to_subtype + have hfd : FiniteDimensional ℚ (adjoin ℚ (insert c (Set.range x))) := + finiteDimensional_adjoin fun y hy ↦ by + rcases hy with rfl | ⟨i, rfl⟩ + · exact hc'.isIntegral + · exact hx i + have : NumberField (adjoin ℚ (insert c (Set.range x))) := {} + have hcE : c ∈ adjoin ℚ (insert c (Set.range x)) := subset_adjoin ℚ _ (Set.mem_insert _ _) + have hxE : ∀ i, x i ∈ adjoin ℚ (insert c (Set.range x)) := fun i ↦ + subset_adjoin ℚ _ (Set.mem_insert_of_mem _ ⟨i, rfl⟩) + have hcxE : ∀ i, (c • x) i ∈ adjoin ℚ (insert c (Set.range x)) := fun i ↦ mul_mem hcE (hxE i) + have hsmul : (fun i ↦ (⟨(c • x) i, hcxE i⟩ : adjoin ℚ (insert c (Set.range x)))) + = (⟨c, hcE⟩ : adjoin ℚ (insert c (Set.range x))) • + fun i ↦ (⟨x i, hxE i⟩ : adjoin ℚ (insert c (Set.range x))) := by + funext i + exact Subtype.ext rfl + rw [absMulHeight_eq_of_mem hcxE, absMulHeight_eq_of_mem hxE, hsmul, + Height.mulHeight_smul_eq_mulHeight _ (fun h ↦ hc (congrArg Subtype.val h))] + · rw [absMulHeight_eq_one_of_not_isIntegral hx, absMulHeight_eq_one_of_not_isIntegral] + intro h + refine hx fun i ↦ ?_ + have hxi : x i = c⁻¹ * (c * x i) := by field_simp + rw [hxi] + exact hc'.inv.isIntegral.mul (h i) + +/-- The logarithmic form of `NumberField.absMulHeight_smul_eq`. -/ +theorem absLogHeight_smul_eq (x : ι → K) {c : K} (hc : c ≠ 0) (hc' : IsAlgebraic ℚ c) : + absLogHeight (c • x) = absLogHeight x := + congrArg Real.log (absMulHeight_smul_eq x hc hc') + +end Basic + +end NumberField + +/-! +### The descent to projective space + +Scaling invariance makes the absolute height a well-defined function on +`Projectivization K (ι → K)` — but only over a field all of whose elements are algebraic, since +that is where `NumberField.absMulHeight_smul_eq` applies to every scalar. +-/ + +namespace Projectivization + +open NumberField Real + +variable {K : Type*} [Field K] [CharZero K] [Algebra.IsAlgebraic ℚ K] {ι : Type*} [Finite ι] + +private lemma absMulHeight_aux (a b : { v : ι → K // v ≠ 0 }) (t : K) (h : a.val = t • b.val) : + NumberField.absMulHeight a.val = NumberField.absMulHeight b.val := + have ht : t ≠ 0 := by + contrapose! h + simpa [h] using a.prop + h ▸ absMulHeight_smul_eq _ ht (Algebra.IsAlgebraic.isAlgebraic t) + +private lemma absLogHeight_aux (a b : { v : ι → K // v ≠ 0 }) (t : K) (h : a.val = t • b.val) : + NumberField.absLogHeight a.val = NumberField.absLogHeight b.val := by + simp only [absLogHeight_eq_log_absMulHeight, absMulHeight_aux a b t h] + +-- We do not expose the bodies of these definitions so that we can keep the "_aux" lemmas +-- above private. + +/-- The absolute multiplicative height of a point of projective space over a field of algebraic +numbers. -/ +noncomputable def absMulHeight (x : Projectivization K (ι → K)) : ℝ := + x.lift (fun r ↦ NumberField.absMulHeight r.val) absMulHeight_aux + +/-- The absolute logarithmic height of a point of projective space over a field of algebraic +numbers. -/ +noncomputable def absLogHeight (x : Projectivization K (ι → K)) : ℝ := + x.lift (fun r ↦ NumberField.absLogHeight r.val) absLogHeight_aux + +lemma absMulHeight_mk {x : ι → K} (hx : x ≠ 0) : + absMulHeight (mk K x hx) = NumberField.absMulHeight x := by + rfl + +lemma absLogHeight_mk {x : ι → K} (hx : x ≠ 0) : + absLogHeight (mk K x hx) = NumberField.absLogHeight x := by + rfl + +lemma absLogHeight_eq_log_absMulHeight (x : Projectivization K (ι → K)) : + absLogHeight x = log (absMulHeight x) := by + rw [← x.mk_rep, absMulHeight_mk, absLogHeight_mk, + NumberField.absLogHeight_eq_log_absMulHeight] + +lemma one_le_absMulHeight (x : Projectivization K (ι → K)) : 1 ≤ absMulHeight x := by + rw [← x.mk_rep, absMulHeight_mk] + exact NumberField.one_le_absMulHeight _ + +lemma absMulHeight_pos (x : Projectivization K (ι → K)) : 0 < absMulHeight x := + zero_lt_one.trans_le <| one_le_absMulHeight x + +lemma absMulHeight_ne_zero (x : Projectivization K (ι → K)) : absMulHeight x ≠ 0 := + (absMulHeight_pos x).ne' + +lemma absLogHeight_nonneg (x : Projectivization K (ι → K)) : 0 ≤ absLogHeight x := by + rw [absLogHeight_eq_log_absMulHeight] + exact log_nonneg x.one_le_absMulHeight +/-- On a subsingleton index type every point of projective space has absolute height `1`; +the projective form of `NumberField.absMulHeight_eq_one_of_subsingleton`. -/ +@[simp] +lemma absMulHeight_eq_one_of_subsingleton [Subsingleton ι] (x : Projectivization K (ι → K)) : + absMulHeight x = 1 := by + rw [← x.mk_rep, absMulHeight_mk] + exact NumberField.absMulHeight_eq_one_of_subsingleton _ + +@[simp] +lemma absLogHeight_eq_zero_of_subsingleton [Subsingleton ι] (x : Projectivization K (ι → K)) : + absLogHeight x = 0 := by + simp [absLogHeight_eq_log_absMulHeight] + +/-- Over a number field the projective absolute height is the projective relative height +normalized by the degree, as it is for tuples. -/ +lemma absMulHeight_eq [NumberField K] (x : Projectivization K (ι → K)) : + absMulHeight x = Projectivization.mulHeight x ^ ((Module.finrank ℚ K : ℝ))⁻¹ := by + rw [← x.mk_rep, absMulHeight_mk, mulHeight_mk, NumberField.absMulHeight_eq] + +end Projectivization + +/-! +### `positivity` extensions + +The absolute heights are positive (multiplicative) resp. nonnegative (logarithmic), exactly as +their relative counterparts in `Mathlib.NumberTheory.Height.Basic`. +-/ + +namespace Mathlib.Meta.Positivity + +open Lean.Meta Qq + +/-- Extension for the `positivity` tactic: `NumberField.absMulHeight` is always positive. -/ +@[positivity NumberField.absMulHeight _] +meta def evalAbsMulHeight : PositivityExt where eval {u α} _ pα? e := + match pα? with | none => pure .none | some _ => do + match u, α, e with + | 0, ~q(ℝ), ~q(@NumberField.absMulHeight $K $KF $KCZ $ι $ιF $a) => + assertInstancesCommute + pure (.positive q(NumberField.absMulHeight_pos $a)) + | _, _, _ => throwError "not NumberField.absMulHeight" + +/-- Extension for the `positivity` tactic: `NumberField.absLogHeight` is always nonnegative. -/ +@[positivity NumberField.absLogHeight _] +meta def evalAbsLogHeight : PositivityExt where eval {u α} _ pα? e := + match pα? with | none => pure .none | some _ => do + match u, α, e with + | 0, ~q(ℝ), ~q(@NumberField.absLogHeight $K $KF $KCZ $ι $ιF $a) => + assertInstancesCommute + pure (.nonnegative q(NumberField.absLogHeight_nonneg $a)) + | _, _, _ => throwError "not NumberField.absLogHeight" + +/-- Extension for the `positivity` tactic: `Projectivization.absMulHeight` is always positive. -/ +@[positivity Projectivization.absMulHeight _] +meta def evalProjAbsMulHeight : PositivityExt where eval {u α} _ pα? e := + match pα? with | none => pure .none | some _ => do + match u, α, e with + | 0, ~q(ℝ), ~q(@Projectivization.absMulHeight $K $KF $KCZ $KA $ι $ιF $a) => + assertInstancesCommute + pure (.positive q(Projectivization.absMulHeight_pos $a)) + | _, _, _ => throwError "not Projectivization.absMulHeight" + +/-- Extension for the `positivity` tactic: `Projectivization.absLogHeight` is always +nonnegative. -/ +@[positivity Projectivization.absLogHeight _] +meta def evalProjAbsLogHeight : PositivityExt where eval {u α} _ pα? e := + match pα? with | none => pure .none | some _ => do + match u, α, e with + | 0, ~q(ℝ), ~q(@Projectivization.absLogHeight $K $KF $KCZ $KA $ι $ιF $a) => + assertInstancesCommute + pure (.nonnegative q(Projectivization.absLogHeight_nonneg $a)) + | _, _, _ => throwError "not Projectivization.absLogHeight" + +end Mathlib.Meta.Positivity + +/-! +### Worked examples + +The layer's acceptance test is that one number has one absolute height, whichever field it is +read in; the rejection test is that the scaling invariance really does need its algebraicity +hypothesis. +-/ + +namespace NumberField + +section Examples + +open Function IntermediateField Module + +/-- Over `ℚ` the absolute height is the relative one, the degree being `1`. The values below +consume it. -/ +private lemma absMulHeight_rat {ι : Type*} [Finite ι] (x : ι → ℚ) : + absMulHeight x = Height.mulHeight x := by + rw [absMulHeight_eq, Module.finrank_self, Nat.cast_one, inv_one, Real.rpow_one] + +/-- `H(3/4) = 4`, from Mathlib's `Rat.mulHeight₁_eq_max`. A definition that does not give `4` here +has the numerator and the denominator confused. -/ +private lemma absMulHeight_fin_two_rat : absMulHeight ![(3 / 4 : ℚ), 1] = 4 := by + rw [absMulHeight_rat, ← Height.mulHeight₁_eq_mulHeight, Rat.mulHeight₁_eq_max] + norm_num + +/-- **Acceptance test.** The same rational tuple has the same absolute height over `ℚ` and over +any number field containing it: this is Layer 0.3's `mulHeight_pow_finrank` and the normalizing +exponent cancelling. -/ +example {K : Type*} [Field K] [NumberField K] {ι : Type*} [Finite ι] (x : ι → ℚ) : + absMulHeight (algebraMap ℚ K ∘ x) = absMulHeight x := + absMulHeight_comp (Algebra.ofId ℚ K) x + +/-- The same number in `ℝ`, where `absMulHeight_eq` does not apply because `ℝ` is not a number +field: `absMulHeight_comp` is what carries the value across. The rejection test below consumes it +as a value. -/ +private lemma absMulHeight_fin_two_real : absMulHeight ![(3 / 4 : ℝ), 1] = 4 := by + rw [show ![(3 / 4 : ℝ), 1] = ⇑(Algebra.ofId ℚ ℝ) ∘ ![(3 / 4 : ℚ), 1] from by + funext i + fin_cases i <;> simp, absMulHeight_comp, absMulHeight_fin_two_rat] + +/-- **Rejection test.** Scaling invariance fails for a transcendental scalar: `c • ![3/4, 1]` has +transcendental coordinates, so its absolute height is the junk value `1`, while the height of +`![3/4, 1]` is `4`. A statement of `absMulHeight_smul_eq` without the algebraicity hypothesis is +refuted here, and with it the descent to `Projectivization K (ι → K)` over a general field of +characteristic zero. -/ +example {c : ℝ} (hc : Transcendental ℚ c) : + absMulHeight (c • ![(3 / 4 : ℝ), 1]) ≠ absMulHeight ![(3 / 4 : ℝ), 1] := by + have hjunk : absMulHeight (c • ![(3 / 4 : ℝ), 1]) = 1 := by + refine absMulHeight_eq_one_of_not_isIntegral fun h ↦ hc ?_ + have h1 : IsIntegral ℚ (c * 1) := h 1 + rw [mul_one] at h1 + exact h1.isAlgebraic + rw [hjunk, absMulHeight_fin_two_real] + norm_num + +end Examples + +end NumberField + +end diff --git a/LeanPool/CCDN/ArithmeticHeights/AdaptedBasis.lean b/LeanPool/CCDN/ArithmeticHeights/AdaptedBasis.lean new file mode 100644 index 0000000000..733e98d841 --- /dev/null +++ b/LeanPool/CCDN/ArithmeticHeights/AdaptedBasis.lean @@ -0,0 +1,373 @@ +/- +Copyright (c) 2026 Ralf Stephan. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Ralf Stephan +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import Mathlib.Algebra.Module.ZLattice.Basic +public import Mathlib.LinearAlgebra.Basis.Fin +public import Mathlib.LinearAlgebra.Dimension.Localization +public import Mathlib.LinearAlgebra.Dimension.RankNullity +public import Mathlib.LinearAlgebra.FreeModule.PID + +/-! +# A lattice basis adapted to a flag + +Let `L` be a discrete `ℤ`-submodule of a finite-dimensional real normed space `E`, and let +`v 0, …, v (n - 1)` be `ℝ`-linearly independent vectors of `L`, with `n = finrank ℝ E`. This file +builds a `ℤ`-basis `b 0, …, b (n - 1)` of `L` adapted to the flag the `v i` span: for every `j`, +the first `j` members of `b` are a `ℤ`-basis of `L ∩ span ℝ (v 0, …, v (j - 1))`. + +## Main definitions + +* `ZLattice.flagPart`: the `j`-th step `L ∩ span ℝ (v 0, …, v (j - 1))` of the flag, as a + `ℤ`-submodule. The construction below is an induction along it, and so is the basis from the + successive minima of Layer 4.6. + +## Main results + +* `ZLattice.exists_basis_adapted`: the adapted basis, stated both integrally — the `ℤ`-span of the + first `j` basis vectors is exactly the part of `L` in the span of the first `j` of the `v i` — + and in the weaker `ℝ`-form, that the two flags of subspaces coincide. +* `ZLattice.exists_basis_mem_span_int_of_mem_span_real`: the consequence the geometry of numbers + uses, that a lattice point in the span of the first `j` of the `v i` is an *integer* combination + of the first `j` basis vectors. +* `ZLattice.exists_extending`: the induction step in isolation — across a saturated step of rank + one, a single vector `y` generates the quotient, in the form `Basis.mkFinSnocOfLE` consumes. +* `ZLattice.extending_congr`: that generating vector is determined only modulo the smaller + module, which is the freedom Layer 4.6 spends on making the new basis vector short. +* `ZLattice.discreteTopology_of_le` and `ZLattice.finrank_int_eq_finrank_real`: the two facts about + a submodule of a discrete lattice that the construction rests on, stated at arbitrary rank — it + is discrete, and its `ℤ`-rank is the `ℝ`-dimension of its span. Minkowski's second theorem needs + both for the sublattices cut out by the flag. + +## Implementation notes + +The construction is the modern one and not Cassels': the part `flagPart L v j` of `L` in the +`j`-th subspace of the flag is saturated in `flagPart L v (j + 1)` — no nonzero multiple of a +lattice point lies in it unless the point does, because the subspaces are `ℝ`-subspaces — so the +quotient is finitely generated and torsion-free, hence free, and its rank is `1` by the +rank-nullity theorem. A generator of that quotient extends a basis of `flagPart L v j` to a basis +of `flagPart L v (j + 1)` through Mathlib's `Basis.mkFinSnocOfLE`, and `n` steps reach `L` itself. + +⚠ **All the analysis is in one rank computation.** Discreteness is used twice: to know that the +parts of `L` are finitely generated over `ℤ`, which is Mathlib's +`instModuleFinite_of_discrete_submodule`, and — indispensably — for +`finrank ℤ (flagPart L v j) = finrank ℝ (span ℝ (flagPart L v j))`, which is Mathlib's +`Real.finrank_eq_int_finrank_of_discrete`. A non-discrete subgroup such as `ℤ + ℤ √2 ⊆ ℝ` is +finitely generated but has `ℤ`-rank larger than the dimension of its span, and then the quotients +above are free of rank `> 1` and no single vector extends the basis. The induction step is +therefore stated with no topology at all: it asks for the rank as a hypothesis. + +⚠ **`IsZLattice ℝ L` is not needed.** The independent family already spans `E` over `ℝ`, so a +lattice that contains it spans `E`; asking for the instance would be asking for a consequence of +the hypotheses. + +## References + +J. W. S. Cassels, *An Introduction to the Geometry of Numbers*, Springer (1959), Chapter I, +Theorem I: for a sublattice `Λ` of a lattice `M` and a basis `a 1, …, a n` of `Λ`, a basis +`b 1, …, b n` of `M` with `a i = ∑_{j ≤ i} v i j • b j` and `v i i ≠ 0` (part B; part A is the +converse, the Hermite normal form of a sublattice). That triangular shape is the `ℝ`-form below, +and the integral form follows from it because the `b i` are a basis of `M`. + +This is the lattice content of Chapter VIII, Lemma 2 — the basis such that `F x < λ i` forces `x` +to be an integer combination of `b 1, …, b (i - 1)` — which is Cassels' Theorem I of Chapter I +applied to the family of Lemma 1, the family realizing the successive minima. + +This is infrastructure for Layers 4.2 and 4.6 of the `ArithmeticHeights` roadmap. +-/ + +public section + +open Module Set Submodule + +variable {E : Type*} [NormedAddCommGroup E] [NormedSpace ℝ E] + +namespace ZLattice + +/-! ### Discreteness and rank -/ + +omit [NormedSpace ℝ E] in +/-- A `ℤ`-submodule of a discrete one is discrete: the inclusion is a continuous injection. This +is stated for a submodule of arbitrary rank, which is what the sublattices cut out by a flag of +subspaces need; Mathlib's instances for lattices assume full rank. -/ +theorem discreteTopology_of_le {L N : Submodule ℤ E} [DiscreteTopology L] (h : N ≤ L) : + DiscreteTopology N := by + refine DiscreteTopology.of_continuous_injective (f := fun x : N ↦ (⟨(x : E), h x.2⟩ : L)) + (continuous_subtype_val.subtype_mk _) fun x y hxy ↦ ?_ + have hxy' : (x : E) = (y : E) := congrArg (fun z : L ↦ (z : E)) hxy + exact Subtype.ext hxy' + +/-- The `ℤ`-rank of a submodule of a discrete lattice is the `ℝ`-dimension of its span. This is +the step discreteness is indispensable for: `ℤ + ℤ √2` has `ℤ`-rank two inside a line. -/ +theorem finrank_int_eq_finrank_real [FiniteDimensional ℝ E] {L N : Submodule ℤ E} + [DiscreteTopology L] (h : N ≤ L) : finrank ℤ N = finrank ℝ (span ℝ (N : Set E)) := by + have hdisc : DiscreteTopology (span ℤ (N : Set E)) := by + rw [Submodule.span_eq] + exact discreteTopology_of_le h + have h2 := Real.finrank_eq_int_finrank_of_discrete hdisc + rw [Set.finrank, Set.finrank, Submodule.span_eq] at h2 + exact h2.symm + +/-! ### The flag of an independent family -/ + +/-- The part of a lattice `L` inside the `ℝ`-span of the first `j` members of a family `v`: the +`j`-th step of the flag along which an adapted basis is built. -/ +@[expose] def flagPart {n : ℕ} (L : Submodule ℤ E) (v : Fin n → E) (j : ℕ) : Submodule ℤ E := + L ⊓ (span ℝ (v '' {i : Fin n | (i : ℕ) < j})).restrictScalars ℤ + +theorem mem_flagPart {n : ℕ} (L : Submodule ℤ E) (v : Fin n → E) (j : ℕ) {x : E} : + x ∈ flagPart L v j ↔ x ∈ L ∧ x ∈ span ℝ (v '' {i : Fin n | (i : ℕ) < j}) := + Iff.rfl + +theorem flagPart_le {n : ℕ} (L : Submodule ℤ E) (v : Fin n → E) (j : ℕ) : flagPart L v j ≤ L := + inf_le_left + +theorem flagPart_zero {n : ℕ} (L : Submodule ℤ E) (v : Fin n → E) : flagPart L v 0 = ⊥ := by + have hempty : {i : Fin n | (i : ℕ) < 0} = (∅ : Set (Fin n)) := by ext i; simp + rw [flagPart, hempty] + simp + +theorem flagPart_mono {n : ℕ} (L : Submodule ℤ E) (v : Fin n → E) {j k : ℕ} (hjk : j ≤ k) : + flagPart L v j ≤ flagPart L v k := by + rintro x ⟨hxL, hxW⟩ + exact ⟨hxL, span_mono (Set.image_mono fun i hi ↦ lt_of_lt_of_le hi hjk) hxW⟩ + +theorem mem_flagPart_of_lt {n : ℕ} (L : Submodule ℤ E) (v : Fin n → E) (hvL : ∀ i, v i ∈ L) + {i : Fin n} {j : ℕ} (hij : (i : ℕ) < j) : v i ∈ flagPart L v j := + ⟨hvL i, subset_span ⟨i, hij, rfl⟩⟩ + +/-- The steps of the flag span the subspaces they were cut out of: no `ℝ`-dimension is lost by +intersecting with the lattice, because the lattice already contains a spanning family. -/ +theorem span_flagPart {n : ℕ} (L : Submodule ℤ E) (v : Fin n → E) (hvL : ∀ i, v i ∈ L) (j : ℕ) : + span ℝ ((flagPart L v j : Submodule ℤ E) : Set E) = span ℝ (v '' {i : Fin n | (i : ℕ) < j}) := + le_antisymm (span_le.2 fun _ hx ↦ hx.2) (span_le.2 (by + rintro _ ⟨i, hi, rfl⟩ + exact subset_span (mem_flagPart_of_lt L v hvL hi))) + +/-- **The steps of the flag are saturated.** A lattice point a nonzero multiple of which lies in +`flagPart L v j` lies there itself: the `j`-th subspace is an `ℝ`-subspace, so it is closed under +dividing by a nonzero integer. -/ +theorem flagPart_saturated {n : ℕ} (L : Submodule ℤ E) (v : Fin n → E) {c : ℤ} (hc : c ≠ 0) + {j : ℕ} {z : E} (hz : z ∈ L) (hcz : c • z ∈ flagPart L v j) : z ∈ flagPart L v j := by + rw [mem_flagPart] + refine ⟨hz, ?_⟩ + have h1 : (c : ℝ) • z ∈ span ℝ (v '' {i : Fin n | (i : ℕ) < j}) := by + rw [Int.cast_smul_eq_zsmul] + exact ((mem_flagPart L v j).1 hcz).2 + have hz' : z = ((c : ℝ))⁻¹ • ((c : ℝ) • z) := by + rw [smul_smul, inv_mul_cancel₀ (by exact_mod_cast hc), one_smul] + rw [hz'] + exact Submodule.smul_mem _ _ h1 + +/-- The `j`-th step of the flag has `ℤ`-rank `j`. This is where discreteness enters. -/ +theorem finrank_flagPart [FiniteDimensional ℝ E] {n : ℕ} (L : Submodule ℤ E) [DiscreteTopology L] + (v : Fin n → E) (hv : LinearIndependent ℝ v) (hvL : ∀ i, v i ∈ L) {j : ℕ} (hj : j ≤ n) : + finrank ℤ (flagPart L v j) = j := by + have hset : v '' {i : Fin n | (i : ℕ) < j} = Set.range (v ∘ Fin.castLE hj) := by + rw [Set.range_comp, Fin.range_castLE] + rw [finrank_int_eq_finrank_real (flagPart_le L v j), span_flagPart L v hvL j, hset, + finrank_span_eq_card (hv.comp _ (Fin.castLE_injective hj)), Fintype.card_fin] + +/-- The last step of the flag is the whole lattice. -/ +theorem flagPart_eq_self [FiniteDimensional ℝ E] {n : ℕ} (L : Submodule ℤ E) (v : Fin n → E) + (hn : n = finrank ℝ E) (hv : LinearIndependent ℝ v) : flagPart L v n = L := by + have huniv : {i : Fin n | (i : ℕ) < n} = (Set.univ : Set (Fin n)) := by ext i; simp + have hWn : span ℝ (v '' {i : Fin n | (i : ℕ) < n}) = ⊤ := by + rw [huniv, Set.image_univ] + exact hv.span_eq_top_of_card_eq_finrank' (by simp [hn]) + rw [flagPart, hWn, Submodule.restrictScalars_top, inf_top_eq] + +/-! ### The induction step -/ + +omit [NormedSpace ℝ E] in +/-- **A generator of a saturated rank-one step.** If `N ≤ N'` are `ℤ`-submodules, `N'` has rank +one more than `N`, and `N` is saturated in `N'` — a point of `N'` a nonzero multiple of which lies +in `N` lies in `N` itself — then a single vector `y` of `N'` generates the quotient, in exactly +the two clauses `Basis.mkFinSnocOfLE` asks for. + +No topology enters: the geometric situation supplies the rank and the saturation, and the rest is +that a finitely generated torsion-free `ℤ`-module of rank `1` is `ℤ`. -/ +theorem exists_extending {N N' : Submodule ℤ E} (hle : N ≤ N') [Module.Finite ℤ N'] + (hsat : ∀ c : ℤ, c ≠ 0 → ∀ z ∈ N', c • z ∈ N → z ∈ N) + (hrank : finrank ℤ N' = finrank ℤ N + 1) : + ∃ y ∈ N', (∀ c : ℤ, ∀ x ∈ N, c • y + x = 0 → c = 0) ∧ + ∀ z ∈ N', ∃ c : ℤ, z + c • y ∈ N := by + classical + set P : Submodule ℤ N' := Submodule.comap N'.subtype N with hP + have hPrank : finrank ℤ P = finrank ℤ N := by + rw [hP, (Submodule.comapSubtypeEquivOfLe hle).finrank_eq] + have hQrank : finrank ℤ (N' ⧸ P) = 1 := by + have h := Submodule.finrank_quotient_add_finrank (R := ℤ) P + omega + have htf : Module.IsTorsionFree ℤ (N' ⧸ P) := by + refine Module.IsTorsionFree.of_smul_eq_zero fun c q hq ↦ ?_ + rcases eq_or_ne c 0 with rfl | hc + · exact Or.inl rfl + refine Or.inr ?_ + obtain ⟨z, rfl⟩ := Submodule.Quotient.mk_surjective P q + rw [← Submodule.Quotient.mk_smul, Submodule.Quotient.mk_eq_zero, hP, + Submodule.mem_comap] at hq + rw [Submodule.Quotient.mk_eq_zero, hP, Submodule.mem_comap] + exact hsat c hc z z.2 (by simpa using hq) + have hfree : Module.Free ℤ (N' ⧸ P) := Module.free_iff_isTorsionFree.2 htf + have hcard : Fintype.card (Module.Free.ChooseBasisIndex ℤ (N' ⧸ P)) = 1 := by + rw [← Module.finrank_eq_card_chooseBasisIndex, hQrank] + set e : Basis (Fin 1) ℤ (N' ⧸ P) := + (Module.Free.chooseBasis ℤ (N' ⧸ P)).reindex (Fintype.equivFinOfCardEq hcard) with he + obtain ⟨y, hy⟩ := Submodule.Quotient.mk_surjective P (e 0) + have hyN : (y : E) ∉ N := by + intro hmem + have : Submodule.Quotient.mk y = (0 : N' ⧸ P) := + (Submodule.Quotient.mk_eq_zero P).2 (by rw [hP, Submodule.mem_comap]; exact hmem) + rw [hy] at this + exact e.ne_zero 0 this + have hli : ∀ c : ℤ, ∀ x ∈ N, c • (y : E) + x = 0 → c = 0 := by + intro c x hx hcx + by_contra hc + refine hyN (hsat c hc _ y.2 ?_) + rw [eq_neg_of_add_eq_zero_left hcx] + exact neg_mem hx + have hsp : ∀ z ∈ N', ∃ c : ℤ, z + c • (y : E) ∈ N := by + intro z hz + refine ⟨-(e.repr (Submodule.Quotient.mk (⟨z, hz⟩ : N')) 0), ?_⟩ + have hz' : Submodule.Quotient.mk (⟨z, hz⟩ : N') + = e.repr (Submodule.Quotient.mk (⟨z, hz⟩ : N')) 0 • e 0 := by + conv_lhs => rw [← e.sum_repr (Submodule.Quotient.mk (⟨z, hz⟩ : N'))] + simp + have hzero : Submodule.Quotient.mk + ((⟨z, hz⟩ : N') + (-(e.repr (Submodule.Quotient.mk (⟨z, hz⟩ : N')) 0)) • y) + = (0 : N' ⧸ P) := by + rw [Submodule.Quotient.mk_add, Submodule.Quotient.mk_smul, hy, neg_smul, ← hz'] + simp + simpa [hP] using (Submodule.Quotient.mk_eq_zero P).1 hzero + exact ⟨(y : E), y.2, hli, hsp⟩ + +omit [NormedSpace ℝ E] in +/-- **The generator is determined only modulo the smaller module.** Moving `y` by an element of +`N` preserves both clauses of `ZLattice.exists_extending`, so the basis vector added at each step +may be shortened before it is added. This is the whole freedom Layer 4.6 has. -/ +theorem extending_congr {N N' : Submodule ℤ E} {y y' : E} (hyy' : y' - y ∈ N) + (hli : ∀ c : ℤ, ∀ x ∈ N, c • y + x = 0 → c = 0) + (hsp : ∀ z ∈ N', ∃ c : ℤ, z + c • y ∈ N) : + (∀ c : ℤ, ∀ x ∈ N, c • y' + x = 0 → c = 0) ∧ ∀ z ∈ N', ∃ c : ℤ, z + c • y' ∈ N := by + constructor + · intro c x hx hcx + refine hli c (c • (y' - y) + x) (N.add_mem (N.smul_mem c hyy') hx) ?_ + rw [← hcx, smul_sub] + abel + · intro z hz + obtain ⟨c, hc⟩ := hsp z hz + refine ⟨c, ?_⟩ + have heq : z + c • y' = z + c • y + c • (y' - y) := by + rw [smul_sub] + abel + rw [heq] + exact N.add_mem hc (N.smul_mem c hyy') + +/-! ### The adapted basis -/ + +/-- **A `ℤ`-basis adapted to the flag of an independent family.** For `ℝ`-linearly independent +lattice vectors `v 0, …, v (n - 1)` spanning `E`, some `ℤ`-basis `b` of `L` has, for every `j`, its +first `j` members a `ℤ`-basis of the part of `L` in the span of `v 0, …, v (j - 1)`; in particular +the two flags of subspaces coincide. + +This is Cassels' Chapter I, Theorem I, part B, in the form Chapter VIII, Lemma 2 uses it. Only +discreteness of `L` is assumed: `IsZLattice ℝ L` follows from the hypotheses on `v`. -/ +theorem exists_basis_adapted [FiniteDimensional ℝ E] (L : Submodule ℤ E) [DiscreteTopology L] + {n : ℕ} (hn : n = finrank ℝ E) {v : Fin n → E} (hvL : ∀ i, v i ∈ L) + (hv : LinearIndependent ℝ v) : + ∃ b : Basis (Fin n) ℤ L, + (∀ j : ℕ, span ℤ ((fun i ↦ ((b i : L) : E)) '' {i : Fin n | (i : ℕ) < j}) = + L ⊓ (span ℝ (v '' {i : Fin n | (i : ℕ) < j})).restrictScalars ℤ) ∧ + ∀ j : ℕ, span ℝ ((fun i ↦ ((b i : L) : E)) '' {i : Fin n | (i : ℕ) < j}) = + span ℝ (v '' {i : Fin n | (i : ℕ) < j}) := by + classical + have key : ∀ j, j ≤ n → ∃ b : Basis (Fin j) ℤ (flagPart L v j), ∀ k, k ≤ j → + span ℤ ((fun i ↦ ((b i : flagPart L v j) : E)) '' {i : Fin j | (i : ℕ) < k}) + = flagPart L v k := by + intro j + induction j with + | zero => + intro _ + have h0 : flagPart L v 0 = ⊥ := flagPart_zero L v + have : Subsingleton (flagPart L v 0) := by rw [h0]; infer_instance + refine ⟨Basis.empty _, fun k hk ↦ ?_⟩ + obtain rfl : k = 0 := Nat.le_zero.1 hk + rw [show {i : Fin 0 | (i : ℕ) < 0} = (∅ : Set (Fin 0)) from Set.eq_empty_of_isEmpty _, + Set.image_empty, Submodule.span_empty, h0] + | succ j ih => + intro hj + obtain ⟨b, hb⟩ := ih (Nat.le_of_succ_le hj) + have hdisc : DiscreteTopology (flagPart L v (j + 1)) := + discreteTopology_of_le (flagPart_le L v (j + 1)) + have hmono : flagPart L v j ≤ flagPart L v (j + 1) := flagPart_mono L v (Nat.le_succ j) + obtain ⟨y, hyN, hli, hsp⟩ := exists_extending hmono + (fun c hc z hz hcz ↦ flagPart_saturated L v hc (flagPart_le L v (j + 1) hz) hcz) + (by rw [finrank_flagPart L v hv hvL hj, + finrank_flagPart L v hv hvL (Nat.le_of_succ_le hj)]) + refine ⟨Basis.mkFinSnocOfLE b hmono y hyN hli hsp, fun k hk ↦ ?_⟩ + have hcs : ∀ i : Fin j, + ((Basis.mkFinSnocOfLE b hmono y hyN hli hsp (Fin.castSucc i) : + flagPart L v (j + 1)) : E) = ((b i : flagPart L v j) : E) := by + intro i + simp + rcases eq_or_lt_of_le hk with rfl | hk' + · have huniv : {i : Fin (j + 1) | (i : ℕ) < j + 1} = (Set.univ : Set (Fin (j + 1))) := by + ext i; simpa using i.isLt + rw [huniv, Set.image_univ, + show (fun i ↦ ((Basis.mkFinSnocOfLE b hmono y hyN hli hsp i : + flagPart L v (j + 1)) : E)) + = (flagPart L v (j + 1)).subtype ∘ Basis.mkFinSnocOfLE b hmono y hyN hli hsp from rfl, + Set.range_comp, ← Submodule.map_span, Basis.span_eq, Submodule.map_subtype_top] + · have hkj : k ≤ j := Nat.lt_succ_iff.1 hk' + have hset : {i : Fin (j + 1) | (i : ℕ) < k} + = Fin.castSucc '' {i : Fin j | (i : ℕ) < k} := by + ext i + simp only [Set.mem_ofPred_eq, Set.mem_image] + constructor + · intro hi + exact ⟨⟨i, lt_of_lt_of_le hi hkj⟩, hi, by ext; rfl⟩ + · rintro ⟨i', hi', rfl⟩ + simpa using hi' + rw [hset, ← Set.image_comp, + show ((fun i ↦ ((Basis.mkFinSnocOfLE b hmono y hyN hli hsp i : + flagPart L v (j + 1)) : E)) ∘ Fin.castSucc) + = fun i ↦ ((b i : flagPart L v j) : E) from funext hcs] + exact hb k hkj + obtain ⟨b, hb⟩ := key n le_rfl + have hmin : ∀ j : ℕ, {i : Fin n | (i : ℕ) < j} = {i : Fin n | (i : ℕ) < min j n} := by + intro j + ext i + simp only [Set.mem_ofPred_eq, lt_min_iff, and_iff_left i.isLt] + have hLn : flagPart L v n = L := flagPart_eq_self L v hn hv + have hcoe : (fun i ↦ (((b.map (LinearEquiv.ofEq _ _ hLn)) i : L) : E)) + = fun i ↦ ((b i : flagPart L v n) : E) := by + funext i + simp + refine ⟨b.map (LinearEquiv.ofEq _ _ hLn), fun j ↦ ?_, fun j ↦ ?_⟩ + · rw [hcoe, hmin j] + exact hb _ (min_le_right j n) + · rw [hcoe, hmin j, ← Submodule.span_span_of_tower (R := ℤ) (S := ℝ), + hb _ (min_le_right j n)] + exact span_flagPart L v hvL _ + +/-- **The integral form.** With the basis of `ZLattice.exists_basis_adapted`, a lattice point in +the `ℝ`-span of `v 0, …, v (j - 1)` is an *integer* combination of `b 0, …, b (j - 1)`. This is the +shape of Cassels' Chapter VIII, Lemma 2, and it is the shape the second theorem consumes. -/ +theorem exists_basis_mem_span_int_of_mem_span_real [FiniteDimensional ℝ E] (L : Submodule ℤ E) + [DiscreteTopology L] {n : ℕ} (hn : n = finrank ℝ E) {v : Fin n → E} (hvL : ∀ i, v i ∈ L) + (hv : LinearIndependent ℝ v) : + ∃ b : Basis (Fin n) ℤ L, ∀ (j : ℕ) (x : E), x ∈ L → + x ∈ span ℝ (v '' {i : Fin n | (i : ℕ) < j}) → + x ∈ span ℤ ((fun i ↦ ((b i : L) : E)) '' {i : Fin n | (i : ℕ) < j}) := by + obtain ⟨b, hb, -⟩ := exists_basis_adapted L hn hvL hv + exact ⟨b, fun j x hxL hxs ↦ (hb j).ge (Submodule.mem_inf.2 + ⟨hxL, (Submodule.restrictScalars_mem ℤ _ x).2 hxs⟩)⟩ + +end ZLattice diff --git a/LeanPool/CCDN/ArithmeticHeights/Affine.lean b/LeanPool/CCDN/ArithmeticHeights/Affine.lean new file mode 100644 index 0000000000..eaeea0058a --- /dev/null +++ b/LeanPool/CCDN/ArithmeticHeights/Affine.lean @@ -0,0 +1,234 @@ +/- +Copyright (c) 2026 Ralf Stephan. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Ralf Stephan +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import Mathlib.NumberTheory.Height.NumberField + +/-! +# The affine height of a tuple + +Mathlib's `Height.mulHeight` is a height on *projective* space: it is invariant under scaling, and +so is a function of the line through a tuple rather than of the tuple. That is the right object +for a point of `ℙⁿ` and the wrong one for anything not homogeneous — the value `∑ i, a i * x i` of +a linear form, or the determinant of a matrix, both of which scale with the tuple. This file adds +the **affine** height, the projective height of the tuple with a coordinate `1` appended: + +`Height.mulHeightAff x := Height.mulHeight fun o : Option ι ↦ o.elim 1 x`. + +It is the tuple analogue of Mathlib's `Height.mulHeight₁ x = mulHeight ![x, 1]`, and agrees with it +on a one-element tuple (`Height.mulHeightAff_fin_one`). Being a normalization and not a theorem, it +is stated here in Layer 0 rather than at its first use. + +`mulHeightAff` is deliberately **not** scaling-invariant, and `Height.exists_mulHeightAff_smul_ne` +records that as a theorem: over `ℚ` the tuples `![1]` and `![2]` have affine heights `1` and `2`. +That failure is the content of the definition, not a defect of it. What survives is one-sided: +the affine height dominates the projective height and the height of every single coordinate, and +is monotone under re-indexing. + +## Main definitions + +* `Height.mulHeightAff x` for `x : ι → K`, and `Height.logHeightAff x` its logarithm. + +## Main results + +* `Height.mulHeight_le_mulHeightAff` and `Height.mulHeight₁_le_mulHeightAff`: the affine height + dominates the projective height of the tuple and the height of each coordinate. The second is + false for `Height.mulHeight`. +* `Height.mulHeightAff_comp_le` and `Height.mulHeightAff_comp_equiv`: monotonicity under + re-indexing, and invariance under a bijective one. +* `Height.mulHeightAff_fin_one`: on a one-element tuple the affine height is `Height.mulHeight₁`. +* `Height.exists_mulHeightAff_smul_ne`: the affine height is not scaling-invariant. + +## Implementation notes + +The appended coordinate is indexed by `none : Option ι` rather than by `Sum.inl` of a `Unit` or by +`Fin.succ`: `Option ι` needs no `Fintype` bookkeeping, inherits `Finite ι` by instance resolution, +and makes re-indexing along `f : ι → ι'` literally `Option.map f`. + +No Arakelov analogue is defined here. Layer 0.1 already has the one-variable +`NumberField.arakelovMulHeight₁`, and no bound in the roadmap needs an affine Arakelov height of a +tuple; the Arakelov bounds that would want one carry the constant `1` and need no affine height at +all. + +## References + +E. Bombieri and W. Gubler, *Heights in Diophantine Geometry*, Cambridge University Press (2006), +§1.5, where the affine and projective heights of a point are distinguished throughout. + +This is Layer 0.5 of the `ArithmeticHeights` roadmap. +-/ + +public section + +open Function Real + +namespace Height + +variable {K : Type*} [Field K] [AdmissibleAbsValues K] {ι ι' : Type*} + +/-- The **affine height** of a tuple: the projective height of the tuple with a coordinate `1` +appended. + +Unlike `Height.mulHeight` this is not invariant under scaling +(`Height.exists_mulHeightAff_smul_ne`), and that is exactly what a bound on a non-homogeneous +quantity needs: the value `∑ i, a i * x i` of a linear form is not a function on projective +space, so no scaling-invariant quantity can bound its height. The affine height dominates the +projective height (`Height.mulHeight_le_mulHeightAff`) and the height of every single coordinate +(`Height.mulHeight₁_le_mulHeightAff`), neither of which `mulHeight` does. -/ +@[expose] noncomputable def mulHeightAff (x : ι → K) : ℝ := mulHeight fun o : Option ι ↦ o.elim 1 x + +/-- The affine logarithmic height of a tuple. As everywhere in this development, the logarithmic +height is *defined* as the logarithm of the multiplicative one. -/ +@[expose] noncomputable def logHeightAff (x : ι → K) : ℝ := log (mulHeightAff x) + +theorem logHeightAff_eq_log_mulHeightAff (x : ι → K) : + logHeightAff x = log (mulHeightAff x) := + rfl + +omit [AdmissibleAbsValues K] in +/-- The tuple whose height is `Height.mulHeightAff x` is never zero: its coordinate at `none` +is `1`. This is what makes the affine height positive with no hypothesis on `x`, and it is the +nondegeneracy that consumers of the Segre relation need. -/ +theorem optionElim_one_ne_zero (x : ι → K) : (fun o : Option ι ↦ o.elim 1 x) ≠ 0 := + fun h ↦ one_ne_zero (congrFun h none) + +section Affine + +variable [Finite ι] + +theorem one_le_mulHeightAff (x : ι → K) : 1 ≤ mulHeightAff x := one_le_mulHeight _ + +theorem mulHeightAff_pos (x : ι → K) : 0 < mulHeightAff x := mulHeight_pos _ + +theorem mulHeightAff_ne_zero (x : ι → K) : mulHeightAff x ≠ 0 := mulHeight_ne_zero _ + +theorem logHeightAff_nonneg (x : ι → K) : 0 ≤ logHeightAff x := logHeight_nonneg _ + +/-- The projective height of a tuple is at most its affine height. -/ +theorem mulHeight_le_mulHeightAff (x : ι → K) : mulHeight x ≤ mulHeightAff x := + mulHeight_comp_le some fun o : Option ι ↦ o.elim 1 x + +/-- The logarithmic form of `Height.mulHeight_le_mulHeightAff`. -/ +theorem logHeight_le_logHeightAff (x : ι → K) : logHeight x ≤ logHeightAff x := + log_le_log (mulHeight_pos _) (mulHeight_le_mulHeightAff x) + +/-- The affine height of a tuple dominates the affine height of each of its coordinates. This is +false for `Height.mulHeight`, which is invariant under scaling. -/ +theorem mulHeight₁_le_mulHeightAff (x : ι → K) (i : ι) : mulHeight₁ (x i) ≤ mulHeightAff x := by + have h : (![x i, 1] : Fin 2 → K) = (fun o : Option ι ↦ o.elim 1 x) ∘ ![some i, none] := by + ext j + fin_cases j <;> rfl + rw [mulHeight₁_eq_mulHeight, h] + exact mulHeight_comp_le _ _ + +/-- The logarithmic form of `Height.mulHeight₁_le_mulHeightAff`. -/ +theorem logHeight₁_le_logHeightAff (x : ι → K) (i : ι) : logHeight₁ (x i) ≤ logHeightAff x := + log_le_log (mulHeight₁_pos _) (mulHeight₁_le_mulHeightAff x i) + +/-- The affine height is monotone under re-indexing: the appended coordinate `1` is common to +both tuples, so re-indexing along `f` is re-indexing along `Option.map f`. -/ +theorem mulHeightAff_comp_le [Finite ι'] (f : ι → ι') (x : ι' → K) : + mulHeightAff (x ∘ f) ≤ mulHeightAff x := by + have h : (fun o : Option ι ↦ o.elim 1 (x ∘ f)) + = (fun o : Option ι' ↦ o.elim 1 x) ∘ Option.map f := by + funext o + cases o <;> rfl + rw [mulHeightAff, mulHeightAff, h] + exact mulHeight_comp_le _ _ + +/-- The logarithmic form of `Height.mulHeightAff_comp_le`. -/ +theorem logHeightAff_comp_le [Finite ι'] (f : ι → ι') (x : ι' → K) : + logHeightAff (x ∘ f) ≤ logHeightAff x := + log_le_log (mulHeightAff_pos _) (mulHeightAff_comp_le f x) + +omit [Finite ι] in +/-- The affine height does not change under a bijective re-indexing. -/ +theorem mulHeightAff_comp_equiv (e : ι ≃ ι') (x : ι' → K) : + mulHeightAff (x ∘ e) = mulHeightAff x := by + have h : (fun o : Option ι ↦ o.elim 1 (x ∘ e)) + = (fun o : Option ι' ↦ o.elim 1 x) ∘ Equiv.optionCongr e := by + funext o + cases o <;> rfl + rw [mulHeightAff, mulHeightAff, h] + exact mulHeight_comp_equiv _ _ + +omit [Finite ι] in +/-- The logarithmic form of `Height.mulHeightAff_comp_equiv`. -/ +theorem logHeightAff_comp_equiv (e : ι ≃ ι') (x : ι' → K) : + logHeightAff (x ∘ e) = logHeightAff x := by + rw [logHeightAff_eq_log_mulHeightAff, logHeightAff_eq_log_mulHeightAff, mulHeightAff_comp_equiv] + +/-- The affine height of the zero tuple is `1` — not by a junk-value convention but because the +tuple whose height is taken is `(1, 0, …, 0)`. -/ +@[simp] +theorem mulHeightAff_zero : mulHeightAff (0 : ι → K) = 1 := by + have hsup (v : AbsoluteValue K ℝ) : + (⨆ o : Option ι, v (Option.elim o 1 (0 : ι → K))) = 1 := + le_antisymm (ciSup_le fun o ↦ by cases o <;> simp) + (Finite.le_ciSup_of_le none (by simp)) + rw [mulHeightAff, mulHeight_eq (optionElim_one_ne_zero (0 : ι → K))] + simp [hsup] + +@[simp] +theorem logHeightAff_zero : logHeightAff (0 : ι → K) = 0 := by + rw [logHeightAff_eq_log_mulHeightAff, mulHeightAff_zero, log_one] + +end Affine + +/-- **The affine height extends Mathlib's `Height.mulHeight₁`**: on a one-element tuple the two +agree. This is the check that the appended coordinate is the right one; a definition that +appended a different constant, or that normalized by the degree, would fail it. -/ +theorem mulHeightAff_fin_one (x : K) : mulHeightAff ![x] = mulHeight₁ x := by + have h : (fun o : Option (Fin 1) ↦ o.elim 1 ![x]) ∘ finSuccEquiv 1 = ![(1 : K), x] := by + ext j + fin_cases j <;> rfl + rw [mulHeightAff, ← mulHeight_comp_equiv (finSuccEquiv 1), h, mulHeight_swap, + ← mulHeight₁_eq_mulHeight] + +/-- The logarithmic form of `Height.mulHeightAff_fin_one`. -/ +theorem logHeightAff_fin_one (x : K) : logHeightAff ![x] = logHeight₁ x := by + rw [logHeightAff_eq_log_mulHeightAff, logHeight₁_eq_log_mulHeight₁, mulHeightAff_fin_one] + +/-- The affine height of `![2]` over `ℚ` is `2`. -/ +private lemma mulHeightAff_two : mulHeightAff (![2] : Fin 1 → ℚ) = 2 := by + rw [show (![2] : Fin 1 → ℚ) = ![((2 : ℕ) : ℚ)] by norm_num, mulHeightAff_fin_one, + Rat.mulHeight₁_natCast] + norm_num + +/-- **The affine height is not invariant under scaling.** Over `ℚ`, doubling the tuple `![1]` +doubles its affine height. This is the whole point of the definition: a bound on a quantity that +scales with its argument — the value of a linear form, the determinant of a matrix — cannot have +a scaling-invariant right-hand side, so `Height.mulHeight` cannot appear there and +`Height.mulHeightAff` must. -/ +theorem exists_mulHeightAff_smul_ne : + ∃ (c : ℚ) (x : Fin 1 → ℚ), c ≠ 0 ∧ mulHeightAff (c • x) ≠ mulHeightAff x := by + refine ⟨2, ![1], two_ne_zero, ?_⟩ + have h : (2 : ℚ) • (![1] : Fin 1 → ℚ) = ![2] := by + ext j + fin_cases j + norm_num + rw [h, mulHeightAff_two, mulHeightAff_fin_one, mulHeight₁_one] + norm_num + +/-! +### Examples + +The acceptance criterion the roadmap states for this milestone: the affine and projective heights +of one and the same tuple differ, so the appended coordinate is doing something. +-/ + +/-- Over `ℚ` the tuple `![2]` has affine height `2` and projective height `1`. A definition of +the affine height that made the two agree has appended the wrong coordinate, and a bound on a +non-homogeneous quantity stated with the projective height is refuted here. -/ +example : mulHeightAff (![2] : Fin 1 → ℚ) = 2 ∧ mulHeight (![2] : Fin 1 → ℚ) = 1 := + ⟨mulHeightAff_two, mulHeight_eq_one_of_subsingleton _⟩ + +end Height + +end diff --git a/LeanPool/CCDN/ArithmeticHeights/Arakelov.lean b/LeanPool/CCDN/ArithmeticHeights/Arakelov.lean new file mode 100644 index 0000000000..0a93375a93 --- /dev/null +++ b/LeanPool/CCDN/ArithmeticHeights/Arakelov.lean @@ -0,0 +1,881 @@ +/- +Copyright (c) 2026 Ralf Stephan. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Ralf Stephan +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import Mathlib.Analysis.SpecialFunctions.Log.PosLog +public import Mathlib.Analysis.SpecialFunctions.Pow.Real +public import Mathlib.NumberTheory.Height.NumberField +public import Mathlib.NumberTheory.Height.Projectivization + +/-! +# The Arakelov height over a number field + +Mathlib's `Height.mulHeight` measures a tuple by the **sup norm** at every place. In Arakelov +theory one instead uses the **ℓ² norm at the archimedean places** — the Fubini–Study metric on +`O(1)` — and keeps the sup norm at the finite places. The resulting height is the one in which +the constants of the Siegel-lemma literature (Bombieri–Vaaler, Schmidt) are stated. + +This file defines that normalization over a number field `K` and proves its basic API — scaling +invariance and the descent to projective space, `1 ≤ H_Ar`, invariance under reindexing, and the +affine case — together with the two-sided comparison with `Height.mulHeight`, which is what lets +a bound stated in either normalization be read in the other. Not all of +`Mathlib.NumberTheory.Height.Basic` transfers here — see the implementation notes. + +## Main definitions + +* `NumberField.arakelovMulHeight x` for `x : ι → K`: the multiplicative Arakelov height + `∏_{v | ∞} (∑ i, v (x i)²)^{mult v / 2} · ∏ᶠ_{v ∤ ∞} ⨆ i, v (x i)`. +* `NumberField.arakelovLogHeight x`: its logarithm. +* `NumberField.arakelovMulHeight₁ x` and `NumberField.arakelovLogHeight₁ x` for `x : K`: + the one-variable (affine) case, the height of the tuple `![x, 1]`. +* `Projectivization.arakelovMulHeight` and `Projectivization.arakelovLogHeight`: the descent + of the above to `Projectivization K (ι → K)`, which the scaling invariance below justifies. + +## Main results + +* `NumberField.arakelovMulHeight_smul_eq`: invariance under scaling by a nonzero element of + `K`, by the product formula. This is what makes the height projective. +* `NumberField.one_le_arakelovMulHeight`: the Arakelov height is at least `1`. +* `NumberField.arakelovMulHeight_comp_equiv`: invariance under reindexing. +* `NumberField.arakelovMulHeight_one` and `NumberField.arakelovMulHeight₁_one`: the values + `#ι ^ (totalWeight K / 2)` and `2 ^ (totalWeight K / 2)` on the all-ones tuple and at `1`, + where the sup-norm height is `1` — the two normalizations genuinely differ. +* `NumberField.arakelovLogHeight₁_eq`: the local formula for the affine logarithmic height, + with `log⁺ (v x)` at the finite places. +* `NumberField.mulHeight_le_arakelovMulHeight` and `NumberField.arakelovMulHeight_le_mulHeight`: + the two comparisons with Mathlib's sup-norm height, the second with the constant + `#ι ^ (totalWeight K / 2)`. These are what transport a constant stated for one normalization + into the other, and the second is sharp: it is an equality at the all-ones tuple. +* `NumberField.arakelovMulHeight₁_le_mulHeight₁`: the affine case of the same, with constant + `2 ^ (totalWeight K / 2)` — there is no equality `arakelovMulHeight₁ = mulHeight₁`. +* `Projectivization.mulHeight_le_arakelovMulHeight` and + `Projectivization.arakelovMulHeight_le_mulHeight`: the same two comparisons on projective space, + where the heights of Layer 3 live. The first is what makes the Northcott property of Layer 1.1 + carry over to the Arakelov normalization, since it bounds the sup-norm height by the Arakelov + one with no constant at all. + +## Implementation notes + +As for `Height.mulHeight`, the zero tuple gets the junk value `1`: the displayed product is `0` +there, which would break `1 ≤ arakelovMulHeight` and the comparison with `Height.mulHeight`. +`NumberField.arakelovMulHeight_eq` recovers the displayed formula for a nonzero tuple, and every +statement below holds unconditionally with this convention. + +The index type carries `Fintype` rather than `Finite` because the archimedean local factor is a +`Finset.sum` over `ι`; the value does not depend on the instance. + +Only the archimedean factor differs from `NumberField.mulHeight_eq`. Accordingly the proof of +scaling invariance does not redo the product-formula bookkeeping at the finite places: the +transformation law of the finite part is *extracted* from Mathlib's +`Height.mulHeight_smul_eq_mulHeight`, which is the only place where the two normalizations +interact here. + +Not every lemma of `Mathlib.NumberTheory.Height.Basic` has an Arakelov analogue, and the two that +fail are not edge cases. `Height.mulHeight_pow` is **false** here — the ℓ² norm is not +multiplicative under coordinatewise powers — and with it `mulHeight₁_pow` and `mulHeight₁_zpow`. +`Height.mulHeight_comp_le` is **false** for a non-injective reindexing, since repeating a +coordinate raises the ℓ² norm at the archimedean places while no sup norm can see the repetition; +it is the injective case that its consumers use. Both are refuted among the examples at the end of +this file. + +## References + +E. Bombieri and W. Gubler, *Heights in Diophantine Geometry*, Cambridge University Press +(2006), Definition 2.8.1 and 2.8.2–2.8.3 for the local factors and the resulting projective +height. Their `h_Ar` is the absolute logarithmic height, with degree-normalized local weights. +Writing `H_Ar = exp(h_Ar)` for its absolute multiplicative form, `arakelovMulHeight` here is +`H_Ar ^ [K : ℚ]`, relative to `K` and matching Mathlib's `Height.mulHeight` convention. + +This is Layers 0.1 and 0.2 of the `ArithmeticHeights` roadmap: the normalization and its +comparison with `Height.mulHeight`. +-/ + +public section + +namespace NumberField + +open Finset Function Height Real + +variable {K : Type*} [Field K] [NumberField K] {ι ι' : Type*} [Fintype ι] [Fintype ι'] + +/-- The **Arakelov height** of a tuple of elements of a number field: the ℓ² norm at the +archimedean places, weighted by `InfinitePlace.mult`, and the sup norm at the finite places. +For the zero tuple we take the junk value `1`, as `Height.mulHeight` does. + +This is the normalization in which the constants of the Siegel-lemma literature are stated; +`Height.mulHeight` uses the sup norm at every place. Both live in the library and every bound +says which one it is in. -/ +@[expose] noncomputable def arakelovMulHeight (x : ι → K) : ℝ := + have : Decidable (x = 0) := Classical.propDecidable _ + if x = 0 then 1 else + (∏ v : InfinitePlace K, (∑ i, v (x i) ^ 2) ^ (v.mult / 2 : ℝ)) * + ∏ᶠ v : FinitePlace K, ⨆ i, v (x i) + +lemma arakelovMulHeight_eq {x : ι → K} (hx : x ≠ 0) : + arakelovMulHeight x = + (∏ v : InfinitePlace K, (∑ i, v (x i) ^ 2) ^ (v.mult / 2 : ℝ)) * + ∏ᶠ v : FinitePlace K, ⨆ i, v (x i) := by + simp [arakelovMulHeight, hx] + +@[simp] +lemma arakelovMulHeight_zero : arakelovMulHeight (0 : ι → K) = 1 := by + simp [arakelovMulHeight] + +/-- The logarithmic Arakelov height. As everywhere in this development, the logarithmic height +is *defined* as the logarithm of the multiplicative one and never independently. -/ +@[expose] noncomputable def arakelovLogHeight (x : ι → K) : ℝ := log (arakelovMulHeight x) + +lemma arakelovLogHeight_eq_log_arakelovMulHeight (x : ι → K) : + arakelovLogHeight x = log (arakelovMulHeight x) := + rfl + +@[simp] +lemma arakelovLogHeight_zero : arakelovLogHeight (0 : ι → K) = 0 := by + simp [arakelovLogHeight_eq_log_arakelovMulHeight] + +/-- The rewriting the archimedean local factor always needs: its exponent `mult v / 2` is real +while the sup-norm factor carries the natural power `mult v`, and `(a ^ 2) ^ (n / 2) = a ^ n` +moves between them. -/ +private lemma sq_rpow_div_two {a : ℝ} (ha : 0 ≤ a) (n : ℕ) : (a ^ 2) ^ ((n : ℝ) / 2) = a ^ n := by + rw [← Real.rpow_natCast a n, ← Real.rpow_natCast a 2, ← Real.rpow_mul ha] + congr 1 + push_cast + ring + +/-! +### Scaling invariance + +The archimedean local factor scales by `v c ^ mult v` and the finite one by `v c`, so the +product formula makes the height invariant. The finite half of that computation is already in +Mathlib, inside `Height.mulHeight_smul_eq_mulHeight`; we read it off rather than redo it. +-/ + +section Smul + +variable {κ : Type*} + +omit [NumberField K] in +private lemma iSup_smul_eq {F : Type*} [FunLike F K ℝ] [MonoidWithZeroHomClass F K ℝ] + [NonnegHomClass F K ℝ] (v : F) (c : K) (x : κ → K) : + ⨆ i, v ((c • x) i) = v c * ⨆ i, v (x i) := by + simp only [Pi.smul_apply, smul_eq_mul, map_mul, Real.mul_iSup_of_nonneg (apply_nonneg v c)] + +/-- The transformation law of the *finite* part of the height under scaling, extracted from +Mathlib's `Height.mulHeight_smul_eq_mulHeight`. This is the product formula for `c`, applied to +the finite places only. -/ +private lemma prod_infinitePlace_mul_finprod_iSup_smul [Finite κ] {x : κ → K} (hx : x ≠ 0) + {c : K} (hc : c ≠ 0) : + (∏ v : InfinitePlace K, v c ^ v.mult) * ∏ᶠ v : FinitePlace K, ⨆ i, v ((c • x) i) = + ∏ᶠ v : FinitePlace K, ⨆ i, v (x i) := by + have hcx : c • x ≠ 0 := by simp [hc, hx] + have hA : (0 : ℝ) < ∏ v : InfinitePlace K, (⨆ i, v (x i)) ^ v.mult := by + obtain ⟨i, hi⟩ : ∃ i, x i ≠ 0 := ne_iff.mp hx + exact prod_pos fun v _ ↦ pow_pos + ((v.pos_iff.mpr hi).trans_le (Finite.le_ciSup_of_le i le_rfl)) _ + have h := Height.mulHeight_smul_eq_mulHeight x hc + rw [NumberField.mulHeight_eq hcx, NumberField.mulHeight_eq hx] at h + simp only [iSup_smul_eq, mul_pow, prod_mul_distrib] at h ⊢ + exact mul_left_cancel₀ hA.ne' (by linear_combination h) + +/-- The Arakelov height is invariant under scaling, by the product formula, so it descends to +projective space exactly as `Height.mulHeight` does. -/ +lemma arakelovMulHeight_smul_eq (x : ι → K) {c : K} (hc : c ≠ 0) : + arakelovMulHeight (c • x) = arakelovMulHeight x := by + rcases eq_or_ne x 0 with rfl | hx + · rw [smul_zero] + have hcx : c • x ≠ 0 := by simp [hc, hx] + have harch (v : InfinitePlace K) : + (∑ i, v ((c • x) i) ^ 2) ^ (v.mult / 2 : ℝ) + = v c ^ v.mult * (∑ i, v (x i) ^ 2) ^ (v.mult / 2 : ℝ) := by + have hsum : ∑ i, v ((c • x) i) ^ 2 = v c ^ 2 * ∑ i, v (x i) ^ 2 := by + rw [mul_sum] + exact sum_congr rfl fun i _ ↦ by simp [mul_pow] + rw [hsum, Real.mul_rpow (by positivity) (by positivity), + sq_rpow_div_two (apply_nonneg v c) v.mult] + rw [arakelovMulHeight_eq hcx, arakelovMulHeight_eq hx] + simp only [harch, prod_mul_distrib] + rw [mul_right_comm, prod_infinitePlace_mul_finprod_iSup_smul hx hc, mul_comm] + +lemma arakelovLogHeight_smul_eq (x : ι → K) {c : K} (hc : c ≠ 0) : + arakelovLogHeight (c • x) = arakelovLogHeight x := by + simp only [arakelovLogHeight_eq_log_arakelovMulHeight, + arakelovMulHeight_smul_eq x hc] + +lemma arakelovMulHeight_neg (x : ι → K) : arakelovMulHeight (-x) = arakelovMulHeight x := by + rw [← neg_one_smul K x, arakelovMulHeight_smul_eq x (neg_ne_zero.mpr one_ne_zero)] + +lemma arakelovLogHeight_neg (x : ι → K) : arakelovLogHeight (-x) = arakelovLogHeight x := by + simp only [arakelovLogHeight_eq_log_arakelovMulHeight, arakelovMulHeight_neg] + +end Smul + +/-! +### Positivity and reindexing +-/ + +/-- The Arakelov height of a tuple is always at least `1`. -/ +lemma one_le_arakelovMulHeight (x : ι → K) : 1 ≤ arakelovMulHeight x := by + rcases eq_or_ne x 0 with rfl | hx + · simp + obtain ⟨i, hi⟩ : ∃ i, x i ≠ 0 := ne_iff.mp hx + have hx' : (x i)⁻¹ • x ≠ 0 := by simp [hi, hx] + have hi' : ((x i)⁻¹ • x) i = 1 := by simp [hi] + rw [← arakelovMulHeight_smul_eq x (inv_ne_zero hi), arakelovMulHeight_eq hx'] + refine one_le_mul_of_one_le_of_one_le (Finset.one_le_prod₀ fun v _ ↦ ?_) + (one_le_finprod fun v ↦ Finite.le_ciSup_of_le i (by simp [hi'])) + have h1 : (1 : ℝ) ≤ ∑ j, v (((x i)⁻¹ • x) j) ^ 2 := + le_trans (le_of_eq (by simp [hi'])) + (single_le_sum (f := fun j ↦ v (((x i)⁻¹ • x) j) ^ 2) (fun j _ ↦ by positivity) (mem_univ i)) + exact Real.one_le_rpow h1 (by positivity) + +lemma arakelovMulHeight_pos (x : ι → K) : 0 < arakelovMulHeight x := + zero_lt_one.trans_le <| one_le_arakelovMulHeight x + +lemma arakelovMulHeight_ne_zero (x : ι → K) : arakelovMulHeight x ≠ 0 := + (arakelovMulHeight_pos x).ne' + +lemma arakelovLogHeight_nonneg (x : ι → K) : 0 ≤ arakelovLogHeight x := + log_nonneg <| one_le_arakelovMulHeight x + +/-- The Arakelov height does not change under re-indexing. -/ +lemma arakelovMulHeight_comp_equiv (e : ι ≃ ι') (x : ι' → K) : + arakelovMulHeight (x ∘ e) = arakelovMulHeight x := by + rcases eq_or_ne x 0 with rfl | hx + · simp + have hx' : x ∘ e ≠ 0 := by + obtain ⟨i, hi⟩ : ∃ i, x i ≠ 0 := ne_iff.mp hx + exact ne_iff.mpr ⟨e.symm i, by simpa using hi⟩ + have hsum (v : InfinitePlace K) : ∑ i, v (x (e i)) ^ 2 = ∑ i, v (x i) ^ 2 := + e.sum_comp fun i ↦ v (x i) ^ 2 + have hsup (v : FinitePlace K) : ⨆ i, v (x (e i)) = ⨆ i, v (x i) := + e.iSup_congr (congrFun rfl) + rw [arakelovMulHeight_eq hx, arakelovMulHeight_eq hx'] + simp only [comp_apply, hsum, hsup] + +lemma arakelovLogHeight_comp_equiv (e : ι ≃ ι') (x : ι' → K) : + arakelovLogHeight (x ∘ e) = arakelovLogHeight x := by + simp only [arakelovLogHeight_eq_log_arakelovMulHeight, arakelovMulHeight_comp_equiv] + +/-- Swapping the two coordinates leaves the Arakelov height of a pair unchanged. -/ +lemma arakelovMulHeight_swap (x y : K) : + arakelovMulHeight ![x, y] = arakelovMulHeight ![y, x] := by + rw [show ![x, y] = ![y, x] ∘ Equiv.swap (0 : Fin 2) 1 from List.ofFn_inj.mp rfl] + exact arakelovMulHeight_comp_equiv (Equiv.swap 0 1) ![y, x] + +lemma arakelovLogHeight_swap (x y : K) : + arakelovLogHeight ![x, y] = arakelovLogHeight ![y, x] := by + simp only [arakelovLogHeight_eq_log_arakelovMulHeight, arakelovMulHeight_swap] + +/-! +### Special values + +`Height.mulHeight (1 : ι → K) = 1` by the product formula, but the Arakelov height of the same +tuple is `#ι ^ (totalWeight K / 2)`: these are the values that exhibit the difference between +the two normalizations without touching the junk value. +-/ + +/-- The Arakelov height of the all-ones tuple. Its sup-norm counterpart `Height.mulHeight_one` +has value `1`. -/ +lemma arakelovMulHeight_one [Nonempty ι] : + arakelovMulHeight (1 : ι → K) = (Fintype.card ι : ℝ) ^ ((totalWeight K : ℝ) / 2) := by + have hne : (1 : ι → K) ≠ 0 := ne_iff.mpr ⟨Classical.arbitrary ι, by simp⟩ + have hfin : ∏ᶠ v : FinitePlace K, ⨆ i, v ((1 : ι → K) i) = 1 := + finprod_eq_one_of_forall_eq_one fun v ↦ by simp + have harch (v : InfinitePlace K) : + (∑ i, v ((1 : ι → K) i) ^ 2) ^ (v.mult / 2 : ℝ) + = ((Fintype.card ι : ℝ) ^ ((1 : ℝ) / 2)) ^ v.mult := by + have h2 : ∑ i, v ((1 : ι → K) i) ^ 2 = (Fintype.card ι : ℝ) := by simp + rw [h2, ← Real.rpow_natCast ((Fintype.card ι : ℝ) ^ ((1 : ℝ) / 2)) v.mult, + ← Real.rpow_mul (by positivity)] + congr 1 + ring + rw [arakelovMulHeight_eq hne, hfin, mul_one, Finset.prod_congr rfl fun v _ ↦ harch v, + Finset.prod_pow_eq_pow_sum, InfinitePlace.sum_mult_eq, + ← Real.rpow_natCast ((Fintype.card ι : ℝ) ^ ((1 : ℝ) / 2)) (Module.finrank ℚ K), + ← Real.rpow_mul (by positivity), totalWeight_eq_finrank] + congr 1 + ring + +/-- On a subsingleton index type every tuple has Arakelov height `1`, by the product formula. +This is the degenerate case in which the Arakelov and sup-norm normalizations agree. -/ +@[simp] +lemma arakelovMulHeight_eq_one_of_subsingleton [Subsingleton ι] (x : ι → K) : + arakelovMulHeight x = 1 := by + rcases eq_or_ne x 0 with rfl | hx + · simp + obtain ⟨i, hi⟩ : ∃ i, x i ≠ 0 := ne_iff.mp hx + have : Nonempty ι := ⟨i⟩ + have hcard : Fintype.card ι = 1 := + le_antisymm (Fintype.card_le_one_iff_subsingleton.mpr ‹_›) Fintype.card_pos + have h1 : (x i)⁻¹ • x = 1 := funext fun j ↦ by + rw [Subsingleton.elim j i] + simp [inv_mul_cancel₀ hi] + rw [← arakelovMulHeight_smul_eq x (inv_ne_zero hi), h1, arakelovMulHeight_one, hcard, + Nat.cast_one, Real.one_rpow] + +@[simp] +lemma arakelovLogHeight_eq_zero_of_subsingleton [Subsingleton ι] (x : ι → K) : + arakelovLogHeight x = 0 := by + simp [arakelovLogHeight_eq_log_arakelovMulHeight] + +/-! +### The comparison with the sup-norm height + +`‖·‖_∞ ≤ ‖·‖_2 ≤ √(#ι) · ‖·‖_∞` at each archimedean place, raised to the weight `mult v` and +multiplied over the places; the finite parts of the two heights are the same number and factor +out. This is Layer 0.2 of the roadmap: the pair of lemmas that transports a constant stated for +the Arakelov height into Mathlib's normalization and back, and so the reason a bound in either +normalization is a bound in the other. + +The constant of the upper comparison cannot be improved: at the all-ones tuple it is an equality, +by `arakelovMulHeight_one` against `Height.mulHeight_one`. +-/ + +section Comparison + +omit [NumberField K] in +/-- The sup norm is at most the ℓ² norm, at one place. -/ +private lemma iSup_pow_two_le_sum_pow_two [Nonempty ι] (v : InfinitePlace K) (x : ι → K) : + (⨆ i, v (x i)) ^ 2 ≤ ∑ i, v (x i) ^ 2 := by + obtain ⟨i, hi⟩ : ∃ i, v (x i) = ⨆ j, v (x j) := exists_eq_ciSup_of_finite + rw [← hi] + exact single_le_sum (f := fun j ↦ v (x j) ^ 2) (fun j _ ↦ by positivity) (mem_univ i) + +omit [NumberField K] in +/-- The ℓ² norm is at most `√(#ι)` times the sup norm, at one place. -/ +private lemma sum_pow_two_le_card_mul (v : InfinitePlace K) (x : ι → K) : + ∑ i, v (x i) ^ 2 ≤ (Fintype.card ι : ℝ) * (⨆ i, v (x i)) ^ 2 := by + rcases isEmpty_or_nonempty ι with _ | _ + · simp + rw [← card_univ, ← nsmul_eq_mul] + refine sum_le_card_nsmul _ _ _ fun i _ ↦ ?_ + gcongr + exact Finite.le_ciSup_of_le i le_rfl + +omit [NumberField K] in +private lemma iSup_pow_mult_le_sum_rpow [Nonempty ι] (v : InfinitePlace K) (x : ι → K) : + (⨆ i, v (x i)) ^ v.mult ≤ (∑ i, v (x i) ^ 2) ^ (v.mult / 2 : ℝ) := by + have h0 : (0 : ℝ) ≤ ⨆ i, v (x i) := Real.iSup_nonneg fun i ↦ apply_nonneg v (x i) + rw [← sq_rpow_div_two h0 v.mult] + exact Real.rpow_le_rpow (by positivity) (iSup_pow_two_le_sum_pow_two v x) (by positivity) + +omit [NumberField K] in +private lemma sum_rpow_le_card_rpow_mul (v : InfinitePlace K) (x : ι → K) : + (∑ i, v (x i) ^ 2) ^ (v.mult / 2 : ℝ) ≤ + ((Fintype.card ι : ℝ) ^ ((1 : ℝ) / 2)) ^ v.mult * (⨆ i, v (x i)) ^ v.mult := by + have h0 : (0 : ℝ) ≤ ⨆ i, v (x i) := Real.iSup_nonneg fun i ↦ apply_nonneg v (x i) + have hc : ((Fintype.card ι : ℝ) ^ ((1 : ℝ) / 2)) ^ v.mult + = (Fintype.card ι : ℝ) ^ (v.mult / 2 : ℝ) := by + rw [← Real.rpow_natCast ((Fintype.card ι : ℝ) ^ ((1 : ℝ) / 2)) v.mult, + ← Real.rpow_mul (by positivity)] + congr 1 + ring + calc (∑ i, v (x i) ^ 2) ^ (v.mult / 2 : ℝ) + ≤ ((Fintype.card ι : ℝ) * (⨆ i, v (x i)) ^ 2) ^ (v.mult / 2 : ℝ) := + Real.rpow_le_rpow (sum_nonneg fun i _ ↦ by positivity) (sum_pow_two_le_card_mul v x) + (by positivity) + _ = (Fintype.card ι : ℝ) ^ (v.mult / 2 : ℝ) * ((⨆ i, v (x i)) ^ 2) ^ (v.mult / 2 : ℝ) := + Real.mul_rpow (by positivity) (by positivity) + _ = ((Fintype.card ι : ℝ) ^ ((1 : ℝ) / 2)) ^ v.mult * (⨆ i, v (x i)) ^ v.mult := by + rw [hc, sq_rpow_div_two h0 v.mult] + +private lemma prod_iSup_pow_mult_le [Nonempty ι] (x : ι → K) : + ∏ v : InfinitePlace K, (⨆ i, v (x i)) ^ v.mult ≤ + ∏ v : InfinitePlace K, (∑ i, v (x i) ^ 2) ^ (v.mult / 2 : ℝ) := + Finset.prod_le_prod₀ (fun v _ ↦ pow_nonneg (Real.iSup_nonneg fun i ↦ apply_nonneg v (x i)) _) + fun v _ ↦ iSup_pow_mult_le_sum_rpow v x + +/-- The archimedean half of the upper comparison. The exponent is collected with +`InfinitePlace.sum_mult_eq` and `totalWeight_eq_finrank`, exactly as in +`arakelovMulHeight_one`, which is the case of equality. -/ +private lemma prod_sum_rpow_le (x : ι → K) : + ∏ v : InfinitePlace K, (∑ i, v (x i) ^ 2) ^ (v.mult / 2 : ℝ) ≤ + (Fintype.card ι : ℝ) ^ ((totalWeight K : ℝ) / 2) * + ∏ v : InfinitePlace K, (⨆ i, v (x i)) ^ v.mult := by + calc ∏ v : InfinitePlace K, (∑ i, v (x i) ^ 2) ^ (v.mult / 2 : ℝ) + ≤ ∏ v : InfinitePlace K, + ((Fintype.card ι : ℝ) ^ ((1 : ℝ) / 2)) ^ v.mult * (⨆ i, v (x i)) ^ v.mult := + Finset.prod_le_prod₀ (fun v _ ↦ by positivity) fun v _ ↦ sum_rpow_le_card_rpow_mul v x + _ = (∏ v : InfinitePlace K, ((Fintype.card ι : ℝ) ^ ((1 : ℝ) / 2)) ^ v.mult) * + ∏ v : InfinitePlace K, (⨆ i, v (x i)) ^ v.mult := prod_mul_distrib + _ = (Fintype.card ι : ℝ) ^ ((totalWeight K : ℝ) / 2) * + ∏ v : InfinitePlace K, (⨆ i, v (x i)) ^ v.mult := by + congr 1 + rw [prod_pow_eq_pow_sum, InfinitePlace.sum_mult_eq, + ← Real.rpow_natCast ((Fintype.card ι : ℝ) ^ ((1 : ℝ) / 2)) (Module.finrank ℚ K), + ← Real.rpow_mul (by positivity), totalWeight_eq_finrank] + congr 1 + ring + +omit [Fintype ι] in +private lemma finprod_finitePlace_nonneg (x : ι → K) : + (0 : ℝ) ≤ ∏ᶠ v : FinitePlace K, ⨆ i, v (x i) := + finprod_nonneg fun v ↦ Real.iSup_nonneg fun i ↦ apply_nonneg v (x i) + +/-- **The sup-norm height is at most the Arakelov height.** The finite parts agree and the +archimedean ones compare place by place, since `‖·‖_∞ ≤ ‖·‖_2`. -/ +lemma mulHeight_le_arakelovMulHeight (x : ι → K) : Height.mulHeight x ≤ arakelovMulHeight x := by + rcases eq_or_ne x 0 with rfl | hx + · simp + have : Nonempty ι := (ne_iff.mp hx).nonempty + rw [NumberField.mulHeight_eq hx, arakelovMulHeight_eq hx] + exact mul_le_mul_of_nonneg_right (prod_iSup_pow_mult_le x) (finprod_finitePlace_nonneg x) + +/-- **The Arakelov height is at most `#ι ^ (totalWeight K / 2)` times the sup-norm height** — +the lemma that carries the constants of the Siegel-lemma literature into Mathlib's +normalization, from `‖·‖_2 ≤ √(#ι) · ‖·‖_∞` at each archimedean place. + +`ι` must be nonempty: on the empty index type both heights take the junk value `1` while the +right-hand side is `0`. The constant is sharp — `arakelovMulHeight_one` is the case of +equality. -/ +lemma arakelovMulHeight_le_mulHeight [Nonempty ι] (x : ι → K) : + arakelovMulHeight x ≤ + (Fintype.card ι : ℝ) ^ ((totalWeight K : ℝ) / 2) * Height.mulHeight x := by + rcases eq_or_ne x 0 with rfl | hx + · rw [arakelovMulHeight_zero, Height.mulHeight_zero, mul_one] + exact Real.one_le_rpow (by exact_mod_cast Fintype.card_pos) (by positivity) + rw [NumberField.mulHeight_eq hx, arakelovMulHeight_eq hx, ← mul_assoc] + exact mul_le_mul_of_nonneg_right (prod_sum_rpow_le x) (finprod_finitePlace_nonneg x) + +/-- On a subsingleton index type the two normalizations agree: both are `1`, by the product +formula. This is the only index type on which they agree unconditionally. -/ +lemma arakelovMulHeight_eq_mulHeight_of_subsingleton [Subsingleton ι] (x : ι → K) : + arakelovMulHeight x = Height.mulHeight x := by + rw [arakelovMulHeight_eq_one_of_subsingleton, Height.mulHeight_eq_one_of_subsingleton] + +lemma logHeight_le_arakelovLogHeight (x : ι → K) : + Height.logHeight x ≤ arakelovLogHeight x := by + rw [Height.logHeight_eq_log_mulHeight, arakelovLogHeight_eq_log_arakelovMulHeight] + exact Real.log_le_log (Height.mulHeight_pos x) (mulHeight_le_arakelovMulHeight x) + +lemma arakelovLogHeight_le_logHeight [Nonempty ι] (x : ι → K) : + arakelovLogHeight x ≤ + (totalWeight K : ℝ) / 2 * log (Fintype.card ι) + Height.logHeight x := by + have hcard : (0 : ℝ) < Fintype.card ι := by exact_mod_cast Fintype.card_pos + rw [arakelovLogHeight_eq_log_arakelovMulHeight, Height.logHeight_eq_log_mulHeight, + ← Real.log_rpow hcard, ← Real.log_mul (by positivity) (Height.mulHeight_pos x).ne'] + exact Real.log_le_log (arakelovMulHeight_pos x) (arakelovMulHeight_le_mulHeight x) + +lemma arakelovLogHeight_eq_logHeight_of_subsingleton [Subsingleton ι] (x : ι → K) : + arakelovLogHeight x = Height.logHeight x := by + rw [arakelovLogHeight_eq_log_arakelovMulHeight, Height.logHeight_eq_log_mulHeight, + arakelovMulHeight_eq_mulHeight_of_subsingleton] + +end Comparison + +/-! +### The one-variable case + +`arakelovMulHeight₁ x` is the Arakelov height of the point `(x : 1)` of the projective line. +Unlike the sup-norm normalization, its archimedean local factor is `((v x) ^ 2 + 1) ^ (mult v / 2)` +rather than `max (v x) 1 ^ mult v`, so it does **not** agree with `Height.mulHeight₁`. +-/ + +/-- The multiplicative Arakelov height of an element of a number field, i.e. of the point +`(x : 1)` of the projective line. -/ +@[expose] noncomputable def arakelovMulHeight₁ (x : K) : ℝ := arakelovMulHeight ![x, 1] + +/-- The logarithmic Arakelov height of an element of a number field. -/ +@[expose] noncomputable def arakelovLogHeight₁ (x : K) : ℝ := log (arakelovMulHeight₁ x) + +lemma arakelovMulHeight₁_eq_arakelovMulHeight (x : K) : + arakelovMulHeight₁ x = arakelovMulHeight ![x, 1] := + rfl + +lemma arakelovLogHeight₁_eq_log_arakelovMulHeight₁ (x : K) : + arakelovLogHeight₁ x = log (arakelovMulHeight₁ x) := + rfl + +lemma arakelovLogHeight₁_eq_arakelovLogHeight (x : K) : + arakelovLogHeight₁ x = arakelovLogHeight ![x, 1] := + rfl + +omit [NumberField K] in +private lemma iSup_cons_one {F : Type*} [FunLike F K ℝ] [MonoidWithZeroHomClass F K ℝ] + (v : F) (x : K) : ⨆ i, v (![x, 1] i) = max (v x) 1 := by + have h (i : Fin 2) : v (![x, 1] i) = ![v x, 1] i := by fin_cases i <;> simp + simp only [h] + exact le_antisymm (ciSup_le fun i ↦ by fin_cases i <;> simp) + (max_le (Finite.le_ciSup_of_le 0 (by simp)) (Finite.le_ciSup_of_le 1 (by simp))) + +/-- The familiar formula for the Arakelov height of a single element. -/ +lemma arakelovMulHeight₁_eq (x : K) : + arakelovMulHeight₁ x = + (∏ v : InfinitePlace K, (v x ^ 2 + 1) ^ (v.mult / 2 : ℝ)) * + ∏ᶠ v : FinitePlace K, max (v x) 1 := by + have hx : ![x, 1] ≠ 0 := by simp + rw [arakelovMulHeight₁_eq_arakelovMulHeight, arakelovMulHeight_eq hx] + simp only [iSup_cons_one, Fin.sum_univ_two, Matrix.cons_val_zero, Matrix.cons_val_one, + Matrix.cons_val_fin_one, map_one, one_pow] + +@[simp] +lemma arakelovMulHeight₁_zero : arakelovMulHeight₁ (0 : K) = 1 := by + simp [arakelovMulHeight₁_eq] + +@[simp] +lemma arakelovLogHeight₁_zero : arakelovLogHeight₁ (0 : K) = 0 := by + simp [arakelovLogHeight₁_eq_log_arakelovMulHeight₁] + +lemma one_le_arakelovMulHeight₁ (x : K) : 1 ≤ arakelovMulHeight₁ x := + one_le_arakelovMulHeight _ + +lemma arakelovMulHeight₁_pos (x : K) : 0 < arakelovMulHeight₁ x := + arakelovMulHeight_pos _ + +lemma arakelovMulHeight₁_ne_zero (x : K) : arakelovMulHeight₁ x ≠ 0 := + arakelovMulHeight_ne_zero _ + +lemma arakelovLogHeight₁_nonneg (x : K) : 0 ≤ arakelovLogHeight₁ x := + arakelovLogHeight_nonneg _ + +/-- The local formula for the affine logarithmic Arakelov height: the finite places contribute +`log⁺ (v x)`, the positive part of the logarithm. -/ +lemma arakelovLogHeight₁_eq (x : K) : + arakelovLogHeight₁ x = + (∑ v : InfinitePlace K, (v.mult / 2 : ℝ) * log (v x ^ 2 + 1)) + + ∑ᶠ v : FinitePlace K, log⁺ (v x) := by + have harch (v : InfinitePlace K) : (0 : ℝ) < (v x ^ 2 + 1) ^ (v.mult / 2 : ℝ) := by positivity + have hfin (v : FinitePlace K) : (0 : ℝ) < max (v x) 1 := lt_max_of_lt_right zero_lt_one + have harchprod : (0 : ℝ) < ∏ v : InfinitePlace K, (v x ^ 2 + 1) ^ (v.mult / 2 : ℝ) := + Finset.prod_pos fun v _ ↦ harch v + have hfinprod : (0 : ℝ) < ∏ᶠ v : FinitePlace K, max (v x) 1 := + zero_lt_one.trans_le <| one_le_finprod fun v ↦ le_max_right _ _ + have hlog (v : InfinitePlace K) : + log ((v x ^ 2 + 1) ^ (v.mult / 2 : ℝ)) = (v.mult / 2 : ℝ) * log (v x ^ 2 + 1) := + log_rpow (by positivity) _ + have hposlog : log (∏ᶠ v : FinitePlace K, max (v x) 1) = ∑ᶠ v : FinitePlace K, log⁺ (v x) := by + rw [Real.log_finprod hfin] + exact finsum_congr fun v ↦ by simp [max_comm, posLog_eq_log_max_one] + rw [arakelovLogHeight₁_eq_log_arakelovMulHeight₁, arakelovMulHeight₁_eq, + log_mul harchprod.ne' hfinprod.ne', Real.log_prod fun v _ ↦ (harch v).ne', hposlog] + simp_rw [hlog] + +/-- The value of the affine Arakelov height at `1`: `2 ^ (totalWeight K / 2)`, not `1`. This is +the positive form of the warning that `arakelovMulHeight₁` is *not* `Height.mulHeight₁`; the two +one-variable heights are related only by the comparison lemmas of Layer 0.2. -/ +lemma arakelovMulHeight₁_one : + arakelovMulHeight₁ (1 : K) = 2 ^ ((totalWeight K : ℝ) / 2) := by + rw [arakelovMulHeight₁_eq_arakelovMulHeight, + show ![(1 : K), 1] = 1 from by ext i; fin_cases i <;> rfl, arakelovMulHeight_one] + simp + +lemma arakelovMulHeight₁_neg (x : K) : arakelovMulHeight₁ (-x) = arakelovMulHeight₁ x := by + have h1 (v : InfinitePlace K) : v (-x) = v x := AbsoluteValue.map_neg v.1 x + have h2 (v : FinitePlace K) : v (-x) = v x := AbsoluteValue.map_neg v.1 x + simp only [arakelovMulHeight₁_eq, h1, h2] + +lemma arakelovLogHeight₁_neg (x : K) : arakelovLogHeight₁ (-x) = arakelovLogHeight₁ x := by + simp only [arakelovLogHeight₁_eq_log_arakelovMulHeight₁, arakelovMulHeight₁_neg] + +/-- The affine Arakelov height is invariant under inversion: `H_Ar(x⁻¹ : 1) = H_Ar(x : 1)`, +since both are the height of the projective point `(x : 1) = (1 : x⁻¹)`. -/ +lemma arakelovMulHeight₁_inv (x : K) : arakelovMulHeight₁ x⁻¹ = arakelovMulHeight₁ x := by + rcases eq_or_ne x 0 with rfl | hx + · rw [inv_zero] + have h : ![x⁻¹, 1] = x⁻¹ • ![1, x] := by + ext i + fin_cases i <;> simp [inv_mul_cancel₀ hx] + rw [arakelovMulHeight₁_eq_arakelovMulHeight, arakelovMulHeight₁_eq_arakelovMulHeight, h, + arakelovMulHeight_smul_eq _ (inv_ne_zero hx), arakelovMulHeight_swap] + +lemma arakelovLogHeight₁_inv (x : K) : arakelovLogHeight₁ x⁻¹ = arakelovLogHeight₁ x := by + simp only [arakelovLogHeight₁_eq_log_arakelovMulHeight₁, arakelovMulHeight₁_inv] + +/-! +The comparison of Layer 0.2, applied to `![x, 1]`. This is the only relation between the two +one-variable heights: `arakelovMulHeight₁ = mulHeight₁` is false, already at `x = 1`. +-/ + +lemma mulHeight₁_le_arakelovMulHeight₁ (x : K) : Height.mulHeight₁ x ≤ arakelovMulHeight₁ x := by + rw [Height.mulHeight₁_eq_mulHeight, arakelovMulHeight₁_eq_arakelovMulHeight] + exact mulHeight_le_arakelovMulHeight _ + +lemma arakelovMulHeight₁_le_mulHeight₁ (x : K) : + arakelovMulHeight₁ x ≤ 2 ^ ((totalWeight K : ℝ) / 2) * Height.mulHeight₁ x := by + rw [Height.mulHeight₁_eq_mulHeight, arakelovMulHeight₁_eq_arakelovMulHeight] + simpa using arakelovMulHeight_le_mulHeight ![x, 1] + +lemma logHeight₁_le_arakelovLogHeight₁ (x : K) : + Height.logHeight₁ x ≤ arakelovLogHeight₁ x := by + rw [Height.logHeight₁_eq_logHeight, arakelovLogHeight₁_eq_arakelovLogHeight] + exact logHeight_le_arakelovLogHeight _ + +lemma arakelovLogHeight₁_le_logHeight₁ (x : K) : + arakelovLogHeight₁ x ≤ (totalWeight K : ℝ) / 2 * log 2 + Height.logHeight₁ x := by + rw [Height.logHeight₁_eq_logHeight, arakelovLogHeight₁_eq_arakelovLogHeight] + simpa using arakelovLogHeight_le_logHeight ![x, 1] + +end NumberField + +/-! +### The descent to projective space + +Scaling invariance makes the Arakelov height a well-defined function on +`Projectivization K (ι → K)`, exactly as for `Projectivization.mulHeight`. +-/ + +namespace Projectivization + +open NumberField Real + +variable {K : Type*} [Field K] [NumberField K] {ι : Type*} [Fintype ι] + +private lemma arakelovMulHeight_aux (a b : { v : ι → K // v ≠ 0 }) (t : K) + (h : a.val = t • b.val) : + NumberField.arakelovMulHeight a.val = NumberField.arakelovMulHeight b.val := + have ht : t ≠ 0 := by + contrapose! h + simpa [h] using a.prop + h ▸ arakelovMulHeight_smul_eq _ ht + +private lemma arakelovLogHeight_aux (a b : { v : ι → K // v ≠ 0 }) (t : K) + (h : a.val = t • b.val) : + NumberField.arakelovLogHeight a.val = NumberField.arakelovLogHeight b.val := by + simp only [arakelovLogHeight_eq_log_arakelovMulHeight, arakelovMulHeight_aux a b t h] + +-- We do not expose the bodies of these definitions so that we can keep the "_aux" lemmas +-- above private. + +/-- The multiplicative Arakelov height of a point of projective space over a number field. -/ +noncomputable def arakelovMulHeight (x : Projectivization K (ι → K)) : ℝ := + x.lift (fun r ↦ NumberField.arakelovMulHeight r.val) arakelovMulHeight_aux + +/-- The logarithmic Arakelov height of a point of projective space over a number field. -/ +noncomputable def arakelovLogHeight (x : Projectivization K (ι → K)) : ℝ := + x.lift (fun r ↦ NumberField.arakelovLogHeight r.val) arakelovLogHeight_aux + +lemma arakelovMulHeight_mk {x : ι → K} (hx : x ≠ 0) : + arakelovMulHeight (mk K x hx) = NumberField.arakelovMulHeight x := by + rfl + +lemma arakelovLogHeight_mk {x : ι → K} (hx : x ≠ 0) : + arakelovLogHeight (mk K x hx) = NumberField.arakelovLogHeight x := by + rfl + +lemma arakelovLogHeight_eq_log_arakelovMulHeight (x : Projectivization K (ι → K)) : + arakelovLogHeight x = log (arakelovMulHeight x) := by + rw [← x.mk_rep, arakelovMulHeight_mk, arakelovLogHeight_mk, + NumberField.arakelovLogHeight_eq_log_arakelovMulHeight] + +lemma one_le_arakelovMulHeight (x : Projectivization K (ι → K)) : 1 ≤ arakelovMulHeight x := by + rw [← x.mk_rep, arakelovMulHeight_mk] + exact NumberField.one_le_arakelovMulHeight _ + +lemma arakelovMulHeight_pos (x : Projectivization K (ι → K)) : 0 < arakelovMulHeight x := + zero_lt_one.trans_le <| one_le_arakelovMulHeight x + +lemma arakelovMulHeight_ne_zero (x : Projectivization K (ι → K)) : arakelovMulHeight x ≠ 0 := + (arakelovMulHeight_pos x).ne' + +lemma arakelovLogHeight_nonneg (x : Projectivization K (ι → K)) : 0 ≤ arakelovLogHeight x := by + rw [arakelovLogHeight_eq_log_arakelovMulHeight] + exact log_nonneg x.one_le_arakelovMulHeight + +/-- On a subsingleton index type every point of projective space has Arakelov height `1`; +the projective form of `NumberField.arakelovMulHeight_eq_one_of_subsingleton`, and the degenerate +case in which the Arakelov and sup-norm normalizations agree. -/ +@[simp] +lemma arakelovMulHeight_eq_one_of_subsingleton [Subsingleton ι] + (x : Projectivization K (ι → K)) : arakelovMulHeight x = 1 := by + rw [← x.mk_rep, arakelovMulHeight_mk] + exact NumberField.arakelovMulHeight_eq_one_of_subsingleton _ + +@[simp] +lemma arakelovLogHeight_eq_zero_of_subsingleton [Subsingleton ι] + (x : Projectivization K (ι → K)) : arakelovLogHeight x = 0 := by + simp [arakelovLogHeight_eq_log_arakelovMulHeight] + +/-! +#### Comparison of the two normalizations on projective space + +`NumberField.mulHeight_le_arakelovMulHeight` and `NumberField.arakelovMulHeight_le_mulHeight` read +on `Projectivization K (ι → K)`. Both sides are computed on any representative, so the comparisons +descend to projective space with the same constant. +-/ + +/-- **The projective sup-norm height is at most the projective Arakelov height.** -/ +lemma mulHeight_le_arakelovMulHeight (x : Projectivization K (ι → K)) : + mulHeight x ≤ arakelovMulHeight x := by + rw [← x.mk_rep, mulHeight_mk, arakelovMulHeight_mk] + exact NumberField.mulHeight_le_arakelovMulHeight _ + +/-- **The projective Arakelov height is at most `#ι ^ (totalWeight K / 2)` times the projective +sup-norm height**, with the constant — sharp — of `NumberField.arakelovMulHeight_le_mulHeight`. -/ +lemma arakelovMulHeight_le_mulHeight [Nonempty ι] (x : Projectivization K (ι → K)) : + arakelovMulHeight x ≤ + (Fintype.card ι : ℝ) ^ ((Height.totalWeight K : ℝ) / 2) * mulHeight x := by + rw [← x.mk_rep, mulHeight_mk, arakelovMulHeight_mk] + exact NumberField.arakelovMulHeight_le_mulHeight _ + +lemma logHeight_le_arakelovLogHeight (x : Projectivization K (ι → K)) : + logHeight x ≤ arakelovLogHeight x := by + rw [← x.mk_rep, logHeight_mk, arakelovLogHeight_mk] + exact NumberField.logHeight_le_arakelovLogHeight _ + +lemma arakelovLogHeight_le_logHeight [Nonempty ι] (x : Projectivization K (ι → K)) : + arakelovLogHeight x ≤ (Height.totalWeight K : ℝ) / 2 * log (Fintype.card ι) + logHeight x := by + rw [← x.mk_rep, logHeight_mk, arakelovLogHeight_mk] + exact NumberField.arakelovLogHeight_le_logHeight _ + +end Projectivization + +/-! +### `positivity` extensions + +The Arakelov heights are positive (multiplicative) resp. nonnegative (logarithmic), exactly as +their sup-norm counterparts in `Mathlib.NumberTheory.Height.Basic`. +-/ + +namespace Mathlib.Meta.Positivity + +open Lean.Meta Qq + +/-- Extension for the `positivity` tactic: `NumberField.arakelovMulHeight` is always positive. -/ +@[positivity NumberField.arakelovMulHeight _] +meta def evalArakelovMulHeight : PositivityExt where eval {u α} _ pα? e := + match pα? with | none => pure .none | some _ => do + match u, α, e with + | 0, ~q(ℝ), ~q(@NumberField.arakelovMulHeight $K $KF $KNF $ι $ιF $a) => + assertInstancesCommute + pure (.positive q(NumberField.arakelovMulHeight_pos $a)) + | _, _, _ => throwError "not NumberField.arakelovMulHeight" + +/-- Extension for the `positivity` tactic: `NumberField.arakelovLogHeight` is always +nonnegative. -/ +@[positivity NumberField.arakelovLogHeight _] +meta def evalArakelovLogHeight : PositivityExt where eval {u α} _ pα? e := + match pα? with | none => pure .none | some _ => do + match u, α, e with + | 0, ~q(ℝ), ~q(@NumberField.arakelovLogHeight $K $KF $KNF $ι $ιF $a) => + assertInstancesCommute + pure (.nonnegative q(NumberField.arakelovLogHeight_nonneg $a)) + | _, _, _ => throwError "not NumberField.arakelovLogHeight" + +/-- Extension for the `positivity` tactic: `NumberField.arakelovMulHeight₁` is always +positive. -/ +@[positivity NumberField.arakelovMulHeight₁ _] +meta def evalArakelovMulHeight₁ : PositivityExt where eval {u α} _ pα? e := + match pα? with | none => pure .none | some _ => do + match u, α, e with + | 0, ~q(ℝ), ~q(@NumberField.arakelovMulHeight₁ $K $KF $KNF $a) => + assertInstancesCommute + pure (.positive q(NumberField.arakelovMulHeight₁_pos $a)) + | _, _, _ => throwError "not NumberField.arakelovMulHeight₁" + +/-- Extension for the `positivity` tactic: `NumberField.arakelovLogHeight₁` is always +nonnegative. -/ +@[positivity NumberField.arakelovLogHeight₁ _] +meta def evalArakelovLogHeight₁ : PositivityExt where eval {u α} _ pα? e := + match pα? with | none => pure .none | some _ => do + match u, α, e with + | 0, ~q(ℝ), ~q(@NumberField.arakelovLogHeight₁ $K $KF $KNF $a) => + assertInstancesCommute + pure (.nonnegative q(NumberField.arakelovLogHeight₁_nonneg $a)) + | _, _, _ => throwError "not NumberField.arakelovLogHeight₁" + +/-- Extension for the `positivity` tactic: `Projectivization.arakelovMulHeight` is always +positive. -/ +@[positivity Projectivization.arakelovMulHeight _] +meta def evalProjArakelovMulHeight : PositivityExt where eval {u α} _ pα? e := + match pα? with | none => pure .none | some _ => do + match u, α, e with + | 0, ~q(ℝ), ~q(@Projectivization.arakelovMulHeight $K $KF $KNF $ι $ιF $a) => + assertInstancesCommute + pure (.positive q(Projectivization.arakelovMulHeight_pos $a)) + | _, _, _ => throwError "not Projectivization.arakelovMulHeight" + +/-- Extension for the `positivity` tactic: `Projectivization.arakelovLogHeight` is always +nonnegative. -/ +@[positivity Projectivization.arakelovLogHeight _] +meta def evalProjArakelovLogHeight : PositivityExt where eval {u α} _ pα? e := + match pα? with | none => pure .none | some _ => do + match u, α, e with + | 0, ~q(ℝ), ~q(@Projectivization.arakelovLogHeight $K $KF $KNF $ι $ιF $a) => + assertInstancesCommute + pure (.nonnegative q(Projectivization.arakelovLogHeight_nonneg $a)) + | _, _, _ => throwError "not Projectivization.arakelovLogHeight" + +end Mathlib.Meta.Positivity + +/-! +### Worked examples + +Cheap checks that the definition means what it should. Over `ℚ` the two normalizations of the +height of the point `(1 : 1)` genuinely differ, so the `√N` factors in the Siegel-lemma +literature are not cosmetic and a proof that silently interchanges the two heights is wrong. + +The third shows that the constant of `arakelovMulHeight_le_mulHeight` is attained, so it cannot be +improved. The last two are rejection tests: they refute the two lemmas of +`Mathlib.NumberTheory.Height.Basic` that a reader would expect to carry over to this normalization +and that do not. +-/ + +section Examples + +open Height NumberField + +/-- The Arakelov height of `(1 : 1)` over `ℚ` is `√2`: the archimedean local factor is the +ℓ² norm `√(1² + 1²)`. -/ +example : arakelovMulHeight ![(1 : ℚ), 1] = Real.sqrt 2 := by + have hx : ![(1 : ℚ), 1] ≠ 0 := by simp + have h1 (i : Fin 2) : ![(1 : ℚ), 1] i = 1 := by fin_cases i <;> rfl + have hfin : ∏ᶠ v : FinitePlace ℚ, ⨆ i, v (![(1 : ℚ), 1] i) = 1 := + finprod_eq_one_of_forall_eq_one fun v ↦ by simp [h1] + have harch (v : InfinitePlace ℚ) : + (∑ i, v (![(1 : ℚ), 1] i) ^ 2) ^ (v.mult / 2 : ℝ) = Real.sqrt 2 ^ v.mult := by + have h2 : ∑ i, v (![(1 : ℚ), 1] i) ^ 2 = (2 : ℝ) := by simp [h1] + rw [h2, Real.sqrt_eq_rpow, ← Real.rpow_natCast ((2 : ℝ) ^ ((1 : ℝ) / 2)) v.mult, + ← Real.rpow_mul (by norm_num)] + congr 1 + ring + rw [arakelovMulHeight_eq hx, hfin, mul_one, Finset.prod_congr rfl fun v _ ↦ harch v, + Finset.prod_pow_eq_pow_sum, InfinitePlace.sum_mult_eq] + simp + +/-- …whereas the sup-norm height of the same tuple is `1`. -/ +example : Height.mulHeight ![(1 : ℚ), 1] = 1 := by + rw [show ![(1 : ℚ), 1] = 1 from by ext i; fin_cases i <;> rfl] + exact Height.mulHeight_one + +/-- The upper comparison is sharp: the all-ones tuple attains it, since its sup-norm height is +`1` by the product formula while its Arakelov height is the constant itself. -/ +example {K : Type*} [Field K] [NumberField K] {ι : Type*} [Fintype ι] [Nonempty ι] : + arakelovMulHeight (1 : ι → K) = + (Fintype.card ι : ℝ) ^ ((totalWeight K : ℝ) / 2) * Height.mulHeight (1 : ι → K) := by + rw [arakelovMulHeight_one, Height.mulHeight_one, mul_one] + +/-- The same `√2` through `arakelovMulHeight_one` instead of through the definition, which +cross-checks the two routes; the rejection tests below consume it as a value. -/ +private lemma arakelovMulHeight_one_fin_two_rat : + arakelovMulHeight (1 : Fin 2 → ℚ) = Real.sqrt 2 := by + rw [arakelovMulHeight_one, Fintype.card_fin, totalWeight_eq_finrank, Module.finrank_self, + Nat.cast_one, Real.sqrt_eq_rpow] + norm_num + +/-- **Rejection test.** `Height.mulHeight_pow` has no Arakelov analogue: coordinatewise squaring +fixes the all-ones tuple over `ℚ`, so it cannot square the height — `√2` against `2`. -/ +example : + arakelovMulHeight ((1 : Fin 2 → ℚ) ^ 2) ≠ arakelovMulHeight (1 : Fin 2 → ℚ) ^ 2 := by + rw [one_pow, arakelovMulHeight_one_fin_two_rat, Real.sq_sqrt (by norm_num : (0 : ℝ) ≤ 2)] + intro h + nlinarith [Real.sq_sqrt (show (0 : ℝ) ≤ 2 by norm_num), Real.sqrt_nonneg 2, h] + +/-- **Rejection test.** `Height.mulHeight_comp_le` fails for a non-injective reindexing: +duplicating the one coordinate of `(1)` raises the ℓ² norm at the real place from `1` to `√2`, +while no sup norm can see a repetition. The injective case — the one its consumers use — is +unaffected. -/ +example : ¬ arakelovMulHeight ((![1] : Fin 1 → ℚ) ∘ (fun _ : Fin 2 ↦ 0)) ≤ + arakelovMulHeight (![1] : Fin 1 → ℚ) := by + have h : (![1] : Fin 1 → ℚ) ∘ (fun _ : Fin 2 ↦ 0) = 1 := by ext i; simp + rw [h, arakelovMulHeight_one_fin_two_rat, arakelovMulHeight_eq_one_of_subsingleton] + nlinarith [Real.sq_sqrt (show (0 : ℝ) ≤ 2 by norm_num), Real.sqrt_nonneg 2] + +end Examples + +end diff --git a/LeanPool/CCDN/ArithmeticHeights/BombieriVaaler.lean b/LeanPool/CCDN/ArithmeticHeights/BombieriVaaler.lean new file mode 100644 index 0000000000..432cc2a37e --- /dev/null +++ b/LeanPool/CCDN/ArithmeticHeights/BombieriVaaler.lean @@ -0,0 +1,568 @@ +/- +Copyright (c) 2026 Ralf Stephan. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Ralf Stephan +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.ArithmeticHeights.CubeSlicing +public import LeanPool.CCDN.ArithmeticHeights.Duality +public import LeanPool.CCDN.ArithmeticHeights.MinkowskiSecond +public import LeanPool.CCDN.ArithmeticHeights.RationalLattice +public import LeanPool.CCDN.ArithmeticHeights.Siegel + +/-! +# Bombieri–Vaaler's theorem over `ℤ` + +For an `M × N` integer matrix `A` whose rows are linearly independent, the integral solutions of +`A x = 0` contain `N - M` linearly independent vectors `x₁, …, x_{N-M}` with + +```text +∏ l, max n, |x l n| ≤ D⁻¹ √(det (A Aᵀ)), +``` + +`D` the greatest common divisor of the maximal minors of `A`, and in particular a nonzero integral +solution with `max n, |x n| ≤ (D⁻¹ √(det (A Aᵀ)))^{1/(N-M)}`. These are Theorems 2 and 1 of +Bombieri–Vaaler 1983, and the right-hand side is an absolute height on the Grassmannian, so — unlike +the bound of Layer 5.1 — it does not move under `A ↦ U A` for `U ∈ GL_M(ℤ)`. + +The proof is the assembly the roadmap prices it at: the covolume identity of Layer 4.3 over `ℚ` +identifies the covolume of the solution lattice with the Arakelov height of the solution space, +Layer 3.5 identifies that with the Arakelov height of the row space and Layer 3.4 computes the +latter as `D⁻¹ √(det (A Aᵀ))`, while Minkowski's second theorem (Layer 4.2) against the central +slice of the unit cube, whose volume Layer 4.5 bounds below by `2^{N-M}`, bounds the product of the +successive minima by that covolume. Layer 4.1 then supplies vectors realizing the minima. + +## Main definitions + +* `Submodule.cubeSlice`: the central slice `{y ∈ V | ∀ i, |y i| ≤ 1}` of the unit cube of + `EuclideanSpace ℝ ι` by a subspace `V`. This is the convex body Minkowski's second theorem is + applied to, and the body Layer 4.5 measures. +* `Int.Matrix.minorGcd`: the greatest common divisor of the maximal minors of an integer matrix, + the `D` of Bombieri–Vaaler. The minors are Layer 3.1's Plücker coordinates of the rows. + +## Main results + +* `Int.Matrix.exists_linearIndependent_mulVec_eq_zero_prod_iSup_abs_le`: **Bombieri–Vaaler's + Theorem 2**, a small basis of the integral solutions. +* `Int.Matrix.exists_linearIndependent_mulVec_eq_zero_prod_mulHeight_le`: the same, with the + multiplicative height of Layer 1 on the left — the `K = ℚ` case of the statement Layer 5.4 makes + over a number field. +* `Int.Matrix.exists_ne_zero_mulVec_eq_zero_iSup_abs_le_det_rpow`: **Bombieri–Vaaler's Theorem 1**, + a single small nonzero integral solution. +* `Int.Matrix.sqrt_det_div_minorGcd_unit_mul`: **the invariance** of the bound under + `A ↦ U A` for `U ∈ GL_M(ℤ)`, which is what Theorem 1 has over Layer 5.1's Siegel lemma. +* `Int.Matrix.minorGcd_mul_arakelovMulHeight_ker`: the identity the bound *is*, + `D · H_Ar(ker A) = √(det (A Aᵀ))`. +* `ZLattice.prod_successiveMinimum_cubeSlice_le_covolume`: Minkowski's second theorem against the + cube slice, with the factor `2^n` cancelled by Vaaler's bound. This is the geometric half of the + theorem, and it knows no arithmetic. +* `NumberField.gcd_mul_arakelovMulHeight_intCast`: the Arakelov analogue of Layer 5.1's + `Rat.gcd_mul_mulHeight_intCast`, the translation the arithmetic half is read through. +* `Int.Matrix.linearIndependent_row_map_rat_iff`, `Int.Matrix.det_pos`, + `Int.Matrix.one_le_minorGcd`: the hypothesis and the two positivity facts that make the bound + meaningful. + +## Implementation notes + +⚠ **The absolute value in Bombieri–Vaaler's `√|det (A Aᵀ)|` is redundant.** The Gram determinant of +the rows is a sum of squares of minors — this is Layer 3.4's `Matrix.det_mul_transpose_self_nonneg` +— and under the rank hypothesis it is *positive*, `Int.Matrix.det_pos`. The statements here carry +no absolute value, and the hypothesis is stated as `Int.Matrix.linearIndependent_row_map_rat_iff` +makes available: linear independence of the rows over `ℚ` is exactly `(A * Aᵀ).det ≠ 0`, a +condition on the integer matrix alone. + +⚠ **The milestone needs Layer 3.5, which its pricing does not mention.** The roadmap prices 5.2 at +"3.4, 4.2, 4.5 and the `ℚ`-case of 4.3", and Layer 4.3 delivers the covolume of the solution +lattice as the Arakelov height of the *solution space*. Turning that into the minors of `A` is the +duality theorem `Matrix.arakelovMulHeight_ker_mulVecLin` of Layer 3.5, and nothing else will do: +the Plücker coordinates of the kernel are the complementary minors, which is what duality says. +So the `ℚ` spine is 3.1 → 3.2 → 3.3 → 3.4, 3.5 → 4.1 → 4.2 → 4.3(`ℚ`) → 4.5 → 5.2, one rung longer +than advertised, and every rung of it is landed. + +⚠ **The `2^n` of Minkowski's second theorem and the `2^n` of Vaaler's cube-slicing theorem cancel +exactly, and that is the whole of why the constant is `1`.** Layer 4.2 bounds +`(∏ λ i) · vol B` by `2^n · covol L`, and Layer 4.5 bounds `vol B` below by `2^n` for `B` the cube +slice; the product of the minima is therefore at most the covolume, with no constant at all. Any +body with a worse slice bound would leave a constant behind, which is precisely what happens over a +number field with a complex place — Layer 5.4's `(2/π)^{k r₂ / d}`. + +⚠ **The basis form needs no hypothesis `M < N`.** +`Int.Matrix.exists_linearIndependent_mulVec_eq_zero_prod_iSup_abs_le` is stated for `Fin (N - M)` +solutions and is sharp at `N = M`, where both sides are `1`: the empty +product on the left, and on the right the Arakelov height of the zero subspace. Only the one-vector +form needs `M < N`, and there it is not removable — the exponent `1/(N - M)` requires it. + +⚠ **The proof passes from sup norms to heights pointwise.** By Layer 5.1's +`Rat.gcd_mul_mulHeight_intCast` the height of a nonzero integer tuple is its sup norm divided by +its content. Applying the resulting height-at-most-sup-norm inequality to each solution gives +`Int.Matrix.exists_linearIndependent_mulVec_eq_zero_prod_mulHeight_le`, the height form that +Layer 5.4 generalizes. + +## References + +E. Bombieri and J. Vaaler, "On Siegel's lemma", *Inventiones Mathematicae* **73** (1983), 11–32, +Theorems 1 and 2, and equation (2.5) for the invariance. + +I. Aliev and M. Henk, "Minkowski's successive minima in convex and discrete geometry", +*Communications in Mathematics* **31**(2) (2023), 35–59, §6, Theorems 6.2–6.3 +(arXiv:2304.00120; doi:10.46298/cm.11155). This section gives an adele-free proof using +the rational covolume identity stated before Theorem 6.3. + +E. Bombieri and W. Gubler, *Heights in Diophantine Geometry*, Cambridge University Press (2006), +Theorem 2.9.4 and Remark 2.9.5 for the number-field statement this is the `K = ℚ` case of. + +This is Layer 5.2 of the `ArithmeticHeights` roadmap. +-/ + +public section + +open Finset MeasureTheory Module Submodule Matrix exteriorPower +open scoped ENNReal Pointwise + +/-! +### The central slice of the unit cube + +The convex body Minkowski's second theorem is applied to. Everything in this section is elementary; +the one statement with content about it is Layer 4.5's `two_pow_finrank_le_volume_inter_cube`, which +bounds its volume below by `2 ^ dim V`. +-/ + +namespace Submodule + +variable {ι : Type*} (V : Submodule ℝ (EuclideanSpace ℝ ι)) + +/-- The **central slice of the unit cube** `[-1, 1]^ι` by a subspace `V` of `EuclideanSpace ℝ ι`, +as a subset of `V`. -/ +def cubeSlice : Set ↥V := {y : ↥V | ∀ i, |(y : EuclideanSpace ℝ ι) i| ≤ 1} + +theorem mem_cubeSlice {y : ↥V} : y ∈ V.cubeSlice ↔ ∀ i, |(y : EuclideanSpace ℝ ι) i| ≤ 1 := + Iff.rfl + +/-- A dilation of the cube slice is the slice of the corresponding cube, which is how a bound by a +successive minimum is read off. -/ +theorem mem_smul_cubeSlice {r : ℝ} (hr : 0 < r) {y : ↥V} : + y ∈ r • V.cubeSlice ↔ ∀ i, |(y : EuclideanSpace ℝ ι) i| ≤ r := by + have h : r • V.cubeSlice = {y : ↥V | ∀ i, |(y : EuclideanSpace ℝ ι) i| ≤ r} := by + rw [show V.cubeSlice = {y : ↥V | ∀ i, |(y : EuclideanSpace ℝ ι) i| ≤ 1} from rfl, + smul_setOf_abs_le V hr, mul_one] + rw [h] + exact Iff.rfl + +theorem convex_cubeSlice : Convex ℝ V.cubeSlice := by + intro x hx y hy a b ha hb hab i + have h : ((a • x + b • y : ↥V) : EuclideanSpace ℝ ι) i + = a * (x : EuclideanSpace ℝ ι) i + b * (y : EuclideanSpace ℝ ι) i := by + simp + rw [h] + calc |a * (x : EuclideanSpace ℝ ι) i + b * (y : EuclideanSpace ℝ ι) i| + ≤ |a * (x : EuclideanSpace ℝ ι) i| + |b * (y : EuclideanSpace ℝ ι) i| := abs_add_le _ _ + _ = a * |(x : EuclideanSpace ℝ ι) i| + b * |(y : EuclideanSpace ℝ ι) i| := by + rw [abs_mul, abs_mul, abs_of_nonneg ha, abs_of_nonneg hb] + _ ≤ a * 1 + b * 1 := by gcongr; exacts [hx i, hy i] + _ = 1 := by rw [mul_one, mul_one, hab] + +theorem neg_mem_cubeSlice {x : ↥V} (hx : x ∈ V.cubeSlice) : -x ∈ V.cubeSlice := by + intro i + have h : ((-x : ↥V) : EuclideanSpace ℝ ι) i = -(x : EuclideanSpace ℝ ι) i := by simp + rw [h, abs_neg] + exact hx i + +theorem isClosed_cubeSlice : IsClosed V.cubeSlice := by + have h : V.cubeSlice = ⋂ i, (fun y : ↥V ↦ (y : EuclideanSpace ℝ ι) i) ⁻¹' (Set.Icc (-1) 1) := by + ext y + simp [cubeSlice, abs_le, Set.mem_Icc] + rw [h] + refine isClosed_iInter fun i ↦ IsClosed.preimage ?_ isClosed_Icc + fun_prop + +theorem isBounded_cubeSlice [Finite ι] : Bornology.IsBounded V.cubeSlice := by + have : Fintype ι := Fintype.ofFinite ι + refine Bornology.IsBounded.subset + (Metric.isBounded_closedBall (x := (0 : ↥V)) (r := Real.sqrt (Fintype.card ι))) ?_ + intro y hy + rw [Metric.mem_closedBall, dist_zero_right] + have hnorm : ‖y‖ = ‖(y : EuclideanSpace ℝ ι)‖ := rfl + rw [hnorm, EuclideanSpace.norm_eq] + refine Real.sqrt_le_sqrt ?_ + calc ∑ i, ‖(y : EuclideanSpace ℝ ι) i‖ ^ 2 ≤ ∑ _i : ι, (1 : ℝ) := by + refine Finset.sum_le_sum fun i _ ↦ ?_ + rw [Real.norm_eq_abs] + nlinarith [abs_nonneg ((y : EuclideanSpace ℝ ι) i), hy i] + _ = (Fintype.card ι : ℝ) := by simp + +theorem interior_cubeSlice_nonempty [Finite ι] : (interior V.cubeSlice).Nonempty := by + set U : Set ↥V := ⋂ i, (fun y : ↥V ↦ (y : EuclideanSpace ℝ ι) i) ⁻¹' (Set.Ioo (-1) 1) with hU + have hsub : U ⊆ V.cubeSlice := by + intro y hy i + rw [hU] at hy + simp only [Set.mem_iInter, Set.mem_preimage, Set.mem_Ioo] at hy + exact abs_le.2 ⟨(hy i).1.le, (hy i).2.le⟩ + have hopen : IsOpen U := by + rw [hU] + refine isOpen_iInter_of_finite fun i ↦ IsOpen.preimage ?_ isOpen_Ioo + fun_prop + have hmem : (0 : ↥V) ∈ U := by rw [hU]; simp + exact ⟨0, interior_maximal hsub hopen hmem⟩ + +end Submodule + +/-! +### Minkowski's second theorem against the cube slice + +Layers 4.2 and 4.5, combined: the two factors `2 ^ n` cancel and the product of the successive +minima is bounded by the covolume with no constant. +-/ + +namespace ZLattice + +variable {ι : Type*} [Fintype ι] (V : Submodule ℝ (EuclideanSpace ℝ ι)) + +/-- **The geometric half of Bombieri–Vaaler's theorem.** For the central slice of the unit cube as +the convex body, the product of the successive minima of a lattice in a subspace of +`EuclideanSpace ℝ ι` is at most the covolume of the lattice. The `2 ^ n` of Minkowski's second +theorem (Layer 4.2) is cancelled exactly by Vaaler's slice bound (Layer 4.5). -/ +theorem prod_successiveMinimum_cubeSlice_le_covolume (L : Submodule ℤ ↥V) [DiscreteTopology L] + [IsZLattice ℝ L] : + ∏ i ∈ Finset.range (finrank ℝ ↥V), successiveMinimum L V.cubeSlice i ≤ covolume L := by + have hM := prod_successiveMinimum_mul_measure_le L volume V.convex_cubeSlice + (fun _ hx ↦ V.neg_mem_cubeSlice hx) V.interior_cubeSlice_nonempty V.isBounded_cubeSlice + have hvol : (2 : ℝ≥0∞) ^ finrank ℝ ↥V ≤ volume V.cubeSlice := + two_pow_finrank_le_volume_inter_cube V + have hfin : volume V.cubeSlice ≠ ⊤ := (V.isBounded_cubeSlice.measure_lt_top).ne + have hvolr : (2 : ℝ) ^ finrank ℝ ↥V ≤ (volume V.cubeSlice).toReal := by + rw [show ((2 : ℝ) ^ finrank ℝ ↥V) = ((2 : ℝ≥0∞) ^ finrank ℝ ↥V).toReal by + rw [ENNReal.toReal_pow]; norm_num] + exact ENNReal.toReal_mono hfin hvol + have hppos : 0 < ∏ i ∈ Finset.range (finrank ℝ ↥V), successiveMinimum L V.cubeSlice i := + Finset.prod_pos fun i hi ↦ successiveMinimum_pos L V.convex_cubeSlice + (fun _ hx ↦ V.neg_mem_cubeSlice hx) V.interior_cubeSlice_nonempty V.isBounded_cubeSlice + (Finset.mem_range.1 hi) + have h2pos : (0 : ℝ) < 2 ^ finrank ℝ ↥V := by positivity + nlinarith [hM, hvolr, hppos, h2pos] + +end ZLattice + +/-! +### The Arakelov height of an integer tuple + +The Arakelov analogue of Layer 5.1's `Rat.gcd_mul_mulHeight_intCast`, which is what turns the +Plücker point of the row space into a euclidean norm of minors. +-/ + +namespace NumberField + +/-- **The Arakelov sup-norm-to-height translation.** The Arakelov height of a nonzero tuple of +integers, read as a tuple of rationals, is its euclidean norm divided by the greatest common +divisor of its entries. Layer 4.3's `NumberField.arakelovMulHeight_intCast_of_gcd_eq_one` is the +primitive case, and Layer 5.1's `Rat.gcd_mul_mulHeight_intCast` is the same statement for the sup +norm. -/ +theorem gcd_mul_arakelovMulHeight_intCast {κ : Type*} [Fintype κ] {x : κ → ℤ} (hx : x ≠ 0) : + ((univ.gcd x : ℤ) : ℝ) * arakelovMulHeight (((↑) : ℤ → ℚ) ∘ x) = + Real.sqrt (∑ i, ((x i : ℝ)) ^ 2) := by + obtain ⟨i₀, hi₀⟩ := Function.ne_iff.mp hx + have hg0 : univ.gcd x ≠ 0 := fun h ↦ hi₀ (Finset.gcd_eq_zero_iff.1 h i₀ (mem_univ i₀)) + have hgnn : 0 ≤ univ.gcd x := by + rw [← Finset.normalize_gcd, ← Int.abs_eq_normalize] + exact abs_nonneg _ + have hdvd : ∀ i, univ.gcd x ∣ x i := fun i ↦ Finset.gcd_dvd (mem_univ i) + set y : κ → ℤ := fun i ↦ x i / univ.gcd x with hy + have hxy : ∀ i, x i = univ.gcd x * y i := fun i ↦ (Int.mul_ediv_cancel' (hdvd i)).symm + have hy1 : univ.gcd y = 1 := Finset.gcd_div_eq_one (mem_univ i₀) hi₀ + have hsmul : (((↑) : ℤ → ℚ) ∘ x) = ((univ.gcd x : ℤ) : ℚ) • (((↑) : ℤ → ℚ) ∘ y) := by + funext i + simp only [Function.comp_apply, Pi.smul_apply, smul_eq_mul] + exact_mod_cast congrArg (Int.cast : ℤ → ℚ) (hxy i) + rw [hsmul, arakelovMulHeight_smul_eq _ (by exact_mod_cast hg0), + arakelovMulHeight_intCast_of_gcd_eq_one hy1, + show ((univ.gcd x : ℤ) : ℝ) = Real.sqrt (((univ.gcd x : ℤ) : ℝ) ^ 2) by + rw [Real.sqrt_sq (by exact_mod_cast hgnn)], ← Real.sqrt_mul (by positivity), Finset.mul_sum] + congr 1 + refine Finset.sum_congr rfl fun i _ ↦ ?_ + rw [← mul_pow] + norm_cast + rw [← hxy i] + +end NumberField + +/-! +### The greatest common divisor of the maximal minors +-/ + +namespace Int.Matrix + +variable {ι : Type*} [Fintype ι] [LinearOrder ι] {m : ℕ} + +/-- The **greatest common divisor of the maximal minors** of an integer matrix — the `D` of +Bombieri–Vaaler. The minors are Layer 3.1's Plücker coordinates of the rows, indexed by the sets of +`m` columns. -/ +noncomputable def minorGcd (A : _root_.Matrix (Fin m) ι ℤ) : ℤ := + (univ : Finset (Set.powersetCard ι m)).gcd (plucker m A.row) + +/-- The minors of the rational matrix are the minors of the integer matrix, cast. -/ +private theorem plucker_row_map_rat (A : _root_.Matrix (Fin m) ι ℤ) : + plucker m (A.map ((↑) : ℤ → ℚ)).row = ((↑) : ℤ → ℚ) ∘ plucker m A.row := + funext fun s ↦ plucker_row_map (Int.castRingHom ℚ) A s + +/-- Under the rank hypothesis some maximal minor is nonzero. -/ +private theorem plucker_row_ne_zero {A : _root_.Matrix (Fin m) ι ℤ} + (hA : LinearIndependent ℚ (A.map ((↑) : ℤ → ℚ)).row) : plucker m A.row ≠ 0 := by + intro h + refine (linearIndependent_row_iff_plucker_row_ne_zero (A.map ((↑) : ℤ → ℚ))).1 hA ?_ + rw [plucker_row_map_rat, h] + rfl + +/-- **The rank hypothesis is a condition on the integer matrix alone**: the rows are linearly +independent over `ℚ` exactly when the Gram determinant of the rows is nonzero. This is Layer 3.4's +Gram criterion, read over `ℤ`. -/ +theorem linearIndependent_row_map_rat_iff (A : _root_.Matrix (Fin m) ι ℤ) : + LinearIndependent ℚ (A.map ((↑) : ℤ → ℚ)).row ↔ (A * Aᵀ).det ≠ 0 := by + have hmap : (A.map ((↑) : ℤ → ℚ)) * (A.map ((↑) : ℤ → ℚ))ᵀ = (A * Aᵀ).map ((↑) : ℤ → ℚ) := by + ext i j + simp only [_root_.Matrix.mul_apply, _root_.Matrix.map_apply, _root_.Matrix.transpose_apply] + push_cast + rfl + have hdet : ((A * Aᵀ).map ((↑) : ℤ → ℚ)).det = ((A * Aᵀ).det : ℚ) := + (RingHom.map_det (Int.castRingHom ℚ) (A * Aᵀ)).symm + rw [linearIndependent_row_iff_det_mul_transpose_self_ne_zero, hmap, hdet, ne_eq, ne_eq, + Int.cast_eq_zero] + +/-- **The Gram determinant is positive** under the rank hypothesis, so the Bombieri–Vaaler bound is +a positive real and the absolute value the literature writes around `det (A Aᵀ)` is redundant. -/ +theorem det_pos {A : _root_.Matrix (Fin m) ι ℤ} + (hA : LinearIndependent ℚ (A.map ((↑) : ℤ → ℚ)).row) : 0 < (A * Aᵀ).det := by + rw [_root_.Matrix.det_mul_transpose_self_eq_sum_sq] + obtain ⟨s, hs⟩ := Function.ne_iff.1 (plucker_row_ne_zero hA) + refine Finset.sum_pos' (fun t _ ↦ sq_nonneg _) ⟨s, mem_univ s, ?_⟩ + have hs' : plucker m A.row s ≠ 0 := by simpa using hs + exact lt_of_le_of_ne (sq_nonneg _) (Ne.symm (pow_ne_zero 2 hs')) + +/-- The divisor `D` is at least `1` under the rank hypothesis: it is a normalized greatest common +divisor of integers not all zero. -/ +theorem one_le_minorGcd {A : _root_.Matrix (Fin m) ι ℤ} + (hA : LinearIndependent ℚ (A.map ((↑) : ℤ → ℚ)).row) : 1 ≤ minorGcd A := by + have hD0 : minorGcd A ≠ 0 := fun h ↦ plucker_row_ne_zero hA + (funext fun s ↦ Finset.gcd_eq_zero_iff.1 h s (mem_univ s)) + have hDnn : 0 ≤ minorGcd A := by + rw [minorGcd, ← Finset.normalize_gcd, ← Int.abs_eq_normalize] + exact abs_nonneg _ + omega + +/-- **The identity the Bombieri–Vaaler bound is**: `D` times the Arakelov height of the solution +space of `A x = 0` is `√(det (A Aᵀ))`. Layer 3.5 identifies the height of the solution space with +that of the row space, Layer 3.3 computes the latter as the height of the tuple of maximal minors, +and `NumberField.gcd_mul_arakelovMulHeight_intCast` divides out its content; the sum of squares of +minors is Layer 3.4's Cauchy–Binet identity. -/ +theorem minorGcd_mul_arakelovMulHeight_ker (A : _root_.Matrix (Fin m) ι ℤ) + (hA : LinearIndependent ℚ (A.map ((↑) : ℤ → ℚ)).row) : + ((minorGcd A : ℤ) : ℝ) * + (LinearMap.ker (A.map ((↑) : ℤ → ℚ)).mulVecLin).arakelovMulHeight + = Real.sqrt (((A * Aᵀ).det : ℤ) : ℝ) := by + have hp := plucker_row_map_rat A + have hne := plucker_row_ne_zero hA + rw [_root_.Matrix.arakelovMulHeight_ker_mulVecLin, Submodule.arakelovMulHeight_span_range hA, hp, + minorGcd, NumberField.gcd_mul_arakelovMulHeight_intCast hne, + _root_.Matrix.det_mul_transpose_self_eq_sum_sq] + push_cast + rfl + +/-- A row operation of determinant `±1` does not change the divisor `D`: it scales every maximal +minor by that determinant, which a normalized greatest common divisor does not see. -/ +theorem minorGcd_unit_mul {U : _root_.Matrix (Fin m) (Fin m) ℤ} (hU : IsUnit U.det) + (A : _root_.Matrix (Fin m) ι ℤ) : minorGcd (U * A) = minorGcd A := by + have hnorm : normalize U.det = 1 := normalize_eq_one.2 hU + rw [minorGcd, minorGcd, plucker_row_mul, + show (U.det • plucker m A.row) = (fun s ↦ U.det * plucker m A.row s) from rfl, + Finset.gcd_mul_left, hnorm, one_mul] + +/-- A row operation of determinant `±1` does not change the Gram determinant of the rows. -/ +theorem det_mul_transpose_self_unit_mul {U : _root_.Matrix (Fin m) (Fin m) ℤ} (hU : IsUnit U.det) + (A : _root_.Matrix (Fin m) ι ℤ) : ((U * A) * (U * A)ᵀ).det = (A * Aᵀ).det := by + have hsq : U.det ^ 2 = 1 := by rcases Int.isUnit_iff.1 hU with h | h <;> rw [h] <;> norm_num + rw [_root_.Matrix.det_mul_transpose_self_eq_sum_sq, + _root_.Matrix.det_mul_transpose_self_eq_sum_sq, plucker_row_mul] + calc ∑ s : Set.powersetCard ι m, (U.det • plucker m A.row) s ^ 2 + = ∑ s : Set.powersetCard ι m, U.det ^ 2 * plucker m A.row s ^ 2 := + Finset.sum_congr rfl fun s _ ↦ by rw [Pi.smul_apply, smul_eq_mul, mul_pow] + _ = ∑ s : Set.powersetCard ι m, plucker m A.row s ^ 2 := by rw [hsq]; simp only [one_mul] + +/-- **The invariance of the Bombieri–Vaaler bound** (Bombieri–Vaaler (2.5)): `D⁻¹ √(det (A Aᵀ))` is +unchanged by `A ↦ U A` for `U ∈ GL_M(ℤ)`, which fixes the solution space. This is what Theorem 1 +has over the classical Siegel lemma of Layer 5.1, whose bound depends on the entries and so moves +under a row operation. -/ +theorem sqrt_det_div_minorGcd_unit_mul {U : _root_.Matrix (Fin m) (Fin m) ℤ} (hU : IsUnit U.det) + (A : _root_.Matrix (Fin m) ι ℤ) : + Real.sqrt ((((U * A) * (U * A)ᵀ).det : ℤ) : ℝ) / ((minorGcd (U * A) : ℤ) : ℝ) + = Real.sqrt (((A * Aᵀ).det : ℤ) : ℝ) / ((minorGcd A : ℤ) : ℝ) := by + rw [det_mul_transpose_self_unit_mul hU, minorGcd_unit_mul hU] + +/-! +### Bombieri–Vaaler's theorems +-/ + +omit [LinearOrder ι] in +/-- The solution space of `A x = 0` has dimension `N - M` when the rows are independent. -/ +private theorem finrank_ker (A : _root_.Matrix (Fin m) ι ℤ) + (hA : LinearIndependent ℚ (A.map ((↑) : ℤ → ℚ)).row) : + finrank ℚ ↥(LinearMap.ker (A.map ((↑) : ℤ → ℚ)).mulVecLin) = Fintype.card ι - m := by + have hrank : (A.map ((↑) : ℤ → ℚ)).rank = m := by + simpa using hA.rank_matrix + have h := LinearMap.finrank_range_add_finrank_ker (A.map ((↑) : ℤ → ℚ)).mulVecLin + rw [show finrank ℚ ↥(LinearMap.range (A.map ((↑) : ℤ → ℚ)).mulVecLin) + = (A.map ((↑) : ℤ → ℚ)).rank from rfl, hrank] at h + simp only [Module.finrank_pi] at h + omega + +/-- **Bombieri–Vaaler's Theorem 2** (Bombieri–Vaaler 1983, Theorem 2; +Aliev–Henk 2023, §6, Theorem 6.2). +For an `M × N` integer matrix with linearly independent rows there are `N - M` linearly independent +integral solutions of `A x = 0` whose sup norms have product at most `D⁻¹ √(det (A Aᵀ))`, `D` the +greatest common divisor of the maximal minors. + +No hypothesis `M < N` is needed: at `N = M` the conclusion is the empty product `1` on the left and +the Arakelov height of the zero subspace, also `1`, on the right. -/ +theorem exists_linearIndependent_mulVec_eq_zero_prod_iSup_abs_le (A : _root_.Matrix (Fin m) ι ℤ) + (hA : LinearIndependent ℚ (A.map ((↑) : ℤ → ℚ)).row) : + ∃ x : Fin (Fintype.card ι - m) → (ι → ℤ), LinearIndependent ℤ x ∧ (∀ l, A *ᵥ x l = 0) ∧ + (∏ l, ((⨆ i, |x l i| : ℤ) : ℝ)) ≤ + Real.sqrt (((A * Aᵀ).det : ℤ) : ℝ) / ((minorGcd A : ℤ) : ℝ) := by + set Aq := A.map ((↑) : ℤ → ℚ) with hAqdef + set V := LinearMap.ker Aq.mulVecLin with hVdef + have hrs : finrank ℝ ↥V.realSpan = Fintype.card ι - m := by + rw [Submodule.finrank_realSpan, hVdef, finrank_ker A hA] + have hmink := ZLattice.prod_successiveMinimum_cubeSlice_le_covolume V.realSpan V.intLattice + rw [Submodule.covolume_intLattice] at hmink + obtain ⟨v, hvL, hvind, hvmem⟩ := + ZLattice.exists_linearIndependent_mem_smul_successiveMinimum V.intLattice + V.realSpan.convex_cubeSlice (fun _ hx ↦ V.realSpan.neg_mem_cubeSlice hx) + V.realSpan.interior_cubeSlice_nonempty V.realSpan.isBounded_cubeSlice + V.realSpan.isClosed_cubeSlice + choose z hzV hzc using fun j ↦ Submodule.mem_intLattice.1 (hvL j) + have hzco : ∀ j i, ((z j i : ℤ) : ℝ) = (v j : EuclideanSpace ℝ ι) i := fun j i ↦ by + rw [hzc j]; rfl + rw [← hrs] + refine ⟨z, ?_, ?_, ?_⟩ + · rw [Fintype.linearIndependent_iff] + intro c hc j + have key : ∑ l, (c l : ℝ) • v l = 0 := by + refine Submodule.coe_eq_zero.1 ?_ + ext i + have hco : ((∑ l, (c l : ℝ) • v l : ↥V.realSpan) : EuclideanSpace ℝ ι) i + = ∑ l, (c l : ℝ) * (v l : EuclideanSpace ℝ ι) i := by + push_cast + simp [Finset.sum_apply] + rw [hco] + have h2 : ∀ l, (c l : ℝ) * (v l : EuclideanSpace ℝ ι) i = ((c l * z l i : ℤ) : ℝ) := + fun l ↦ by rw [← hzco l i]; push_cast; ring + rw [Finset.sum_congr rfl fun l _ ↦ h2 l, ← Int.cast_sum] + have h3 : ∑ l, c l * z l i = 0 := by + have := congrFun hc i + simpa [Finset.sum_apply] using this + rw [h3] + simp + exact_mod_cast Fintype.linearIndependent_iff.1 hvind (fun l ↦ (c l : ℝ)) key j + · intro l + funext i + have h : (Aq *ᵥ Rat.piIntCast ι (z l)) i = 0 := by + have hk := LinearMap.mem_ker.1 (hzV l) + rw [_root_.Matrix.mulVecLin_apply] at hk + exact congrFun hk i + have hcast : ((A *ᵥ z l) i : ℚ) = (Aq *ᵥ Rat.piIntCast ι (z l)) i := by + simp only [_root_.Matrix.mulVec, dotProduct, hAqdef, _root_.Matrix.map_apply, + Rat.piIntCast_apply] + push_cast + rfl + have hz : ((A *ᵥ z l) i : ℚ) = 0 := by rw [hcast, h] + exact_mod_cast hz + · have hid : ((minorGcd A : ℤ) : ℝ) * V.arakelovMulHeight + = Real.sqrt (((A * Aᵀ).det : ℤ) : ℝ) := minorGcd_mul_arakelovMulHeight_ker A hA + have hD1 : (1 : ℝ) ≤ ((minorGcd A : ℤ) : ℝ) := by exact_mod_cast one_le_minorGcd hA + have hDne : ((minorGcd A : ℤ) : ℝ) ≠ 0 := by linarith + have hrhs : Real.sqrt (((A * Aᵀ).det : ℤ) : ℝ) / ((minorGcd A : ℤ) : ℝ) + = V.arakelovMulHeight := by + rw [← hid, mul_comm, mul_div_assoc, div_self hDne, mul_one] + rw [hrhs] + refine le_trans ?_ hmink + rw [← Fin.prod_univ_eq_prod_range] + have hbound : ∀ l : Fin (finrank ℝ ↥V.realSpan), + 0 ≤ (((⨆ i, |z l i| : ℤ)) : ℝ) ∧ (((⨆ i, |z l i| : ℤ)) : ℝ) ≤ + ZLattice.successiveMinimum V.intLattice V.realSpan.cubeSlice l := by + intro l + have hne : Nonempty ι := by + have h1 : 0 < finrank ℝ ↥V.realSpan := lt_of_le_of_lt (Nat.zero_le _) l.isLt + rw [hrs] at h1 + exact Fintype.card_pos_iff.1 (by omega) + obtain ⟨i₀, hi₀⟩ := exists_eq_ciSup_of_finite (f := fun i ↦ |z l i|) + have hval : (((⨆ i, |z l i| : ℤ)) : ℝ) = |(v l : EuclideanSpace ℝ ι) i₀| := by + rw [← hi₀, Int.cast_abs, hzco l i₀] + rw [hval] + refine ⟨abs_nonneg _, (V.realSpan.mem_smul_cubeSlice ?_).1 (hvmem l) i₀⟩ + exact ZLattice.successiveMinimum_pos V.intLattice V.realSpan.convex_cubeSlice + (fun _ hx ↦ V.realSpan.neg_mem_cubeSlice hx) V.realSpan.interior_cubeSlice_nonempty + V.realSpan.isBounded_cubeSlice l.isLt + exact Finset.prod_le_prod₀ (fun l _ ↦ (hbound l).1) fun l _ ↦ (hbound l).2 + +/-- **Bombieri–Vaaler's Theorem 2, in height form** — the `K = ℚ` case of the statement Layer 5.4 +makes over a number field, the discriminant being `1`. The proof applies the pointwise inequality +`Rat.mulHeight_intCast_le` to the sup-norm form above: the height of a nonzero integer tuple is +its sup norm divided by its content, by `Rat.gcd_mul_mulHeight_intCast`. -/ +theorem exists_linearIndependent_mulVec_eq_zero_prod_mulHeight_le (A : _root_.Matrix (Fin m) ι ℤ) + (hA : LinearIndependent ℚ (A.map ((↑) : ℤ → ℚ)).row) : + ∃ x : Fin (Fintype.card ι - m) → (ι → ℤ), LinearIndependent ℤ x ∧ (∀ l, A *ᵥ x l = 0) ∧ + (∏ l, Height.mulHeight (((↑) : ℤ → ℚ) ∘ x l)) ≤ + Real.sqrt (((A * Aᵀ).det : ℤ) : ℝ) / ((minorGcd A : ℤ) : ℝ) := by + obtain ⟨x, hxind, hxA, hxprod⟩ := exists_linearIndependent_mulVec_eq_zero_prod_iSup_abs_le A hA + refine ⟨x, hxind, hxA, le_trans (Finset.prod_le_prod₀ (fun l _ ↦ ?_) fun l _ ↦ ?_) hxprod⟩ + · exact (Height.mulHeight_pos _).le + · have hne : Nonempty ι := Fintype.card_pos_iff.1 (by have := l.isLt; omega) + exact Rat.mulHeight_intCast_le (hxind.ne_zero l) + +/-- **Bombieri–Vaaler's Theorem 1** (Bombieri–Vaaler 1983, Theorem 1; also a one-vector +consequence of the product bound in Aliev–Henk 2023, §6, Theorem 6.2). +For an `M × N` integer matrix with linearly independent rows and `M < N` there is a nonzero +integral solution of `A x = 0` with sup norm at most `(D⁻¹ √(det (A Aᵀ)))^{1/(N - M)}`. + +Unlike the bound of Layer 5.1 this one is invariant under `A ↦ U A` for `U ∈ GL_M(ℤ)`, by +`Int.Matrix.sqrt_det_div_minorGcd_unit_mul`. -/ +theorem exists_ne_zero_mulVec_eq_zero_iSup_abs_le_det_rpow (A : _root_.Matrix (Fin m) ι ℤ) + (hA : LinearIndependent ℚ (A.map ((↑) : ℤ → ℚ)).row) (hmn : m < Fintype.card ι) : + ∃ x : ι → ℤ, x ≠ 0 ∧ A *ᵥ x = 0 ∧ + ((⨆ i, |x i| : ℤ) : ℝ) ≤ + (Real.sqrt (((A * Aᵀ).det : ℤ) : ℝ) / ((minorGcd A : ℤ) : ℝ)) ^ + (((Fintype.card ι : ℝ) - m)⁻¹) := by + have hk0 : Fintype.card ι - m ≠ 0 := by omega + have hkr : ((Fintype.card ι : ℝ) - m) = ((Fintype.card ι - m : ℕ) : ℝ) := by + rw [Nat.cast_sub hmn.le] + have hD1 : (1 : ℝ) ≤ ((minorGcd A : ℤ) : ℝ) := by exact_mod_cast one_le_minorGcd hA + have hCpos : 0 < Real.sqrt (((A * Aᵀ).det : ℤ) : ℝ) / ((minorGcd A : ℤ) : ℝ) := by + refine div_pos (Real.sqrt_pos.2 ?_) (by linarith) + exact_mod_cast det_pos hA + obtain ⟨x, hxind, hxA, hxprod⟩ := exists_linearIndependent_mulVec_eq_zero_prod_iSup_abs_le A hA + have hsnn : ∀ l, 0 ≤ ((⨆ i, |x l i| : ℤ) : ℝ) := by + intro l + have hne : Nonempty ι := Fintype.card_pos_iff.1 (by omega) + obtain ⟨i₀, hi₀⟩ := exists_eq_ciSup_of_finite (f := fun i ↦ |x l i|) + rw [← hi₀] + exact_mod_cast abs_nonneg (x l i₀) + have hne : Nonempty (Fin (Fintype.card ι - m)) := + Fin.pos_iff_nonempty.1 (Nat.pos_of_ne_zero hk0) + obtain ⟨l₀, -, hmin⟩ := Finset.exists_min_image (Finset.univ : Finset (Fin (Fintype.card ι - m))) + (fun l ↦ ((⨆ i, |x l i| : ℤ) : ℝ)) ⟨Classical.arbitrary _, mem_univ _⟩ + have hpow : ((⨆ i, |x l₀ i| : ℤ) : ℝ) ^ (Fintype.card ι - m) + ≤ Real.sqrt (((A * Aᵀ).det : ℤ) : ℝ) / ((minorGcd A : ℤ) : ℝ) := by + calc ((⨆ i, |x l₀ i| : ℤ) : ℝ) ^ (Fintype.card ι - m) + = ∏ _l : Fin (Fintype.card ι - m), ((⨆ i, |x l₀ i| : ℤ) : ℝ) := by + rw [Finset.prod_const, Finset.card_univ, Fintype.card_fin] + _ ≤ ∏ l, ((⨆ i, |x l i| : ℤ) : ℝ) := + Finset.prod_le_prod₀ (fun l _ ↦ hsnn l₀) fun l _ ↦ hmin l (mem_univ l) + _ ≤ _ := hxprod + refine ⟨x l₀, hxind.ne_zero l₀, hxA l₀, ?_⟩ + rw [hkr] + refine (pow_le_pow_iff_left₀ (hsnn l₀) (Real.rpow_nonneg hCpos.le _) hk0).1 ?_ + rw [Real.rpow_inv_natCast_pow hCpos.le hk0] + exact hpow + +end Int.Matrix diff --git a/LeanPool/CCDN/ArithmeticHeights/CauchyBinet.lean b/LeanPool/CCDN/ArithmeticHeights/CauchyBinet.lean new file mode 100644 index 0000000000..e5ac83584e --- /dev/null +++ b/LeanPool/CCDN/ArithmeticHeights/CauchyBinet.lean @@ -0,0 +1,359 @@ +/- +Copyright (c) 2026 Ralf Stephan. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Ralf Stephan +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.ArithmeticHeights.RowSpace +public import Mathlib.Analysis.RCLike.Basic +public import Mathlib.LinearAlgebra.Dual.Basis + +/-! +# The Cauchy–Binet identity + +The product of a matrix with the transpose of another is a determinant that the maximal minors +compute: `det (A Bᵀ) = ∑ₛ det Aₛ · det Bₛ`, the sum over the sets `s` of `m` columns. Taking +`B = A` turns the sum of squares of the maximal minors into the **Gram determinant** `det (A Aᵀ)` +of the rows, and taking `B = conj A` turns the sum of their squared absolute values into +`det (A Aᴴ)`. Those two are the archimedean local factors of the Arakelov height of the tuple of +minors — of the row space, by Layer 3.3 — and they are where the `√|det (A Aᵀ)|` of the +Bombieri–Vaaler bound comes from. + +Everything here is proved once, over an arbitrary commutative ring, for the Plücker coordinates of +Layer 3.1; the matrix statements are that identity read through Layer 3.3's dictionary. + +## Main results + +* `exteriorPower.sum_plucker_mul_plucker`: the identity itself, for two families of vectors — + `∑ₛ plucker k v s * plucker k w s` is the determinant of the matrix of dot products `v i ⬝ᵥ w j`. +* `Matrix.det_mul_eq_sum_plucker` and `Matrix.det_mul_transpose_eq_sum_plucker`, with + `Matrix.det_mul_eq_sum_det_submatrix` and `Matrix.det_mul_transpose_eq_sum_det_submatrix` + spelling the same statements with `Matrix.submatrix` and `Finset.orderEmbOfFin`. +* `Matrix.det_mul_transpose_self_eq_sum_sq`: the **real** specialization, `det (A Aᵀ)` is the sum of + the squares of the maximal minors, over any commutative ring. +* `Matrix.det_mul_conjTranspose_self_eq_sum`: the **complex** specialization, over a commutative + star ring, with `Matrix.det_mul_conjTranspose_self_eq_sum_sq_norm` its `RCLike` form + `det (A Aᴴ) = ∑ₛ ‖det Aₛ‖²` and + `NumberField.InfinitePlace.det_map_embedding_mul_conjTranspose_self` its form at an infinite + place of a number field, which is the local factor named above. +* `Matrix.det_mul_transpose_self_eq_zero_iff` and + `Matrix.linearIndependent_row_iff_det_mul_transpose_self_ne_zero`: over an ordered field the Gram + determinant of the rows detects full row rank, the Gram criterion. +* `exteriorPower.plucker_map`: the Plücker coordinates commute with a ring homomorphism, which is + what carries the identity to a completion or an embedding. + +## Implementation notes + +The proof is Mathlib's pairing `exteriorPower.pairingDual` between `⋀ⁿ (Dual R M)` and `⋀ⁿ M`, +evaluated on the image of a wedge under `exteriorPower.map n (Pi.basisFun R ι).toDual`. Read on the +induced basis of the exterior power that composite is the diagonal bilinear form +`∑ₛ xₛ yₛ`, because `Basis.toDual` sends the standard basis to the dual basis and +`exteriorPower.ιMultiDual` — the coordinate functional of Layer 3.1's basis — is *by definition* +the pairing against the wedge of dual basis vectors; read on wedges it is a determinant of +dot products, by `exteriorPower.pairingDual_ιMulti_ιMulti`. So the identity is the statement that +one bilinear form has two descriptions, and no expansion of a determinant over permutations is +carried out anywhere. + +Mathlib's `LinearMap.BilinForm.exteriorPower` is that same composite and would be the natural name +for it. What is missing is its defining equation: `LinearMap.BilinForm.bilinForm_ιMulti_ιMulti`, +which evaluates the form on two wedges as a determinant, carries no `public` in a module that has +no `public section`, so it is invisible outside its own file and every user has to re-prove it. +That proof is exactly the content of the identity below, so the composite is written out here. + +`Pi.basisFun_toDual_apply`, that `Basis.toDual` of the standard basis is the dot product, is +stated here because Mathlib has `Basis.toDual_apply_left` only for a basis vector in the second +argument. + +## References + +A. L. Cauchy (1812) and J. P. M. Binet (1812); the identity is Proposition 2.8.8 of +E. Bombieri and W. Gubler, *Heights in Diophantine Geometry*, Cambridge University Press (2006), +and §2 Lemma 1 of W. M. Schmidt, "On heights of algebraic subspaces and diophantine +approximations", *Annals of Mathematics* **85** (1967), 430–472. It enters the Siegel-lemma +literature as the local-height formula (iv) in §2, p. 15, of E. Bombieri and J. Vaaler, +"On Siegel's lemma", +*Inventiones Mathematicae* **73** (1983), 11–32, whose bound is stated in terms of +`√|det (A Aᵀ)|`. + +This is Layer 3.4 of the `ArithmeticHeights` roadmap. +-/ + +public section + +namespace exteriorPower + +open Matrix Module + +/-! +### The identity in Plücker coordinates +-/ + +section Dual + +variable {R : Type*} [CommRing R] {ι : Type*} [Fintype ι] [DecidableEq ι] + +/-- **The dual map of the standard basis is the dot product.** Mathlib's `Basis.toDual_apply_left` +evaluates `b.toDual x` on a basis vector; for the standard basis of `ι → R` the value on an +arbitrary vector is the dot product. -/ +theorem _root_.Pi.basisFun_toDual_apply (x y : ι → R) : + (Pi.basisFun R ι).toDual x y = x ⬝ᵥ y := by + conv_lhs => rw [← (Pi.basisFun R ι).sum_repr y] + rw [map_sum] + simp only [map_smul, Basis.toDual_apply_left, Pi.basisFun_repr, smul_eq_mul, dotProduct] + exact Finset.sum_congr rfl fun i _ ↦ mul_comm _ _ + +end Dual + +section Basis + +variable {R : Type*} [CommRing R] {ι : Type*} [Finite ι] [LinearOrder ι] + +/-- The pairing against the image of a basis vector of `⋀^k (ι → R)` reads off a coordinate: this +is `exteriorPower.ιMultiDual` unfolded, since that functional is *defined* as the pairing against +the wedge of the dual basis vectors indexed by `s`. -/ +private lemma pairingDual_map_toDual_basis (k : ℕ) (s : Set.powersetCard ι k) + (y : ⋀[R]^k (ι → R)) : + pairingDual R (ι → R) k + (map k (Pi.basisFun R ι).toDual ((Pi.basisFun R ι).exteriorPower k s)) y + = ((Pi.basisFun R ι).exteriorPower k).repr y s := by + rw [basis_apply, map_apply_ιMulti_family, + show (Pi.basisFun R ι).toDual ∘ (Pi.basisFun R ι) = (Pi.basisFun R ι).coord from + funext fun i ↦ Basis.coe_toDual_self _ i, basis_repr_apply] + rfl + +end Basis + +section CauchyBinet + +variable {R : Type*} [CommRing R] {ι : Type*} [Fintype ι] [LinearOrder ι] {k : ℕ} + +/-- On the basis of `⋀^k (ι → R)` induced by the standard basis, the bilinear form obtained by +pairing along `Basis.toDual` is the diagonal one. -/ +private lemma pairingDual_map_toDual (k : ℕ) (x y : ⋀[R]^k (ι → R)) : + pairingDual R (ι → R) k (map k (Pi.basisFun R ι).toDual x) y + = ∑ s : Set.powersetCard ι k, ((Pi.basisFun R ι).exteriorPower k).repr x s * + ((Pi.basisFun R ι).exteriorPower k).repr y s := by + conv_lhs => rw [← ((Pi.basisFun R ι).exteriorPower k).sum_repr x] + rw [map_sum, map_sum, LinearMap.sum_apply] + refine Finset.sum_congr rfl fun s _ ↦ ?_ + rw [map_smul, map_smul, LinearMap.smul_apply, pairingDual_map_toDual_basis, smul_eq_mul] + +/-- **The Cauchy–Binet identity.** The sum over the `k`-element sets of columns of the product of +the two Plücker coordinates is the determinant of the `k × k` matrix of dot products. Over any +commutative ring, and with no hypothesis on either family. -/ +theorem sum_plucker_mul_plucker (k : ℕ) (v w : Fin k → (ι → R)) : + ∑ s : Set.powersetCard ι k, plucker k v s * plucker k w s + = (Matrix.of fun i j ↦ v i ⬝ᵥ w j).det := by + have h := pairingDual_map_toDual k (ιMulti R k v) (ιMulti R k w) + rw [map_apply_ιMulti, pairingDual_ιMulti_ιMulti] at h + simp only [plucker, Basis.equivFun_apply] + rw [← h] + simp only [Function.comp_apply, Pi.basisFun_toDual_apply] + rw [← Matrix.det_transpose (Matrix.of fun i j ↦ v i ⬝ᵥ w j)] + rfl + +/-- **The Plücker coordinates commute with a ring homomorphism**, each being a determinant. This is +what carries the identity along an embedding of a number field into `ℂ`. -/ +theorem plucker_map {S : Type*} [CommRing S] (f : R →+* S) (k : ℕ) (v : Fin k → (ι → R)) + (s : Set.powersetCard ι k) : plucker k (fun i ↦ f ∘ v i) s = f (plucker k v s) := by + rw [plucker_apply, plucker_apply, RingHom.map_det] + rfl + +end CauchyBinet + +end exteriorPower + +namespace Matrix + +open Module exteriorPower + +/-! +### The identity for matrices +-/ + +section CauchyBinet + +variable {R : Type*} [CommRing R] {ι : Type*} [Fintype ι] [LinearOrder ι] {m : ℕ} + +/-- The Plücker coordinates of the rows commute with a ring homomorphism applied entrywise. -/ +theorem plucker_row_map {S : Type*} [CommRing S] (f : R →+* S) (A : Matrix (Fin m) ι R) + (s : Set.powersetCard ι m) : plucker m (A.map f).row s = f (plucker m A.row s) := + plucker_map f m A.row s + +/-- **The Cauchy–Binet identity**, for `A Bᵀ`: the determinant of the matrix of dot products of the +rows of `A` with the rows of `B` is the sum over the sets of `m` columns of the products of +corresponding maximal minors. -/ +theorem det_mul_transpose_eq_sum_plucker (A B : Matrix (Fin m) ι R) : + (A * Bᵀ).det = ∑ s : Set.powersetCard ι m, plucker m A.row s * plucker m B.row s := + (sum_plucker_mul_plucker m A.row B.row).symm + +/-- **The Cauchy–Binet identity**, for a product `A B` of an `m × ι` by an `ι × m` matrix: the +determinant is the sum over the sets of `m` indices of the product of the maximal minor of `A` on +those columns with the maximal minor of `B` on those rows. -/ +theorem det_mul_eq_sum_plucker (A : Matrix (Fin m) ι R) (B : Matrix ι (Fin m) R) : + (A * B).det = ∑ s : Set.powersetCard ι m, plucker m A.row s * plucker m B.col s := by + conv_lhs => rw [← Matrix.transpose_transpose B] + exact (sum_plucker_mul_plucker m A.row Bᵀ.row).symm + +/-- The Cauchy–Binet identity in the `submatrix` spelling of Layer 3.3: the minors are taken on the +columns of `s`, enumerated in increasing order. -/ +theorem det_mul_transpose_eq_sum_det_submatrix (A B : Matrix (Fin m) ι R) : + (A * Bᵀ).det = ∑ s : Set.powersetCard ι m, + (A.submatrix id ((s : Finset ι).orderEmbOfFin (Set.powersetCard.card_eq s))).det * + (B.submatrix id ((s : Finset ι).orderEmbOfFin (Set.powersetCard.card_eq s))).det := by + rw [det_mul_transpose_eq_sum_plucker] + exact Finset.sum_congr rfl fun s _ ↦ by + rw [plucker_row_eq_det_submatrix, plucker_row_eq_det_submatrix] + +/-- The Cauchy–Binet identity for `A B` in the `submatrix` spelling: the maximal minors of `A` on +the columns of `s` against those of `B` on the rows of `s`. -/ +theorem det_mul_eq_sum_det_submatrix (A : Matrix (Fin m) ι R) (B : Matrix ι (Fin m) R) : + (A * B).det = ∑ s : Set.powersetCard ι m, + (A.submatrix id ((s : Finset ι).orderEmbOfFin (Set.powersetCard.card_eq s))).det * + (B.submatrix ((s : Finset ι).orderEmbOfFin (Set.powersetCard.card_eq s)) id).det := by + rw [det_mul_eq_sum_plucker] + refine Finset.sum_congr rfl fun s _ ↦ ?_ + rw [plucker_row_eq_det_submatrix, show B.col = Bᵀ.row from rfl, plucker_row_eq_det_submatrix, + ← Matrix.det_transpose (B.submatrix _ id), Matrix.transpose_submatrix] + +end CauchyBinet + +/-! +### The Gram determinant of the rows +-/ + +section Gram + +variable {R : Type*} [CommRing R] {ι : Type*} [Fintype ι] [LinearOrder ι] {m : ℕ} + +/-- **The archimedean specialization at a real place.** The Gram determinant of the rows is the sum +of the squares of the maximal minors. This is the `√|det (A Aᵀ)|` of the Bombieri–Vaaler bound: +its square is the ℓ² local factor of the tuple of minors. -/ +theorem det_mul_transpose_self_eq_sum_sq (A : Matrix (Fin m) ι R) : + (A * Aᵀ).det = ∑ s : Set.powersetCard ι m, plucker m A.row s ^ 2 := by + rw [det_mul_transpose_eq_sum_plucker] + exact Finset.sum_congr rfl fun s _ ↦ (sq _).symm + +variable {S : Type*} [CommRing S] [LinearOrder S] [IsStrictOrderedRing S] + +/-- The Gram determinant of the rows is nonnegative. -/ +theorem det_mul_transpose_self_nonneg (A : Matrix (Fin m) ι S) : 0 ≤ (A * Aᵀ).det := by + rw [det_mul_transpose_self_eq_sum_sq] + exact Finset.sum_nonneg fun s _ ↦ sq_nonneg _ + +variable {K : Type*} [Field K] [LinearOrder K] [IsStrictOrderedRing K] + +/-- **The Gram criterion.** Over an ordered field the Gram determinant of the rows vanishes exactly +when the rows are linearly dependent. -/ +theorem det_mul_transpose_self_eq_zero_iff (A : Matrix (Fin m) ι K) : + (A * Aᵀ).det = 0 ↔ ¬ LinearIndependent K A.row := by + rw [det_mul_transpose_self_eq_sum_sq, ← plucker_eq_zero_iff m A.row, + Finset.sum_eq_zero_iff_of_nonneg fun s _ ↦ sq_nonneg _] + simp [funext_iff] + +/-- **The Gram criterion**, in the form the Bombieri–Vaaler bound needs: full row rank is exactly +positivity of the Gram determinant, so `√|det (A Aᵀ)|` is nonzero there. -/ +theorem det_mul_transpose_self_pos_iff (A : Matrix (Fin m) ι K) : + 0 < (A * Aᵀ).det ↔ LinearIndependent K A.row := + ⟨fun h ↦ by + by_contra hA + exact h.ne' ((det_mul_transpose_self_eq_zero_iff A).2 hA), + fun hA ↦ lt_of_le_of_ne (det_mul_transpose_self_nonneg A) + (Ne.symm fun h ↦ (det_mul_transpose_self_eq_zero_iff A).1 h hA)⟩ + +/-- Full row rank, detected by the Gram determinant. -/ +theorem linearIndependent_row_iff_det_mul_transpose_self_ne_zero (A : Matrix (Fin m) ι K) : + LinearIndependent K A.row ↔ (A * Aᵀ).det ≠ 0 := + ⟨fun hA h ↦ (det_mul_transpose_self_eq_zero_iff A).1 h hA, + fun h ↦ not_not.1 fun hA ↦ h ((det_mul_transpose_self_eq_zero_iff A).2 hA)⟩ + +end Gram + +/-! +### The Gram determinant at a complex place +-/ + +section Star + +variable {R : Type*} [CommRing R] [StarRing R] {ι : Type*} [Fintype ι] [LinearOrder ι] {m : ℕ} + +/-- **The archimedean specialization at a complex place.** Pairing `A` against its conjugate +transpose replaces the squares of the maximal minors by their norms. -/ +theorem det_mul_conjTranspose_self_eq_sum (A : Matrix (Fin m) ι R) : + (A * Aᴴ).det = + ∑ s : Set.powersetCard ι m, plucker m A.row s * star (plucker m A.row s) := by + rw [show Aᴴ = (A.map (starRingEnd R))ᵀ from rfl, det_mul_transpose_eq_sum_plucker] + exact Finset.sum_congr rfl fun s _ ↦ by rw [plucker_row_map]; rfl + +variable {K : Type*} [RCLike K] + +/-- The complex specialization, with the squared absolute values displayed: `det (A Aᴴ)` is the +square of the ℓ² norm of the tuple of maximal minors. -/ +theorem det_mul_conjTranspose_self_eq_sum_sq_norm (A : Matrix (Fin m) ι K) : + (A * Aᴴ).det = ((∑ s : Set.powersetCard ι m, ‖plucker m A.row s‖ ^ 2 : ℝ) : K) := by + rw [det_mul_conjTranspose_self_eq_sum] + push_cast + exact Finset.sum_congr rfl fun s _ ↦ RCLike.mul_conj _ + +end Star + +end Matrix + +namespace NumberField.InfinitePlace + +open Matrix exteriorPower + +variable {K : Type*} [Field K] {ι : Type*} [Fintype ι] [LinearOrder ι] {m : ℕ} + +/-- **The archimedean local factor of the tuple of maximal minors is a Gram determinant.** At an +infinite place `v` of a number field, `∑ₛ v(det Aₛ)²` — the quantity the ℓ² normalization of Layer +0.1 raises to the power `mult v / 2` — is the determinant of the Gram matrix of the rows of `A` +read through the embedding attached to `v`. At a real place the conjugate transpose is the +transpose, so this covers both kinds of place. -/ +theorem det_map_embedding_mul_conjTranspose_self (v : InfinitePlace K) (A : Matrix (Fin m) ι K) : + ((A.map v.embedding) * (A.map v.embedding)ᴴ).det + = ((∑ s : Set.powersetCard ι m, v (plucker m A.row s) ^ 2 : ℝ) : ℂ) := by + rw [Matrix.det_mul_conjTranspose_self_eq_sum_sq_norm] + exact congrArg _ (Finset.sum_congr rfl fun s _ ↦ by + rw [Matrix.plucker_row_map, norm_embedding_eq]) + +end NumberField.InfinitePlace + +section Examples + +open Matrix exteriorPower + +/-- **Acceptance test: the sum of squares of the maximal minors, computed as a Gram determinant.** +The three maximal minors of this `2 × 3` matrix are `1`, `3` and `−2` — the acceptance test of +Layer 3.3 — so the sum of their squares is `1 + 9 + 4 = 14`, and Cauchy–Binet produces it from the +`2 × 2` determinant of `A Aᵀ` without enumerating a single minor. -/ +example : (∑ s : Set.powersetCard (Fin 3) 2, + plucker 2 (Matrix.of ![![1, 0, 2], ![0, 1, 3]] : Matrix (Fin 2) (Fin 3) ℚ).row s ^ 2) = 14 := by + rw [← Matrix.det_mul_transpose_self_eq_sum_sq, Matrix.det_fin_two] + norm_num [Matrix.mul_apply, Fin.sum_univ_succ] + +/-- **Rejection test: `A Aᵀ` and not `Aᵀ A`.** The Gram matrix of the *rows* is `m × m`; the `n × n` +matrix `Aᵀ A` is singular whenever `m < n`, so its determinant is `0` and carries none of the +information. A statement of Cauchy–Binet with the factors in the other order is refuted here. -/ +example : ((Matrix.of ![![1, 0, 2], ![0, 1, 3]] : Matrix (Fin 2) (Fin 3) ℚ)ᵀ * + (Matrix.of ![![1, 0, 2], ![0, 1, 3]] : Matrix (Fin 2) (Fin 3) ℚ)).det = 0 := by + rw [Matrix.det_fin_three] + norm_num [Matrix.mul_apply, Fin.sum_univ_succ] + +/-- **Conformance.** The milestone's three statements: the identity over a commutative ring, its +real specialization, and its complex specialization. -/ +example {R : Type*} [CommRing R] {ι : Type*} [Fintype ι] [LinearOrder ι] {m : ℕ} + (A B : Matrix (Fin m) ι R) (C : Matrix (Fin m) ι ℂ) : + (A * Bᵀ).det = ∑ s : Set.powersetCard ι m, plucker m A.row s * plucker m B.row s ∧ + (A * Aᵀ).det = ∑ s : Set.powersetCard ι m, plucker m A.row s ^ 2 ∧ + (C * Cᴴ).det = ((∑ s : Set.powersetCard ι m, ‖plucker m C.row s‖ ^ 2 : ℝ) : ℂ) := + ⟨Matrix.det_mul_transpose_eq_sum_plucker A B, Matrix.det_mul_transpose_self_eq_sum_sq A, + Matrix.det_mul_conjTranspose_self_eq_sum_sq_norm C⟩ + +end Examples + +end diff --git a/LeanPool/CCDN/ArithmeticHeights/CubeSlicing.lean b/LeanPool/CCDN/ArithmeticHeights/CubeSlicing.lean new file mode 100644 index 0000000000..ce5707f6f5 --- /dev/null +++ b/LeanPool/CCDN/ArithmeticHeights/CubeSlicing.lean @@ -0,0 +1,467 @@ +/- +Copyright (c) 2026 Ralf Stephan. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Ralf Stephan +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.ArithmeticHeights.ProductOfBalls +public import Mathlib.Analysis.InnerProductSpace.ProdL2 + +/-! +# Vaaler's cube-slicing theorem + +**Bombieri–Gubler, Theorem C.3.8 (Vaaler 1979).** Let `Q` be a product of euclidean balls each of +volume one, one factor per block of a partition of the coordinates of `ℝᴺ`. Then every central +slice of `Q` by a linear subspace `V` has volume at least one, the volume being computed on `V` +with its own inner-product structure. Two corollaries: the cube `[-1, 1]ᴺ` has central slices of +volume at least `2 ^ dim V`, and the unit polydisc of `ℂⁿ` has central slices of real volume at +least `2 ^ (dim V / 2)`. + +## Main definitions + +* `Submodule.prodOrthogonalEquiv V`: the orthogonal decomposition `E ≃ₗ[ℝ] V × Vᗮ`, as a + measure-preserving linear equivalence. +* `unitBallVolume n`: the volume of the unit ball of `EuclideanSpace ℝ (Fin n)`. + +## Main results + +* `one_le_volume_subtype_mem`: **Bombieri–Gubler, Theorem C.3.8** in the form the slice bound gives + it, for an arbitrary measurable convex symmetric `Q` satisfying `HasSliceBound`. +* `one_le_volume_inter_prodBall`: the product-of-balls theorem in the roadmap's form. +* `two_pow_finrank_le_volume_inter_cube`: **Vaaler's cube-slicing theorem**. +* `two_pow_le_volume_inter_polydisc`: the inscribed-cube bound at a complex place. + +## Implementation notes + +⚠ **The `ε`-thickening needs no dominated convergence, because a slice of a symmetric convex body +is largest at the centre.** Bombieri–Gubler pass to the limit `ε → 0` in +`vol (Q ∩ L_ε) / vol (B_ε)` by computing the limit of the integrand and dominating it. The same +limit is avoided here: the function `z ↦ vol_V {y | y + z ∈ Q}` on `Vᗮ` is log-concave, by Lemma +C.3.4, and even, because `Q` is symmetric, so it is *bounded by its value at zero* — which is the +slice volume being estimated. That turns the right-hand side into +`vol_V (Q ∩ V) * vol (B_ε)` with no limit at all, and the left-hand side is bounded below by +`exp (-π ε ^ 2) * vol (B_ε)` because the Gauss density is. Dividing and letting `ε → 0` is then a +one-line limit of `exp (-π ε ^ 2)`. Brunn's principle — that the central slice is the largest one — +is thus the only extra ingredient, and log-concavity of marginals already supplies it. + +⚠ **The complex structure is not needed for the polydisc bound.** The roadmap states +`two_pow_le_volume_inter_polydisc` for a subspace closed under the complex structure `J`, since +that is where the bound is applied; but the proof inscribes the cube of half-side `2 ^ (-1/2)` in +the polydisc and applies the cube case, which gives `√2 ^ dim V`, and `2 ^ (dim V / 2) ≤ √2 ^ dim V` +holds whether or not `dim V` is even. So the hypothesis is dropped here. + +## References + +E. Bombieri and W. Gubler, *Heights in Diophantine Geometry*, Cambridge University Press (2006), +Theorem C.3.8; J. D. Vaaler, *A geometric inequality with applications to linear forms*, Pacific +Journal of Mathematics **83** (1979), 543–553. + +This is Layer 4.5 of the `ArithmeticHeights` roadmap. +-/ + +public section + +open MeasureTheory Measure Set ENNReal Pointwise +open scoped Real + +section OrthogonalDecomposition + +variable {E : Type*} [NormedAddCommGroup E] [InnerProductSpace ℝ E] [FiniteDimensional ℝ E] + +/-- The orthogonal decomposition `E ≃ₗ[ℝ] V × Vᗮ`, forgetting the `L²` structure of the target. -/ +noncomputable def Submodule.prodOrthogonalEquiv (V : Submodule ℝ E) : + E ≃ₗ[ℝ] (V × (Vᗮ : Submodule ℝ E)) := + V.orthogonalDecomposition.toLinearEquiv ≪≫ₗ WithLp.linearEquiv 2 ℝ (V × (Vᗮ : Submodule ℝ E)) + +theorem Submodule.prodOrthogonalEquiv_symm_apply (V : Submodule ℝ E) + (q : V × (Vᗮ : Submodule ℝ E)) : V.prodOrthogonalEquiv.symm q = (q.1 : E) + (q.2 : E) := by + simp [Submodule.prodOrthogonalEquiv] + +theorem Submodule.norm_prodOrthogonalEquiv_symm (V : Submodule ℝ E) + (q : V × (Vᗮ : Submodule ℝ E)) : + ‖V.prodOrthogonalEquiv.symm q‖ ^ 2 = ‖q.1‖ ^ 2 + ‖q.2‖ ^ 2 := by + have h : V.prodOrthogonalEquiv.symm q = V.orthogonalDecomposition.symm (WithLp.toLp 2 q) := rfl + rw [h, V.orthogonalDecomposition.symm.norm_map, WithLp.prod_norm_sq_eq_of_L2] + simp + +variable [MeasurableSpace E] [BorelSpace E] + +theorem Submodule.measurePreserving_prodOrthogonalEquiv (V : Submodule ℝ E) : + MeasurePreserving V.prodOrthogonalEquiv (volume : Measure E) + ((volume : Measure V).prod (volume : Measure (Vᗮ : Submodule ℝ E))) := by + have h1 : MeasurePreserving V.orthogonalDecomposition (volume : Measure E) volume := + LinearIsometryEquiv.measurePreserving _ + have h2 := WithLp.volume_preserving_ofLp (V : Type _) ((Vᗮ : Submodule ℝ E) : Type _) + rw [← Measure.volume_eq_prod] + exact h2.comp h1 + +end OrthogonalDecomposition + +variable {E : Type*} [NormedAddCommGroup E] [InnerProductSpace ℝ E] [FiniteDimensional ℝ E] + [MeasurableSpace E] [BorelSpace E] + +/-- **A log-concave even function attains its maximum at the origin** (Brunn's principle, for the +marginals of a symmetric convex body). -/ +theorem LogConcave.le_apply_zero {F : Type*} [AddCommGroup F] [Module ℝ F] {f : F → ℝ≥0∞} + (hf : LogConcave f) (hfe : ∀ x, f (-x) = f x) (x : F) : f x ≤ f 0 := by + have h := hf (1/2) (1/2) (by norm_num) (by norm_num) (by norm_num) x (-x) + rw [hfe x, show (1/2 : ℝ) • x + (1/2 : ℝ) • (-x) = 0 by module] at h + calc f x = f x ^ ((1:ℝ)/2) * f x ^ ((1:ℝ)/2) := by + rw [← ENNReal.rpow_add_of_nonneg _ _ (by norm_num) (by norm_num)] + norm_num + _ ≤ f 0 := h + +/-- **Bombieri–Gubler, Theorem C.3.8.** If the Gauss measure of every measurable convex symmetric +set is at most its volume inside `Q`, then every central slice of `Q` by a linear subspace has +volume at least one, computed on the subspace. -/ +theorem one_le_volume_subtype_mem + {Q : Set E} (hQm : MeasurableSet Q) (hQc : Convex ℝ Q) (hQs : ∀ x ∈ Q, -x ∈ Q) + (hQ : HasSliceBound (volume : Measure E) gaussDensity Q) (V : Submodule ℝ E) : + 1 ≤ volume {y : V | (y : E) ∈ Q} := by + have hψmeas : Measurable V.prodOrthogonalEquiv := + V.measurePreserving_prodOrthogonalEquiv.measurable + have hψsymm : Measurable V.prodOrthogonalEquiv.symm := + (LinearMap.continuous_of_finiteDimensional + (V.prodOrthogonalEquiv.symm : (V × (Vᗮ : Submodule ℝ E)) →ₗ[ℝ] E)).measurable + let ψm : E ≃ᵐ (V × (Vᗮ : Submodule ℝ E)) := + ⟨V.prodOrthogonalEquiv.toEquiv, hψmeas, hψsymm⟩ + have hMPsymm : MeasurePreserving V.prodOrthogonalEquiv.symm + ((volume : Measure V).prod (volume : Measure (Vᗮ : Submodule ℝ E))) (volume : Measure E) := + (V.measurePreserving_prodOrthogonalEquiv).symm ψm + have hQ' := HasSliceBound.of_measurePreserving V.prodOrthogonalEquiv.symm hMPsymm hψmeas + measurable_gaussDensity hQm hQ + set W := (Vᗮ : Submodule ℝ E) + set QQ : Set (V × W) := V.prodOrthogonalEquiv.symm ⁻¹' Q with hQQdef + have hQQm : MeasurableSet QQ := hψsymm hQm + have hQQc : Convex ℝ QQ := + hQc.linear_preimage (V.prodOrthogonalEquiv.symm : (V × W) →ₗ[ℝ] E) + have hQQs : ∀ q ∈ QQ, -q ∈ QQ := by + intro q hq + have hneg : V.prodOrthogonalEquiv.symm (-q) = -(V.prodOrthogonalEquiv.symm q) := map_neg _ _ + simpa [hQQdef, Set.mem_preimage, hneg] using hQs _ hq + -- the marginal of `QQ` in the `V` direction + set f : W → ℝ≥0∞ := fun z => (volume : Measure V) {y : V | (y, z) ∈ QQ} with hfdef + have hfmeas : Measurable f := measurable_measure_prodMk_right hQQm + have hsecm : ∀ z : W, MeasurableSet {y : V | (y, z) ∈ QQ} := + fun z => hQQm.preimage (by fun_prop) + have hfeven : ∀ z, f (-z) = f z := by + intro z + have hset : {y : V | (y, -z) ∈ QQ} = -{y : V | (y, z) ∈ QQ} := by + ext y + simp only [Set.mem_neg, Set.mem_ofPred_eq] + exact ⟨fun hy => by simpa using hQQs _ hy, fun hy => by simpa using hQQs _ hy⟩ + rw [hfdef] + simp only + rw [hset, measure_neg] + have hflc : LogConcave f := by + have hswapc : Convex ℝ {q : W × V | (q.2, q.1) ∈ QQ} := + fun _ h1 _ h2 _ _ ha hb hab => hQQc h1 h2 ha hb hab + have heq : (fun z => ∫⁻ y in (Set.univ : Set V), + Set.indicator {q : W × V | (q.2, q.1) ∈ QQ} (fun _ => (1 : ℝ≥0∞)) (z, y) + ∂(volume : Measure V)) = f := by + funext z + rw [show (fun y : V => + Set.indicator {q : W × V | (q.2, q.1) ∈ QQ} (fun _ => (1 : ℝ≥0∞)) (z, y)) + = Set.indicator {y : V | (y, z) ∈ QQ} (fun _ => (1 : ℝ≥0∞)) from rfl, + lintegral_indicator (hsecm z), setLIntegral_one, hfdef] + simp + exact heq ▸ LogConcave.setLIntegral_prod_right hasPrekopaLeindler_innerProductSpace + (measurable_one.indicator (hQQm.preimage (by fun_prop))) (logConcave_indicator hswapc 1) + MeasurableSet.univ convex_univ + have hfle : ∀ z, f z ≤ f 0 := fun z => hflc.le_apply_zero hfeven z + have hf0 : f 0 = volume {y : V | (y : E) ∈ Q} := by + rw [hfdef] + simp only + congr 1 + ext y + simp [hQQdef, Set.mem_preimage, Submodule.prodOrthogonalEquiv_symm_apply] + have hdens : ∀ q : V × W, gaussDensity (V.prodOrthogonalEquiv.symm q) + = gaussDensity q.1 * gaussDensity q.2 := by + intro q + rw [gaussDensity_def, gaussDensity_def, gaussDensity_def, + V.norm_prodOrthogonalEquiv_symm q, ← ENNReal.ofReal_mul (Real.exp_pos _).le, ← Real.exp_add] + congr 2 + ring + have key : ∀ ε : ℝ, 0 < ε → ENNReal.ofReal (Real.exp (-π * ε ^ 2)) ≤ f 0 := by + intro ε hε + have hSm : MeasurableSet (Metric.closedBall (0 : W) ε) := measurableSet_closedBall + have hApp := hQ' (Set.univ ×ˢ Metric.closedBall (0 : W) ε) + (MeasurableSet.univ.prod hSm) + (convex_univ.prod (convex_closedBall _ _)) + (fun q hq => ⟨Set.mem_univ _, by + have hn : ‖-q.2‖ ≤ ε := by + rw [norm_neg]; simpa [Metric.mem_closedBall, dist_zero_right] using hq.2 + simpa [Metric.mem_closedBall, dist_zero_right] using hn⟩) + have hlhs : ∫⁻ q in Set.univ ×ˢ Metric.closedBall (0 : W) ε, + gaussDensity (V.prodOrthogonalEquiv.symm q) + ∂((volume : Measure V).prod (volume : Measure W)) + = (∫⁻ y : V, gaussDensity y) * ∫⁻ z in Metric.closedBall (0 : W) ε, gaussDensity z := by + rw [← Measure.prod_restrict, Measure.restrict_univ, lintegral_congr hdens] + exact lintegral_prod_mul measurable_gaussDensity.aemeasurable + measurable_gaussDensity.aemeasurable + have hlow : ENNReal.ofReal (Real.exp (-π * ε ^ 2)) * volume (Metric.closedBall (0 : W) ε) + ≤ ∫⁻ z in Metric.closedBall (0 : W) ε, gaussDensity z := by + rw [← setLIntegral_const] + refine setLIntegral_mono' hSm fun z hz => ?_ + rw [gaussDensity_def] + refine ENNReal.ofReal_le_ofReal (Real.exp_le_exp.mpr ?_) + have hznorm : ‖z‖ ≤ ε := by simpa [Metric.mem_closedBall, dist_zero_right] using hz + have hsq : ‖z‖ ^ 2 ≤ ε ^ 2 := by nlinarith [norm_nonneg z] + nlinarith [Real.pi_pos] + have hrhs : ((volume : Measure V).prod (volume : Measure W)) + ((Set.univ ×ˢ Metric.closedBall (0 : W) ε) ∩ QQ) + ≤ f 0 * volume (Metric.closedBall (0 : W) ε) := by + rw [Measure.prod_apply_symm ((MeasurableSet.univ.prod hSm).inter hQQm)] + calc ∫⁻ z : W, (volume : Measure V) + ((fun y => (y, z)) ⁻¹' ((Set.univ ×ˢ Metric.closedBall (0 : W) ε) ∩ QQ)) + ≤ ∫⁻ z : W, Set.indicator (Metric.closedBall (0 : W) ε) (fun _ => f 0) z := by + refine lintegral_mono fun z => ?_ + by_cases hz : z ∈ Metric.closedBall (0 : W) ε + · rw [Set.indicator_of_mem hz] + refine le_trans (le_of_eq ?_) (hfle z) + rw [hfdef] + congr 1 + ext y; simp [hz] + · rw [Set.indicator_of_notMem hz, + show ((fun y => (y, z)) ⁻¹' + ((Set.univ ×ˢ Metric.closedBall (0 : W) ε) ∩ QQ)) = ∅ by ext y; simp [hz]] + simp + _ = f 0 * volume (Metric.closedBall (0 : W) ε) := by + rw [lintegral_indicator hSm, setLIntegral_const] + have hvpos : volume (Metric.closedBall (0 : W) ε) ≠ 0 := + (Metric.measure_closedBall_pos volume 0 hε).ne' + have hvtop : volume (Metric.closedBall (0 : W) ε) ≠ ⊤ := measure_closedBall_lt_top.ne + have hcomb : ENNReal.ofReal (Real.exp (-π * ε ^ 2)) * volume (Metric.closedBall (0 : W) ε) + ≤ f 0 * volume (Metric.closedBall (0 : W) ε) := by + calc ENNReal.ofReal (Real.exp (-π * ε ^ 2)) * volume (Metric.closedBall (0 : W) ε) + ≤ ∫⁻ z in Metric.closedBall (0 : W) ε, gaussDensity z := hlow + _ = (∫⁻ y : V, gaussDensity y) * ∫⁻ z in Metric.closedBall (0 : W) ε, gaussDensity z := by + rw [lintegral_gaussDensity_eq_one, one_mul] + _ = ∫⁻ q in Set.univ ×ˢ Metric.closedBall (0 : W) ε, + gaussDensity (V.prodOrthogonalEquiv.symm q) + ∂((volume : Measure V).prod (volume : Measure W)) := hlhs.symm + _ ≤ ((volume : Measure V).prod (volume : Measure W)) + ((Set.univ ×ˢ Metric.closedBall (0 : W) ε) ∩ QQ) := hApp + _ ≤ f 0 * volume (Metric.closedBall (0 : W) ε) := hrhs + have hcomb' : volume (Metric.closedBall (0 : W) ε) * ENNReal.ofReal (Real.exp (-π * ε ^ 2)) + ≤ volume (Metric.closedBall (0 : W) ε) * f 0 := by + rw [mul_comm, mul_comm (volume (Metric.closedBall (0 : W) ε)) (f 0)]; exact hcomb + exact (ENNReal.mul_le_mul_iff_right hvpos hvtop).mp hcomb' + rw [← hf0] + have hcont : Filter.Tendsto (fun ε : ℝ => ENNReal.ofReal (Real.exp (-π * ε ^ 2))) + (nhdsWithin 0 (Set.Ioi 0)) (nhds 1) := by + have h1 : Filter.Tendsto (fun ε : ℝ => ENNReal.ofReal (Real.exp (-π * ε ^ 2))) + (nhds 0) (nhds (ENNReal.ofReal (Real.exp (-π * (0 : ℝ) ^ 2)))) := + (ENNReal.continuous_ofReal.comp (Real.continuous_exp.comp (by fun_prop))).tendsto 0 + simpa using h1.mono_left nhdsWithin_le_nhds + refine le_of_tendsto hcont ?_ + filter_upwards [self_mem_nhdsWithin] with ε hε + exact key ε hε + +section Milestones + +/-- The volume `ω_n` of the unit ball of `EuclideanSpace ℝ (Fin n)`. -/ +@[expose] noncomputable def unitBallVolume (n : ℕ) : ℝ := + (volume (Metric.ball (0 : EuclideanSpace ℝ (Fin n)) 1)).toReal + +theorem unitBallVolume_pos (n : ℕ) : 0 < unitBallVolume n := volume_ball_one_pos + +theorem ballRadius_eq (n : ℕ) : ballRadius n = unitBallVolume n ^ (-(1 / (n : ℝ))) := by + rw [ballRadius, unitVolumeRadius, finrank_euclideanSpace_fin, unitBallVolume] + +theorem ballRadius_sq (n : ℕ) : ballRadius n ^ 2 = unitBallVolume n ^ (-(2 / (n : ℝ))) := by + rw [ballRadius_eq, ← Real.rpow_natCast _ 2, ← Real.rpow_mul (unitBallVolume_pos n).le] + congr 1 + push_cast + ring + +theorem unitBallVolume_one : unitBallVolume 1 = 2 := by + rw [unitBallVolume, EuclideanSpace.volume_ball] + simp only [Fintype.card_fin, Nat.cast_one, ofReal_one, one_mul, pow_one] + rw [Real.Gamma_add_one (by norm_num), Real.Gamma_one_half_eq, + ENNReal.toReal_ofReal (by positivity), div_eq_iff (by positivity)] + have h : Real.sqrt π ≠ 0 := by positivity + field_simp + +theorem ballRadius_one : ballRadius 1 = 1 / 2 := by + rw [ballRadius_eq, unitBallVolume_one, Nat.cast_one, div_one, Real.rpow_neg_one] + norm_num + +theorem unitBallVolume_two : unitBallVolume 2 = π := by + rw [unitBallVolume, EuclideanSpace.volume_ball_fin_two] + simp [Real.pi_pos.le] + +theorem ballRadius_two : ballRadius 2 = 1 / Real.sqrt π := by + rw [ballRadius_eq, unitBallVolume_two, show ((2 : ℕ) : ℝ) = 2 by norm_num, + Real.rpow_neg Real.pi_pos.le, ← Real.sqrt_eq_rpow, one_div] + +theorem sq_le_sq_iff_abs_le {a c : ℝ} (hc : 0 ≤ c) : a ^ 2 ≤ c ^ 2 ↔ |a| ≤ c := by + rw [sq_le_sq, abs_of_nonneg hc] + +/-- **The product-of-balls theorem for an arbitrary index type.** -/ +theorem one_le_volume_prodBall_subtype {ι κ : Type*} [Fintype ι] [DecidableEq κ] [Countable κ] + (blk : ι → κ) (V : Submodule ℝ (EuclideanSpace ℝ ι)) : + 1 ≤ volume {y : V | (y : EuclideanSpace ℝ ι) ∈ prodBall blk} := + one_le_volume_subtype_mem (measurableSet_prodBall blk) (convex_prodBall blk) + (fun _ hx => neg_mem_prodBall blk hx) (hasSliceBound_prodBall blk) V + +/-- With singleton blocks, the product of balls of volume one is the cube of half-side `1 / 2`. -/ +theorem prodBall_id {ι : Type*} [Fintype ι] [DecidableEq ι] : + prodBall (id : ι → ι) = {x : EuclideanSpace ℝ ι | ∀ i, |x i| ≤ 1 / 2} := by + have hsum : ∀ (f : ι → ℝ) (k : ι), ∑ j : {j : ι // id j = k}, f ↑j = f k := by + intro f k + rw [← Finset.sum_subtype ({k} : Finset ι) (by simp) f] + simp + have hcard : ∀ k : ι, Fintype.card {j : ι // id j = k} = 1 := by + intro k + rw [Fintype.card_subtype] + simp [Finset.filter_eq'] + ext x + constructor + · intro h i + have hi := h i + rw [hsum (fun j => (x j) ^ 2) i, hcard i, ballRadius_one] at hi + exact (sq_le_sq_iff_abs_le (by norm_num)).mp hi + · intro h k + rw [hsum (fun j => (x j) ^ 2) k, hcard k, ballRadius_one] + exact (sq_le_sq_iff_abs_le (by norm_num)).mpr (h k) + +theorem smul_setOf_abs_le {ι : Type*} (V : Submodule ℝ (EuclideanSpace ℝ ι)) + {r c : ℝ} (hr : 0 < r) : + (r • {y : V | ∀ i, |(y : EuclideanSpace ℝ ι) i| ≤ c}) + = {y : V | ∀ i, |(y : EuclideanSpace ℝ ι) i| ≤ r * c} := by + ext y + rw [Set.mem_smul_set_iff_inv_smul_mem₀ hr.ne'] + constructor + · intro h i + have hi := h i + rw [Submodule.coe_smul] at hi + simp only [PiLp.smul_apply, smul_eq_mul, abs_mul, abs_of_pos (inv_pos.mpr hr)] at hi + rw [inv_mul_le_iff₀ hr] at hi + linarith + · intro h i + have hi := h i + rw [Submodule.coe_smul] + simp only [PiLp.smul_apply, smul_eq_mul, abs_mul, abs_of_pos (inv_pos.mpr hr)] + rw [inv_mul_le_iff₀ hr] + linarith + +/-- **Vaaler's cube-slicing theorem** (Vaaler 1979; Bombieri–Gubler, Theorem C.3.8): every central +slice of the cube `[-1, 1]ᴺ` by a `k`-dimensional subspace has `k`-volume at least `2 ^ k`. -/ +theorem two_pow_finrank_le_volume_inter_cube {ι : Type*} [Fintype ι] + (V : Submodule ℝ (EuclideanSpace ℝ ι)) : + (2 : ℝ≥0∞) ^ Module.finrank ℝ V ≤ + volume {y : V | ∀ i, |(y : EuclideanSpace ℝ ι) i| ≤ 1} := by + classical + have h1 : 1 ≤ volume {y : V | ∀ i, |(y : EuclideanSpace ℝ ι) i| ≤ 1 / 2} := by + have h := one_le_volume_prodBall_subtype (id : ι → ι) V + rwa [prodBall_id] at h + have h2 : {y : V | ∀ i, |(y : EuclideanSpace ℝ ι) i| ≤ 1} + = (2 : ℝ) • {y : V | ∀ i, |(y : EuclideanSpace ℝ ι) i| ≤ 1 / 2} := by + rw [smul_setOf_abs_le V (by norm_num : (0 : ℝ) < 2)] + norm_num + rw [h2, Measure.addHaar_smul] + calc (2 : ℝ≥0∞) ^ Module.finrank ℝ V + = ENNReal.ofReal |(2 : ℝ) ^ Module.finrank ℝ (V : Type _)| * 1 := by + rw [mul_one, abs_of_pos (by positivity), ENNReal.ofReal_pow (by norm_num)] + norm_num + _ ≤ ENNReal.ofReal |(2 : ℝ) ^ Module.finrank ℝ (V : Type _)| * + volume {y : V | ∀ i, |(y : EuclideanSpace ℝ ι) i| ≤ 1 / 2} := by gcongr + +/-- **The product-of-balls theorem** (Bombieri–Gubler, Theorem C.3.8), in the roadmap's form: +partition the `N` coordinates into blocks by `blk`, let `Q` be the product over the blocks of the +euclidean ball of volume one in that block's coordinates, and every central slice of `Q` by a +subspace `V` has volume at least one, computed on `V`. -/ +theorem one_le_volume_inter_prodBall {N r : ℕ} (blk : Fin N → Fin r) + (V : Submodule ℝ (EuclideanSpace ℝ (Fin N))) : + 1 ≤ volume {x : V | ∀ i : Fin r, + ∑ j ∈ Finset.univ.filter (fun j => blk j = i), (x : EuclideanSpace ℝ (Fin N)) j ^ 2 ≤ + unitBallVolume (Finset.univ.filter (fun j => blk j = i)).card ^ + (-(2 / ((Finset.univ.filter (fun j => blk j = i)).card : ℝ)))} := by + have hset : {x : V | ∀ i : Fin r, + ∑ j ∈ Finset.univ.filter (fun j => blk j = i), (x : EuclideanSpace ℝ (Fin N)) j ^ 2 ≤ + unitBallVolume (Finset.univ.filter (fun j => blk j = i)).card ^ + (-(2 / ((Finset.univ.filter (fun j => blk j = i)).card : ℝ)))} + = {y : V | (y : EuclideanSpace ℝ (Fin N)) ∈ prodBall blk} := by + have hsum : ∀ (y : V) (i : Fin r), + ∑ j ∈ Finset.univ.filter (fun j => blk j = i), (y : EuclideanSpace ℝ (Fin N)) j ^ 2 + = ∑ j : {j : Fin N // blk j = i}, (y : EuclideanSpace ℝ (Fin N)) (j : Fin N) ^ 2 := + fun y i => Finset.sum_subtype _ (by simp) _ + have hcard : ∀ i : Fin r, (Finset.univ.filter (fun j => blk j = i)).card + = Fintype.card {j : Fin N // blk j = i} := fun i => (Fintype.card_subtype _).symm + ext y + constructor + · intro h i + have hi := h i + rw [hsum y i, hcard i] at hi + rw [← ballRadius_sq] at hi + exact hi + · intro h i + have hi := h i + rw [hsum y i, hcard i, ← ballRadius_sq] + exact hi + rw [hset] + exact one_le_volume_prodBall_subtype blk V + +/-- **The inscribed-cube bound at a complex place**: a subspace of `ℝ^{2n}`, with coordinates +paired as the real and imaginary parts of `ℂⁿ`, meets the unit polydisc in volume at least +`2 ^ (dim V / 2)`. The polydisc contains the cube of half-side `2 ^ (-1/2)`, and the cube case +gives `√2 ^ dim V` there, which dominates `2 ^ (dim V / 2)` for every dimension, even or odd. -/ +theorem two_pow_le_volume_inter_polydisc {n : ℕ} + (V : Submodule ℝ (EuclideanSpace ℝ (Fin n × Fin 2))) : + (2 : ℝ≥0∞) ^ (Module.finrank ℝ V / 2) ≤ + volume {y : V | ∀ i : Fin n, + (y : EuclideanSpace ℝ (Fin n × Fin 2)) (i, 0) ^ 2 + + (y : EuclideanSpace ℝ (Fin n × Fin 2)) (i, 1) ^ 2 ≤ 1} := by + set k := Module.finrank ℝ (V : Type _) with hk + have hs2 : (0 : ℝ) < Real.sqrt 2 := Real.sqrt_pos.mpr (by norm_num) + have hsq2 : Real.sqrt 2 ^ 2 = 2 := Real.sq_sqrt (by norm_num) + have hsub : {y : V | ∀ p, |(y : EuclideanSpace ℝ (Fin n × Fin 2)) p| ≤ 1 / Real.sqrt 2} + ⊆ {y : V | ∀ i : Fin n, + (y : EuclideanSpace ℝ (Fin n × Fin 2)) (i, 0) ^ 2 + + (y : EuclideanSpace ℝ (Fin n × Fin 2)) (i, 1) ^ 2 ≤ 1} := by + intro y hy i + have e0 := (sq_le_sq_iff_abs_le (a := (y : EuclideanSpace ℝ (Fin n × Fin 2)) (i, 0)) + (c := 1 / Real.sqrt 2) (by positivity)).mpr (hy (i, 0)) + have e1 := (sq_le_sq_iff_abs_le (a := (y : EuclideanSpace ℝ (Fin n × Fin 2)) (i, 1)) + (c := 1 / Real.sqrt 2) (by positivity)).mpr (hy (i, 1)) + have hhalf : (1 / Real.sqrt 2 : ℝ) ^ 2 = 1 / 2 := by rw [div_pow, one_pow, hsq2] + rw [hhalf] at e0 e1 + linarith + refine le_trans ?_ (measure_mono hsub) + have hscale : {y : V | ∀ p, |(y : EuclideanSpace ℝ (Fin n × Fin 2)) p| ≤ 1 / Real.sqrt 2} + = (1 / Real.sqrt 2 : ℝ) • {y : V | ∀ p, |(y : EuclideanSpace ℝ (Fin n × Fin 2)) p| ≤ 1} := by + rw [smul_setOf_abs_le V (by positivity)] + norm_num + rw [hscale, Measure.addHaar_smul, ← hk] + have hcube := two_pow_finrank_le_volume_inter_cube V + rw [← hk] at hcube + have habs : |(1 / Real.sqrt 2 : ℝ) ^ k| = (1 / Real.sqrt 2) ^ k := abs_of_pos (by positivity) + rw [habs] + have hreal : (2 : ℝ) ^ (k / 2) ≤ (1 / Real.sqrt 2) ^ k * 2 ^ k := by + have hprod : (1 / Real.sqrt 2 : ℝ) ^ k * 2 ^ k = Real.sqrt 2 ^ k := by + rw [← mul_pow] + congr 1 + field_simp + nlinarith [hsq2] + rw [hprod] + conv_rhs => rw [← Nat.div_add_mod k 2] + rw [pow_add, pow_mul, hsq2] + have h1 : (1 : ℝ) ≤ Real.sqrt 2 ^ (k % 2) := + one_le_pow₀ (by nlinarith [hsq2, hs2]) + nlinarith [pow_pos (show (0 : ℝ) < 2 by norm_num) (k / 2)] + calc (2 : ℝ≥0∞) ^ (k / 2) = ENNReal.ofReal ((2 : ℝ) ^ (k / 2)) := by + rw [ENNReal.ofReal_pow (by norm_num)] + norm_num + _ ≤ ENNReal.ofReal ((1 / Real.sqrt 2 : ℝ) ^ k * 2 ^ k) := ENNReal.ofReal_le_ofReal hreal + _ = ENNReal.ofReal ((1 / Real.sqrt 2 : ℝ) ^ k) * (2 : ℝ≥0∞) ^ k := by + rw [ENNReal.ofReal_mul (by positivity), ENNReal.ofReal_pow (by norm_num)] + norm_num + _ ≤ ENNReal.ofReal ((1 / Real.sqrt 2 : ℝ) ^ k) * + volume {y : V | ∀ p, |(y : EuclideanSpace ℝ (Fin n × Fin 2)) p| ≤ 1} := by gcongr + +end Milestones diff --git a/LeanPool/CCDN/ArithmeticHeights/Duality.lean b/LeanPool/CCDN/ArithmeticHeights/Duality.lean new file mode 100644 index 0000000000..0ec8c0ff8c --- /dev/null +++ b/LeanPool/CCDN/ArithmeticHeights/Duality.lean @@ -0,0 +1,870 @@ +/- +Copyright (c) 2026 Ralf Stephan. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Ralf Stephan +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.ArithmeticHeights.RowSpace +public import Mathlib.LinearAlgebra.Dual.Defs +public import Mathlib.LinearAlgebra.StdBasis + +-- Used only inside proofs: the complement of a subspace and the basis it produces, the dual +-- basis, the dimension of an annihilator, and the two-sided inverse of a square matrix. +import Mathlib.LinearAlgebra.Basis.Prod +import Mathlib.LinearAlgebra.Dual.Lemmas +import Mathlib.LinearAlgebra.Matrix.NonsingularInverse +import Mathlib.LinearAlgebra.Projection + +/-! +# The duality theorem for the height of a subspace + +A subspace and its annihilator have the same height. This is W. M. Schmidt's theorem: the Plücker +coordinates of the annihilator are those of the subspace read at the complementary index, up to a +sign at each index and one common nonzero factor, and every height in this development is +insensitive to exactly that much — a signed reindexing and a scalar. + +## Main results + +* `Submodule.mulHeight_comap_piEquiv_dualAnnihilator`, with its Arakelov and absolute companions + `Submodule.arakelovMulHeight_comap_piEquiv_dualAnnihilator` and + `Submodule.absMulHeight_comap_piEquiv_dualAnnihilator` and every logarithmic form: **the duality + theorem**, the height of `V` against the height of its annihilator, transported back to `ι → K` + along the standard basis. +* `Matrix.mulHeight_ker_mulVecLin` and its five companions: the solution space of `A x = 0` + and the row space of `A` have the same height in each supported normalization. + The Arakelov case corresponds to Bombieri–Gubler Corollary 2.8.12. +* `Submodule.exists_plucker_eq_plucker_compl`: the coordinate statement behind both. A subspace and + its annihilator have bases whose Plücker coordinates agree at complementary indices up to a sign + and one common nonzero factor — Schmidt's involution `τ`. +* `Submodule.mem_comap_piEquiv_dualAnnihilator`: the transported annihilator is the orthogonal + complement for the standard bilinear form, and + `Submodule.eq_comap_piEquiv_dualAnnihilator_of_forall_dotProduct_eq_zero` is that reading as a + way into the theorems above. `Submodule.comap_piEquiv_dualAnnihilator_eq_map` is the same + subspace in the other spelling, the `map` along the inverse identification. +* `Height.mulHeight_eq_of_forall_eq_or_eq_neg` and its Arakelov and absolute companions: no height + in this development sees a change of sign in a coordinate. These are statements about Mathlib's + heights and belong upstream. + +## Implementation notes + +The annihilator lives in the dual space, so it is transported back to `ι → K` along +`Module.piEquiv ι K K`, whose value at `x` is the functional `v ↦ v ⬝ᵥ x`; the transported +annihilator is therefore the orthogonal complement of `V` for the standard bilinear form. +`Submodule.comap_piEquiv_dualAnnihilator_eq_map` turns the `comap` used here into the `map` along +the inverse equivalence, which is the other spelling in use. + +⚠ The annihilator and not the orthogonal complement is what the *proof* must use, even though the +two are identified here. Over a field that is not formally real the two summands `V` and `V^⊥` can +meet — over `ℚ(i)`, the line spanned by `(1, i)` is its own orthogonal complement — so the matrix +whose rows are a basis of `V` stacked on a basis of `V^⊥` is singular, and every argument through +its determinant fails. The proof below is stated for a *complement* `W` of `V`, which always +exists, and reaches the annihilator through the dual basis. + +The proof is elementary, and in particular uses neither a Laplace expansion along a block of rows +nor Jacobi's identity for the complementary minors of an inverse, neither of which Mathlib has. +Instead: let the rows of `C` be a basis of `ι → K` whose first block is a basis of `V`, and let the +rows of `D` be the dual basis read back in `ι → K`, so that `C * Dᵀ = 1`. For a set `s` of `k` +columns, multiply `C` by the matrix whose first `k` columns are the standard basis vectors at `s` +and whose remaining columns are the second block of `Dᵀ`. The product is block triangular with the +`s`-minor of the first block of `C` in one corner and an identity in the other, while the second +factor, read through the enumeration of `ι` that lists `s` and then its complement, is block +triangular with the complementary minor of the second block of `D` in one corner. Two applications +of `Matrix.det_fromBlocks_zero₁₂` and `Matrix.det_fromBlocks_zero₂₁` give the identity, and the +factor that appears is the determinant of `C` read through that enumeration, which depends on `s` +only through a permutation sign (`Matrix.det_permute'`). + +## References + +W. M. Schmidt, *On heights of algebraic subspaces and diophantine approximations*, Annals of +Mathematics 85 (1967), §1, equations (2) and (4), where the involution on Grassmann coordinates +that reverses the index order and attaches a sign is what makes the theorem hold in every +normalization at once. E. Bombieri and W. Gubler, *Heights in Diophantine Geometry*, Cambridge +University Press (2006), Proposition 2.8.10 and Corollary 2.8.12 for the absolute Arakelov +version. The same Plücker identity also gives the sup-norm counterparts here. +The underlying linear algebra is +Hindry–Silverman, *Diophantine Geometry: An Introduction*, Exercise A.1.11(c). + +This is Layer 3.5 of the `ArithmeticHeights` roadmap. +-/ + +public section + +open Finset Module Set.powersetCard exteriorPower + +namespace Height + +variable {K : Type*} [Field K] [AdmissibleAbsValues K] {ι : Type*} + +/-- **A change of sign in a coordinate does not change the height.** Every absolute value takes +the same value at `x` and at `-x`, and the tuple is zero exactly when the original is, so both +the displayed product and the junk value are unchanged. This is the invariance that the duality +theorem of Layer 3.5 needs, the Plücker coordinates of an annihilator carrying a sign that depends +on the index. -/ +theorem mulHeight_eq_of_forall_eq_or_eq_neg {x y : ι → K} (h : ∀ i, y i = x i ∨ y i = -x i) : + mulHeight y = mulHeight x := by + have hv (v : AbsoluteValue K ℝ) (i : ι) : v (y i) = v (x i) := by + rcases h i with hi | hi + · rw [hi] + · rw [hi, v.map_neg] + have hzero : y = 0 ↔ x = 0 := by + simp only [funext_iff, Pi.zero_apply] + refine ⟨fun h0 i ↦ ?_, fun h0 i ↦ ?_⟩ <;> rcases h i with hi | hi + · rw [← hi]; exact h0 i + · have := h0 i; rw [hi, neg_eq_zero] at this; exact this + · rw [hi, h0 i] + · rw [hi, h0 i, neg_zero] + rcases eq_or_ne x 0 with rfl | hx + · rw [hzero.2 rfl] + · have hy : y ≠ 0 := fun h0 ↦ hx (hzero.1 h0) + rw [mulHeight_eq hx, mulHeight_eq hy] + congr 1 + · exact congrArg Multiset.prod (Multiset.map_congr rfl fun v _ ↦ iSup_congr (hv v)) + · exact finprod_congr fun v ↦ iSup_congr (hv v.val) + +/-- The logarithmic form of `Height.mulHeight_eq_of_forall_eq_or_eq_neg`. -/ +theorem logHeight_eq_of_forall_eq_or_eq_neg {x y : ι → K} (h : ∀ i, y i = x i ∨ y i = -x i) : + logHeight y = logHeight x := by + rw [logHeight_eq_log_mulHeight, logHeight_eq_log_mulHeight, + mulHeight_eq_of_forall_eq_or_eq_neg h] + +end Height + +namespace NumberField + +variable {K : Type*} [Field K] {ι : Type*} + +section Arakelov + +variable [NumberField K] [Fintype ι] + +/-- **A change of sign in a coordinate does not change the Arakelov height** either: the +archimedean factor is a sum of squares of absolute values and the finite factor a supremum of +absolute values, and neither sees a sign. -/ +theorem arakelovMulHeight_eq_of_forall_eq_or_eq_neg {x y : ι → K} + (h : ∀ i, y i = x i ∨ y i = -x i) : arakelovMulHeight y = arakelovMulHeight x := by + have hinf (v : InfinitePlace K) (i : ι) : v (y i) = v (x i) := by + rcases h i with hi | hi + · rw [hi] + · rw [hi]; exact v.1.map_neg _ + have hfin (v : FinitePlace K) (i : ι) : v (y i) = v (x i) := by + rcases h i with hi | hi + · rw [hi] + · rw [hi]; simp + have hzero : y = 0 ↔ x = 0 := by + simp only [funext_iff, Pi.zero_apply] + refine ⟨fun h0 i ↦ ?_, fun h0 i ↦ ?_⟩ <;> rcases h i with hi | hi + · rw [← hi]; exact h0 i + · have := h0 i; rw [hi, neg_eq_zero] at this; exact this + · rw [hi, h0 i] + · rw [hi, h0 i, neg_zero] + rcases eq_or_ne x 0 with rfl | hx + · rw [hzero.2 rfl] + · have hy : y ≠ 0 := fun h0 ↦ hx (hzero.1 h0) + rw [arakelovMulHeight_eq hx, arakelovMulHeight_eq hy] + congr 1 + · exact Finset.prod_congr rfl fun v _ ↦ by + rw [Finset.sum_congr rfl fun i _ ↦ by rw [hinf v i]] + · exact finprod_congr fun v ↦ iSup_congr (hfin v) + +/-- The logarithmic form of `NumberField.arakelovMulHeight_eq_of_forall_eq_or_eq_neg`. -/ +theorem arakelovLogHeight_eq_of_forall_eq_or_eq_neg {x y : ι → K} + (h : ∀ i, y i = x i ∨ y i = -x i) : arakelovLogHeight y = arakelovLogHeight x := by + rw [arakelovLogHeight, arakelovLogHeight, arakelovMulHeight_eq_of_forall_eq_or_eq_neg h] + +end Arakelov + +section Absolute + +variable [CharZero K] [Finite ι] + +/-- **A change of sign in a coordinate does not change the absolute height.** The coordinates of +`y` lie in the field generated by those of `x` and conversely, so the two are computed in one and +the same number field, where the relative statement applies. -/ +theorem absMulHeight_eq_of_forall_eq_or_eq_neg {x y : ι → K} (h : ∀ i, y i = x i ∨ y i = -x i) : + absMulHeight y = absMulHeight x := by + by_cases hx : ∀ i, IsIntegral ℚ (x i) + · have hmem (i : ι) : y i ∈ IntermediateField.adjoin ℚ (Set.range x) := by + rcases h i with hi | hi + · exact hi ▸ IntermediateField.subset_adjoin ℚ _ ⟨i, rfl⟩ + · exact hi ▸ neg_mem (IntermediateField.subset_adjoin ℚ _ ⟨i, rfl⟩) + have hmemx (i : ι) : x i ∈ IntermediateField.adjoin ℚ (Set.range x) := + IntermediateField.subset_adjoin ℚ _ ⟨i, rfl⟩ + have : FiniteDimensional ℚ (IntermediateField.adjoin ℚ (Set.range x)) := + IntermediateField.finiteDimensional_adjoin fun z hz ↦ by + obtain ⟨i, rfl⟩ := hz; exact hx i + have : NumberField (IntermediateField.adjoin ℚ (Set.range x)) := {} + rw [absMulHeight_eq_of_mem hmem, absMulHeight_eq_of_mem hmemx] + refine congrArg (· ^ _) (Height.mulHeight_eq_of_forall_eq_or_eq_neg fun i ↦ ?_) + rcases h i with hi | hi + · exact Or.inl (Subtype.ext hi) + · exact Or.inr (Subtype.ext hi) + · have hy : ¬ ∀ i, IsIntegral ℚ (y i) := fun hy ↦ hx fun i ↦ by + rcases h i with hi | hi + · exact hi ▸ hy i + · have := (hy i).neg + rwa [hi, neg_neg] at this + rw [absMulHeight_eq_one_of_not_isIntegral hx, absMulHeight_eq_one_of_not_isIntegral hy] + +/-- The logarithmic form of `NumberField.absMulHeight_eq_of_forall_eq_or_eq_neg`. -/ +theorem absLogHeight_eq_of_forall_eq_or_eq_neg {x y : ι → K} (h : ∀ i, y i = x i ∨ y i = -x i) : + absLogHeight y = absLogHeight x := by + rw [absLogHeight_eq_log_absMulHeight, absLogHeight_eq_log_absMulHeight, + absMulHeight_eq_of_forall_eq_or_eq_neg h] + +end Absolute + +end NumberField + +namespace Matrix + +variable {R : Type*} [CommRing R] {ι : Type*} [Fintype ι] {k l : ℕ} + +/-- Mathlib's `Matrix.submatrix_mul_equiv`, read as a re-indexing of the inner index of a +product: with both factors square it turns a product of rectangular matrices into a product of +square ones, where `Matrix.det_mul` applies. -/ +private lemma mul_eq_submatrix_mul_submatrix {m p r : Type*} [Fintype r] + (X : Matrix m ι R) (Y : Matrix ι p R) (e : r ≃ ι) : + X * Y = X.submatrix id e * Y.submatrix e id := by + rw [Matrix.submatrix_mul_equiv X Y id e id, Matrix.submatrix_id_id] + +variable [LinearOrder ι] + +/-- The enumeration of `ι` that lists the elements of `s` in increasing order and then those of +its complement. This is the index identification the complementary-minor identity runs on; the +determinant of a matrix read through it depends on `s` only up to a sign. -/ +private noncomputable def enumEquiv {s : Finset ι} (hs : s.card = k) (hsc : sᶜ.card = l) : + Fin k ⊕ Fin l ≃ ι := + (Equiv.sumCongr (s.orderIsoOfFin hs).toEquiv + ((sᶜ.orderIsoOfFin hsc).toEquiv.trans + (Equiv.subtypeEquivRight fun _ ↦ Finset.mem_compl))).trans (Equiv.sumCompl (· ∈ s)) + +private lemma enumEquiv_inl {s : Finset ι} (hs : s.card = k) (hsc : sᶜ.card = l) (a : Fin k) : + enumEquiv hs hsc (Sum.inl a) = s.orderEmbOfFin hs a := + Finset.coe_orderIsoOfFin_apply s hs a + +private lemma enumEquiv_inr {s : Finset ι} (hs : s.card = k) (hsc : sᶜ.card = l) (b : Fin l) : + enumEquiv hs hsc (Sum.inr b) = sᶜ.orderEmbOfFin hsc b := + Finset.coe_orderIsoOfFin_apply sᶜ hsc b + +/-- **The complementary-minor identity.** If the rows of `C` and the rows of `D` are dual to one +another for the standard bilinear form, then the maximal minor of the first block of `C` on the +columns `s` is the complementary minor of the second block of `D` times the determinant of `C` +read through the enumeration attached to `s`. This is the whole content of duality: the +`s`-dependence of the factor is a sign, by `Matrix.det_permute'`. -/ +private theorem det_submatrix_inl_eq_mul (C D : Matrix (Fin k ⊕ Fin l) ι R) (hCD : C * Dᵀ = 1) + {s : Finset ι} (hs : s.card = k) (hsc : sᶜ.card = l) : + ((C.submatrix Sum.inl id).submatrix id (s.orderEmbOfFin hs)).det + = (C.submatrix id (enumEquiv hs hsc)).det * + ((D.submatrix Sum.inr id).submatrix id (sᶜ.orderEmbOfFin hsc)).det := by + classical + have hdual (r c : Fin k ⊕ Fin l) : + ∑ i, C r i * D c i = (1 : Matrix (Fin k ⊕ Fin l) (Fin k ⊕ Fin l) R) r c := by + rw [← hCD, Matrix.mul_apply] + rfl + set E : Matrix ι (Fin k ⊕ Fin l) R := + Matrix.of fun i r ↦ Sum.elim (fun a ↦ if i = s.orderEmbOfFin hs a then 1 else 0) + (fun b ↦ D (Sum.inr b) i) r with hE + have hCE : C * E = Matrix.fromBlocks + ((C.submatrix Sum.inl id).submatrix id (s.orderEmbOfFin hs)) 0 + ((C.submatrix Sum.inr id).submatrix id (s.orderEmbOfFin hs)) 1 := by + ext r c + rcases c with a | b + · rcases r with a' | b' <;> + simp [Matrix.mul_apply, hE, mul_ite, Finset.sum_ite_eq' Finset.univ] + · rcases r with a' | b' <;> + simp [Matrix.mul_apply, hE, hdual _ (Sum.inr b), Matrix.one_apply] + have hEσ : E.submatrix (enumEquiv hs hsc) id = Matrix.fromBlocks 1 + (Matrix.of fun a' b ↦ D (Sum.inr b) (s.orderEmbOfFin hs a')) 0 + ((D.submatrix Sum.inr id).submatrix id (sᶜ.orderEmbOfFin hsc))ᵀ := by + ext r c + rcases r with a' | b' <;> rcases c with a | b + · simp only [Matrix.submatrix_apply, id_eq, enumEquiv_inl, hE, Matrix.of_apply, Sum.elim_inl, + Matrix.fromBlocks_apply₁₁, Matrix.one_apply] + exact if_congr ⟨fun h ↦ (s.orderEmbOfFin hs).injective h, fun h ↦ by rw [h]⟩ rfl rfl + · simp [hE, enumEquiv_inl] + · have hne : sᶜ.orderEmbOfFin hsc b' ≠ s.orderEmbOfFin hs a := by + intro hcon + have h1 : sᶜ.orderEmbOfFin hsc b' ∈ sᶜ := sᶜ.orderEmbOfFin_mem hsc b' + rw [hcon, Finset.mem_compl] at h1 + exact h1 (s.orderEmbOfFin_mem hs a) + simp [hE, enumEquiv_inr, hne] + · simp [hE, enumEquiv_inr, Matrix.transpose_apply] + have hdet : (C * E).det = ((C.submatrix Sum.inl id).submatrix id (s.orderEmbOfFin hs)).det := by + rw [hCE, Matrix.det_fromBlocks_zero₁₂, Matrix.det_one, mul_one] + rw [← hdet, mul_eq_submatrix_mul_submatrix C E (enumEquiv hs hsc), Matrix.det_mul, hEσ, + Matrix.det_fromBlocks_zero₂₁, Matrix.det_one, one_mul, Matrix.det_transpose] + +end Matrix + +namespace Matrix + +variable {K : Type*} [Field K] {ι : Type*} [Fintype ι] {r : Type*} [Fintype r] [DecidableEq r] + +omit [Fintype ι] in +/-- Two enumerations of the column index differ by a permutation, so the determinants they +produce differ by a sign. -/ +theorem det_submatrix_equiv_eq_or_eq_neg (X : Matrix r ι K) (e₁ e₂ : r ≃ ι) : + (X.submatrix id e₁).det = (X.submatrix id e₂).det ∨ + (X.submatrix id e₁).det = -(X.submatrix id e₂).det := by + have h : X.submatrix id e₁ = (X.submatrix id e₂).submatrix id (e₁.trans e₂.symm) := by + ext i j + simp + rw [h, Matrix.det_permute'] + rcases Int.units_eq_one_or (Equiv.Perm.sign (e₁.trans e₂.symm)) with hs | hs <;> rw [hs] <;> simp + +/-- Dual rows make the enumerated determinant a unit. -/ +theorem isUnit_det_submatrix_of_mul_transpose_eq_one {C D : Matrix r ι K} + (hCD : C * Dᵀ = 1) (e : r ≃ ι) : IsUnit (C.submatrix id e).det := + IsUnit.of_mul_eq_one ((Dᵀ).submatrix e id).det <| by + rw [← Matrix.det_mul, ← mul_eq_submatrix_mul_submatrix, hCD, Matrix.det_one] + +omit [Fintype r] in +/-- Rows dual to a family of rows are linearly independent. -/ +theorem linearIndependent_row_of_mul_transpose_eq_one {C D : Matrix r ι K} + (hCD : C * Dᵀ = 1) : LinearIndependent K C.row := by + refine linearIndependent_iff'.2 fun t g hg i hi ↦ ?_ + have h := congrArg (· ⬝ᵥ D.row i) hg + simp only [sum_dotProduct, smul_dotProduct, zero_dotProduct, smul_eq_mul] at h + rw [Finset.sum_congr rfl fun j _ ↦ by + rw [show C.row j ⬝ᵥ D.row i = (C * Dᵀ) j i from rfl, hCD]] at h + simpa [Matrix.one_apply, Finset.sum_ite_eq' t, hi] using h + +/-- The two-sided form of duality of rows: the dual rows expand every vector, with the pairings +against the original rows as coefficients. -/ +private lemma eq_sum_dotProduct_smul_row {C D : Matrix r ι K} (hCD : C * Dᵀ = 1) + (hcard : Fintype.card r = Fintype.card ι) (x : ι → K) : + x = ∑ j, (C.row j ⬝ᵥ x) • D.row j := by + classical + obtain ⟨e⟩ := Fintype.truncEquivOfCardEq hcard + have hsq : (Dᵀ.submatrix e id) * (C.submatrix id e) = 1 := by + refine mul_eq_one_comm.1 ?_ + rw [← mul_eq_submatrix_mul_submatrix, hCD] + have hDC : Dᵀ * C = 1 := by + have hsub : (Dᵀ * C).submatrix e e = (Dᵀ.submatrix e id) * (C.submatrix id e) := by + ext a b + simp [Matrix.mul_apply] + have h : (Dᵀ * C).submatrix e e = (1 : Matrix ι ι K).submatrix e e := by + rw [hsub, hsq, Matrix.submatrix_one_equiv] + have h2 := congrArg (fun M ↦ M.submatrix e.symm e.symm) h + simpa [Matrix.submatrix_submatrix] using h2 + funext i + have hx : x i = ∑ a, (Dᵀ * C) i a * x a := by + rw [hDC] + simp [Matrix.one_apply, Finset.sum_ite_eq] + rw [hx, Finset.sum_apply] + simp only [Matrix.mul_apply, Matrix.transpose_apply, Pi.smul_apply, smul_eq_mul, dotProduct, + Matrix.row, Finset.sum_mul] + rw [Finset.sum_comm] + exact Finset.sum_congr rfl fun j _ ↦ Finset.sum_congr rfl fun a _ ↦ by ring + +/-- **The complementary-minor identity, for any enumeration.** If the rows of `C` and of `D` are +dual for the standard bilinear form, the Plücker coordinate at `s` of the first block of `C` is, +up to a sign, the determinant of `C` read through `e` times the Plücker coordinate at the +complement of `s` of the second block of `D`. The factor does not depend on `s`; this is the +local form of duality the twisted duality theorem runs on. -/ +theorem plucker_inl_eq_or_eq_neg [LinearOrder ι] {k l : ℕ} (C D : Matrix (Fin k ⊕ Fin l) ι K) + (hCD : C * Dᵀ = 1) (hlk : l + k = Fintype.card ι) (e : Fin k ⊕ Fin l ≃ ι) + (s : Set.powersetCard ι k) : + exteriorPower.plucker k (C.submatrix Sum.inl id).row s = + (C.submatrix id e).det * + exteriorPower.plucker l (D.submatrix Sum.inr id).row (Set.powersetCard.compl hlk s) ∨ + exteriorPower.plucker k (C.submatrix Sum.inl id).row s = + -((C.submatrix id e).det * + exteriorPower.plucker l (D.submatrix Sum.inr id).row (Set.powersetCard.compl hlk s)) := by + have hs : (s : Finset ι).card = k := Set.powersetCard.card_eq s + have hsc : ((s : Finset ι))ᶜ.card = l := by + rw [Finset.card_compl, hs] + omega + rw [Matrix.plucker_row_eq_det_submatrix, Matrix.plucker_row_eq_det_submatrix, + Matrix.det_submatrix_inl_eq_mul C D hCD hs hsc] + rcases det_submatrix_equiv_eq_or_eq_neg C (Matrix.enumEquiv hs hsc) e with hsign | hsign + · refine Or.inl ?_ + rw [hsign] + rfl + · refine Or.inr ?_ + rw [hsign, neg_mul] + rfl + +end Matrix + +namespace Submodule + +open Matrix + +variable {K : Type*} [Field K] {ι : Type*} [Fintype ι] {k l : ℕ} + +/-- **The transported annihilator is the orthogonal complement.** `Module.piEquiv ι K K` sends +`x` to the functional `v ↦ v ⬝ᵥ x`, so a vector lies in the annihilator of `V` read back in +`ι → K` exactly when it is orthogonal to `V` for the standard bilinear form. -/ +theorem mem_comap_piEquiv_dualAnnihilator {V : Submodule K (ι → K)} {x : ι → K} : + x ∈ V.dualAnnihilator.comap (Module.piEquiv ι K K).toLinearMap ↔ ∀ v ∈ V, v ⬝ᵥ x = 0 := by + simp only [Submodule.mem_comap, Submodule.mem_dualAnnihilator] + refine forall₂_congr fun v _ ↦ ?_ + rw [show ((Module.piEquiv ι K K).toLinearMap x) v = Module.piEquiv ι K K x v from rfl, + Module.piEquiv_apply_apply] + simp [dotProduct] + +omit [Fintype ι] in +/-- The two spellings of the transported annihilator: the `comap` along the identification of +`ι → K` with its dual, and the `map` along the inverse. This is Mathlib's +`Submodule.comap_equiv_eq_map_symm` in the form the roadmap pins. -/ +theorem comap_piEquiv_dualAnnihilator_eq_map [Finite ι] (V : Submodule K (ι → K)) : + V.dualAnnihilator.comap (Module.piEquiv ι K K).toLinearMap + = V.dualAnnihilator.map (Module.piEquiv ι K K).symm.toLinearMap := by + ext x + rw [Submodule.mem_comap, Submodule.mem_map] + refine ⟨fun h ↦ ⟨Module.piEquiv ι K K x, h, (Module.piEquiv ι K K).symm_apply_apply x⟩, ?_⟩ + rintro ⟨y, hy, rfl⟩ + rwa [show (Module.piEquiv ι K K).toLinearMap ((Module.piEquiv ι K K).symm.toLinearMap y) = y from + (Module.piEquiv ι K K).apply_symm_apply y] + +/-- The span of a family is orthogonal to `x` as soon as every member is. -/ +private lemma span_le_ker_piEquiv {s : Set (ι → K)} {x : ι → K} (h : ∀ v ∈ s, v ⬝ᵥ x = 0) : + span K s ≤ LinearMap.ker (Module.piEquiv ι K K x) := by + rw [Submodule.span_le] + intro v hv + simpa only [SetLike.mem_coe, LinearMap.mem_ker, Module.piEquiv_apply_apply, smul_eq_mul, + dotProduct] using h v hv + +/-- **Every subspace sits in a dual pair of matrices.** The rows of `C` are a basis of `ι → K` +whose first block is a basis of `V`; the rows of `D` are the dual basis, read back in `ι → K` +through the standard bilinear form. -/ +theorem exists_dual_matrices (V : Submodule K (ι → K)) (hk : finrank K V = k) + (hlk : l + k = Fintype.card ι) : + ∃ C D : Matrix (Fin k ⊕ Fin l) ι K, C * Dᵀ = 1 ∧ + span K (Set.range (C.submatrix Sum.inl id).row) = V := by + classical + obtain ⟨W, hW⟩ := V.exists_isCompl + have hWrank : finrank K W = l := by + have h := Submodule.finrank_sup_add_finrank_inf_eq V W + rw [hW.sup_eq_top, hW.inf_eq_bot, finrank_top, finrank_bot, hk, Module.finrank_pi] at h + omega + let bV := Module.finBasisOfFinrankEq K V hk + let bW := Module.finBasisOfFinrankEq K W hWrank + let c : Module.Basis (Fin k ⊕ Fin l) K (ι → K) := + (bV.prod bW).map (V.prodEquivOfIsCompl W hW) + have hcl (a : Fin k) : c (Sum.inl a) = (bV a : ι → K) := by + simp only [c, Module.Basis.map_apply] + rw [show (bV.prod bW) (Sum.inl a) = ((bV a : V), (0 : W)) from + Prod.ext (bV.prod_apply_inl_fst bW a) (bV.prod_apply_inl_snd bW a)] + simp [V.coe_prodEquivOfIsCompl' W hW ((bV a : V), (0 : W))] + refine ⟨Matrix.of fun r ↦ (c r), Matrix.of fun s i ↦ c.coord s (Pi.basisFun K ι i), ?_, ?_⟩ + · ext r s + rw [Matrix.mul_apply, Matrix.one_apply] + have : ∑ i, c r i * c.coord s (Pi.basisFun K ι i) = c.coord s (c r) := by + conv_rhs => rw [← (Pi.basisFun K ι).sum_repr (c r)] + rw [map_sum] + exact Finset.sum_congr rfl fun i _ ↦ by + rw [map_smul, smul_eq_mul, Pi.basisFun_repr] + simp only [Matrix.transpose_apply, Matrix.of_apply] + rw [this, Module.Basis.coord_apply, Module.Basis.repr_self_apply] + · have hrange : Set.range ((Matrix.of fun r ↦ (c r) : Matrix (Fin k ⊕ Fin l) ι K).submatrix + Sum.inl id).row = V.subtype '' (Set.range bV) := by + rw [← Set.range_comp] + exact congrArg Set.range (funext fun a ↦ hcl a) + rw [hrange, Submodule.span_image, bV.span_eq, Submodule.map_top, + Submodule.range_subtype] + +variable [LinearOrder ι] + +omit [LinearOrder ι] in +/-- **The second block of the dual basis spans the orthogonal complement.** -/ +theorem span_range_inr_row_eq {V : Submodule K (ι → K)} {C D : Matrix (Fin k ⊕ Fin l) ι K} + (hCD : C * Dᵀ = 1) (hlk : l + k = Fintype.card ι) + (hV : span K (Set.range (C.submatrix Sum.inl id).row) = V) : + span K (Set.range (D.submatrix Sum.inr id).row) + = V.dualAnnihilator.comap (Module.piEquiv ι K K).toLinearMap := by + have hdual (r t : Fin k ⊕ Fin l) : + C.row r ⬝ᵥ D.row t = (1 : Matrix (Fin k ⊕ Fin l) (Fin k ⊕ Fin l) K) r t := by + rw [← hCD] + rfl + have hcard : Fintype.card (Fin k ⊕ Fin l) = Fintype.card ι := by + simp only [Fintype.card_sum, Fintype.card_fin] + omega + refine le_antisymm (Submodule.span_le.2 ?_) fun x hx ↦ ?_ + · rintro _ ⟨b, rfl⟩ + rw [SetLike.mem_coe, mem_comap_piEquiv_dualAnnihilator] + intro v hv + rw [← hV] at hv + have hker := span_le_ker_piEquiv (s := Set.range (C.submatrix Sum.inl id).row) + (x := (D.submatrix Sum.inr id).row b) (fun y hy ↦ ?_) hv + · simpa only [LinearMap.mem_ker, Module.piEquiv_apply_apply, smul_eq_mul, dotProduct] + using hker + · obtain ⟨a, rfl⟩ := hy + rw [show (C.submatrix Sum.inl id).row a ⬝ᵥ (D.submatrix Sum.inr id).row b + = C.row (Sum.inl a) ⬝ᵥ D.row (Sum.inr b) from rfl, hdual] + simp + · rw [mem_comap_piEquiv_dualAnnihilator] at hx + have hzero (a : Fin k) : C.row (Sum.inl a) ⬝ᵥ x = 0 := + hx _ (hV ▸ Submodule.subset_span ⟨a, rfl⟩) + have hmem : (∑ j, (C.row j ⬝ᵥ x) • D.row j) + ∈ span K (Set.range (D.submatrix Sum.inr id).row) := by + rw [Fintype.sum_sum_type] + simp only [hzero, zero_smul, Finset.sum_const_zero, zero_add] + exact Submodule.sum_mem _ fun b _ ↦ + Submodule.smul_mem _ _ (Submodule.subset_span ⟨b, rfl⟩) + rwa [← Matrix.eq_sum_dotProduct_smul_row hCD hcard x] at hmem + +/-- **Plücker duality in coordinates** (W. M. Schmidt 1967, §1, equations (2) and (4)). A +subspace and its annihilator have bases whose Plücker coordinates agree at complementary indices, +up to a sign at each index and one common nonzero factor — the involution Schmidt calls `τ`. +Every height in this development is invariant under exactly that much. -/ +theorem exists_plucker_eq_plucker_compl (V : Submodule K (ι → K)) (hk : finrank K V = k) + (hlk : l + k = Fintype.card ι) : + ∃ (v : Fin k → (ι → K)) (w : Fin l → (ι → K)) (c : K), c ≠ 0 ∧ + LinearIndependent K v ∧ LinearIndependent K w ∧ + span K (Set.range v) = V ∧ + span K (Set.range w) = V.dualAnnihilator.comap (Module.piEquiv ι K K).toLinearMap ∧ + ∀ s : Set.powersetCard ι k, + exteriorPower.plucker k v s + = c * exteriorPower.plucker l w (Set.powersetCard.compl hlk s) ∨ + exteriorPower.plucker k v s + = -(c * exteriorPower.plucker l w (Set.powersetCard.compl hlk s)) := by + classical + obtain ⟨C, D, hCD, hV⟩ := exists_dual_matrices V hk hlk + have hcard : Fintype.card (Fin k ⊕ Fin l) = Fintype.card ι := by + simp only [Fintype.card_sum, Fintype.card_fin] + omega + obtain ⟨e₀⟩ := Fintype.truncEquivOfCardEq hcard + have hDC : D * Cᵀ = 1 := by + have h := congrArg Matrix.transpose hCD + rwa [Matrix.transpose_mul, Matrix.transpose_transpose, Matrix.transpose_one] at h + refine ⟨(C.submatrix Sum.inl id).row, (D.submatrix Sum.inr id).row, (C.submatrix id e₀).det, + (Matrix.isUnit_det_submatrix_of_mul_transpose_eq_one hCD e₀).ne_zero, + (Matrix.linearIndependent_row_of_mul_transpose_eq_one hCD).comp _ Sum.inl_injective, + (Matrix.linearIndependent_row_of_mul_transpose_eq_one hDC).comp _ Sum.inr_injective, + hV, span_range_inr_row_eq hCD hlk hV, fun s ↦ ?_⟩ + have hs : (s : Finset ι).card = k := Set.powersetCard.card_eq s + have hsc : ((s : Finset ι))ᶜ.card = l := by + rw [Finset.card_compl, hs] + omega + rw [Matrix.plucker_row_eq_det_submatrix, Matrix.plucker_row_eq_det_submatrix, + Matrix.det_submatrix_inl_eq_mul C D hCD hs hsc] + rcases Matrix.det_submatrix_equiv_eq_or_eq_neg C (Matrix.enumEquiv hs hsc) e₀ with hsign | hsign + · refine Or.inl ?_ + rw [hsign] + rfl + · refine Or.inr ?_ + rw [hsign, neg_mul] + rfl + +end Submodule + +namespace Submodule + +open Matrix exteriorPower + +variable {K : Type*} [Field K] {ι : Type*} [Fintype ι] [LinearOrder ι] + +/-- The two tuples of Plücker coordinates attached to `V` and to its annihilator, with the +relation between them. This is `Submodule.exists_plucker_eq_plucker_compl` with the two ranks +existentially bound, the form the height statements consume. -/ +private lemma exists_plucker_pair (V : Submodule K (ι → K)) : + ∃ (k l : ℕ) (hlk : l + k = Fintype.card ι) (v : Fin k → (ι → K)) (w : Fin l → (ι → K)) + (c : K), c ≠ 0 ∧ LinearIndependent K v ∧ LinearIndependent K w ∧ + span K (Set.range v) = V ∧ + span K (Set.range w) = V.dualAnnihilator.comap (Module.piEquiv ι K K).toLinearMap ∧ + ∀ s : Set.powersetCard ι k, + plucker k v s = c * plucker l w (Set.powersetCard.compl hlk s) ∨ + plucker k v s = -(c * plucker l w (Set.powersetCard.compl hlk s)) := by + have hle : Module.finrank K V ≤ Fintype.card ι := by + have h := V.finrank_le (R := K) + rwa [Module.finrank_pi] at h + obtain ⟨v, w, c, hc, hv, hw, hVv, hWw, hpl⟩ := + exists_plucker_eq_plucker_compl V (l := Fintype.card ι - Module.finrank K V) rfl (by omega) + exact ⟨_, _, _, v, w, c, hc, hv, hw, hVv, hWw, hpl⟩ + +section Relative + +variable [Height.AdmissibleAbsValues K] + +/-- **Sup-norm duality:** a subspace and its annihilator have the same height, with the annihilator +read back in `ι → K` along the standard basis. This is the sup-norm counterpart of the Arakelov +version in Bombieri–Gubler Proposition 2.8.10. By `Submodule.mem_comap_piEquiv_dualAnnihilator` +the subspace on the left is the orthogonal complement of `V` for the standard bilinear form. -/ +theorem mulHeight_comap_piEquiv_dualAnnihilator (V : Submodule K (ι → K)) : + (V.dualAnnihilator.comap (Module.piEquiv ι K K).toLinearMap).mulHeight = V.mulHeight := by + obtain ⟨k, l, hlk, v, w, c, hc, hv, hw, hVv, hWw, hpl⟩ := exists_plucker_pair V + rw [← hWw, ← hVv, mulHeight_span_range hv, mulHeight_span_range hw, + Height.mulHeight_eq_of_forall_eq_or_eq_neg hpl, + show (fun s ↦ c * plucker l w (Set.powersetCard.compl hlk s)) + = c • (plucker l w ∘ (Set.powersetCard.compl hlk)) from rfl, + Height.mulHeight_smul_eq_mulHeight _ hc, Height.mulHeight_comp_equiv] + +/-- The logarithmic form of `Submodule.mulHeight_comap_piEquiv_dualAnnihilator`. -/ +theorem logHeight_comap_piEquiv_dualAnnihilator (V : Submodule K (ι → K)) : + (V.dualAnnihilator.comap (Module.piEquiv ι K K).toLinearMap).logHeight = V.logHeight := by + rw [logHeight_eq_log_mulHeight, logHeight_eq_log_mulHeight, + mulHeight_comap_piEquiv_dualAnnihilator] + +end Relative + +section Arakelov + +variable [NumberField K] + +/-- **The relative multiplicative form of Arakelov duality** (Bombieri–Gubler Proposition 2.8.10). +Schmidt's mechanism gives both normalizations at once: the two coordinate tuples differ by a +signed reindexing and a scalar, and no height in Layer 0 sees either. -/ +theorem arakelovMulHeight_comap_piEquiv_dualAnnihilator (V : Submodule K (ι → K)) : + (V.dualAnnihilator.comap (Module.piEquiv ι K K).toLinearMap).arakelovMulHeight = + V.arakelovMulHeight := by + obtain ⟨k, l, hlk, v, w, c, hc, hv, hw, hVv, hWw, hpl⟩ := exists_plucker_pair V + rw [← hWw, ← hVv, arakelovMulHeight_span_range hv, arakelovMulHeight_span_range hw, + NumberField.arakelovMulHeight_eq_of_forall_eq_or_eq_neg hpl, + show (fun s ↦ c * plucker l w (Set.powersetCard.compl hlk s)) + = c • (plucker l w ∘ (Set.powersetCard.compl hlk)) from rfl, + NumberField.arakelovMulHeight_smul_eq _ hc, NumberField.arakelovMulHeight_comp_equiv] + +/-- The logarithmic form of `Submodule.arakelovMulHeight_comap_piEquiv_dualAnnihilator`. -/ +theorem arakelovLogHeight_comap_piEquiv_dualAnnihilator (V : Submodule K (ι → K)) : + (V.dualAnnihilator.comap (Module.piEquiv ι K K).toLinearMap).arakelovLogHeight = + V.arakelovLogHeight := by + rw [arakelovLogHeight_eq_log_arakelovMulHeight, arakelovLogHeight_eq_log_arakelovMulHeight, + arakelovMulHeight_comap_piEquiv_dualAnnihilator] + +end Arakelov + +section Absolute + +variable [CharZero K] [Algebra.IsAlgebraic ℚ K] + +/-- **The duality theorem for the absolute height.** -/ +theorem absMulHeight_comap_piEquiv_dualAnnihilator (V : Submodule K (ι → K)) : + (V.dualAnnihilator.comap (Module.piEquiv ι K K).toLinearMap).absMulHeight = + V.absMulHeight := by + obtain ⟨k, l, hlk, v, w, c, hc, hv, hw, hVv, hWw, hpl⟩ := exists_plucker_pair V + rw [← hWw, ← hVv, absMulHeight_span_range hv, absMulHeight_span_range hw, + NumberField.absMulHeight_eq_of_forall_eq_or_eq_neg hpl, + show (fun s ↦ c * plucker l w (Set.powersetCard.compl hlk s)) + = c • (plucker l w ∘ (Set.powersetCard.compl hlk)) from rfl, + NumberField.absMulHeight_smul_eq _ hc (Algebra.IsAlgebraic.isAlgebraic c), + NumberField.absMulHeight_comp_equiv] + +/-- The logarithmic form of `Submodule.absMulHeight_comap_piEquiv_dualAnnihilator`. -/ +theorem absLogHeight_comap_piEquiv_dualAnnihilator (V : Submodule K (ι → K)) : + (V.dualAnnihilator.comap (Module.piEquiv ι K K).toLinearMap).absLogHeight = + V.absLogHeight := by + rw [absLogHeight_eq_log_absMulHeight, absLogHeight_eq_log_absMulHeight, + absMulHeight_comap_piEquiv_dualAnnihilator] + +end Absolute + +end Submodule + +namespace Submodule + +variable {K : Type*} [Field K] {ι : Type*} [Fintype ι] + +/-- **The orthogonal-complement form of the hypothesis.** A subspace cut out by orthogonality to +`V` for the standard bilinear form *is* the transported annihilator, so each duality theorem above +applies to it. `Submodule.comap_piEquiv_dualAnnihilator_eq_map` turns the `comap` into a `map` +along the inverse, which is the other spelling in use. -/ +theorem eq_comap_piEquiv_dualAnnihilator_of_forall_dotProduct_eq_zero + {V W : Submodule K (ι → K)} (h : ∀ x, x ∈ W ↔ ∀ v ∈ V, v ⬝ᵥ x = 0) : + W = V.dualAnnihilator.comap (Module.piEquiv ι K K).toLinearMap := + SetLike.ext fun x ↦ (h x).trans mem_comap_piEquiv_dualAnnihilator.symm + +end Submodule + +namespace Matrix + +variable {K : Type*} [Field K] {ι : Type*} [Fintype ι] [LinearOrder ι] {m : Type*} + +omit [LinearOrder ι] in +/-- **The solution space of `A x = 0` is the orthogonal complement of the row space of `A`.** -/ +theorem ker_mulVecLin_eq_comap_piEquiv_dualAnnihilator (A : Matrix m ι K) : + LinearMap.ker A.mulVecLin = + (Submodule.span K (Set.range A.row)).dualAnnihilator.comap + (Module.piEquiv ι K K).toLinearMap := by + refine Submodule.eq_comap_piEquiv_dualAnnihilator_of_forall_dotProduct_eq_zero fun x ↦ ?_ + simp only [LinearMap.mem_ker, Matrix.mulVecLin_apply] + refine ⟨fun h v hv ↦ ?_, fun h ↦ funext fun i ↦ h _ (Submodule.subset_span ⟨i, rfl⟩)⟩ + have hker := Submodule.span_le_ker_piEquiv (s := Set.range A.row) (x := x) + (fun y hy ↦ by obtain ⟨i, rfl⟩ := hy; exact congrFun h i) hv + simpa only [LinearMap.mem_ker, Module.piEquiv_apply_apply, smul_eq_mul, dotProduct] using hker + +section Relative + +variable [Height.AdmissibleAbsValues K] + +/-- **The sup-norm heights of the solution space of `A x = 0` and the row space of `A` agree.** +This is the sup-norm counterpart of the Arakelov duality in Bombieri–Gubler Corollary 2.8.12. -/ +theorem mulHeight_ker_mulVecLin (A : Matrix m ι K) : + (LinearMap.ker A.mulVecLin).mulHeight = (Submodule.span K (Set.range A.row)).mulHeight := by + rw [ker_mulVecLin_eq_comap_piEquiv_dualAnnihilator, + Submodule.mulHeight_comap_piEquiv_dualAnnihilator] + +/-- The logarithmic form of `Matrix.mulHeight_ker_mulVecLin`. -/ +theorem logHeight_ker_mulVecLin (A : Matrix m ι K) : + (LinearMap.ker A.mulVecLin).logHeight = (Submodule.span K (Set.range A.row)).logHeight := by + rw [ker_mulVecLin_eq_comap_piEquiv_dualAnnihilator, + Submodule.logHeight_comap_piEquiv_dualAnnihilator] + +end Relative + +section Arakelov + +variable [NumberField K] + +/-- **The relative Arakelov heights of the solution space and row space agree.** +This is the relative multiplicative form of Bombieri–Gubler Corollary 2.8.12. In the full-row-rank +number-field setting of Bombieri–Vaaler, the right-hand side is `H(A) ^ [K : ℚ]`. -/ +theorem arakelovMulHeight_ker_mulVecLin (A : Matrix m ι K) : + (LinearMap.ker A.mulVecLin).arakelovMulHeight = + (Submodule.span K (Set.range A.row)).arakelovMulHeight := by + rw [ker_mulVecLin_eq_comap_piEquiv_dualAnnihilator, + Submodule.arakelovMulHeight_comap_piEquiv_dualAnnihilator] + +/-- The logarithmic form of `Matrix.arakelovMulHeight_ker_mulVecLin`. -/ +theorem arakelovLogHeight_ker_mulVecLin (A : Matrix m ι K) : + (LinearMap.ker A.mulVecLin).arakelovLogHeight = + (Submodule.span K (Set.range A.row)).arakelovLogHeight := by + rw [ker_mulVecLin_eq_comap_piEquiv_dualAnnihilator, + Submodule.arakelovLogHeight_comap_piEquiv_dualAnnihilator] + +end Arakelov + +section Absolute + +variable [CharZero K] [Algebra.IsAlgebraic ℚ K] + +/-- **The absolute height of a subspace is the absolute height of any matrix cutting it out.** -/ +theorem absMulHeight_ker_mulVecLin (A : Matrix m ι K) : + (LinearMap.ker A.mulVecLin).absMulHeight = + (Submodule.span K (Set.range A.row)).absMulHeight := by + rw [ker_mulVecLin_eq_comap_piEquiv_dualAnnihilator, + Submodule.absMulHeight_comap_piEquiv_dualAnnihilator] + +/-- The logarithmic form of `Matrix.absMulHeight_ker_mulVecLin`. -/ +theorem absLogHeight_ker_mulVecLin (A : Matrix m ι K) : + (LinearMap.ker A.mulVecLin).absLogHeight = + (Submodule.span K (Set.range A.row)).absLogHeight := by + rw [ker_mulVecLin_eq_comap_piEquiv_dualAnnihilator, + Submodule.absLogHeight_comap_piEquiv_dualAnnihilator] + +end Absolute + +end Matrix + +/-! +### Worked examples +-/ + +section Examples + +open Matrix Submodule + +/-- The height of the point `(1 : 3)` of the rational projective line. -/ +private lemma mulHeight_one_three : Height.mulHeight ![(1 : ℚ), 3] = 3 := by + have hx : (![(1 : ℚ), 3]) = ((↑) : ℤ → ℚ) ∘ ![(1 : ℤ), 3] := by + funext i + fin_cases i <;> simp + have hs : (⨆ i, |(![(1 : ℤ), 3]) i|) = 3 := by + refine le_antisymm (ciSup_le fun i ↦ ?_) + (le_ciSup_of_le (Finite.bddAbove_range _) 1 (by norm_num)) + fin_cases i <;> norm_num + rw [hx, Rat.mulHeight_eq_max_abs_of_gcd_eq_one (by decide), hs] + norm_num + +/-- The height of the point `(0 : 1)`. -/ +private lemma mulHeight_zero_one : Height.mulHeight ![(0 : ℚ), 1] = 1 := by + have hx : (![(0 : ℚ), 1]) = ((↑) : ℤ → ℚ) ∘ ![(0 : ℤ), 1] := by + funext i + fin_cases i <;> simp + have hs : (⨆ i, |(![(0 : ℤ), 1]) i|) = 1 := by + refine le_antisymm (ciSup_le fun i ↦ ?_) + (le_ciSup_of_le (Finite.bddAbove_range _) 1 (by norm_num)) + fin_cases i <;> norm_num + rw [hx, Rat.mulHeight_eq_max_abs_of_gcd_eq_one (by decide), hs] + norm_num + +/-- **Acceptance test: the height of a solution space, computed from the matrix.** The line +`x₀ + 3 x₁ = 0` in `ℚ²` has height `3`, the height of the single row `(1, 3)` that cuts it out — +this illustrates the sup-norm counterpart of Bombieri–Gubler Corollary 2.8.12. The Arakelov +height in that source uses the Euclidean norm instead. +A duality theorem that returned the height of the entries of the *solution*, or that returned `1`, +is refuted here. -/ +example : + (LinearMap.ker (Matrix.of ![![(1 : ℚ), 3]] : Matrix (Fin 1) (Fin 2) ℚ).mulVecLin).mulHeight + = 3 := by + rw [Matrix.mulHeight_ker_mulVecLin] + have hrange : Set.range (Matrix.of ![![(1 : ℚ), 3]] : Matrix (Fin 1) (Fin 2) ℚ).row + = {![(1 : ℚ), 3]} := Set.range_unique + have hne : ![(1 : ℚ), 3] ≠ 0 := fun h ↦ by simpa using congrFun h 0 + rw [hrange, Submodule.mulHeight_span_singleton hne, mulHeight_one_three] + +/-- **Rejection test: the annihilator, not an arbitrary complement.** The line spanned by +`(0, 1)` is a complement of the line spanned by `(1, 3)` in `ℚ²`, and the two have different +heights — `1` against `3`. So the duality theorem is a statement about the annihilator and not +about complements, and the proof above may not replace the annihilator by the complement it picks +up along the way when it extends a basis. -/ +example : (span ℚ {![(1 : ℚ), 3]}) ⊔ (span ℚ {![(0 : ℚ), 1]}) = ⊤ ∧ + (span ℚ {![(0 : ℚ), 1]}).mulHeight ≠ (span ℚ {![(1 : ℚ), 3]}).mulHeight := by + constructor + · refine Submodule.eq_top_iff'.2 fun x ↦ ?_ + have hx : x = x 0 • ![(1 : ℚ), 3] + (x 1 - 3 * x 0) • ![(0 : ℚ), 1] := by + funext i + fin_cases i + · simp + · simp + ring + rw [hx] + exact Submodule.add_mem _ + (Submodule.mem_sup_left (Submodule.smul_mem _ _ (Submodule.mem_span_singleton_self _))) + (Submodule.mem_sup_right (Submodule.smul_mem _ _ (Submodule.mem_span_singleton_self _))) + · have h0 : ![(0 : ℚ), 1] ≠ 0 := fun h ↦ by simpa using congrFun h 1 + have h1 : ![(1 : ℚ), 3] ≠ 0 := fun h ↦ by simpa using congrFun h 0 + rw [Submodule.mulHeight_span_singleton h0, Submodule.mulHeight_span_singleton h1, + mulHeight_zero_one, mulHeight_one_three] + norm_num + +/-- **Acceptance test in the Arakelov normalization, the roadmap's worked case.** The plane +`x₀ + x₁ + x₂ = 0` in `ℚ³` has Arakelov height `√3`, the Arakelov height of the row `(1, 1, 1)` +that cuts it out — and Cauchy–Binet reads the same number off the Gram determinant +`det (A Aᵀ) = 3`. A duality theorem that returned `1` here has confused the Arakelov height with +the sup-norm height, which is `1` on this tuple by the product formula. -/ +example : + (LinearMap.ker + (Matrix.of ![![(1 : ℚ), 1, 1]] : Matrix (Fin 1) (Fin 3) ℚ).mulVecLin).arakelovMulHeight + = Real.sqrt 3 := by + have hrange : Set.range (Matrix.of ![![(1 : ℚ), 1, 1]] : Matrix (Fin 1) (Fin 3) ℚ).row + = {![(1 : ℚ), 1, 1]} := Set.range_unique + have hne : ![(1 : ℚ), 1, 1] ≠ 0 := fun h ↦ by simpa using congrFun h 0 + rw [Matrix.arakelovMulHeight_ker_mulVecLin, hrange, + Submodule.arakelovMulHeight_span_singleton hne, + show ![(1 : ℚ), 1, 1] = 1 from by funext i; fin_cases i <;> rfl, + NumberField.arakelovMulHeight_one, Fintype.card_fin, NumberField.totalWeight_eq_finrank, + Module.finrank_self, Nat.cast_one, Real.sqrt_eq_rpow] + norm_num + +/-- **Rejection test: a subspace can meet its own orthogonal complement.** Over `ℂ` the line +spanned by `(1, i)` is isotropic — `1 · 1 + i · i = 0` — so it lies inside the orthogonal +complement it is supposed to be dual to, and the square matrix obtained by stacking a basis of the +line on a basis of the complement is singular. Every proof of duality that expands the determinant +of that stack therefore fails over a field that is not formally real; the proof above runs through +an arbitrary complement and the dual basis instead, and never forms the stack. -/ +example : ![(1 : ℂ), Complex.I] ∈ + (Submodule.span ℂ {![(1 : ℂ), Complex.I]}).dualAnnihilator.comap + (Module.piEquiv (Fin 2) ℂ ℂ).toLinearMap := by + rw [Submodule.mem_comap_piEquiv_dualAnnihilator] + intro v hv + obtain ⟨c, rfl⟩ := Submodule.mem_span_singleton.1 hv + simp only [dotProduct, Fin.sum_univ_two, Pi.smul_apply, smul_eq_mul, Matrix.cons_val_zero, + Matrix.cons_val_one, Matrix.cons_val_fin_one, smul_eq_mul, mul_one] + rw [mul_assoc, Complex.I_mul_I] + ring + +/-- **Conformance.** The milestone's two statements: a subspace and its annihilator have the same +height, and the height of a subspace is the height of any matrix cutting it out. -/ +example {K : Type*} [Field K] [Height.AdmissibleAbsValues K] {ι : Type*} [Fintype ι] + [LinearOrder ι] (V : Submodule K (ι → K)) {m : Type*} (A : Matrix m ι K) : + (V.dualAnnihilator.comap (Module.piEquiv ι K K).toLinearMap).mulHeight = V.mulHeight ∧ + (LinearMap.ker A.mulVecLin).mulHeight = (span K (Set.range A.row)).mulHeight := + ⟨Submodule.mulHeight_comap_piEquiv_dualAnnihilator V, Matrix.mulHeight_ker_mulVecLin A⟩ + +end Examples diff --git a/LeanPool/CCDN/ArithmeticHeights/Extension.lean b/LeanPool/CCDN/ArithmeticHeights/Extension.lean new file mode 100644 index 0000000000..ce57894eb3 --- /dev/null +++ b/LeanPool/CCDN/ArithmeticHeights/Extension.lean @@ -0,0 +1,409 @@ +/- +Copyright (c) 2026 Ralf Stephan. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Ralf Stephan +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.ArithmeticHeights.Arakelov +public import Mathlib.RingTheory.Ideal.Norm.RelNorm + +/-! +# Heights under a finite extension of number fields + +Mathlib's `Height.mulHeight` is the height **relative** to the field it is computed over. This +file proves how it changes when that field grows: for a finite extension `L / K` of number +fields and a tuple `x` over `K`, + +`mulHeight x ^ [L : K] = mulHeight (algebraMap K L ∘ x)`. + +Equivalently, `mulHeight x ^ (finrank ℚ K)⁻¹` does not depend on the field the coordinates are +read in — the statement that the *absolute* height is well defined, recorded here for a single +element as `NumberField.absMulHeight₁_eq`. + +## Main results + +* `NumberField.mulHeight_pow_finrank` and `NumberField.mulHeight₁_pow_finrank`: the tuple and + one-variable forms of the displayed identity. +* `NumberField.finrank_nsmul_logHeight` and `NumberField.finrank_nsmul_logHeight₁`: the + logarithmic forms, where the power becomes an `nsmul`. +* `NumberField.absMulHeight₁_eq`: over *any* number field `K` containing it, the absolute height + of `x` is `mulHeight₁ x ^ (finrank ℚ K : ℝ)⁻¹`. This is the classical statement that the + absolute height does not depend on the field of definition, and + `NumberField.absMulHeight₁_pow_finrank` is the same identity with a natural-number exponent. + `NumberField.absLogHeight₁_eq` is its logarithmic form: the absolute logarithmic height is the + relative one divided by the degree. + +## Implementation notes + +The two factors of the height are handled by different mechanisms, and neither is the +places-above-places bookkeeping one might expect. + +The **archimedean** factor is first turned into a product over the complex embeddings — this is +what `InfinitePlace.card_filter_mk_eq` does, the exponent `mult v` being the number of embeddings +inducing `v`. The statement then becomes the count `#{φ : L →+* ℂ | φ.comp (algebraMap K L) = ψ} += [L : K]`, which is `AlgHom.card` once `ℂ` carries the `K`-algebra structure of `ψ`. The +roadmap asks for `InfinitePlace.liesOver_iff_comap_eq` as the first step of this layer; it is not +needed on this route, and Mathlib meanwhile carries that theory itself, in +`Mathlib/NumberTheory/NumberField/InfinitePlace/Ramification.lean`. + +The **nonarchimedean** factor of the height of an *integral* tuple is the inverse of the absolute +norm of the ideal its coordinates generate, by +`NumberField.absNorm_mul_finprod_finitePlace_eq_one`. So that half reduces to +`Ideal.absNorm (I.map (algebraMap (𝓞 K) (𝓞 L))) = Ideal.absNorm I ^ [L : K]`, which is +`Ideal.relNorm_algebraMap` followed by `Ideal.absNorm_relNorm` — no ramification or inertia +appears, since those are already inside Mathlib's relative ideal norm. + +⚠ The nonarchimedean factor alone is **not** invariant under scaling: `∏ᶠ v, ⨆ i, v (c * x i)` +picks up the finite part of the product formula for `c`. The reduction from an arbitrary tuple to +an integral one is therefore performed on the whole height, where +`Height.mulHeight_smul_eq_mulHeight` applies to both sides at once, and not factor by factor. + +## References + +These are the statements of +[mathlib4#41606](https://github.com/leanprover-community/mathlib4/pull/41606) and carry its +names, so that adopting Mathlib's version is a deletion plus an import. + +This is Layer 0.3 of the `ArithmeticHeights` roadmap. +-/ + +public section + +namespace NumberField + +open Finset Function Height InfinitePlace Module + +variable {K L : Type*} [Field K] [Field L] [NumberField K] [NumberField L] [Algebra K L] +variable {ι : Type*} [Finite ι] + +/-! +### The archimedean factor + +A product over the infinite places, weighted by `mult`, is a product over the complex embeddings; +in that form the extension statement is the count of extensions of an embedding. +-/ + +section Archimedean + +/-- A `mult`-weighted product over the infinite places is the product over the complex +embeddings, each place counted once per embedding inducing it. -/ +lemma prod_embeddings_eq {M : Type*} [CommMonoid M] (f : InfinitePlace K → M) : + ∏ φ : K →+* ℂ, f (InfinitePlace.mk φ) = ∏ w : InfinitePlace K, f w ^ w.mult := by + classical + rw [← Finset.prod_fiberwise Finset.univ InfinitePlace.mk (fun φ ↦ f (InfinitePlace.mk φ))] + refine Finset.prod_congr rfl fun w _ ↦ ?_ + rw [Finset.prod_congr rfl (fun φ hφ ↦ by rw [(Finset.mem_filter.mp hφ).2]), + Finset.prod_const, card_filter_mk_eq] + +open scoped Classical in +/-- Every complex embedding of `K` has exactly `[L : K]` extensions to `L`. -/ +private lemma card_filter_comp_eq (ψ : K →+* ℂ) : + #{φ : L →+* ℂ | φ.comp (algebraMap K L) = ψ} = finrank K L := by + let : Algebra K ℂ := ψ.toAlgebra + rw [← AlgHom.card K L ℂ] + refine (Finset.card_nbij AlgHom.toRingHom (fun σ _ ↦ ?_) AlgHom.toRingHom_injective.injOn + (fun φ hφ ↦ ?_)).symm + · simp only [Finset.coe_filter, Set.mem_ofPred_eq, mem_univ, true_and] + ext r + simp [RingHom.algebraMap_toAlgebra] + · simp only [Finset.coe_filter, Set.mem_ofPred_eq, mem_univ, true_and] at hφ + exact ⟨⟨φ, fun r ↦ by simp [RingHom.algebraMap_toAlgebra, ← hφ]⟩, mem_univ _, rfl⟩ + +open scoped Classical in +/-- A product over the complex embeddings of `L` of a factor that reads an embedding only through +its restriction to `K` is the `[L : K]`-th power of the product over the embeddings of `K`. -/ +lemma prod_comp_eq_prod_pow (F : (K →+* ℂ) → ℝ) : + ∏ φ : L →+* ℂ, F (φ.comp (algebraMap K L)) = (∏ ψ : K →+* ℂ, F ψ) ^ finrank K L := by + rw [← Finset.prod_fiberwise Finset.univ (fun φ : L →+* ℂ ↦ φ.comp (algebraMap K L)) + (fun φ ↦ F (φ.comp (algebraMap K L))), ← Finset.prod_pow] + refine Finset.prod_congr rfl fun ψ _ ↦ ?_ + rw [Finset.prod_congr rfl (fun φ hφ ↦ by rw [(Finset.mem_filter.mp hφ).2]), + Finset.prod_const, card_filter_comp_eq] + +/-- **The archimedean half of every extension formula for a height.** A `mult`-weighted product +over the infinite places of `L` of a local factor `g` that reads a place only through its +restriction to `K` is the `[L : K]`-th power of the corresponding product over `K`. The two +normalizations of Layer 0 differ in `g` and in nothing else, so both take this lemma as it +stands. -/ +theorem prod_infinitePlace_pow_mult_eq (f : InfinitePlace K → ℝ) (g : InfinitePlace L → ℝ) + (h : ∀ φ : L →+* ℂ, g (InfinitePlace.mk φ) = f (InfinitePlace.mk (φ.comp (algebraMap K L)))) : + ∏ w : InfinitePlace L, g w ^ w.mult = (∏ v : InfinitePlace K, f v ^ v.mult) ^ finrank K L := by + rw [← prod_embeddings_eq (K := L) g, ← prod_embeddings_eq (K := K) f, + ← prod_comp_eq_prod_pow (fun ψ ↦ f (InfinitePlace.mk ψ))] + exact Finset.prod_congr rfl fun φ _ ↦ h φ + +omit [Finite ι] in +private lemma prod_infinitePlace_algebraMap (x : ι → K) : + ∏ w : InfinitePlace L, (⨆ i, w (algebraMap K L (x i))) ^ w.mult = + (∏ v : InfinitePlace K, (⨆ i, v (x i)) ^ v.mult) ^ finrank K L := + prod_infinitePlace_pow_mult_eq _ _ fun φ ↦ iSup_congr fun i ↦ by simp [InfinitePlace.apply] + +end Archimedean + +/-! +### The nonarchimedean factor + +For an integral tuple this factor is the inverse of the absolute norm of the ideal the +coordinates generate, so the extension statement is one about ideal norms. +-/ + +section NonArchimedean + +private lemma absNorm_map_eq (I : Ideal (𝓞 K)) : + Ideal.absNorm (I.map (algebraMap (𝓞 K) (𝓞 L))) = Ideal.absNorm I ^ finrank K L := by + rw [← Ideal.absNorm_relNorm (𝓞 K), Ideal.relNorm_algebraMap, map_pow, + IsFractionRing.finrank_eq (𝓞 K) K (𝓞 L) L] + +omit [NumberField K] [NumberField L] in +private lemma coe_algebraMap_ringOfIntegers (a : 𝓞 K) : + ((algebraMap (𝓞 K) (𝓞 L) a : 𝓞 L) : L) = algebraMap K L (a : K) := + (IsScalarTower.algebraMap_apply K K L a).symm + +private lemma finprod_finitePlace_algebraMap_int {y : ι → 𝓞 K} (hy : y ≠ 0) : + ∏ᶠ w : FinitePlace L, ⨆ i, w (algebraMap K L (y i : K)) = + (∏ᶠ v : FinitePlace K, ⨆ i, v (y i : K)) ^ finrank K L := by + have hinj : Function.Injective (algebraMap (𝓞 K) (𝓞 L)) := + FaithfulSMul.algebraMap_injective (𝓞 K) (𝓞 L) + have hy' : (fun i ↦ algebraMap (𝓞 K) (𝓞 L) (y i)) ≠ 0 := by + obtain ⟨i, hi⟩ := Function.ne_iff.mp hy + exact Function.ne_iff.mpr ⟨i, fun h ↦ hi (hinj (h.trans (map_zero _).symm))⟩ + have hspan : Ideal.span (Set.range fun i ↦ algebraMap (𝓞 K) (𝓞 L) (y i)) = + (Ideal.span (Set.range y)).map (algebraMap (𝓞 K) (𝓞 L)) := by + rw [Ideal.map_span, ← Set.range_comp] + rfl + have hK := absNorm_mul_finprod_finitePlace_eq_one hy + have hL := absNorm_mul_finprod_finitePlace_eq_one hy' + rw [hspan, absNorm_map_eq] at hL + simp_rw [coe_algebraMap_ringOfIntegers (L := L)] at hL + have hN : ((Ideal.span (Set.range y)).absNorm : ℝ) ≠ 0 := by + have : Ideal.span (Set.range y) ≠ ⊥ := by + obtain ⟨i, hi⟩ := Function.ne_iff.mp hy + exact fun h ↦ hi (by simpa using (Ideal.span_eq_bot.mp h) (y i) ⟨i, rfl⟩) + exact_mod_cast Ideal.absNorm_eq_zero_iff.not.mpr this + refine mul_left_cancel₀ (pow_ne_zero (finrank K L) hN) ?_ + rw [← mul_pow, hK, one_pow] + exact_mod_cast hL + +end NonArchimedean + +/-! +### The extension formula +-/ + +/-- Every tuple over a number field is a nonzero multiple of an integral one. -/ +private lemma exists_integer_tuple (x : ι → K) : + ∃ (d : K) (y : ι → 𝓞 K), d ≠ 0 ∧ ∀ i, (y i : K) = d * x i := by + have := Fintype.ofFinite ι + obtain ⟨b, hb⟩ := IsLocalization.exist_integer_multiples (nonZeroDivisors (𝓞 K)) + (Finset.univ : Finset ι) x + refine ⟨algebraMap (𝓞 K) K (b : 𝓞 K), fun i ↦ (hb i (Finset.mem_univ i)).choose, ?_, fun i ↦ ?_⟩ + · exact (map_ne_zero_iff _ (FaithfulSMul.algebraMap_injective (𝓞 K) K)).mpr + (nonZeroDivisors.coe_ne_zero b) + · have h := (hb i (Finset.mem_univ i)).choose_spec + rw [show (((hb i (Finset.mem_univ i)).choose : 𝓞 K) : K) + = algebraMap (𝓞 K) K (hb i (Finset.mem_univ i)).choose from rfl, h, Algebra.smul_def] + +private lemma mulHeight_pow_finrank_int (y : ι → 𝓞 K) : + Height.mulHeight (fun i ↦ (y i : K)) ^ finrank K L + = Height.mulHeight (fun i ↦ algebraMap K L (y i : K)) := by + rcases eq_or_ne y 0 with rfl | hy + · simp + have hz : (fun i ↦ (y i : K)) ≠ 0 := by + obtain ⟨i, hi⟩ := Function.ne_iff.mp hy + exact Function.ne_iff.mpr ⟨i, by simpa using hi⟩ + have hz' : (fun i ↦ algebraMap K L (y i : K)) ≠ 0 := by + obtain ⟨i, hi⟩ := Function.ne_iff.mp hz + exact Function.ne_iff.mpr ⟨i, fun h ↦ hi (FaithfulSMul.algebraMap_injective K L + (by simpa using h))⟩ + rw [NumberField.mulHeight_eq hz, NumberField.mulHeight_eq hz', mul_pow, + prod_infinitePlace_algebraMap (L := L), finprod_finitePlace_algebraMap_int hy] + +/-- **The height over `L` is the `[L : K]`-th power of the height over `K`.** The relative height +grows with the field the tuple is read in, by exactly the degree. -/ +theorem mulHeight_pow_finrank (x : ι → K) : + Height.mulHeight x ^ finrank K L = Height.mulHeight (algebraMap K L ∘ x) := by + obtain ⟨d, y, hd, hy⟩ := exists_integer_tuple (K := K) x + have hd' : algebraMap K L d ≠ 0 := + (map_ne_zero_iff _ (FaithfulSMul.algebraMap_injective K L)).mpr hd + have h1 : (fun i ↦ (y i : K)) = d • x := funext fun i ↦ by rw [hy i]; rfl + have h2 : (fun i ↦ algebraMap K L (y i : K)) = algebraMap K L d • (algebraMap K L ∘ x) := by + funext i + simp [hy i] + have e1 : Height.mulHeight (fun i ↦ (y i : K)) = Height.mulHeight x := + h1 ▸ Height.mulHeight_smul_eq_mulHeight x hd + have e2 : Height.mulHeight (fun i ↦ algebraMap K L (y i : K)) + = Height.mulHeight (algebraMap K L ∘ x) := + h2 ▸ Height.mulHeight_smul_eq_mulHeight _ hd' + rw [← e1, ← e2] + exact mulHeight_pow_finrank_int y + +/-- The one-variable form of `NumberField.mulHeight_pow_finrank`. -/ +theorem mulHeight₁_pow_finrank (x : K) : + Height.mulHeight₁ x ^ finrank K L = Height.mulHeight₁ (algebraMap K L x) := by + rw [Height.mulHeight₁_eq_mulHeight, Height.mulHeight₁_eq_mulHeight, + mulHeight_pow_finrank (L := L)] + congr 1 + funext i + fin_cases i <;> simp + +/-- The logarithmic form of `NumberField.mulHeight_pow_finrank`. -/ +theorem finrank_nsmul_logHeight (x : ι → K) : + finrank K L • Height.logHeight x = Height.logHeight (algebraMap K L ∘ x) := by + rw [Height.logHeight_eq_log_mulHeight, Height.logHeight_eq_log_mulHeight, + ← mulHeight_pow_finrank (L := L), Real.log_pow, nsmul_eq_mul] + +/-- The logarithmic form of `NumberField.mulHeight₁_pow_finrank`. -/ +theorem finrank_nsmul_logHeight₁ (x : K) : + finrank K L • Height.logHeight₁ x = Height.logHeight₁ (algebraMap K L x) := by + rw [Height.logHeight₁_eq_log_mulHeight₁, Height.logHeight₁_eq_log_mulHeight₁, + ← mulHeight₁_pow_finrank (L := L), Real.log_pow, nsmul_eq_mul] + +open scoped Classical IntermediateField in +/-- **The absolute height does not depend on the field of definition.** Mathlib defines +`absMulHeight₁ x` through `ℚ⟮x⟯`; this computes it in any number field containing `x`. -/ +theorem absMulHeight₁_eq (x : K) : + absMulHeight₁ x = Height.mulHeight₁ x ^ (finrank ℚ K : ℝ)⁻¹ := by + have hx : IsIntegral ℚ x := Algebra.IsIntegral.isIntegral x + have : FiniteDimensional ℚ ℚ⟮x⟯ := IntermediateField.adjoin.finiteDimensional hx + have : NumberField ℚ⟮x⟯ := {} + rw [absMulHeight₁, dite_eq_left_of_eq_true (eq_true hx)] + have key : Height.mulHeight₁ (IntermediateField.AdjoinSimple.gen ℚ x) ^ finrank ℚ⟮x⟯ K + = Height.mulHeight₁ x := mulHeight₁_pow_finrank _ + have hpos : (0 : ℝ) ≤ Height.mulHeight₁ (IntermediateField.AdjoinSimple.gen ℚ x) := + le_trans zero_le_one (Height.one_le_mulHeight₁ _) + rw [← key, ← Real.rpow_natCast _ (finrank ℚ⟮x⟯ K), ← Real.rpow_mul hpos] + congr 1 + rw [← Module.finrank_mul_finrank ℚ ℚ⟮x⟯ K] + have h1 : (Module.finrank ℚ ℚ⟮x⟯ : ℝ) ≠ 0 := by + exact_mod_cast (Module.finrank_pos (R := ℚ) (M := ℚ⟮x⟯)).ne' + have h2 : (Module.finrank ℚ⟮x⟯ K : ℝ) ≠ 0 := by + exact_mod_cast (Module.finrank_pos (R := ℚ⟮x⟯) (M := K)).ne' + push_cast + field_simp + +/-- The relative height is the `[K : ℚ]`-th power of the absolute one: `absMulHeight₁_eq` read +backwards, in the form that carries a natural-number exponent rather than a real one. -/ +theorem absMulHeight₁_pow_finrank (x : K) : + absMulHeight₁ x ^ finrank ℚ K = Height.mulHeight₁ x := by + rw [absMulHeight₁_eq, ← Real.rpow_natCast _ (finrank ℚ K), + ← Real.rpow_mul (Height.mulHeight₁_nonneg x), inv_mul_cancel₀, Real.rpow_one] + exact Nat.cast_ne_zero.mpr (Module.finrank_pos (R := ℚ) (M := K)).ne' + +/-- The logarithmic form of `NumberField.absMulHeight₁_eq`: the absolute logarithmic height is +the relative one divided by the degree. -/ +theorem absLogHeight₁_eq (x : K) : + absLogHeight₁ x = Height.logHeight₁ x / finrank ℚ K := by + rw [absLogHeight₁, absMulHeight₁_eq, Real.log_rpow (Height.mulHeight₁_pos x), + Height.logHeight₁_eq_log_mulHeight₁, inv_mul_eq_div] + +/-! +### The Arakelov normalization + +`NumberField.arakelovMulHeight` differs from `Height.mulHeight` only in the archimedean local +factor, and that factor reads a place in exactly the same way, so the two counts above prove the +extension formula for it too. The nonarchimedean factor is literally the same expression, and the +reduction to an integral tuple is the same reduction. +-/ + +section Arakelov + +variable {κ : Type*} [Fintype κ] + +/-- `√S ^ n = S ^ (n / 2)`. The Arakelov local factor carries the real exponent `mult v / 2` +while `prod_infinitePlace_pow_mult_eq` carries the natural power `mult v`; this moves between +them. -/ +private lemma sqrt_pow_eq_rpow {S : ℝ} (hS : 0 ≤ S) (n : ℕ) : + Real.sqrt S ^ n = S ^ ((n : ℝ) / 2) := by + rw [Real.sqrt_eq_rpow, ← Real.rpow_natCast (S ^ ((1 : ℝ) / 2)) n, ← Real.rpow_mul hS] + congr 1 + ring + +private lemma prod_infinitePlace_arakelov_algebraMap (x : κ → K) : + ∏ w : InfinitePlace L, (∑ i, w (algebraMap K L (x i)) ^ 2) ^ ((w.mult : ℝ) / 2) = + (∏ v : InfinitePlace K, (∑ i, v (x i) ^ 2) ^ ((v.mult : ℝ) / 2)) ^ finrank K L := by + have key := prod_infinitePlace_pow_mult_eq (K := K) (L := L) + (fun v ↦ Real.sqrt (∑ i, v (x i) ^ 2)) + (fun w ↦ Real.sqrt (∑ i, w (algebraMap K L (x i)) ^ 2)) + (fun φ ↦ congrArg Real.sqrt + (Finset.sum_congr rfl fun i _ ↦ by simp [InfinitePlace.apply])) + calc ∏ w : InfinitePlace L, (∑ i, w (algebraMap K L (x i)) ^ 2) ^ ((w.mult : ℝ) / 2) + = ∏ w : InfinitePlace L, Real.sqrt (∑ i, w (algebraMap K L (x i)) ^ 2) ^ w.mult := + Finset.prod_congr rfl fun w _ ↦ (sqrt_pow_eq_rpow (by positivity) w.mult).symm + _ = (∏ v : InfinitePlace K, Real.sqrt (∑ i, v (x i) ^ 2) ^ v.mult) ^ finrank K L := key + _ = _ := by + congr 1 + exact Finset.prod_congr rfl fun v _ ↦ sqrt_pow_eq_rpow (by positivity) v.mult + +private lemma arakelovMulHeight_pow_finrank_int (y : κ → 𝓞 K) : + arakelovMulHeight (fun i ↦ (y i : K)) ^ finrank K L + = arakelovMulHeight (fun i ↦ algebraMap K L (y i : K)) := by + rcases eq_or_ne y 0 with rfl | hy + · simp [show (fun _ : κ ↦ (0 : K)) = 0 from rfl, show (fun _ : κ ↦ (0 : L)) = 0 from rfl] + have hz : (fun i ↦ (y i : K)) ≠ 0 := by + obtain ⟨i, hi⟩ := Function.ne_iff.mp hy + exact Function.ne_iff.mpr ⟨i, by simpa using hi⟩ + have hz' : (fun i ↦ algebraMap K L (y i : K)) ≠ 0 := by + obtain ⟨i, hi⟩ := Function.ne_iff.mp hz + exact Function.ne_iff.mpr ⟨i, fun h ↦ hi (FaithfulSMul.algebraMap_injective K L + (by simpa using h))⟩ + rw [arakelovMulHeight_eq hz, arakelovMulHeight_eq hz', mul_pow, + prod_infinitePlace_arakelov_algebraMap (L := L), finprod_finitePlace_algebraMap_int hy] + +/-- **The Arakelov height over `L` is the `[L : K]`-th power of the Arakelov height over `K`**, +the companion of `NumberField.mulHeight_pow_finrank` in the normalization of Layer 0.1. Only the +archimedean factor has to be redone, and only through the local factor it reads off a place. -/ +theorem arakelovMulHeight_pow_finrank (x : κ → K) : + arakelovMulHeight x ^ finrank K L = arakelovMulHeight (algebraMap K L ∘ x) := by + obtain ⟨d, y, hd, hy⟩ := exists_integer_tuple (K := K) x + have hd' : algebraMap K L d ≠ 0 := + (map_ne_zero_iff _ (FaithfulSMul.algebraMap_injective K L)).mpr hd + have h1 : (fun i ↦ (y i : K)) = d • x := funext fun i ↦ by rw [hy i]; rfl + have h2 : (fun i ↦ algebraMap K L (y i : K)) = algebraMap K L d • (algebraMap K L ∘ x) := by + funext i + simp [hy i] + have e1 : arakelovMulHeight (fun i ↦ (y i : K)) = arakelovMulHeight x := + h1 ▸ arakelovMulHeight_smul_eq x hd + have e2 : arakelovMulHeight (fun i ↦ algebraMap K L (y i : K)) + = arakelovMulHeight (algebraMap K L ∘ x) := + h2 ▸ arakelovMulHeight_smul_eq _ hd' + rw [← e1, ← e2] + exact arakelovMulHeight_pow_finrank_int y + +/-- The logarithmic form of `NumberField.arakelovMulHeight_pow_finrank`. -/ +theorem finrank_nsmul_arakelovLogHeight (x : κ → K) : + finrank K L • arakelovLogHeight x = arakelovLogHeight (algebraMap K L ∘ x) := by + rw [arakelovLogHeight_eq_log_arakelovMulHeight, arakelovLogHeight_eq_log_arakelovMulHeight, + ← arakelovMulHeight_pow_finrank (L := L), Real.log_pow, nsmul_eq_mul] + +end Arakelov + +/-! +### Worked examples + +The special case `K = ℚ` is the acceptance test the roadmap names for the general statement: it +needs no places above places, and it is literally an instance. +-/ + +section Examples + +/-- `H_K(x) = H_ℚ(x) ^ [K : ℚ]` for a rational tuple. -/ +example {K : Type*} [Field K] [NumberField K] {ι : Type*} [Finite ι] (x : ι → ℚ) : + Height.mulHeight x ^ finrank ℚ K = Height.mulHeight (algebraMap ℚ K ∘ x) := + mulHeight_pow_finrank x + +/-- A concrete value: `H_ℚ(3/4) = 4` by `Rat.mulHeight₁_eq_max`, so over a number field of +degree `d` the same number has height `4 ^ d`. -/ +example {K : Type*} [Field K] [NumberField K] : + Height.mulHeight₁ (algebraMap ℚ K (3 / 4)) = 4 ^ finrank ℚ K := by + rw [← mulHeight₁_pow_finrank, Rat.mulHeight₁_eq_max] + norm_num + +end Examples + +end NumberField + +end diff --git a/LeanPool/CCDN/ArithmeticHeights/GaussMeasure.lean b/LeanPool/CCDN/ArithmeticHeights/GaussMeasure.lean new file mode 100644 index 0000000000..ce1a1aba1d --- /dev/null +++ b/LeanPool/CCDN/ArithmeticHeights/GaussMeasure.lean @@ -0,0 +1,212 @@ +/- +Copyright (c) 2026 Ralf Stephan. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Ralf Stephan +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import Mathlib.MeasureTheory.Constructions.HaarToSphere +public import Mathlib.MeasureTheory.Integral.Pi +public import Mathlib.MeasureTheory.Measure.Lebesgue.VolumeOfBalls + +/-! +# The Gauss measure and polar decomposition + +The density `exp (-π ‖x‖ ^ 2)` of the Gauss measure, the fact that it is a probability density on +a finite-dimensional real inner product space, and the radius of the ball of volume one. These are +the two normalizations Vaaler's cube-slicing theorem compares: Bombieri–Gubler's Lemma C.3.7 says +that the Gauss measure of a symmetric convex set never exceeds the volume of its intersection with +a product of balls of volume one, and the base case of that induction is a comparison, ray by ray, +of these two measures of total mass one. + +Alongside them, the polar decomposition of an additive Haar measure as an integral over the unit +sphere, in the `ℝ≥0∞`-valued form the comparison needs. + +## Main definitions + +* `gaussDensity x = ENNReal.ofReal (exp (-π * ‖x‖ ^ 2))`, the Gauss density, with the defining + equation `gaussDensity_def`. +* `unitVolumeRadius E`, the radius `ρ` for which the ball of radius `ρ` in `E` has volume `1`. + +## Main results + +* `lintegral_eq_lintegral_sphere` and `lintegral_eq_lintegral_sphere'`: **polar decomposition** of + an additive Haar measure, in product and iterated form. +* `lintegral_gaussDensity_eq_one`: `∫⁻ x, exp (-π ‖x‖ ^ 2) = 1` on a finite-dimensional real + inner product space. +* `volume_closedBall_unitVolumeRadius`: the ball of radius `unitVolumeRadius E` has volume `1`. + +## Implementation notes + +⚠ **The Gaussian is normalized with `π` in the exponent precisely so that no constant survives.** +`∫_ℝ exp (-π t ^ 2) dt = 1`, so the `n`-dimensional integral is `1` for every `n` by Fubini, and the +comparison in `SliceBound.lean` can cancel the sphere measure between the two total masses instead +of evaluating either radial integral. This is what makes Bombieri–Gubler's Remark C.3.6 — the +closed form `ρ(n) = π ^ (-1/2) * Γ (n/2 + 1) ^ (1/n)` — unnecessary: `unitVolumeRadius` is defined +by the normalization it is used for, and no Gamma function appears anywhere below. + +⚠ **Mathlib's polar decomposition is a measure-preserving homeomorphism, not an integral formula.** +`MeasureTheory.Measure.measurePreserving_homeomorphUnitSphereProd` identifies the Haar measure of +`E \ {0}` with `μ.toSphere.prod (volumeIoiPow (n - 1))`; the only integral formula built on it, +`integral_fun_norm_addHaar`, is for Bochner integrals of *radial* functions. The comparison below +integrates the indicator of a convex set, which is not radial, so the formula is proved here in the +raw `ℝ≥0∞` form, with no hypothesis on the integrand at all. + +## References + +E. Bombieri and W. Gubler, *Heights in Diophantine Geometry*, Cambridge University Press (2006), +Remarks C.3.5 and C.3.6. + +This is Layer 4.5 (infrastructure) of the `ArithmeticHeights` roadmap. +-/ + +public section + +open MeasureTheory Measure Set Metric ENNReal +open scoped Real + +/-- The **Gauss density** `exp (-π ‖x‖ ^ 2)`, as an `ℝ≥0∞`-valued function. With `π` in the +exponent it is a probability density for every finite-dimensional real inner product space. -/ +@[expose] noncomputable def gaussDensity {E : Type*} [NormedAddCommGroup E] (x : E) : + ℝ≥0∞ := + ENNReal.ofReal (Real.exp (-π * ‖x‖ ^ 2)) + +theorem gaussDensity_def {E : Type*} [NormedAddCommGroup E] (x : E) : + gaussDensity x = ENNReal.ofReal (Real.exp (-π * ‖x‖ ^ 2)) := rfl + +@[simp] +theorem gaussDensity_zero {E : Type*} [NormedAddCommGroup E] : gaussDensity (0 : E) = 1 := by + rw [gaussDensity_def, norm_zero] + norm_num + +@[fun_prop] +theorem measurable_gaussDensity {E : Type*} [NormedAddCommGroup E] [MeasurableSpace E] + [OpensMeasurableSpace E] : Measurable (gaussDensity : E → ℝ≥0∞) := by + unfold gaussDensity; fun_prop + +theorem gaussDensity_le_one {E : Type*} [NormedAddCommGroup E] (x : E) : gaussDensity x ≤ 1 := by + rw [gaussDensity, ← ENNReal.ofReal_one] + refine ENNReal.ofReal_le_ofReal ?_ + rw [Real.exp_le_one_iff] + nlinarith [Real.pi_pos, sq_nonneg ‖x‖] + +theorem gaussDensity_neg {E : Type*} [NormedAddCommGroup E] (x : E) : + gaussDensity (-x) = gaussDensity x := by + rw [gaussDensity, gaussDensity, norm_neg] + +/-- **Polar decomposition of an additive Haar measure.** Every `ℝ≥0∞`-valued integral against an +additive Haar measure is an integral over `sphere 0 1 × Ioi 0`, with the radial factor `r ^ (n-1)` +carried by `MeasureTheory.Measure.volumeIoiPow`. No measurability hypothesis is needed. -/ +theorem lintegral_eq_lintegral_sphere {E : Type*} [NormedAddCommGroup E] [NormedSpace ℝ E] + [MeasurableSpace E] [BorelSpace E] [FiniteDimensional ℝ E] [Nontrivial E] + (μ : Measure E) [μ.IsAddHaarMeasure] (f : E → ℝ≥0∞) : + ∫⁻ x, f x ∂μ = + ∫⁻ p : sphere (0 : E) 1 × Ioi (0 : ℝ), f ((p.2 : ℝ) • (p.1 : E)) + ∂(μ.toSphere.prod (Measure.volumeIoiPow (Module.finrank ℝ E - 1))) := by + have h1 : ∫⁻ x, f x ∂μ = ∫⁻ x : ({(0 : E)}ᶜ : Set E), f x.1 ∂(μ.comap (↑)) := by + rw [lintegral_subtype_comap (measurableSet_singleton _).compl, restrict_compl_singleton] + rw [h1, ← (μ.measurePreserving_homeomorphUnitSphereProd).lintegral_comp_emb + (Homeomorph.measurableEmbedding _) (fun p => f ((p.2 : ℝ) • (p.1 : E)))] + refine lintegral_congr fun x => ?_ + have hx : (x : E) ≠ 0 := x.2 + have hn : (0 : ℝ) < ‖(x : E)‖ := by simpa using hx + rw [homeomorphUnitSphereProd_apply_snd_coe, homeomorphUnitSphereProd_apply_fst_coe, smul_smul, + mul_inv_cancel₀ hn.ne', one_smul] + +/-- **Polar decomposition**, iterated form: the inner integral is over the ray. -/ +theorem lintegral_eq_lintegral_sphere' {E : Type*} [NormedAddCommGroup E] [NormedSpace ℝ E] + [MeasurableSpace E] [BorelSpace E] [FiniteDimensional ℝ E] [Nontrivial E] + (μ : Measure E) [μ.IsAddHaarMeasure] {f : E → ℝ≥0∞} (hf : Measurable f) : + ∫⁻ x, f x ∂μ = + ∫⁻ u : sphere (0 : E) 1, ∫⁻ r : Ioi (0 : ℝ), f ((r : ℝ) • (u : E)) + ∂(Measure.volumeIoiPow (Module.finrank ℝ E - 1)) ∂μ.toSphere := by + rw [lintegral_eq_lintegral_sphere μ f, lintegral_prod] + exact (hf.comp (by fun_prop)).aemeasurable + +/-- The Gauss density integrates to `1` over `ι → ℝ`: the one-dimensional Gaussian integral +`∫ exp (-π t ^ 2) = 1` and Fubini. -/ +theorem lintegral_gaussian_pi (ι : Type*) [Fintype ι] : + ∫⁻ y : ι → ℝ, ENNReal.ofReal (Real.exp (-π * ∑ i, (y i) ^ 2)) = 1 := by + have hprod : ∀ y : ι → ℝ, Real.exp (-π * ∑ i, (y i) ^ 2) = ∏ i, Real.exp (-π * (y i) ^ 2) := by + intro y + rw [← Real.exp_sum, Finset.mul_sum] + have hint : Integrable (fun y : ι → ℝ => ∏ i, Real.exp (-π * (y i) ^ 2)) := + Integrable.fintype_prod (fun _ => integrable_exp_neg_mul_sq Real.pi_pos) + have hone : (∫ t : ℝ, Real.exp (-π * t ^ 2)) = 1 := by + rw [integral_gaussian, div_self Real.pi_ne_zero, Real.sqrt_one] + have hval : ∫ y : ι → ℝ, ∏ i, Real.exp (-π * (y i) ^ 2) = 1 := by + rw [integral_fintype_prod_volume_eq_pow (fun t : ℝ => Real.exp (-π * t ^ 2)), hone, one_pow] + calc ∫⁻ y : ι → ℝ, ENNReal.ofReal (Real.exp (-π * ∑ i, (y i) ^ 2)) + = ∫⁻ y : ι → ℝ, ENNReal.ofReal (∏ i, Real.exp (-π * (y i) ^ 2)) := + lintegral_congr fun y => by rw [hprod] + _ = ENNReal.ofReal (∫ y : ι → ℝ, ∏ i, Real.exp (-π * (y i) ^ 2)) := + (ofReal_integral_eq_lintegral_ofReal hint + (.of_forall fun _ => Finset.prod_nonneg fun _ _ => (Real.exp_pos _).le)).symm + _ = 1 := by rw [hval, ENNReal.ofReal_one] + +theorem lintegral_gaussDensity_euclidean (ι : Type*) [Fintype ι] : + ∫⁻ x : EuclideanSpace ℝ ι, gaussDensity x = 1 := by + have hm : Measurable fun y : ι → ℝ => ENNReal.ofReal (Real.exp (-π * ∑ i, (y i) ^ 2)) := by + fun_prop + rw [← lintegral_gaussian_pi ι, ← (PiLp.volume_preserving_ofLp ι).lintegral_comp hm] + refine lintegral_congr fun x => ?_ + rw [gaussDensity, EuclideanSpace.norm_eq, Real.sq_sqrt (by positivity)] + simp + +/-- **The Gauss measure is a probability measure** on a finite-dimensional real inner product +space. -/ +theorem lintegral_gaussDensity_eq_one {E : Type*} [NormedAddCommGroup E] [InnerProductSpace ℝ E] + [FiniteDimensional ℝ E] [MeasurableSpace E] [BorelSpace E] : + ∫⁻ x : E, gaussDensity x = 1 := by + have hm : Measurable (gaussDensity : EuclideanSpace ℝ (Fin (Module.finrank ℝ E)) → ℝ≥0∞) := by + unfold gaussDensity; fun_prop + rw [← lintegral_gaussDensity_euclidean (Fin (Module.finrank ℝ E)), + ← (stdOrthonormalBasis ℝ E).measurePreserving_repr.lintegral_comp hm] + exact lintegral_congr fun x => by + rw [gaussDensity, gaussDensity, (stdOrthonormalBasis ℝ E).repr.norm_map] + +/-- The radius `ρ` of the ball of volume `1`: Bombieri–Gubler's `ρ(n)`, defined by the +normalization it is used for rather than by its closed form. -/ +@[expose] noncomputable def unitVolumeRadius (E : Type*) [NormedAddCommGroup E] + [InnerProductSpace ℝ E] [MeasurableSpace E] [BorelSpace E] [FiniteDimensional ℝ E] : ℝ := + (volume (ball (0 : E) 1)).toReal ^ (-(1 / (Module.finrank ℝ E : ℝ))) + +section + +variable {E : Type*} [NormedAddCommGroup E] [InnerProductSpace ℝ E] [MeasurableSpace E] + [BorelSpace E] [FiniteDimensional ℝ E] + +theorem volume_ball_one_pos : 0 < (volume (ball (0 : E) 1)).toReal := + ENNReal.toReal_pos (measure_ball_pos volume 0 one_pos).ne' measure_ball_lt_top.ne + +theorem unitVolumeRadius_pos : 0 < unitVolumeRadius E := + Real.rpow_pos_of_pos volume_ball_one_pos _ + +/-- **The ball of radius `unitVolumeRadius E` has volume one.** -/ +theorem volume_closedBall_unitVolumeRadius : + volume (closedBall (0 : E) (unitVolumeRadius E)) = 1 := by + rcases subsingleton_or_nontrivial E with hE | hE + · have h1 : closedBall (0 : E) (unitVolumeRadius E) = univ := by + ext x; simp [Subsingleton.elim x (0 : E), unitVolumeRadius_pos.le] + have hg : ∀ x : E, gaussDensity x = 1 := fun x => by + rw [Subsingleton.elim x (0 : E), gaussDensity_zero] + calc volume (closedBall (0 : E) (unitVolumeRadius E)) = ∫⁻ _ : E, 1 := by + rw [lintegral_one, h1] + _ = ∫⁻ x : E, gaussDensity x := lintegral_congr fun x => (hg x).symm + _ = 1 := lintegral_gaussDensity_eq_one + have hv : 0 < (volume (ball (0 : E) 1)).toReal := volume_ball_one_pos + have hn : (0 : ℝ) < (Module.finrank ℝ E : ℝ) := by + exact_mod_cast Module.finrank_pos (R := ℝ) (M := E) + rw [Measure.addHaar_closedBall volume 0 unitVolumeRadius_pos.le, unitVolumeRadius, + ← Real.rpow_natCast ((volume (ball (0 : E) 1)).toReal ^ (-(1 / (Module.finrank ℝ E : ℝ)))) + (Module.finrank ℝ E), + ← Real.rpow_mul hv.le, + show -(1 / (Module.finrank ℝ E : ℝ)) * (Module.finrank ℝ E : ℝ) = -1 by field_simp, + Real.rpow_neg_one, ENNReal.ofReal_inv_of_pos hv, + ENNReal.ofReal_toReal measure_ball_lt_top.ne, + ENNReal.inv_mul_cancel (measure_ball_pos volume 0 one_pos).ne' measure_ball_lt_top.ne] + +end diff --git a/LeanPool/CCDN/ArithmeticHeights/GramCovolume.lean b/LeanPool/CCDN/ArithmeticHeights/GramCovolume.lean new file mode 100644 index 0000000000..7c79d91470 --- /dev/null +++ b/LeanPool/CCDN/ArithmeticHeights/GramCovolume.lean @@ -0,0 +1,171 @@ +/- +Copyright (c) 2026 Ralf Stephan. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Ralf Stephan +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import Mathlib.Algebra.Module.ZLattice.Covolume +public import Mathlib.MeasureTheory.Measure.Haar.InnerProductSpace +public import Mathlib.Analysis.InnerProductSpace.Adjoint + +/-! +Modified for Lean/Mathlib 4.35.0: add the explicit inner-product Haar-measure import. + +# The covolume of a lattice as a Gram determinant + +For a `ℤ`-lattice `L` in a finite-dimensional real inner product space, the covolume of `L` — the +volume of a fundamental domain for the canonical measure, the one giving the parallelepiped of an +orthonormal basis volume `1` — is the square root of the Gram determinant `det ⟪b i, b j⟫` of any +`ℤ`-basis `b` of `L`. + +This is the form in which a covolume is computed in the geometry of numbers whenever the lattice +is presented by generators rather than by a fundamental domain, and it is the step that turns the +covolume of a lattice of rational points into a sum of squares of maximal minors: composed with the +Cauchy–Binet identity it says that the squared covolume of the lattice spanned by the rows of a +matrix is the sum of the squares of its maximal minors. + +Mathlib has `ZLattice.covolume_eq_det`, the corresponding statement in `ι → ℝ` with the Lebesgue +measure, where the answer is the determinant of the basis matrix; in an inner product space there +is no ambient basis to take a determinant against, and the Gram matrix is what replaces it. + +## Main results + +* `ZLattice.covolume_eq_abs_det_orthonormalBasis`: the covolume is the absolute determinant of the + coordinate matrix of a `ℤ`-basis in any orthonormal basis of the ambient space. +* `ZLattice.covolume_sq_eq_det_gram`: the squared covolume is the Gram determinant of any + `ℤ`-basis. +* `ZLattice.covolume_eq_sqrt_det_gram`: the covolume is the square root of that determinant. + +## Implementation notes + +Mathlib's naming convention in `Mathlib/Algebra/Module/ZLattice/Covolume.lean` is that a result +about `ι → ℝ` carries the plain name and its `InnerProductSpace` counterpart carries a prime. The +three results here are not primed counterparts of anything: `covolume_eq_det` has no Gram matrix +in it, and the Gram determinant is not available in `ι → ℝ` without choosing an inner product. + +The index type of the `ℤ`-basis is arbitrary; the orthonormal basis of the ambient space is +obtained from `stdOrthonormalBasis` by reindexing along the equivalence that +`ZLattice.rank`— through `Basis.ofZLatticeBasis` — provides, so no hypothesis relating the index +type to `Module.finrank` is needed. + +## References + +J. W. S. Cassels, *An Introduction to the Geometry of Numbers*, Springer (1959), Chapter I, §2.1, +pp. 9–10, defines the lattice determinant as the absolute determinant of a basis matrix. +The Gram formula proved here is the corresponding Euclidean computation for a lattice in a +subspace, using orthonormal coordinates on its span. + +This is Layer 4.3 of the `ArithmeticHeights` roadmap, the infrastructure half: the covolume +identity of that milestone is proved by computing both sides of it against a Gram determinant. +-/ + +public section + +noncomputable section + +namespace ZLattice + +open Module MeasureTheory Matrix Real + +variable {E : Type*} [NormedAddCommGroup E] [InnerProductSpace ℝ E] [FiniteDimensional ℝ E] + [MeasurableSpace E] [BorelSpace E] (L : Submodule ℤ E) [DiscreteTopology L] [IsZLattice ℝ L] + {κ : Type*} [Fintype κ] [DecidableEq κ] + +/-- **The covolume against an orthonormal basis.** The parallelepiped of an orthonormal basis has +volume `1`, so the covolume of a lattice is the absolute determinant of the coordinates of any of +its `ℤ`-bases in any orthonormal basis of the ambient space. -/ +theorem covolume_eq_abs_det_orthonormalBasis (b : Basis κ ℤ L) (o : OrthonormalBasis κ ℝ E) : + covolume L = |(o.toBasis.toMatrix ((↑) ∘ b)).det| := by + have hfd : (volume : Measure E).real (ZSpan.fundamentalDomain o.toBasis) = 1 := by + rw [measureReal_congr (ZSpan.fundamentalDomain_ae_parallelepiped o.toBasis volume)] + simp [measureReal_def, o.volume_parallelepiped] + rw [covolume_eq_det_mul_measureReal L volume b o.toBasis, hfd, mul_one, Basis.det_apply] + +/-- **The squared covolume is the Gram determinant of any `ℤ`-basis.** -/ +theorem covolume_sq_eq_det_gram (b : Basis κ ℤ L) : + covolume L ^ 2 = (Matrix.of fun i j ↦ inner ℝ (b i : E) (b j : E)).det := by + have hcard : Fintype.card κ = finrank ℝ E := + (finrank_eq_card_basis (b.ofZLatticeBasis ℝ L)).symm + let o : OrthonormalBasis κ ℝ E := + (stdOrthonormalBasis ℝ E).reindex (Fintype.equivFinOfCardEq hcard).symm + have hG : (Matrix.of fun i j ↦ inner ℝ (b i : E) (b j : E)) + = (o.toBasis.toMatrix ((↑) ∘ b))ᵀ * o.toBasis.toMatrix ((↑) ∘ b) := by + ext i j + rw [Matrix.mul_apply] + simp only [Matrix.transpose_apply, Basis.toMatrix_apply, Function.comp_apply, + OrthonormalBasis.coe_toBasis_repr_apply, OrthonormalBasis.repr_apply_apply, Matrix.of_apply] + rw [← o.sum_inner_mul_inner (b i : E) (b j : E)] + exact Finset.sum_congr rfl fun l _ ↦ by rw [real_inner_comm] + rw [hG, covolume_eq_abs_det_orthonormalBasis L b o, Matrix.det_mul, Matrix.det_transpose, ← sq, + sq_abs] + +/-- **The covolume is the square root of the Gram determinant of any `ℤ`-basis.** -/ +theorem covolume_eq_sqrt_det_gram (b : Basis κ ℤ L) : + covolume L = √((Matrix.of fun i j ↦ inner ℝ (b i : E) (b j : E)).det) := by + rw [← covolume_sq_eq_det_gram L b, Real.sqrt_sq (covolume_pos L volume).le] + +section Comp + +omit [MeasurableSpace E] [BorelSpace E] + +variable {F : Type*} [NormedAddCommGroup F] [InnerProductSpace ℝ F] [FiniteDimensional ℝ F] + +omit [FiniteDimensional ℝ E] [FiniteDimensional ℝ F] in +/-- **A linear map multiplies a Gram determinant by the determinant of its Gram operator.** If +`A` satisfies `⟪f x, f y⟫ = ⟪A x, y⟫` — that is, `A = f* f` — then the Gram matrix of the image of +a basis is `Mᵀ G`, with `G` the Gram matrix of the basis and `M` the matrix of `A`. This is the +linear-algebra form of the statement that a linear map scales volume by `√(det (f* f))`, which is +the factor by which it scales a covolume. -/ +theorem det_gram_of_inner (f : E →ₗ[ℝ] F) (A : E →ₗ[ℝ] E) (b : Basis κ ℝ E) + (h : ∀ x y, inner ℝ (f x) (f y) = inner ℝ (A x) y) : + (Matrix.of fun i j ↦ inner ℝ (f (b i)) (f (b j))).det + = A.det * (Matrix.of fun i j ↦ inner ℝ (b i) (b j)).det := by + classical + set M := LinearMap.toMatrix b b A with hM + have key : (Matrix.of fun i j ↦ inner ℝ (f (b i)) (f (b j))) + = Mᵀ * (Matrix.of fun i j ↦ inner ℝ (b i) (b j)) := by + ext i j + rw [Matrix.mul_apply] + simp only [Matrix.of_apply, Matrix.transpose_apply] + rw [h (b i) (b j), ← b.sum_repr (A (b i)), sum_inner] + exact Finset.sum_congr rfl fun l _ ↦ by + rw [real_inner_smul_left, hM, LinearMap.toMatrix_apply] + rw [key, Matrix.det_mul, Matrix.det_transpose, hM, LinearMap.det_toMatrix] + +/-- The adjoint form of `ZLattice.det_gram_of_inner`. -/ +theorem det_gram_comp (f : E →ₗ[ℝ] F) (b : Basis κ ℝ E) : + (Matrix.of fun i j ↦ inner ℝ (f (b i)) (f (b j))).det + = (LinearMap.adjoint f ∘ₗ f).det * (Matrix.of fun i j ↦ inner ℝ (b i) (b j)).det := + det_gram_of_inner f _ b fun x y ↦ by simp [LinearMap.adjoint_inner_left] + +variable [MeasurableSpace F] [BorelSpace F] + +omit [FiniteDimensional ℝ E] in +/-- **The covolume of the image of a lattice under a linear map.** If a `ℤ`-basis of a lattice +sitting in a subspace `W` of `F` is the image under `f` of an `ℝ`-basis `b`, then its squared +covolume is `det A` times the Gram determinant of `b`, for any `A` with `⟪f x, f y⟫ = ⟪A x, y⟫`. +This is the form in which the archimedean half of a covolume identity is computed: the lattice is +transported from a space where its covolume is known, and the transport factor is an operator +determinant. -/ +theorem covolume_sq_eq_det_gram_comp {W : Submodule ℝ F} (L : Submodule ℤ ↥W) + [DiscreteTopology L] [IsZLattice ℝ L] (c : Basis κ ℤ L) (f : E →ₗ[ℝ] F) (A : E →ₗ[ℝ] E) + (b : Basis κ ℝ E) (h : ∀ x y, inner ℝ (f x) (f y) = inner ℝ (A x) y) + (hc : ∀ i, ((c i : ↥W) : F) = f (b i)) : + covolume L ^ 2 = A.det * (Matrix.of fun i j ↦ inner ℝ (b i) (b j)).det := by + rw [covolume_sq_eq_det_gram L c, ← det_gram_of_inner f A b h] + refine congrArg Matrix.det (Matrix.ext fun i j ↦ ?_) + simp only [Matrix.of_apply] + rw [← hc i, ← hc j] + exact rfl + +end Comp + +end ZLattice + +end + +end diff --git a/LeanPool/CCDN/ArithmeticHeights/LinearForm.lean b/LeanPool/CCDN/ArithmeticHeights/LinearForm.lean new file mode 100644 index 0000000000..42904cb32f --- /dev/null +++ b/LeanPool/CCDN/ArithmeticHeights/LinearForm.lean @@ -0,0 +1,455 @@ +/- +Copyright (c) 2026 Ralf Stephan. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Ralf Stephan +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.ArithmeticHeights.Affine +public import LeanPool.CCDN.ArithmeticHeights.Arakelov +public import Mathlib.NumberTheory.Height.MvPolynomial +import all Mathlib.NumberTheory.Height.Basic + +/-! +# Linear forms, sums of coordinates, and the Segre relation + +Mathlib bounds the height of a sum of *field elements*, `Height.mulHeight₁_sum_le`, with the +constant `#s ^ totalWeight K`, and the height of the image of a tuple under a *linear map*, +`Height.mulHeight_linearMap_apply_le`, with the constant `Nat.card ι ^ totalWeight K`. It also has +the Segre relation `Height.mulHeight_fun_mul_eq`, that the height of the multiplication table +`fun (i, j) ↦ x i * y j` is the product of the heights. This file adds the two statements about +linear forms that those leave open, and the Arakelov form of the second. + +The first is the **tuple sum bound**: if every term of a sum is an entry of one and the same tuple +`w`, read off along index maps, then the height of the tuple of sums exceeds `mulHeight w` by at +most `#s ^ totalWeight K` — a *single* height on the right, not a product of heights of the terms. +This is what `Height.mulHeight₁_sum_le` cannot say, and it is the form an iteration needs, since +it costs one additive `totalWeight K * log #s` per step rather than a multiplicative factor. + +The second is the **value of a linear form**, `∑ i, a i * x i`. Its projective height is not +bounded by the projective heights of `a` and `x` in any way: scaling `a` by `c` multiplies the +value by `c` and leaves `mulHeight a` fixed. The bound that is true replaces both by the *affine* +height `Height.mulHeightAff` of Layer 0.5, and then the constant is again +`Nat.card ι ^ totalWeight K`. Both are deduced here from the tuple sum bound and the Segre +relation, applied to the multiplication table of the two affine tuples. + +Finally, in the Arakelov normalization of `ArithmeticHeights.Arakelov` — the ℓ² norm at the +archimedean places — **the constant disappears**: Cauchy–Schwarz at each infinite place gives +`NumberField.arakelovMulHeight_linearMap_apply_le` with constant `1`, against +`Nat.card ι ^ totalWeight K` for the sup norm. This is the first place in the development where +the Arakelov normalization is strictly better and not merely different. + +## Main results + +* `Height.mulHeight_sum_comp_le` and `Height.logHeight_sum_comp_le`: the tuple sum bound, with the + constant `#s ^ totalWeight K`; `Height.mulHeight_sum_comp_le'` is the version whose index set of + summation may depend on the coordinate. +* `Height.mulHeight₁_sum_mul_le` and `Height.logHeight₁_sum_mul_le`: the value of a linear form + against the affine heights of its coefficients and of the point, with the constant + `Nat.card ι ^ totalWeight K`. +* `Height.exists_not_mulHeight_add_le`: the naive tuple analogue of `Height.mulHeight₁_sum_le` + is false over `ℚ`, for every constant. +* `NumberField.arakelovMulHeight_linearMap_apply_le` and + `NumberField.arakelovLogHeight_linearMap_apply_le`: the Arakelov height of the image of a tuple + under a linear map, with constant `1`. + +## Implementation notes + +The local halves are separated out as `AbsoluteValue.iSup_abv_sum_comp_le'` and +`IsNonarchimedean.iSup_abv_sum_comp_le'`, following Mathlib's +`AbsoluteValue.iSup_abv_linearMap_apply_le`, so that the archimedean cost `#s` and its +disappearance at the nonarchimedean places are visible before any global product is formed. + +The global step needs `Height.hasFiniteMulSupport_iSup_nonarchAbsVal`, which is `private` in +`Mathlib/NumberTheory/Height/Basic.lean`; it is reached by `import all`, the way Mathlib's own +`Mathlib/NumberTheory/Height/MvPolynomial.lean` reaches it. It should be public upstream. + +The affine height is a normalization and not a theorem about linear forms, so it lives in Layer +0.5, `ArithmeticHeights.Affine`, and not here. What this file uses of it is that it is not +invariant under scaling — `Height.exists_mulHeightAff_smul_ne` — which is why it and not +`Height.mulHeight` can appear on the right of a bound on a linear form's value. + +The Arakelov bound is stated for a number field because `Height.AdmissibleAbsValues` records no +archimedean structure — no inner product at an archimedean place, hence no Cauchy–Schwarz. + +## References + +E. Bombieri and W. Gubler, *Heights in Diophantine Geometry*, Cambridge University Press (2006), +Proposition 1.5.15 and 1.5.14 for the sum bound and the Segre relation, and Definition 2.8.1 for +the Arakelov normalization used here. + +This is Layer 2.4 of the `ArithmeticHeights` roadmap. +-/ + +public section + +open Finset Function Height AdmissibleAbsValues Real + +namespace AbsoluteValue + +variable {K : Type*} [Field K] {α ι κ : Type*} + +/-- The local form of the tuple sum bound at an arbitrary absolute value, with the index set of +the sum allowed to depend on the coordinate: if every sum has at most `n` terms, then the supremum +of `v` over the entries of `fun i ↦ ∑ a ∈ s i, w (f a i)` is at most `n` times the supremum of `v` +over the entries of `w`. -/ +theorem iSup_abv_sum_comp_le' [Nonempty ι] [Finite κ] (v : AbsoluteValue K ℝ) {n : ℕ} + {s : ι → Finset α} (hs : ∀ i, #(s i) ≤ n) (f : α → ι → κ) (w : κ → K) : + ⨆ i, v (∑ a ∈ s i, w (f a i)) ≤ n * ⨆ k, v (w k) := by + have hw : (0 : ℝ) ≤ ⨆ k, v (w k) := Real.iSup_nonneg_of_nonnegHomClass v _ + refine ciSup_le fun i ↦ ?_ + calc v (∑ a ∈ s i, w (f a i)) + ≤ ∑ a ∈ s i, v (w (f a i)) := v.sum_le _ _ + _ ≤ ∑ _a ∈ s i, ⨆ k, v (w k) := + Finset.sum_le_sum fun a _ ↦ Finite.le_ciSup_of_le (f a i) le_rfl + _ = #(s i) * ⨆ k, v (w k) := by rw [Finset.sum_const, nsmul_eq_mul] + _ ≤ n * ⨆ k, v (w k) := by gcongr; exact_mod_cast hs i + +/-- The local form of the tuple sum bound at an arbitrary absolute value. -/ +theorem iSup_abv_sum_comp_le [Nonempty ι] [Finite κ] (v : AbsoluteValue K ℝ) (s : Finset α) + (f : α → ι → κ) (w : κ → K) : + ⨆ i, v (∑ a ∈ s, w (f a i)) ≤ #s * ⨆ k, v (w k) := + v.iSup_abv_sum_comp_le' (s := fun _ ↦ s) (fun _ ↦ le_rfl) f w + +end AbsoluteValue + +namespace IsNonarchimedean + +variable {K : Type*} [Field K] {α ι κ : Type*} + +/-- The local form of the tuple sum bound at a nonarchimedean absolute value: there the factor +`#s` disappears, because the absolute value of a sum is bounded by the maximum of the absolute +values of its terms. -/ +theorem iSup_abv_sum_comp_le' [Nonempty ι] [Finite κ] {v : AbsoluteValue K ℝ} + (hv : IsNonarchimedean v) (s : ι → Finset α) (f : α → ι → κ) (w : κ → K) : + ⨆ i, v (∑ a ∈ s i, w (f a i)) ≤ ⨆ k, v (w k) := by + have hw : (0 : ℝ) ≤ ⨆ k, v (w k) := Real.iSup_nonneg_of_nonnegHomClass v _ + refine ciSup_le fun i ↦ (hv.apply_sum_le).trans ?_ + rcases isEmpty_or_nonempty (s i : Type _) with h | h + · simpa using hw + exact ciSup_le fun a ↦ Finite.le_ciSup_of_le (f a.val i) le_rfl + +/-- The local form of the tuple sum bound at a nonarchimedean absolute value. -/ +theorem iSup_abv_sum_comp_le [Nonempty ι] [Finite κ] {v : AbsoluteValue K ℝ} + (hv : IsNonarchimedean v) (s : Finset α) (f : α → ι → κ) (w : κ → K) : + ⨆ i, v (∑ a ∈ s, w (f a i)) ≤ ⨆ k, v (w k) := + hv.iSup_abv_sum_comp_le' (fun _ ↦ s) f w + +end IsNonarchimedean + +namespace Height + +variable {K : Type*} [Field K] [AdmissibleAbsValues K] {α ι κ : Type*} + +/-! +### The height of a tuple of sums of entries of a single tuple +-/ + +/-- **The tuple sum bound**, with the index set of summation allowed to depend on the coordinate. +If every term of the sum is an entry of one and the same tuple `w : κ → K`, read off along index +maps `f a : ι → κ`, and every sum has at most `n` terms, then the height of the resulting tuple +exceeds that of `w` by at most `n ^ totalWeight K`. + +Contrast `Height.mulHeight₁_sum_le`, whose right-hand side is a *product* of the heights of the +terms; here a single `mulHeight w` suffices, which is what makes an iteration cost an additive +rather than a multiplicative constant per step. -/ +theorem mulHeight_sum_comp_le' [Finite ι] [Finite κ] {n : ℕ} (hn : 0 < n) {s : ι → Finset α} + (hs : ∀ i, #(s i) ≤ n) (f : α → ι → κ) (w : κ → K) : + mulHeight (fun i ↦ ∑ a ∈ s i, w (f a i)) ≤ (n : ℝ) ^ totalWeight K * mulHeight w := by + have hcard : (1 : ℝ) ≤ (n : ℝ) := by exact_mod_cast hn + have hs1 : (1 : ℝ) ≤ (n : ℝ) ^ totalWeight K := one_le_pow₀ hcard + rcases eq_or_ne (fun i ↦ ∑ a ∈ s i, w (f a i)) (0 : ι → K) with h0 | h0 + · rw [h0, mulHeight_zero] + exact hs1.trans <| le_mul_of_one_le_right (by positivity) (one_le_mulHeight w) + obtain ⟨i₀, hi₀⟩ := Function.ne_iff.mp h0 + have hι : Nonempty ι := ⟨i₀⟩ + have hw : w ≠ 0 := by + rintro rfl + exact hi₀ (by simp) + rw [mulHeight_eq h0, mulHeight_eq hw] + have hconst : ((n : ℝ)) ^ totalWeight K + = (Multiset.map (fun _ : AbsoluteValue K ℝ ↦ (n : ℝ)) archAbsVal).prod := by + rw [Multiset.map_const', Multiset.prod_replicate] + rfl + rw [hconst, ← mul_assoc, ← Multiset.prod_map_mul] + refine mul_le_mul ?_ ?_ ?_ ?_ + · exact Multiset.prod_map_le_prod_map₀ _ _ + (fun v _ ↦ Real.iSup_nonneg_of_nonnegHomClass v _) fun v _ ↦ v.iSup_abv_sum_comp_le' hs f w + · exact finprod_le_finprod₀ (hasFiniteMulSupport_iSup_nonarchAbsVal h0) + (fun v ↦ Real.iSup_nonneg_of_nonnegHomClass v.val _) + (hasFiniteMulSupport_iSup_nonarchAbsVal hw) fun v ↦ + (isNonarchimedean _ v.prop).iSup_abv_sum_comp_le' s f w + · exact finprod_nonneg fun v ↦ Real.iSup_nonneg_of_nonnegHomClass v.val _ + · exact Multiset.prod_map_nonneg fun v _ ↦ + mul_nonneg (by positivity) (Real.iSup_nonneg_of_nonnegHomClass v _) + +/-- **The tuple sum bound**, all the sums over the same index set. -/ +theorem mulHeight_sum_comp_le [Finite ι] [Finite κ] {s : Finset α} (hs : s.Nonempty) + (f : α → ι → κ) (w : κ → K) : + mulHeight (fun i ↦ ∑ a ∈ s, w (f a i)) ≤ (#s : ℝ) ^ totalWeight K * mulHeight w := + mulHeight_sum_comp_le' hs.card_pos (s := fun _ ↦ s) (fun _ ↦ le_rfl) f w + +/-- The logarithmic form of `Height.mulHeight_sum_comp_le`. -/ +theorem logHeight_sum_comp_le [Finite ι] [Finite κ] {s : Finset α} (hs : s.Nonempty) + (f : α → ι → κ) (w : κ → K) : + logHeight (fun i ↦ ∑ a ∈ s, w (f a i)) ≤ totalWeight K * log #s + logHeight w := by + have hcard : (0 : ℝ) < (#s : ℝ) := by exact_mod_cast hs.card_pos + simp only [logHeight_eq_log_mulHeight] + refine (log_le_log (mulHeight_pos _) (mulHeight_sum_comp_le hs f w)).trans_eq ?_ + rw [log_mul (by positivity) (mulHeight_ne_zero w), log_pow] + +/-! +### The value of a linear form +-/ + +/-- **The height of the value of a linear form.** The affine height of `∑ i, a i * x i` is at +most `Nat.card ι ^ totalWeight K` times the product of the affine heights of the coefficient +tuple `a` and of the point `x`. + +Both heights on the right have to be affine: scaling `a` by `c` multiplies the value by `c` and +leaves `mulHeight a` unchanged, so with projective heights the statement is false for every +constant. + +The proof is the Segre relation `Height.mulHeight_fun_mul_eq` applied to the multiplication table +of the two affine tuples, followed by `Height.mulHeight_sum_comp_le'`: the pair +`![∑ i, a i * x i, 1]` reads off `Nat.card ι` entries of that table in its first coordinate and a +single entry in its second. -/ +theorem mulHeight₁_sum_mul_le [Fintype ι] [Nonempty ι] (a x : ι → K) : + mulHeight₁ (∑ i, a i * x i) + ≤ (Nat.card ι : ℝ) ^ totalWeight K * (mulHeightAff a * mulHeightAff x) := by + classical + have hpos : 0 < Nat.card ι := Nat.card_pos + set A : Option ι → K := fun o ↦ o.elim 1 a with hA + set X : Option ι → K := fun o ↦ o.elim 1 x with hX + set w : Option ι × Option ι → K := fun p ↦ A p.1 * X p.2 with hw + set s : Fin 2 → Finset (Option ι) := + ![(Finset.univ : Finset ι).image some, {none}] with hs + have hcard : ∀ j, #(s j) ≤ Nat.card ι := by + intro j + fin_cases j + · simp [hs, Finset.card_image_of_injective _ (Option.some_injective ι), + Nat.card_eq_fintype_card] + · simpa [hs] using hpos.nat_succ_le + have key : (fun j : Fin 2 ↦ ∑ o ∈ s j, w (o, o)) = ![∑ i, a i * x i, 1] := by + ext j + fin_cases j + · simp [hs, hw, hA, hX, Finset.sum_image, Option.some_injective ι] + · simp [hs, hw, hA, hX] + have hbound := mulHeight_sum_comp_le' (K := K) hpos hcard (fun o (_ : Fin 2) ↦ (o, o)) w + rw [key] at hbound + rw [mulHeight₁_eq_mulHeight] + refine hbound.trans ?_ + gcongr + exact le_of_eq (mulHeight_fun_mul_eq (optionElim_one_ne_zero a) (optionElim_one_ne_zero x)) + +/-- The logarithmic form of `Height.mulHeight₁_sum_mul_le`. -/ +theorem logHeight₁_sum_mul_le [Fintype ι] [Nonempty ι] (a x : ι → K) : + logHeight₁ (∑ i, a i * x i) + ≤ totalWeight K * log (Nat.card ι) + (logHeightAff a + logHeightAff x) := by + have hpos : (0 : ℝ) < Nat.card ι := by exact_mod_cast Nat.card_pos (α := ι) + simp only [logHeight₁_eq_log_mulHeight₁, logHeightAff_eq_log_mulHeightAff] + refine (log_le_log (mulHeight₁_pos _) (mulHeight₁_sum_mul_le a x)).trans_eq ?_ + rw [log_mul (pow_ne_zero _ hpos.ne') (mul_ne_zero (mulHeightAff_ne_zero a) + (mulHeightAff_ne_zero x)), + log_mul (mulHeightAff_ne_zero a) (mulHeightAff_ne_zero x), log_pow] + +/-! +### The naive tuple analogue of the sum bound is false +-/ + +/-- Any field element whose height exceeds `2 ^ totalWeight K` refutes the tuple analogue of +`Height.mulHeight₁_sum_le`: the two tuples `![x, 0]` and `![0, 1]` both have height `1`, while +their sum `![x, 1]` has height `mulHeight₁ x`. The mechanism is scaling invariance +(`Height.mulHeight_smul_eq_mulHeight`), which the left-hand side of such a bound does not enjoy, +so no choice of constant can repair the statement. -/ +theorem not_mulHeight_add_le_of_lt_mulHeight₁ {x : K} + (hx : (2 : ℝ) ^ totalWeight K < mulHeight₁ x) : + ¬ mulHeight ((![x, 0] : Fin 2 → K) + (![0, 1] : Fin 2 → K)) + ≤ (2 : ℝ) ^ totalWeight K + * (mulHeight (![x, 0] : Fin 2 → K) * mulHeight (![0, 1] : Fin 2 → K)) := by + have h1 : (![x, 0] : Fin 2 → K) + (![0, 1] : Fin 2 → K) = ![x, 1] := by + ext i + fin_cases i <;> simp + rw [h1, ← mulHeight₁_eq_mulHeight] + simpa using not_le.2 hx + +/-- The tuple analogue of `Height.mulHeight₁_sum_le` fails already over `ℚ`. This is why the +right-hand side of `Height.mulHeight₁_sum_mul_le` is stated with affine heights, and why +`Height.mulHeight_sum_comp_le` reads off all its summands from *one* tuple. -/ +theorem exists_not_mulHeight_add_le : + ∃ x : ℚ, ¬ mulHeight ((![x, 0] : Fin 2 → ℚ) + (![0, 1] : Fin 2 → ℚ)) + ≤ (2 : ℝ) ^ totalWeight ℚ + * (mulHeight (![x, 0] : Fin 2 → ℚ) * mulHeight (![0, 1] : Fin 2 → ℚ)) := by + obtain ⟨n, hn⟩ := exists_nat_gt ((2 : ℝ) ^ totalWeight ℚ) + have hpos : (0 : ℝ) ≤ 2 ^ totalWeight ℚ := by positivity + have hn0 : n ≠ 0 := by + rintro rfl + rw [Nat.cast_zero] at hn + exact hpos.not_gt hn + have : NeZero n := ⟨hn0⟩ + exact ⟨(n : ℚ), not_mulHeight_add_le_of_lt_mulHeight₁ (by rwa [Rat.mulHeight₁_natCast])⟩ + +end Height + +namespace NumberField + +variable {K : Type*} [Field K] [NumberField K] {ι ι' : Type*} [Fintype ι] [Fintype ι'] + +/-! +### Linear maps in the Arakelov normalization + +At an infinite place the local factor of the Arakelov height is an ℓ² norm, so Cauchy–Schwarz +bounds the local factor of `A x` by the product of those of `A` and `x` with no constant at all; +at a finite place the sup norm does the same, by the ultrametric inequality. The constant +`Nat.card ι ^ totalWeight K` of `Height.mulHeight_linearMap_apply_le` therefore disappears. +-/ + +omit [NumberField K] in +/-- Cauchy–Schwarz at an infinite place: the squared ℓ² norm of `A x` is at most the product of +those of `A` and `x`. This is where the cardinality of the index set is not paid. -/ +private lemma sum_sq_apply_linearMap_le (v : InfinitePlace K) (A : ι' × ι → K) (x : ι → K) : + ∑ j, v (∑ i, A (j, i) * x i) ^ 2 ≤ (∑ p, v (A p) ^ 2) * ∑ i, v (x i) ^ 2 := by + rw [Fintype.sum_prod_type, Finset.sum_mul] + refine Finset.sum_le_sum fun j _ ↦ ?_ + calc v (∑ i, A (j, i) * x i) ^ 2 + ≤ (∑ i, v (A (j, i)) * v (x i)) ^ 2 := by + gcongr ?_ ^ 2 + refine (v.1.sum_le _ _).trans_eq (Finset.sum_congr rfl fun i _ ↦ ?_) + exact map_mul .. + _ ≤ (∑ i, v (A (j, i)) ^ 2) * ∑ i, v (x i) ^ 2 := Finset.sum_mul_sq_le_sq_mul_sq _ _ _ + +/-- **The Arakelov height of the image of a tuple under a linear map**, with constant `1`. Let +`A : ι' × ι → K`, read as a linear map from `ι → K` to `ι' → K`. Then + +`arakelovMulHeight (A x) ≤ arakelovMulHeight A * arakelovMulHeight x`. + +Mathlib's sup-norm statement `Height.mulHeight_linearMap_apply_le` carries the constant +`Nat.card ι ^ totalWeight K`, which cannot be removed there — the affine form of that is the +second example at the end of this file; here Cauchy–Schwarz at each infinite place removes it. +No hypothesis on `A` or `x` is needed: the junk value `arakelovMulHeight 0 = 1` is at most the +right-hand side, which is at least `1`. -/ +theorem arakelovMulHeight_linearMap_apply_le (A : ι' × ι → K) (x : ι → K) : + arakelovMulHeight (fun j ↦ ∑ i, A (j, i) * x i) + ≤ arakelovMulHeight A * arakelovMulHeight x := by + rcases eq_or_ne (fun j ↦ ∑ i, A (j, i) * x i) (0 : ι' → K) with h0 | h0 + · rw [h0, arakelovMulHeight_zero] + exact one_le_mul_of_one_le_of_one_le (one_le_arakelovMulHeight A) (one_le_arakelovMulHeight x) + have hA : A ≠ 0 := by + rintro rfl + exact h0 (by ext j; simp) + have hx : x ≠ 0 := by + rintro rfl + exact h0 (by ext j; simp) + have hfinA : (fun v : FinitePlace K ↦ ⨆ p, v (A p)).HasFiniteMulSupport := + Height.hasFiniteMulSupport_iSup_nonarchAbsVal hA + have hfinx : (fun v : FinitePlace K ↦ ⨆ i, v (x i)).HasFiniteMulSupport := + Height.hasFiniteMulSupport_iSup_nonarchAbsVal hx + have hfin0 : (fun v : FinitePlace K ↦ ⨆ j, v (∑ i, A (j, i) * x i)).HasFiniteMulSupport := + Height.hasFiniteMulSupport_iSup_nonarchAbsVal h0 + have hnonarch (v : FinitePlace K) : + ⨆ j, v (∑ i, A (j, i) * x i) ≤ (⨆ p, v (A p)) * ⨆ i, v (x i) := + IsNonarchimedean.iSup_abv_linearMap_apply_le (v := v.1) (FinitePlace.add_le v) A x + rw [arakelovMulHeight_eq h0, arakelovMulHeight_eq hA, arakelovMulHeight_eq hx, mul_mul_mul_comm] + refine mul_le_mul ?_ ?_ ?_ ?_ + · rw [← Finset.prod_mul_distrib] + refine Finset.prod_le_prod₀ (fun v _ ↦ by positivity) fun v _ ↦ ?_ + rw [← Real.mul_rpow (by positivity) (by positivity)] + exact Real.rpow_le_rpow (by positivity) (sum_sq_apply_linearMap_le v A x) (by positivity) + · rw [← _root_.finprod_mul_distrib hfinA hfinx] + exact finprod_le_finprod₀ hfin0 (fun v ↦ Real.iSup_nonneg fun _ ↦ apply_nonneg _ _) + (hfinA.mul hfinx) hnonarch + · exact _root_.finprod_nonneg fun v ↦ Real.iSup_nonneg fun _ ↦ apply_nonneg _ _ + · positivity + +/-- The logarithmic form of `NumberField.arakelovMulHeight_linearMap_apply_le`: in the Arakelov +normalization a linear map costs nothing beyond the heights of the matrix and of the point. -/ +theorem arakelovLogHeight_linearMap_apply_le (A : ι' × ι → K) (x : ι → K) : + arakelovLogHeight (fun j ↦ ∑ i, A (j, i) * x i) + ≤ arakelovLogHeight A + arakelovLogHeight x := by + simp only [arakelovLogHeight_eq_log_arakelovMulHeight] + refine (log_le_log (arakelovMulHeight_pos _) + (arakelovMulHeight_linearMap_apply_le A x)).trans_eq ?_ + rw [log_mul (arakelovMulHeight_ne_zero A) (arakelovMulHeight_ne_zero x)] + +end NumberField + +/-! +### Examples + +The tests that fix the constants: that `#s ^ totalWeight K` in the tuple sum bound and +`Nat.card ι ^ totalWeight K` in the linear-form bound are attained over `ℚ`, and that the +Arakelov constant `1` is attained too. +-/ + +section Examples + +open Height NumberField + +/-- **Sharpness of the tuple sum bound.** Over `ℚ`, reading two entries of `w = ![1, 0]` off in +each of two coordinates produces the tuple `![2, 1]` of height `2`, while `w` has height `1` and +`#s ^ totalWeight ℚ = 2`. The constant of `Height.mulHeight_sum_comp_le` is therefore attained. -/ +example : + mulHeight (fun i : Fin 2 ↦ ∑ a ∈ (Finset.univ : Finset (Fin 2)), + (![1, 0] : Fin 2 → ℚ) (![![0, 0], ![0, 1]] a i)) = 2 + ∧ mulHeight (![1, 0] : Fin 2 → ℚ) = 1 + ∧ ((#(Finset.univ : Finset (Fin 2)) : ℝ)) ^ totalWeight ℚ = 2 := by + have hw : totalWeight ℚ = 1 := by + rw [NumberField.totalWeight_eq_finrank, Module.finrank_self] + have h1 : (fun i : Fin 2 ↦ ∑ a ∈ (Finset.univ : Finset (Fin 2)), + (![1, 0] : Fin 2 → ℚ) (![![0, 0], ![0, 1]] a i)) + = ((↑) : ℤ → ℚ) ∘ (![2, 1] : Fin 2 → ℤ) := by + ext i + fin_cases i <;> norm_num [Fin.sum_univ_two] + have h2 : (![1, 0] : Fin 2 → ℚ) = ((↑) : ℤ → ℚ) ∘ (![1, 0] : Fin 2 → ℤ) := by + ext i + fin_cases i <;> simp + refine ⟨?_, ?_, by rw [hw]; norm_num⟩ + · rw [h1, Rat.mulHeight_eq_max_abs_of_gcd_eq_one (by decide)] + norm_num [show (⨆ i : Fin 2, |(![2, 1] : Fin 2 → ℤ) i|) = 2 from + le_antisymm (ciSup_le fun i ↦ by fin_cases i <;> decide) + (Finite.le_ciSup_of_le 0 (by decide))] + · rw [h2, Rat.mulHeight_eq_max_abs_of_gcd_eq_one (by decide)] + norm_num [show (⨆ i : Fin 2, |(![1, 0] : Fin 2 → ℤ) i|) = 1 from + le_antisymm (ciSup_le fun i ↦ by fin_cases i <;> decide) + (Finite.le_ciSup_of_le 0 (by decide))] + +/-- **Sharpness of the linear-form bound.** Over `ℚ` with `a = x = ![1, 1]` the value is `2`, of +affine height `2`, while both affine heights on the right are `1` and +`Nat.card (Fin 2) ^ totalWeight ℚ = 2`. So the cardinality in `Height.mulHeight₁_sum_mul_le` +cannot be removed — which is exactly what Cauchy–Schwarz achieves in the Arakelov normalization, +`NumberField.arakelovMulHeight_linearMap_apply_le`. -/ +example : + mulHeight₁ (∑ i, (![1, 1] : Fin 2 → ℚ) i * (![1, 1] : Fin 2 → ℚ) i) = 2 + ∧ mulHeightAff (![1, 1] : Fin 2 → ℚ) = 1 + ∧ (Nat.card (Fin 2) : ℝ) ^ totalWeight ℚ = 2 := by + have hw : totalWeight ℚ = 1 := by + rw [NumberField.totalWeight_eq_finrank, Module.finrank_self] + have haff : (fun o : Option (Fin 2) ↦ o.elim 1 (![1, 1] : Fin 2 → ℚ)) = 1 := by + ext o + rcases o with _ | i + · rfl + · fin_cases i <;> rfl + refine ⟨?_, by rw [mulHeightAff, haff, mulHeight_one], + by rw [hw, Nat.card_eq_fintype_card, Fintype.card_fin]; norm_num⟩ + rw [show (∑ i, (![1, 1] : Fin 2 → ℚ) i * (![1, 1] : Fin 2 → ℚ) i) = ((2 : ℕ) : ℚ) by + norm_num [Fin.sum_univ_two]] + rw [Rat.mulHeight₁_natCast] + norm_num + +/-- **Sharpness of the Arakelov constant.** For a one-element source the linear map is a +reindexing, so `NumberField.arakelovMulHeight_linearMap_apply_le` is an equality and the +constant `1` cannot be lowered. -/ +example {K : Type*} [Field K] [NumberField K] {ι' : Type*} [Fintype ι'] (A : ι' × Fin 1 → K) : + arakelovMulHeight (fun j ↦ ∑ i, A (j, i) * (1 : Fin 1 → K) i) + = arakelovMulHeight A * arakelovMulHeight (1 : Fin 1 → K) := by + have he : (fun j ↦ ∑ i, A (j, i) * (1 : Fin 1 → K) i) + = A ∘ (Equiv.prodUnique ι' (Fin 1)).symm := by + ext j + simp + rw [he, arakelovMulHeight_comp_equiv (Equiv.prodUnique ι' (Fin 1)).symm, + arakelovMulHeight_eq_one_of_subsingleton (1 : Fin 1 → K), mul_one] + +end Examples diff --git a/LeanPool/CCDN/ArithmeticHeights/LogConcave.lean b/LeanPool/CCDN/ArithmeticHeights/LogConcave.lean new file mode 100644 index 0000000000..e486954834 --- /dev/null +++ b/LeanPool/CCDN/ArithmeticHeights/LogConcave.lean @@ -0,0 +1,165 @@ +/- +Copyright (c) 2026 Ralf Stephan. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Ralf Stephan +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.ArithmeticHeights.PrekopaLeindler + +/-! +# Log-concave functions, and the log-concavity of a marginal + +A function `f` with values in `ℝ≥0∞` is *log-concave* if + +`f x ^ a * f y ^ b ≤ f (a • x + b • y)` for all `x`, `y` and all `a, b > 0` with `a + b = 1`, + +which for a positive real-valued function says exactly that `log ∘ f` is concave. The two facts +about log-concave functions that Vaaler's cube-slicing theorem needs are here: the set where such +a function is nonzero is convex, and a marginal of a log-concave function over a convex set is +log-concave. The second is Bombieri–Gubler's Lemma C.3.4, and it is the only consumer of the +Prékopa–Leindler inequality inside the roadmap. + +## Main definitions + +* `LogConcave f`: `f : E → ℝ≥0∞` is log-concave. + +## Main results + +* `LogConcave.convex_support`: `{x | f x ≠ 0}` is convex (Bombieri–Gubler, Remark C.3.2). +* `LogConcave.convex_lt`: every strict superlevel set `{x | c < f x}` is convex. +* `logConcave_indicator`: the indicator of a convex set, at any value, is log-concave. +* `LogConcave.mul`: a product of log-concave functions is log-concave. +* `LogConcave.setLIntegral_prod_right`: **Bombieri–Gubler, Lemma C.3.4.** If `f` is a log-concave + function on `E × F` and `A ⊆ F` is convex and measurable, then `x ↦ ∫⁻ y in A, f (x, y)` is + log-concave on `E`, provided Prékopa–Leindler holds on `F`. + +## Implementation notes + +⚠ **Log-concavity is stated for `ℝ≥0∞`-valued functions, not for the book's nonnegative real +ones.** The functions that C.3.4 and C.3.7 apply it to are *measures of slices* — +`y ↦ vol (B ∩ A_y)` in Bombieri–Gubler's own induction step — so `ℝ≥0∞` is where they naturally +live, and there the +statement carries no finiteness hypothesis at all. Bombieri–Gubler have to say "if the integral is +always finite" in C.3.4; here that hypothesis is absent. The codomain supplies nonnegativity +and permits infinite values. + +⚠ **`0 ^ a = 0` for `a > 0` is what makes the characteristic function of a convex set log-concave**, +with no case distinction beyond membership, and that is the whole of Bombieri–Gubler's step from +C.3.3 to C.3.4: their `f i y = χ_A y * f (x i, y)` is log-concave because a product of log-concave +functions is, and `χ_A` is log-concave because `A` is convex. The proof of +`LogConcave.setLIntegral_prod_right` below inlines that product rather than forming it, since the +Prékopa–Leindler hypothesis is checked pointwise anyway. + +⚠ **C.3.4 needs nothing of `E` but that it is a real vector space.** There is no measure on `E`, no +finite-dimensionality, and no measurability hypothesis on the marginal: log-concavity is a +pointwise statement in `x`, and the Prékopa–Leindler inequality is applied on `F` alone, once for +each pair `x₁`, `x₂`. + +## References + +E. Bombieri and W. Gubler, *Heights in Diophantine Geometry*, Cambridge University Press (2006), +Definition C.3.1, Remark C.3.2 and Lemma C.3.4. + +This is Layer 4.5 (infrastructure) of the `ArithmeticHeights` roadmap. +-/ + +public section + +open MeasureTheory Set + +/-- A function `f : E → ℝ≥0∞` on a real vector space is **log-concave** if +`f x ^ a * f y ^ b ≤ f (a • x + b • y)` for all `x`, `y` and all weights `a, b > 0` summing to +`1`; for positive real-valued `f` this says that `log ∘ f` is concave. -/ +@[expose] def LogConcave {E : Type*} [AddCommGroup E] [Module ℝ E] (f : E → ENNReal) : Prop := + ∀ a b : ℝ, 0 < a → 0 < b → a + b = 1 → ∀ x y, f x ^ a * f y ^ b ≤ f (a • x + b • y) + +/-- **The set where a log-concave function is nonzero is convex** (Bombieri–Gubler, Remark +C.3.2). -/ +theorem LogConcave.convex_support {E : Type*} [AddCommGroup E] [Module ℝ E] {f : E → ENNReal} + (hf : LogConcave f) : Convex ℝ {x | f x ≠ 0} := by + intro x hx y hy a b ha hb hab + rcases eq_or_lt_of_le ha with ha0 | ha0 + · have hb1 : b = 1 := by rw [← hab, ← ha0, zero_add] + simpa [← ha0, hb1] using hy + rcases eq_or_lt_of_le hb with hb0 | hb0 + · have ha1 : a = 1 := by rw [← hab, ← hb0, add_zero] + simpa [← hb0, ha1] using hx + refine ne_of_gt (lt_of_lt_of_le ?_ (hf a b ha0 hb0 hab x y)) + refine pos_iff_ne_zero.mpr (mul_ne_zero ?_ ?_) + · exact fun hc => hx (by simpa [ENNReal.rpow_eq_zero_iff, ha0, ha0.not_gt] using hc) + · exact fun hc => hy (by simpa [ENNReal.rpow_eq_zero_iff, hb0, hb0.not_gt] using hc) + +/-- **Every strict superlevel set of a log-concave function is convex.** This is what makes the +layer-cake decomposition of a log-concave density a superposition of convex symmetric sets, which +is the form Bombieri–Gubler's Lemma C.3.7 consumes. -/ +theorem LogConcave.convex_lt {E : Type*} [AddCommGroup E] [Module ℝ E] {f : E → ENNReal} + (hf : LogConcave f) (c : ENNReal) : Convex ℝ {x | c < f x} := by + intro x hx y hy a b ha hb hab + rcases eq_or_lt_of_le ha with ha0 | ha0 + · have hb1 : b = 1 := by rw [← hab, ← ha0, zero_add] + simpa [← ha0, hb1] using hy + rcases eq_or_lt_of_le hb with hb0 | hb0 + · have ha1 : a = 1 := by rw [← hab, ← hb0, add_zero] + simpa [← hb0, ha1] using hx + refine (lt_min hx hy).trans_le (le_trans ?_ (hf a b ha0 hb0 hab x y)) + calc min (f x) (f y) = min (f x) (f y) ^ (a + b) := by rw [hab, ENNReal.rpow_one] + _ = min (f x) (f y) ^ a * min (f x) (f y) ^ b := + ENNReal.rpow_add_of_nonneg a b ha0.le hb0.le + _ ≤ f x ^ a * f y ^ b := + mul_le_mul' (ENNReal.rpow_le_rpow (min_le_left _ _) ha0.le) + (ENNReal.rpow_le_rpow (min_le_right _ _) hb0.le) + +/-- **The indicator of a convex set is log-concave**, at any value `c`. -/ +theorem logConcave_indicator {E : Type*} [AddCommGroup E] [Module ℝ E] {s : Set E} + (hs : Convex ℝ s) (c : ENNReal) : LogConcave (Set.indicator s (fun _ => c)) := by + intro a b ha hb hab x y + by_cases hx : x ∈ s + · by_cases hy : y ∈ s + · rw [Set.indicator_of_mem hx, Set.indicator_of_mem hy, + Set.indicator_of_mem (hs hx hy ha.le hb.le hab), + ← ENNReal.rpow_add_of_nonneg a b ha.le hb.le, hab, ENNReal.rpow_one] + · simp [Set.indicator_of_notMem hy, ENNReal.zero_rpow_of_pos hb] + · simp [Set.indicator_of_notMem hx, ENNReal.zero_rpow_of_pos ha] + +/-- A product of log-concave functions is log-concave. -/ +theorem LogConcave.mul {E : Type*} [AddCommGroup E] [Module ℝ E] {f g : E → ENNReal} + (hf : LogConcave f) (hg : LogConcave g) : LogConcave (f * g) := by + intro a b ha hb hab x y + have h1 : (f x * g x) ^ a = f x ^ a * g x ^ a := ENNReal.mul_rpow_of_nonneg _ _ ha.le + have h2 : (f y * g y) ^ b = f y ^ b * g y ^ b := ENNReal.mul_rpow_of_nonneg _ _ hb.le + simp only [Pi.mul_apply, h1, h2] + calc f x ^ a * g x ^ a * (f y ^ b * g y ^ b) + = f x ^ a * f y ^ b * (g x ^ a * g y ^ b) := by ring + _ ≤ f (a • x + b • y) * g (a • x + b • y) := + mul_le_mul' (hf a b ha hb hab x y) (hg a b ha hb hab x y) + +/-- **Bombieri–Gubler, Lemma C.3.4: a marginal of a log-concave function over a convex set is +log-concave.** If `f : E × F → ℝ≥0∞` is measurable and log-concave and `A ⊆ F` is convex and +measurable, then `x ↦ ∫⁻ y in A, f (x, y) ∂ν` is log-concave on `E`, for any measure `ν` on `F` +satisfying the Prékopa–Leindler inequality. -/ +theorem LogConcave.setLIntegral_prod_right {E F : Type*} [MeasurableSpace E] [AddCommGroup E] + [Module ℝ E] [MeasurableSpace F] [AddCommGroup F] [Module ℝ F] {ν : Measure F} + (hF : HasPrekopaLeindler ν) {f : E × F → ENNReal} (hf : Measurable f) (hlc : LogConcave f) + {A : Set F} (hA : MeasurableSet A) (hAc : Convex ℝ A) : + LogConcave fun x => ∫⁻ y in A, f (x, y) ∂ν := by + intro a b ha hb hab x₁ x₂ + have hind : ∀ x : E, (∫⁻ y in A, f (x, y) ∂ν) + = ∫⁻ y, Set.indicator A (fun y => f (x, y)) y ∂ν := + fun x => (lintegral_indicator hA _).symm + have hmeas : ∀ x : E, Measurable (Set.indicator A fun y => f (x, y)) := + fun x => (hf.comp measurable_prodMk_left).indicator hA + simp only [hind] + refine hF a b ha hb hab _ _ _ (hmeas x₁) (hmeas x₂) (hmeas (a • x₁ + b • x₂)) fun y₁ y₂ => ?_ + by_cases hy₁ : y₁ ∈ A + · by_cases hy₂ : y₂ ∈ A + · rw [Set.indicator_of_mem hy₁, Set.indicator_of_mem hy₂, + Set.indicator_of_mem (hAc hy₁ hy₂ ha.le hb.le hab)] + exact hlc a b ha hb hab (x₁, y₁) (x₂, y₂) + · simp [Set.indicator_of_notMem hy₂, ENNReal.zero_rpow_of_pos hb] + · simp [Set.indicator_of_notMem hy₁, ENNReal.zero_rpow_of_pos ha] + +end diff --git a/LeanPool/CCDN/ArithmeticHeights/Matrix.lean b/LeanPool/CCDN/ArithmeticHeights/Matrix.lean new file mode 100644 index 0000000000..4216aeae1a --- /dev/null +++ b/LeanPool/CCDN/ArithmeticHeights/Matrix.lean @@ -0,0 +1,707 @@ +/- +Copyright (c) 2026 Ralf Stephan. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Ralf Stephan +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.ArithmeticHeights.Extension +public import LeanPool.CCDN.ArithmeticHeights.LinearForm +public import Mathlib.LinearAlgebra.Matrix.Determinant.Basic +import all Mathlib.NumberTheory.Height.Basic + +/-! +# The height of a matrix + +The height of a matrix here is the sup-norm height of the tuple of its **entries**. +Row-space heights instead use the tuple of maximal minors and belong to Layer 3. +Bombieri–Vaaler's `H(A)` is the absolute Arakelov height of those minors, using Euclidean norms +at infinity. This file uses `Matrix.mulHeight` for the sup-norm entry height. + +The content of the file is four bounds. + +* The **product bound** `Matrix.mulHeight_mul_le`, with the constant `Fintype.card n ^ totalWeight + K` for the inner index type `n`. It is the tuple sum bound of Layer 2.4 applied to the + multiplication table of the entries of `A` and of `B`: an entry of `A * B` is a sum of + `Fintype.card n` entries of that one table, and the Segre relation evaluates its height. +* The **Arakelov product bound** `Matrix.arakelovMulHeight_mul_le`, with constant exactly `1`. + Cauchy–Schwarz entry by entry produces the Frobenius norm, which is submultiplicative; the + cardinality of the inner index type is not paid at all. This is the matrix form of + `NumberField.arakelovMulHeight_linearMap_apply_le` and the reason Layer 0 carries two + normalizations. +* The **determinant bound** `Matrix.mulHeight₁_det_le`. It cannot be stated with `mulHeight A` on + the right: `A ↦ c • A` fixes the projective height of the matrix and multiplies the determinant + by `c ^ n`, which `Matrix.exists_not_mulHeight₁_det_le` records. What is true uses the *affine* + height of Layer 0.5, and the sharp form of it is columnwise, one affine column height per column, + with the constant `(Fintype.card n)! ^ totalWeight K` from the Leibniz expansion. +* The action on tuples, `Matrix.mulHeight_mulVec_le`, which is Mathlib's + `Height.mulHeight_linearMap_apply_le` read through the definition, and its Arakelov companion. + +The Leibniz expansion carries the signs of the permutations, so the tuple sum bound is used here +through `Height.mulHeight_sum_sign_comp_le'`, a version whose summands may be multiplied by `±1`. +That version is deduced from the unsigned one by doubling the alphabet with a `Bool`, whose two +letters `1` and `-1` have height `1`. + +## Main definitions + +* `Matrix.mulHeight A` and `Matrix.logHeight A`: the height of the tuple of entries of `A`. +* `Matrix.mulHeightAff A` and `Matrix.logHeightAff A`: its affine counterpart, the height of the + tuple of entries with a coordinate `1` appended. +* `Matrix.arakelovMulHeight A` and `Matrix.arakelovLogHeight A`: the same in the Arakelov + normalization, over a number field. + +## Main results + +* `Matrix.mulHeight_transpose`, `Matrix.mulHeight_submatrix_le`, + `Matrix.mulHeight_submatrix_equiv` and `Matrix.mulHeight_smul`: invariance under transpose, under + re-indexing of rows and columns — in particular under row and column permutations — and under + scaling, and the submatrix bound. +* `Matrix.mulHeight_mul_le` and `Matrix.logHeight_mul_le`: the product bound. +* `Matrix.arakelovMulHeight_mul_le` and `Matrix.arakelovLogHeight_mul_le`: the product bound in the + Arakelov normalization, with constant `1`. +* `Matrix.mulHeight₁_det_le` and `Matrix.logHeight₁_det_le`: the determinant against the affine + heights of the columns, and `Matrix.mulHeight₁_det_le_mulHeightAff_pow` against the affine height + of the matrix. +* `Matrix.exists_not_mulHeight₁_det_le`: no constant bounds the height of a determinant by the + projective height of the matrix. +* `Height.mulHeight_sum_sign_comp_le'`: the signed tuple sum bound. + +## Implementation notes + +`Matrix m n K` is definitionally `m → n → K`, and `Height.mulHeight` takes a tuple, so +`Matrix.mulHeight` is the height of `fun q : m × n ↦ A q.1 q.2`. Every statement about +re-indexing is then a statement about `Prod.map`, and transpose is `Equiv.prodComm`. + +The finiteness hypotheses are `Finite` wherever the height alone is involved and `Fintype` only +where a matrix product or a determinant is formed. + +`Height.mulHeight_sum_sign_comp_le'` belongs next to the unsigned tuple sum bound of Layer 2.4; +it is here because the determinant is the only consumer so far. + +## References + +E. Bombieri and W. Gubler, *Heights in Diophantine Geometry*, Cambridge University Press (2006), +2.9.8 (p. 75) for the entry-tuple height `H(A)`, and Definition 2.8.1 with 2.8.2–2.8.3 +(pp. 66–67) for the local Euclidean factors and absolute Arakelov normalization. +Definition 2.8.11 instead concerns the Grassmannian row-space height, treated separately in +`ArithmeticHeights.RowSpace`. + +This is Layer 2.5 of the `ArithmeticHeights` roadmap. +-/ + +public section + +open Finset Function Height AdmissibleAbsValues Real + +namespace Height + +variable {K : Type*} [Field K] [AdmissibleAbsValues K] {α ι κ : Type*} + +/-- The two-letter alphabet `{1, -1}` has height `1`: it is the tuple `![-1, 1]` up to the +re-indexing `finTwoEquiv` and a swap, and `mulHeight₁ (-1) = mulHeight₁ 1 = 1`. -/ +private lemma mulHeight_boolSign : + mulHeight (fun b : Bool ↦ if b then (-1 : K) else 1) = 1 := by + have he : (fun b : Bool ↦ if b then (-1 : K) else 1) ∘ finTwoEquiv = ![(1 : K), -1] := by + ext i + fin_cases i <;> rfl + rw [← mulHeight_comp_equiv finTwoEquiv, he, mulHeight_swap, ← mulHeight₁_eq_mulHeight, + show (-1 : K) = -(1 : K) from rfl, mulHeight₁_neg, mulHeight₁_one] + +/-- **The signed tuple sum bound.** `Height.mulHeight_sum_comp_le'` with each summand allowed a +sign: if in each coordinate `i` at most `n` entries of one tuple `w`, each possibly negated, are +summed, the height of the tuple of sums is at most `n ^ totalWeight K` times `mulHeight w`. + +Signs cost nothing, because `v (-x) = v x` at every absolute value. Formally this is the unsigned +bound for the doubled alphabet `κ × Bool`, whose height is `mulHeight w` by the Segre relation and +`Height.mulHeight_boolSign`. The Leibniz expansion of a determinant is the intended consumer. -/ +theorem mulHeight_sum_sign_comp_le' [Finite ι] [Finite κ] {n : ℕ} (hn : 0 < n) + {s : ι → Finset α} (hs : ∀ i, #(s i) ≤ n) {ε : α → ι → K} + (hε : ∀ a i, ε a i = 1 ∨ ε a i = -1) (f : α → ι → κ) (w : κ → K) : + mulHeight (fun i ↦ ∑ a ∈ s i, ε a i * w (f a i)) ≤ (n : ℝ) ^ totalWeight K * mulHeight w := by + classical + have hn1 : (1 : ℝ) ≤ (n : ℝ) ^ totalWeight K := one_le_pow₀ (by exact_mod_cast hn) + rcases eq_or_ne w 0 with rfl | hw + · have h0 : (fun i ↦ ∑ a ∈ s i, ε a i * (0 : κ → K) (f a i)) = (0 : ι → K) := by + ext i + simp + rw [h0, mulHeight_zero, mulHeight_zero, mul_one] + exact hn1 + have hy : (fun b : Bool ↦ if b then (-1 : K) else 1) ≠ 0 := fun h ↦ by + simpa using congrFun h false + have key := mulHeight_sum_comp_le' hn hs (fun a i ↦ (f a i, decide (ε a i = -1))) + (fun q : κ × Bool ↦ w q.1 * (if q.2 then (-1 : K) else 1)) + rw [mulHeight_fun_mul_eq hw hy, mulHeight_boolSign, mul_one] at key + refine le_trans (le_of_eq ?_) key + congr 1 + funext i + refine Finset.sum_congr rfl fun a _ ↦ ?_ + by_cases hd : ε a i = -1 + · rw [show decide (ε a i = -1) = true from decide_eq_true hd] + simp [hd] + · rw [show decide (ε a i = -1) = false from decide_eq_false hd] + rcases hε a i with h | h + · simp [h] + · exact absurd h hd + +end Height + +namespace Matrix + +variable {K : Type*} [Field K] [AdmissibleAbsValues K] {m n p m' n' : Type*} + +/-- **The height of a matrix** is the sup-norm height of the tuple of its **entries**, +corresponding to the entry-tuple construction in Bombieri–Gubler 2.9.8. +Bombieri–Vaaler's `H(A)` instead uses the absolute Arakelov height of the maximal minors. -/ +@[expose] noncomputable def mulHeight (A : Matrix m n K) : ℝ := + Height.mulHeight fun q : m × n ↦ A q.1 q.2 + +/-- The logarithmic height of a matrix. As everywhere in this development, the logarithmic height +is *defined* as the logarithm of the multiplicative one and never independently. -/ +@[expose] noncomputable def logHeight (A : Matrix m n K) : ℝ := log (mulHeight A) + +theorem logHeight_eq_log_mulHeight (A : Matrix m n K) : logHeight A = log (mulHeight A) := rfl + +theorem logHeight_eq (A : Matrix m n K) : + logHeight A = Height.logHeight fun q : m × n ↦ A q.1 q.2 := rfl + +/-- **The affine height of a matrix**: the height of the tuple of its entries with a coordinate +`1` appended, `Height.mulHeightAff` of the entries. Unlike `Matrix.mulHeight` it is not invariant +under scaling, which is exactly what a bound on the determinant needs. -/ +@[expose] noncomputable def mulHeightAff (A : Matrix m n K) : ℝ := + Height.mulHeightAff fun q : m × n ↦ A q.1 q.2 + +/-- The logarithmic affine height of a matrix. -/ +@[expose] noncomputable def logHeightAff (A : Matrix m n K) : ℝ := log (mulHeightAff A) + +theorem logHeightAff_eq_log_mulHeightAff (A : Matrix m n K) : + logHeightAff A = log (mulHeightAff A) := + rfl + +@[simp] +theorem mulHeight_zero : mulHeight (0 : Matrix m n K) = 1 := Height.mulHeight_zero + +@[simp] +theorem logHeight_zero : logHeight (0 : Matrix m n K) = 0 := by + rw [logHeight_eq_log_mulHeight, mulHeight_zero, log_one] + +theorem mulHeight_neg (A : Matrix m n K) : mulHeight (-A) = mulHeight A := + Height.mulHeight_neg _ + +theorem logHeight_neg (A : Matrix m n K) : logHeight (-A) = logHeight A := by + rw [logHeight_eq_log_mulHeight, logHeight_eq_log_mulHeight, mulHeight_neg] + +/-- The height of a matrix is invariant under transpose: transposing re-indexes the entries by +`Equiv.prodComm`. -/ +theorem mulHeight_transpose (A : Matrix m n K) : mulHeight Aᵀ = mulHeight A := + Height.mulHeight_comp_equiv (Equiv.prodComm n m) fun q : m × n ↦ A q.1 q.2 + +theorem logHeight_transpose (A : Matrix m n K) : logHeight Aᵀ = logHeight A := by + rw [logHeight_eq_log_mulHeight, logHeight_eq_log_mulHeight, mulHeight_transpose] + +/-- Re-indexing rows and columns by equivalences leaves the height unchanged; taking +`e` and `e'` to be permutations, the height is invariant under row and column permutations. -/ +theorem mulHeight_submatrix_equiv (A : Matrix m n K) (e : m' ≃ m) (e' : n' ≃ n) : + mulHeight (A.submatrix e e') = mulHeight A := + Height.mulHeight_comp_equiv (e.prodCongr e') fun q : m × n ↦ A q.1 q.2 + +theorem logHeight_submatrix_equiv (A : Matrix m n K) (e : m' ≃ m) (e' : n' ≃ n) : + logHeight (A.submatrix e e') = logHeight A := by + rw [logHeight_eq_log_mulHeight, logHeight_eq_log_mulHeight, mulHeight_submatrix_equiv] + +section Basic + +variable [Finite m] [Finite n] + +theorem one_le_mulHeight (A : Matrix m n K) : 1 ≤ mulHeight A := Height.one_le_mulHeight _ + +theorem mulHeight_pos (A : Matrix m n K) : 0 < mulHeight A := Height.mulHeight_pos _ + +theorem mulHeight_ne_zero (A : Matrix m n K) : mulHeight A ≠ 0 := Height.mulHeight_ne_zero _ + +theorem logHeight_nonneg (A : Matrix m n K) : 0 ≤ logHeight A := Height.logHeight_nonneg _ + +theorem one_le_mulHeightAff (A : Matrix m n K) : 1 ≤ mulHeightAff A := + Height.one_le_mulHeightAff _ + +theorem mulHeightAff_pos (A : Matrix m n K) : 0 < mulHeightAff A := Height.mulHeightAff_pos _ + +theorem mulHeightAff_ne_zero (A : Matrix m n K) : mulHeightAff A ≠ 0 := + Height.mulHeightAff_ne_zero _ + +theorem logHeightAff_nonneg (A : Matrix m n K) : 0 ≤ logHeightAff A := + Height.logHeightAff_nonneg _ + +theorem mulHeight_le_mulHeightAff (A : Matrix m n K) : mulHeight A ≤ mulHeightAff A := + Height.mulHeight_le_mulHeightAff _ + +theorem logHeight_le_logHeightAff (A : Matrix m n K) : logHeight A ≤ logHeightAff A := + Height.logHeight_le_logHeightAff _ + +/-- The affine height of a column of `A` is at most the affine height of `A`: a column is a +sub-tuple of the entries, and the appended coordinate `1` is common to both. -/ +theorem mulHeightAff_col_le (A : Matrix m n K) (j : n) : + Height.mulHeightAff (fun i ↦ A i j) ≤ mulHeightAff A := + Height.mulHeightAff_comp_le (fun i ↦ (i, j)) fun q : m × n ↦ A q.1 q.2 + +theorem mulHeight_submatrix_le [Finite m'] [Finite n'] (A : Matrix m n K) (f : m' → m) + (g : n' → n) : mulHeight (A.submatrix f g) ≤ mulHeight A := + Height.mulHeight_comp_le (Prod.map f g) fun q : m × n ↦ A q.1 q.2 + +theorem logHeight_submatrix_le [Finite m'] [Finite n'] (A : Matrix m n K) (f : m' → m) + (g : n' → n) : logHeight (A.submatrix f g) ≤ logHeight A := + Height.logHeight_comp_le (Prod.map f g) fun q : m × n ↦ A q.1 q.2 + +theorem mulHeight_smul (A : Matrix m n K) {c : K} (hc : c ≠ 0) : mulHeight (c • A) = mulHeight A := + Height.mulHeight_smul_eq_mulHeight _ hc + +theorem logHeight_smul (A : Matrix m n K) {c : K} (hc : c ≠ 0) : logHeight (c • A) = logHeight A := + by rw [logHeight_eq_log_mulHeight, logHeight_eq_log_mulHeight, mulHeight_smul _ hc] + +end Basic + +section Mul + +variable [Finite m] [Fintype n] [Finite p] + +/-- **The product bound.** An entry of `A * B` is a sum of +`Fintype.card n` entries of the multiplication table of the entries of `A` and of `B`, so the +tuple sum bound of Layer 2.4 applies with `#s = Fintype.card n`, and the Segre relation evaluates +the height of that table as `mulHeight A * mulHeight B`. + +`n` must be nonempty: for `n` empty `A * B = 0` has the junk height `1` against a right-hand side +of `0`. The constant is sharp, and its exponent `totalWeight K` is needed as soon as `K` has +degree `> 1`; both are the examples at the end of this file. In the Arakelov normalization the +constant disappears entirely, `Matrix.arakelovMulHeight_mul_le`. -/ +theorem mulHeight_mul_le [Nonempty n] (A : Matrix m n K) (B : Matrix n p K) : + mulHeight (A * B) ≤ (Fintype.card n : ℝ) ^ totalWeight K * (mulHeight A * mulHeight B) := by + have hcard : (1 : ℝ) ≤ (Fintype.card n : ℝ) ^ totalWeight K := + one_le_pow₀ (by exact_mod_cast Fintype.card_pos) + have key := Height.mulHeight_sum_comp_le' (K := K) (ι := m × p) (α := n) + (n := Fintype.card n) Fintype.card_pos (s := fun _ ↦ (univ : Finset n)) + (fun _ ↦ le_of_eq card_univ) (fun k q ↦ ((q.1, k), (k, q.2))) + (fun r : (m × n) × (n × p) ↦ A r.1.1 r.1.2 * B r.2.1 r.2.2) + have hL : (fun q : m × p ↦ ∑ k ∈ (univ : Finset n), A q.1 k * B k q.2) + = fun q : m × p ↦ (A * B) q.1 q.2 := by + ext q + rw [Matrix.mul_apply] + rw [hL] at key + have hone : (1 : ℝ) ≤ (Fintype.card n : ℝ) ^ totalWeight K * (mulHeight A * mulHeight B) := + hcard.trans <| le_mul_of_one_le_right (by positivity) + (one_le_mul_of_one_le_of_one_le (one_le_mulHeight _) (one_le_mulHeight _)) + rcases eq_or_ne (fun q : m × n ↦ A q.1 q.2) 0 with hA | hA + · have hAB : A * B = 0 := by + have : A = 0 := by ext i k; exact congrFun hA (i, k) + simp [this] + rwa [hAB, mulHeight_zero] + rcases eq_or_ne (fun q : n × p ↦ B q.1 q.2) 0 with hB | hB + · have hAB : A * B = 0 := by + have : B = 0 := by ext k j; exact congrFun hB (k, j) + simp [this] + rwa [hAB, mulHeight_zero] + rwa [Height.mulHeight_fun_mul_eq hA hB] at key + +/-- The logarithmic form of `Matrix.mulHeight_mul_le`. -/ +theorem logHeight_mul_le [Nonempty n] (A : Matrix m n K) (B : Matrix n p K) : + logHeight (A * B) ≤ totalWeight K * log (Fintype.card n) + (logHeight A + logHeight B) := by + have hcard : (0 : ℝ) < (Fintype.card n : ℝ) := by exact_mod_cast Fintype.card_pos (α := n) + simp only [logHeight_eq_log_mulHeight] + refine (log_le_log (mulHeight_pos _) (mulHeight_mul_le A B)).trans_eq ?_ + rw [log_mul (pow_ne_zero _ hcard.ne') (mul_ne_zero (mulHeight_ne_zero A) (mulHeight_ne_zero B)), + log_mul (mulHeight_ne_zero A) (mulHeight_ne_zero B), log_pow] + +/-- **The action on tuples**, Mathlib's `Height.mulHeight_linearMap_apply_le` read through the +definition of `Matrix.mulHeight`. Nothing is reproved here. -/ +theorem mulHeight_mulVec_le [Nonempty n] (A : Matrix m n K) (x : n → K) : + Height.mulHeight (A.mulVec x) + ≤ (Fintype.card n : ℝ) ^ totalWeight K * mulHeight A * Height.mulHeight x := by + have := Height.mulHeight_linearMap_apply_le (fun q : m × n ↦ A q.1 q.2) x + rwa [Nat.card_eq_fintype_card] at this + +/-- The logarithmic form of `Matrix.mulHeight_mulVec_le`. -/ +theorem logHeight_mulVec_le [Nonempty n] (A : Matrix m n K) (x : n → K) : + Height.logHeight (A.mulVec x) + ≤ totalWeight K * log (Fintype.card n) + (logHeight A + Height.logHeight x) := by + have hcard : (0 : ℝ) < (Fintype.card n : ℝ) := by exact_mod_cast Fintype.card_pos (α := n) + simp only [logHeight_eq_log_mulHeight, Height.logHeight_eq_log_mulHeight] + refine (log_le_log (Height.mulHeight_pos _) (mulHeight_mulVec_le A x)).trans_eq ?_ + rw [mul_assoc, log_mul (pow_ne_zero _ hcard.ne') + (mul_ne_zero (mulHeight_ne_zero A) (Height.mulHeight_ne_zero x)), + log_mul (mulHeight_ne_zero A) (Height.mulHeight_ne_zero x), log_pow] + +end Mul + +section Det + +variable [Fintype n] [DecidableEq n] + +/-- **The height of a determinant**, from the Leibniz expansion. The determinant is a sum of +`(Fintype.card n)!` products, one entry from each column, so the signed tuple sum bound applies to +the multiplication table of the affine columns and the Segre relation of +`Height.mulHeight_fun_prod_eq` evaluates its height as the product of the affine column heights. + +The right-hand side cannot be made scaling-invariant: see +`Matrix.exists_not_mulHeight₁_det_le`. -/ +theorem mulHeight₁_det_le (A : Matrix n n K) : + Height.mulHeight₁ A.det ≤ (Nat.factorial (Fintype.card n) : ℝ) ^ totalWeight K + * ∏ j, Height.mulHeightAff fun i ↦ A i j := by + classical + set x : n → Option n → K := fun j o ↦ o.elim 1 fun i ↦ A i j with hxdef + have hxne : ∀ j, x j ≠ 0 := fun j ↦ Height.optionElim_one_ne_zero _ + set w : (n → Option n) → K := fun I ↦ ∏ j, x j (I j) with hwdef + have hwh : Height.mulHeight w = ∏ j, Height.mulHeightAff fun i ↦ A i j := + Height.mulHeight_fun_prod_eq hxne + set ee : Equiv.Perm n → Fin 2 → K := fun σ ↦ ![((Equiv.Perm.sign σ : ℤ) : K), 1] with heedef + have hee : ∀ σ i, ee σ i = 1 ∨ ee σ i = -1 := by + intro σ i + fin_cases i + · rcases Int.units_eq_one_or (Equiv.Perm.sign σ) with h | h <;> simp [heedef, h] + · simp [heedef] + set s : Fin 2 → Finset (Equiv.Perm n) := ![Finset.univ, {1}] with hsdef + have hcard : ∀ i, #(s i) ≤ Nat.factorial (Fintype.card n) := by + intro i + fin_cases i + · simp [hsdef, Finset.card_univ, Fintype.card_perm] + · simpa [hsdef] using (Nat.factorial_pos (Fintype.card n)).nat_succ_le + set f : Equiv.Perm n → Fin 2 → (n → Option n) := + fun σ ↦ ![fun j ↦ some (σ j), fun _ ↦ none] with hfdef + have key := Height.mulHeight_sum_sign_comp_le' (K := K) (Nat.factorial_pos (Fintype.card n)) + hcard hee f w + have hval : (fun i ↦ ∑ a ∈ s i, ee a i * w (f a i)) = ![A.det, 1] := by + ext i + fin_cases i + · simp [hsdef, hwdef, hxdef, heedef, hfdef, Matrix.det_apply'] + · simp [hsdef, hwdef, hxdef, heedef, hfdef] + rw [hval, hwh] at key + rwa [Height.mulHeight₁_eq_mulHeight] + +/-- The logarithmic form of `Matrix.mulHeight₁_det_le`. -/ +theorem logHeight₁_det_le (A : Matrix n n K) : + Height.logHeight₁ A.det ≤ totalWeight K * log (Nat.factorial (Fintype.card n)) + + ∑ j, Height.logHeightAff fun i ↦ A i j := by + have hf : (0 : ℝ) < Nat.factorial (Fintype.card n) := by + exact_mod_cast Nat.factorial_pos (Fintype.card n) + simp only [Height.logHeight₁_eq_log_mulHeight₁, Height.logHeightAff_eq_log_mulHeightAff] + rw [← log_prod fun j _ ↦ Height.mulHeightAff_ne_zero (fun i ↦ A i j)] + refine (log_le_log (Height.mulHeight₁_pos _) (mulHeight₁_det_le A)).trans_eq ?_ + rw [log_mul (pow_ne_zero _ hf.ne') + (Finset.prod_ne_zero_iff.mpr fun j _ ↦ Height.mulHeightAff_ne_zero _), log_pow] + +/-- `Matrix.mulHeight₁_det_le` with the affine height of the whole matrix in place of the affine +heights of its columns. This is the form the roadmap states, except that the height on the right +is the affine one. -/ +theorem mulHeight₁_det_le_mulHeightAff_pow (A : Matrix n n K) : + Height.mulHeight₁ A.det ≤ (Nat.factorial (Fintype.card n) : ℝ) ^ totalWeight K + * mulHeightAff A ^ Fintype.card n := by + refine (mulHeight₁_det_le A).trans (mul_le_mul_of_nonneg_left ?_ (by positivity)) + calc ∏ j, Height.mulHeightAff (fun i ↦ A i j) ≤ ∏ _j : n, mulHeightAff A := + Finset.prod_le_prod₀ (fun j _ ↦ (Height.mulHeightAff_pos _).le) + fun j _ ↦ mulHeightAff_col_le A j + _ = mulHeightAff A ^ Fintype.card n := by rw [Finset.prod_const, Finset.card_univ] + +/-- The logarithmic form of `Matrix.mulHeight₁_det_le_mulHeightAff_pow`. -/ +theorem logHeight₁_det_le_mulHeightAff_pow (A : Matrix n n K) : + Height.logHeight₁ A.det ≤ totalWeight K * log (Nat.factorial (Fintype.card n)) + + Fintype.card n * logHeightAff A := by + have hf : (0 : ℝ) < Nat.factorial (Fintype.card n) := by + exact_mod_cast Nat.factorial_pos (Fintype.card n) + simp only [Height.logHeight₁_eq_log_mulHeight₁, logHeightAff_eq_log_mulHeightAff] + refine (log_le_log (Height.mulHeight₁_pos _) (mulHeight₁_det_le_mulHeightAff_pow A)).trans_eq ?_ + rw [log_mul (pow_ne_zero _ hf.ne') (pow_ne_zero _ (mulHeightAff_ne_zero A)), log_pow, log_pow] + +end Det + +/-- **The determinant is not bounded by the projective height of the matrix.** For a `1 × 1` +matrix the index type is a subsingleton, so `mulHeight A = 1` whatever the entry is, while the +determinant is that entry. This is the same scaling obstruction as in Layer 2.4: `A ↦ c • A` +fixes `mulHeight A` and multiplies `A.det` by `c ^ n`. The affine height of +`Matrix.mulHeight₁_det_le` is what the statement needs. -/ +theorem exists_not_mulHeight₁_det_le (c : ℝ) : + ∃ A : Matrix (Fin 1) (Fin 1) ℚ, ¬ Height.mulHeight₁ A.det ≤ c * mulHeight A := by + obtain ⟨N, hN⟩ := exists_nat_gt (max c 1) + have hN1 : (1 : ℝ) < N := (le_max_right c 1).trans_lt hN + have hN0 : N ≠ 0 := by + rintro rfl + norm_num at hN1 + have : NeZero N := ⟨hN0⟩ + refine ⟨Matrix.of ![![(N : ℚ)]], ?_⟩ + rw [mulHeight, Height.mulHeight_eq_one_of_subsingleton, mul_one, Matrix.det_fin_one, + show (Matrix.of ![![(N : ℚ)]] : Matrix (Fin 1) (Fin 1) ℚ) 0 0 = ((N : ℕ) : ℚ) from rfl, + Rat.mulHeight₁_natCast] + exact not_le.2 ((le_max_left c 1).trans_lt hN) + +end Matrix + +namespace Matrix + +variable {K : Type*} [Field K] [NumberField K] {m n p : Type*} [Fintype m] [Fintype n] [Fintype p] + +/-! +### Matrices in the Arakelov normalization + +At an infinite place the local factor of the Arakelov height is the Frobenius norm of the matrix, +and Cauchy–Schwarz entry by entry makes it submultiplicative; at a finite place the sup norm is +submultiplicative by the ultrametric inequality. So the constant +`Fintype.card n ^ totalWeight K` of `Matrix.mulHeight_mul_le` disappears. +-/ + +/-- **The Arakelov height of a matrix**: the Arakelov height of the tuple of its entries, so the +Frobenius norm at each infinite place and the sup norm at each finite one. -/ +@[expose] noncomputable def arakelovMulHeight (A : Matrix m n K) : ℝ := + NumberField.arakelovMulHeight fun q : m × n ↦ A q.1 q.2 + +/-- The logarithmic Arakelov height of a matrix. -/ +@[expose] noncomputable def arakelovLogHeight (A : Matrix m n K) : ℝ := log (arakelovMulHeight A) + +theorem arakelovLogHeight_eq_log_arakelovMulHeight (A : Matrix m n K) : + arakelovLogHeight A = log (arakelovMulHeight A) := + rfl + +@[simp] +theorem arakelovMulHeight_zero : arakelovMulHeight (0 : Matrix m n K) = 1 := + NumberField.arakelovMulHeight_zero + +theorem one_le_arakelovMulHeight (A : Matrix m n K) : 1 ≤ arakelovMulHeight A := + NumberField.one_le_arakelovMulHeight _ + +theorem arakelovMulHeight_pos (A : Matrix m n K) : 0 < arakelovMulHeight A := + NumberField.arakelovMulHeight_pos _ + +theorem arakelovMulHeight_ne_zero (A : Matrix m n K) : arakelovMulHeight A ≠ 0 := + NumberField.arakelovMulHeight_ne_zero _ + +theorem arakelovLogHeight_nonneg (A : Matrix m n K) : 0 ≤ arakelovLogHeight A := + NumberField.arakelovLogHeight_nonneg _ + +theorem mulHeight_le_arakelovMulHeight (A : Matrix m n K) : + mulHeight A ≤ arakelovMulHeight A := + NumberField.mulHeight_le_arakelovMulHeight _ + +omit [NumberField K] in +/-- Cauchy–Schwarz entry by entry at an infinite place: the squared Frobenius norm of `A * B` is +at most the product of those of `A` and of `B`. This is where the cardinality of the inner index +type is not paid. -/ +private lemma sum_sq_mul_le (v : NumberField.InfinitePlace K) (A : Matrix m n K) + (B : Matrix n p K) : + ∑ q : m × p, v ((A * B) q.1 q.2) ^ 2 + ≤ (∑ r : m × n, v (A r.1 r.2) ^ 2) * ∑ t : n × p, v (B t.1 t.2) ^ 2 := by + calc ∑ q : m × p, v ((A * B) q.1 q.2) ^ 2 = ∑ i, ∑ j, v ((A * B) i j) ^ 2 := + Fintype.sum_prod_type .. + _ ≤ ∑ i, ∑ j, (∑ k, v (A i k) ^ 2) * ∑ k, v (B k j) ^ 2 := by + refine Finset.sum_le_sum fun i _ ↦ Finset.sum_le_sum fun j _ ↦ ?_ + calc v ((A * B) i j) ^ 2 = v (∑ k, A i k * B k j) ^ 2 := by rw [Matrix.mul_apply] + _ ≤ (∑ k, v (A i k) * v (B k j)) ^ 2 := by + gcongr ?_ ^ 2 + refine (v.1.sum_le _ _).trans_eq (Finset.sum_congr rfl fun k _ ↦ ?_) + exact map_mul .. + _ ≤ _ := Finset.sum_mul_sq_le_sq_mul_sq _ _ _ + _ = (∑ i, ∑ k, v (A i k) ^ 2) * ∑ j, ∑ k, v (B k j) ^ 2 := (Finset.sum_mul_sum ..).symm + _ = (∑ r : m × n, v (A r.1 r.2) ^ 2) * ∑ t : n × p, v (B t.1 t.2) ^ 2 := by + rw [Fintype.sum_prod_type, Fintype.sum_prod_type] + congr 1 + exact Finset.sum_comm + +omit [NumberField K] [Fintype m] [Fintype p] in +/-- The ultrametric inequality at a finite place: the sup norm is submultiplicative on matrix +products. -/ +private lemma iSup_mul_le [Finite m] [Finite p] {v : AbsoluteValue K ℝ} + (hv : IsNonarchimedean v) (A : Matrix m n K) (B : Matrix n p K) : + ⨆ q : m × p, v ((A * B) q.1 q.2) + ≤ (⨆ r : m × n, v (A r.1 r.2)) * ⨆ t : n × p, v (B t.1 t.2) := by + have hA : (0 : ℝ) ≤ ⨆ r : m × n, v (A r.1 r.2) := Real.iSup_nonneg fun _ ↦ v.nonneg _ + have hB : (0 : ℝ) ≤ ⨆ t : n × p, v (B t.1 t.2) := Real.iSup_nonneg fun _ ↦ v.nonneg _ + rcases isEmpty_or_nonempty (m × p) with h | h + · simpa using mul_nonneg hA hB + refine ciSup_le fun q ↦ ?_ + rw [Matrix.mul_apply] + refine hv.apply_sum_univ_le.trans ?_ + rcases isEmpty_or_nonempty n with hn | hn + · simp + refine ciSup_le fun k ↦ ?_ + rw [map_mul] + gcongr + · exact Finite.le_ciSup_of_le (q.1, k) le_rfl + · exact Finite.le_ciSup_of_le (k, q.2) le_rfl + +/-- **The Arakelov product bound, with constant `1`.** Against +`Fintype.card n ^ totalWeight K` for the sup-norm height of `Matrix.mulHeight_mul_le`, and with no +hypothesis on the index types at all — for `n` empty, `A * B = 0` has the junk height `1`, which +is at most the right-hand side. This is the matrix form of +`NumberField.arakelovMulHeight_linearMap_apply_le`. -/ +theorem arakelovMulHeight_mul_le (A : Matrix m n K) (B : Matrix n p K) : + arakelovMulHeight (A * B) ≤ arakelovMulHeight A * arakelovMulHeight B := by + rcases eq_or_ne (fun q : m × p ↦ (A * B) q.1 q.2) 0 with h0 | h0 + · rw [arakelovMulHeight, h0, NumberField.arakelovMulHeight_zero] + exact one_le_mul_of_one_le_of_one_le (one_le_arakelovMulHeight A) (one_le_arakelovMulHeight B) + have hA : (fun r : m × n ↦ A r.1 r.2) ≠ 0 := by + intro h + have hA0 : A = 0 := by ext i k; exact congrFun h (i, k) + exact h0 (funext fun q ↦ by simp [hA0]) + have hB : (fun t : n × p ↦ B t.1 t.2) ≠ 0 := by + intro h + have hB0 : B = 0 := by ext k j; exact congrFun h (k, j) + exact h0 (funext fun q ↦ by simp [hB0]) + have hfinA : + (fun v : NumberField.FinitePlace K ↦ ⨆ r : m × n, v (A r.1 r.2)).HasFiniteMulSupport := + Height.hasFiniteMulSupport_iSup_nonarchAbsVal hA + have hfinB : + (fun v : NumberField.FinitePlace K ↦ ⨆ t : n × p, v (B t.1 t.2)).HasFiniteMulSupport := + Height.hasFiniteMulSupport_iSup_nonarchAbsVal hB + have hfin0 : + (fun v : NumberField.FinitePlace K ↦ ⨆ q : m × p, v ((A * B) q.1 q.2)).HasFiniteMulSupport := + Height.hasFiniteMulSupport_iSup_nonarchAbsVal h0 + rw [arakelovMulHeight, arakelovMulHeight, arakelovMulHeight, + NumberField.arakelovMulHeight_eq h0, NumberField.arakelovMulHeight_eq hA, + NumberField.arakelovMulHeight_eq hB, mul_mul_mul_comm] + refine mul_le_mul ?_ ?_ ?_ ?_ + · rw [← Finset.prod_mul_distrib] + refine Finset.prod_le_prod₀ (fun v _ ↦ by positivity) fun v _ ↦ ?_ + rw [← Real.mul_rpow (by positivity) (by positivity)] + exact Real.rpow_le_rpow (by positivity) (sum_sq_mul_le v A B) (by positivity) + · rw [← _root_.finprod_mul_distrib hfinA hfinB] + exact finprod_le_finprod₀ hfin0 (fun v ↦ Real.iSup_nonneg fun _ ↦ apply_nonneg _ _) + (hfinA.mul hfinB) fun v ↦ iSup_mul_le (NumberField.FinitePlace.add_le v) A B + · exact _root_.finprod_nonneg fun v ↦ Real.iSup_nonneg fun _ ↦ apply_nonneg _ _ + · positivity + +/-- The logarithmic form of `Matrix.arakelovMulHeight_mul_le`: in the Arakelov normalization a +matrix product costs nothing beyond the two heights. -/ +theorem arakelovLogHeight_mul_le (A : Matrix m n K) (B : Matrix n p K) : + arakelovLogHeight (A * B) ≤ arakelovLogHeight A + arakelovLogHeight B := by + simp only [arakelovLogHeight_eq_log_arakelovMulHeight] + refine (log_le_log (arakelovMulHeight_pos _) (arakelovMulHeight_mul_le A B)).trans_eq ?_ + rw [log_mul (arakelovMulHeight_ne_zero A) (arakelovMulHeight_ne_zero B)] + +/-- The action on tuples in the Arakelov normalization, from +`NumberField.arakelovMulHeight_linearMap_apply_le`. -/ +theorem arakelovMulHeight_mulVec_le (A : Matrix m n K) (x : n → K) : + NumberField.arakelovMulHeight (A.mulVec x) + ≤ arakelovMulHeight A * NumberField.arakelovMulHeight x := + NumberField.arakelovMulHeight_linearMap_apply_le (fun q : m × n ↦ A q.1 q.2) x + +/-- The logarithmic form of `Matrix.arakelovMulHeight_mulVec_le`. -/ +theorem arakelovLogHeight_mulVec_le (A : Matrix m n K) (x : n → K) : + NumberField.arakelovLogHeight (A.mulVec x) + ≤ arakelovLogHeight A + NumberField.arakelovLogHeight x := + NumberField.arakelovLogHeight_linearMap_apply_le (fun q : m × n ↦ A q.1 q.2) x + +end Matrix + +/-! +### Examples + +The tests that fix the constants of the product bound: that `Fintype.card n ^ totalWeight K` is +attained, that its *exponent* is needed as soon as `K` has degree `> 1` — the rejection test the +roadmap states over `ℚ(i)`, here for every number field at once — that `[Nonempty n]` cannot be +dropped, and that the Arakelov constant `1` is attained. +-/ + +section Examples + +open Height NumberField + +private def exA : Matrix (Fin 1) (Fin 2) ℚ := Matrix.of ![![1, 1]] + +private def exB : Matrix (Fin 2) (Fin 2) ℚ := Matrix.of ![![1, 0], ![1, 1]] + +private lemma mulHeight_exA : Matrix.mulHeight exA = 1 := by + have h : (fun q : Fin 1 × Fin 2 ↦ exA q.1 q.2) = 1 := by + ext q + obtain ⟨i, j⟩ := q + fin_cases i + fin_cases j <;> rfl + rw [Matrix.mulHeight, h, mulHeight_one] + +private lemma mulHeight_exB : Matrix.mulHeight exB = 1 := by + have h : (fun q : Fin 2 × Fin 2 ↦ exB q.1 q.2) + = ((↑) : ℤ → ℚ) ∘ fun q : Fin 2 × Fin 2 ↦ ![![1, 0], ![1, 1]] q.1 q.2 := by + ext q + obtain ⟨i, j⟩ := q + fin_cases i <;> fin_cases j <;> rfl + have hsup : (⨆ q : Fin 2 × Fin 2, |(![![1, 0], ![1, 1]] : Fin 2 → Fin 2 → ℤ) q.1 q.2|) = 1 := + le_antisymm + (ciSup_le fun q ↦ by obtain ⟨i, j⟩ := q; fin_cases i <;> fin_cases j <;> decide) + (Finite.le_ciSup_of_le (0, 0) (by decide)) + rw [Matrix.mulHeight, h, Rat.mulHeight_eq_max_abs_of_gcd_eq_one (by decide), hsup] + norm_num + +private lemma mulHeight_exA_mul_exB : Matrix.mulHeight (exA * exB) = 2 := by + have h : (fun q : Fin 1 × Fin 2 ↦ (exA * exB) q.1 q.2) + = ((↑) : ℤ → ℚ) ∘ fun q : Fin 1 × Fin 2 ↦ ![2, 1] q.2 := by + ext q + obtain ⟨i, j⟩ := q + fin_cases i + fin_cases j <;> norm_num [exA, exB, Matrix.mul_apply, Fin.sum_univ_two] + have hsup : (⨆ q : Fin 1 × Fin 2, |(![2, 1] : Fin 2 → ℤ) q.2|) = 2 := + le_antisymm + (ciSup_le fun q ↦ by + obtain ⟨i, j⟩ := q + fin_cases i + fin_cases j <;> decide) + (Finite.le_ciSup_of_le (0, 0) (by decide)) + rw [Matrix.mulHeight, h, Rat.mulHeight_eq_max_abs_of_gcd_eq_one (by decide), hsup] + norm_num + +/-- **Sharpness of the product bound over `ℚ`.** With `A = ![![1, 1]]` and +`B = ![![1, 0], ![1, 1]]` the product is `![![2, 1]]`, of height `2`, while both factors have +height `1` and `Fintype.card (Fin 2) ^ totalWeight ℚ = 2`. The constant of +`Matrix.mulHeight_mul_le` is attained. -/ +example : + Matrix.mulHeight (exA * exB) = 2 ∧ Matrix.mulHeight exA = 1 ∧ Matrix.mulHeight exB = 1 + ∧ (Fintype.card (Fin 2) : ℝ) ^ totalWeight ℚ = 2 := by + have hw : totalWeight ℚ = 1 := by + rw [NumberField.totalWeight_eq_finrank, Module.finrank_self] + exact ⟨mulHeight_exA_mul_exB, mulHeight_exA, mulHeight_exB, by rw [hw]; norm_num⟩ + +private lemma mulHeight_map_eq {K : Type*} [Field K] [NumberField K] {m' n' : Type*} [Finite m'] + [Finite n'] (M : Matrix m' n' ℚ) : + Matrix.mulHeight (M.map (algebraMap ℚ K)) = Matrix.mulHeight M ^ Module.finrank ℚ K := by + have he : (fun q : m' × n' ↦ (M.map (algebraMap ℚ K)) q.1 q.2) + = algebraMap ℚ K ∘ fun q : m' × n' ↦ M q.1 q.2 := rfl + rw [Matrix.mulHeight, Matrix.mulHeight, he] + exact (NumberField.mulHeight_pow_finrank _).symm + +/-- **The exponent `totalWeight K` is needed.** Reading the same two matrices in a number field +`K` raises every height to the power `totalWeight K = [K : ℚ]`, so the product bound is attained +with the constant `2 ^ totalWeight K` and false with any smaller one. Over a quadratic field such +as `ℚ(i)` the absolute constant `Fintype.card (Fin 2) = 2` already fails, which is the roadmap's +`ℚ(i)` rejection test — here for every number field at once. -/ +example (K : Type*) [Field K] [NumberField K] : + Matrix.mulHeight (exA.map (algebraMap ℚ K) * exB.map (algebraMap ℚ K)) = 2 ^ totalWeight K + ∧ Matrix.mulHeight (exA.map (algebraMap ℚ K)) = 1 + ∧ Matrix.mulHeight (exB.map (algebraMap ℚ K)) = 1 := by + have hdeg : totalWeight K = Module.finrank ℚ K := NumberField.totalWeight_eq_finrank K + refine ⟨?_, ?_, ?_⟩ + · rw [← Matrix.map_mul, mulHeight_map_eq, mulHeight_exA_mul_exB, hdeg] + · rw [mulHeight_map_eq, mulHeight_exA, one_pow] + · rw [mulHeight_map_eq, mulHeight_exB, one_pow] + +/-- **`[Nonempty n]` cannot be dropped from the product bound.** For `n` empty the product is the +zero matrix, of junk height `1`, while the right-hand side has the factor +`(Fintype.card n : ℝ) ^ totalWeight K = 0`. -/ +example : + ¬ Matrix.mulHeight ((0 : Matrix (Fin 1) (Fin 0) ℚ) * (0 : Matrix (Fin 0) (Fin 1) ℚ)) + ≤ (Fintype.card (Fin 0) : ℝ) ^ totalWeight ℚ + * (Matrix.mulHeight (0 : Matrix (Fin 1) (Fin 0) ℚ) + * Matrix.mulHeight (0 : Matrix (Fin 0) (Fin 1) ℚ)) := by + have hw : totalWeight ℚ = 1 := by + rw [NumberField.totalWeight_eq_finrank, Module.finrank_self] + rw [Matrix.zero_mul, Matrix.mulHeight_zero, Matrix.mulHeight_zero, Matrix.mulHeight_zero, hw] + norm_num + +/-- **Sharpness of the Arakelov constant.** Multiplying by the `1 × 1` identity matrix changes +nothing, and the identity matrix has Arakelov height `1` because its index type is a +subsingleton; so `Matrix.arakelovMulHeight_mul_le` is an equality and the constant `1` cannot be +lowered. -/ +example {K : Type*} [Field K] [NumberField K] {m : Type*} [Fintype m] (A : Matrix m (Fin 1) K) : + Matrix.arakelovMulHeight (A * (1 : Matrix (Fin 1) (Fin 1) K)) + = Matrix.arakelovMulHeight A * Matrix.arakelovMulHeight (1 : Matrix (Fin 1) (Fin 1) K) := by + have h1 : Matrix.arakelovMulHeight (1 : Matrix (Fin 1) (Fin 1) K) = 1 := + arakelovMulHeight_eq_one_of_subsingleton _ + rw [Matrix.mul_one, h1, mul_one] + +end Examples + +end diff --git a/LeanPool/CCDN/ArithmeticHeights/MinkowskiSecond.lean b/LeanPool/CCDN/ArithmeticHeights/MinkowskiSecond.lean new file mode 100644 index 0000000000..964d367cb6 --- /dev/null +++ b/LeanPool/CCDN/ArithmeticHeights/MinkowskiSecond.lean @@ -0,0 +1,695 @@ +/- +Copyright (c) 2026 Ralf Stephan. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Ralf Stephan +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.ArithmeticHeights.AdaptedBasis +public import LeanPool.CCDN.ArithmeticHeights.QuotientFubini +public import LeanPool.CCDN.ArithmeticHeights.SuccessiveMinima +public import Mathlib.Analysis.Convex.Measure +public import Mathlib.MeasureTheory.Measure.Haar.Disintegration +public import Mathlib.MeasureTheory.Measure.Lebesgue.VolumeOfBalls + +/-! +# Minkowski's second theorem + +For a lattice `L` in a finite-dimensional real normed space `E` of dimension `n` and a symmetric +convex body `B`, Minkowski's second theorem is the pair of inequalities + +```text +2 ^ n / n ! * covolume L ≤ (∏ i < n, λ i) * volume B ≤ 2 ^ n * covolume L, +``` + +with `λ i` the successive minima of Layer 4.1. This file proves both, and the two pieces of +Cassels' route that are statements in their own right. + +## Main results + +* `ZLattice.covolume_le_prod_successiveMinimum_mul_measure`: the lower bound. The independent + lattice vectors realizing the minima, divided by their minima, span a cross-polytope inside `B` + of volume `2 ^ n / n !` times the covolume of the lattice they generate, divided by `∏ i, λ i`; + and that covolume is a positive integer multiple of the covolume of `L`. +* `ZLattice.prod_successiveMinimum_mul_measure_le`: the upper bound, the substantial half — the + direction Layer 5 and 6.3 consume. Weyl's proof, through Cassels' Theorem IV, which is + `ZLattice.pow_mul_measure_inter_add_le` of `ArithmeticHeights/QuotientFubini.lean`: the measure + of the image of the open dilate `{gauge B < t}` in `E ⧸ L` is scaled up one minimum at a time, + from `t = λ 0 / 2` — where the dilate injects into the quotient — to `t = λ (n - 1) / 2`, where + it is bounded by the covolume. +* `ZLattice.pow_successiveMinimum_zero_mul_measure_le`: `λ 0 ^ n * volume B ≤ 2 ^ n * covolume L`, + which is Minkowski's first theorem read homogeneously. It is the upper bound with `∏ i < n, λ i` + weakened to `λ 0 ^ n`, and it is everything the first theorem gives. +* `ZLattice.exists_basis_mem_span_int_of_gauge_lt`: Cassels' Chapter VIII, Lemma 2 — a `ℤ`-basis + of `L` against which every lattice point of gauge below the `j`-th minimum is an *integer* + combination of the first `j` members. Layer 4.6 consumes it; the upper bound below does not. + +## Implementation notes + +⚠ **The reduction that would make the upper bound easy is false.** The worked example at the end +of this file machine-checks that shrinking one coordinate of a symmetric convex body can leave the +body. The linear map sending the realizing vector `v i` to `(λ i / λ (n - 1)) • v i` has +determinant `∏ λ i / λ (n - 1) ^ n` and nondecreasing coordinate factors in `(0, 1]`, which is +exactly the shape that example refutes: such a map need not send the body into itself, so the +upper bound is not the statement that some one set is a packing, and no rescaling of the body +makes it one. Weyl's proof replaces the set inclusion by a measure estimate in the quotient +`E ⧸ L`, and is a genuinely different argument. + +⚠ **Neither inequality needs the body closed, bounded away from nothing, or compact.** The lower +bound uses the vectors through `gauge B (v i) = λ i` and meets the body through +`mem_smul_of_gauge_lt`, the half of the gauge dictionary that holds for any body. The upper bound +is chained on the *open* dilates `{gauge B < t}`, which is what lets the separation hypothesis of +Cassels' Theorem IV hold at `2 t = λ J` rather than only below it; the body and its interior have +the same measure because a convex set has null frontier. Only +`ZLattice.pow_successiveMinimum_zero_mul_measure_le` carries `IsCompact B`, and only because +Mathlib's form of Minkowski's first theorem does. + +⚠ **Cassels' Lemma 2 is not on the path to the upper bound.** He needs the adapted basis because +he works in coordinates, where the integrality of the first `J` coordinates is how one sees that +the translation stays inside the sublattice. Coordinate-free the sublattice +`L ∩ span (v 0, …, v (J - 1))` is at hand, and the only thing the estimate wants of it is its +`ℤ`-rank, which is the `ℝ`-dimension of its span by `ZLattice.finrank_int_eq_finrank_real`. The +lemma stays in this file because Layer 4.6 does want the basis. + +## References + +J. W. S. Cassels, *An Introduction to the Geometry of Numbers*, Springer (1959), Chapter VIII §4, +Theorem V: both inequalities, as (12) and (13) of VIII.1. The lower bound is the cross-polytope +computation on p. 218; `pow_successiveMinimum_zero_mul_measure_le` is the inequality (11) there; +the upper bound is Weyl's argument of §4.2, whose kernel is Cassels' Theorem IV. +E. Bombieri and W. Gubler, *Heights in Diophantine Geometry*, Cambridge University Press (2006), +Appendix C.2. + +This is Layer 4.2 of the `ArithmeticHeights` roadmap. +-/ + +public section + +open Metric Module MeasureTheory Set + +open scoped ENNReal Pointwise Topology + +variable {E : Type*} [NormedAddCommGroup E] [NormedSpace ℝ E] {B : Set E} + +namespace ZLattice + +variable [FiniteDimensional ℝ E] [MeasurableSpace E] [BorelSpace E] {L : Submodule ℤ E} + +/-! ### The lower bound -/ + +/-- The cross-polytope on a basis: the points whose coordinates have `ℓ¹`-norm less than one. -/ +private def crossPolytope {n : ℕ} (b : Basis (Fin n) ℝ E) : Set E := + b.equivFun ⁻¹' {x : Fin n → ℝ | ∑ i, |x i| < 1} + +omit [FiniteDimensional ℝ E] [MeasurableSpace E] [BorelSpace E] in +/-- The cross-polytope on a basis of vectors of gauge one lies inside the body. This is where the +gauge dictionary is used, in the half that needs no closedness. -/ +private theorem crossPolytope_subset {n : ℕ} (b : Basis (Fin n) ℝ E) (hB₀ : Convex ℝ B) + (h₀ : B ∈ 𝓝 (0 : E)) (hB₁ : ∀ x ∈ B, -x ∈ B) (hb : ∀ j, gauge B (b j) = 1) : + crossPolytope b ⊆ B := by + have hbal : Balanced ℝ B := (balanced_iff_neg_mem hB₀).2 fun x hx ↦ hB₁ x hx + intro x hx + have hx' : ∑ i, |b.repr x i| < 1 := hx + have hg : gauge B x ≤ ∑ i, |b.repr x i| := by + conv_lhs => rw [← b.sum_repr x] + refine (gauge_sum_le hB₀ (absorbent_nhds_zero h₀) _ _).trans + (Finset.sum_le_sum fun i _ ↦ ?_) + rw [gauge_smul hbal, hb i, mul_one, Real.norm_eq_abs] + simpa using mem_smul_of_gauge_lt hB₀ h₀ (hg.trans_lt hx') + +/-- The cross-polytope on a basis has measure `2 ^ n / n !` times the fundamental domain of that +basis: both are preimages under the coordinate map of standard sets in `Fin n → ℝ`, and any two +additive Haar measures there are proportional. -/ +private theorem measure_crossPolytope {n : ℕ} (b : Basis (Fin n) ℝ E) (μ : Measure E) + [μ.IsAddHaarMeasure] : + μ (crossPolytope b) = + ENNReal.ofReal (2 ^ n / Nat.factorial n) * μ (ZSpan.fundamentalDomain b) := by + classical + set K : Set (Fin n → ℝ) := {x : Fin n → ℝ | ∑ i, |x i| < 1} with hK + set Q : Set (Fin n → ℝ) := Set.univ.pi fun _ ↦ Set.Ico (0 : ℝ) 1 with hQ + have hmK : MeasurableSet K := by + have : Continuous fun x : Fin n → ℝ ↦ ∑ i, |x i| := by fun_prop + exact this.measurable measurableSet_Iio + have hmQ : MeasurableSet Q := MeasurableSet.univ_pi fun _ ↦ measurableSet_Ico + have hmeas : Measurable (b.equivFun : E → (Fin n → ℝ)) := + (b.equivFun.toLinearMap.continuous_of_finiteDimensional).measurable + obtain ⟨c, _, hc⟩ := b.equivFun.toLinearMap.exists_map_addHaar_eq_smul_addHaar μ volume + b.equivFun.surjective + have hpre : ∀ S : Set (Fin n → ℝ), MeasurableSet S → + μ ((b.equivFun : E → (Fin n → ℝ)) ⁻¹' S) = c * volume S := by + intro S hS + rw [← Measure.map_apply hmeas hS] + change (Measure.map (b.equivFun.toLinearMap : E → (Fin n → ℝ)) μ) S = _ + rw [hc, Measure.smul_apply, smul_eq_mul] + have hfd : ZSpan.fundamentalDomain b = (b.equivFun : E → (Fin n → ℝ)) ⁻¹' Q := by + ext x + simp [ZSpan.fundamentalDomain, hQ, Basis.equivFun_apply] + have hvolQ : volume Q = 1 := by + rw [hQ, volume_pi_pi] + simp + have hvolK : volume K = ENNReal.ofReal (2 ^ n / Nat.factorial n) := by + have hKeq : K = {x : Fin n → ℝ | ∑ i, |x i| ^ (1 : ℝ) < 1} := by + simp only [hK, Real.rpow_one] + have hΓ2 : Real.Gamma 2 = 1 := by + rw [show (2 : ℝ) = 1 + 1 by norm_num, Real.Gamma_add_one one_ne_zero, Real.Gamma_one, + mul_one] + rw [hKeq, MeasureTheory.volume_sum_rpow_lt_one (Fin n) (p := 1) le_rfl] + norm_num [hΓ2, Real.Gamma_nat_eq_factorial n] + rw [crossPolytope, hpre _ hmK, hfd, hpre _ hmQ, hvolK, hvolQ, mul_one] + ring + +/-- **Minkowski's second theorem, the lower bound** (Cassels, Chapter VIII, Theorem V, the +inequality (13) of VIII.1). The independent lattice vectors `v i` realizing the successive minima, +scaled to gauge one, span a cross-polytope of volume `2 ^ n / n !` times `|det v|` inside the body, +and `|det v|` is an integer multiple of the covolume of `L`. + +⚠ No closedness or compactness of the body is needed. -/ +theorem covolume_le_prod_successiveMinimum_mul_measure (L : Submodule ℤ E) [DiscreteTopology L] + [IsZLattice ℝ L] (μ : Measure E) [μ.IsAddHaarMeasure] (hB₀ : Convex ℝ B) + (hB₁ : ∀ x ∈ B, -x ∈ B) (hB₂ : (interior B).Nonempty) (hB₃ : Bornology.IsBounded B) : + 2 ^ finrank ℝ E / (Nat.factorial (finrank ℝ E)) * covolume L μ ≤ + (∏ i ∈ Finset.range (finrank ℝ E), successiveMinimum L B i) * (μ B).toReal := by + classical + set n := finrank ℝ E with hn + have h₀ : B ∈ 𝓝 (0 : E) := hB₀.mem_nhds_zero_of_symmetric hB₁ hB₂ + obtain ⟨v, hvL, hvind, hvg⟩ := + exists_linearIndependent_gauge_eq_successiveMinimum L hB₀ hB₁ hB₂ hB₃ + have hpos : ∀ j : Fin n, 0 < successiveMinimum L B (j : ℕ) := + fun j ↦ successiveMinimum_pos L hB₀ hB₁ hB₂ hB₃ j.isLt + have hcard : Fintype.card (Fin n) = finrank ℝ E := by simp [hn] + -- the scaled family, a basis of vectors of gauge one + set w : Fin n → E := fun j ↦ (successiveMinimum L B (j : ℕ))⁻¹ • v j with hw + have hwind : LinearIndependent ℝ w := + hvind.units_smul fun j : Fin n ↦ + Units.mk0 ((successiveMinimum L B (j : ℕ))⁻¹) (inv_ne_zero (hpos j).ne') + set b₀ : Basis (Fin n) ℝ E := basisOfLinearIndependentOfCardEqFinrank' w hwind hcard with hb₀ + have hb₀c : ⇑b₀ = w := coe_basisOfLinearIndependentOfCardEqFinrank' _ _ _ + have hgw : ∀ j, gauge B (b₀ j) = 1 := by + intro j + rw [hb₀c, hw, gauge_smul_of_nonneg (inv_nonneg.2 (hpos j).le), hvg j, smul_eq_mul, + inv_mul_cancel₀ (hpos j).ne'] + -- the two bases of `E` coming from the lattice and from the minima + have hcardL : Fintype.card (Module.Free.ChooseBasisIndex ℤ L) = n := by + rw [← Module.finrank_eq_card_chooseBasisIndex, ZLattice.rank ℝ L, hn] + set bZ : Basis (Fin n) ℤ L := + (Module.Free.chooseBasis ℤ L).reindex (Fintype.equivFinOfCardEq hcardL) with hbZ + set bR : Basis (Fin n) ℝ E := bZ.ofZLatticeBasis ℝ L with hbR + set bv : Basis (Fin n) ℝ E := basisOfLinearIndependentOfCardEqFinrank' v hvind hcard with hbv + have hbvc : ⇑bv = v := coe_basisOfLinearIndependentOfCardEqFinrank' _ _ _ + -- the determinant of the realizing family in the scaled basis is the product of the minima + have hprod : b₀.det ⇑bv = ∏ j : Fin n, successiveMinimum L B (j : ℕ) := by + have hsm : ⇑bv = fun j : Fin n ↦ successiveMinimum L B (j : ℕ) • b₀ j := by + funext j + rw [hbvc, hb₀c, hw, smul_smul, mul_inv_cancel₀ (hpos j).ne', one_smul] + rw [hsm, b₀.det.map_smul_univ, Basis.det_self, smul_eq_mul, mul_one] + -- the determinant of the realizing family in a lattice basis is a nonzero integer + have hint : ∃ m : ℤ, bR.det ⇑bv = (m : ℝ) := by + refine ⟨bZ.det fun j ↦ (⟨v j, hvL j⟩ : L), ?_⟩ + have hmat : bR.toMatrix ⇑bv + = (bZ.toMatrix fun j ↦ (⟨v j, hvL j⟩ : L)).map ((↑) : ℤ → ℝ) := by + ext i j + rw [Matrix.map_apply, Basis.toMatrix_apply, Basis.toMatrix_apply, hbvc] + exact bZ.ofZLatticeBasis_repr_apply ℝ L ⟨v j, hvL j⟩ i + rw [Basis.det_apply, hmat, Basis.det_apply] + exact (RingHom.map_det (Int.castRingHom ℝ) _).symm + obtain ⟨m, hm⟩ := hint + have hne : bR.det ⇑bv ≠ 0 := by + intro h + have := Basis.det_mul_det bR bv bR + rw [h, zero_mul, Basis.det_self] at this + exact one_ne_zero this.symm + have hone : (1 : ℝ) ≤ |bR.det ⇑bv| := by + rw [hm, ← Int.cast_abs] + exact_mod_cast Int.one_le_abs (by simpa [hm] using hne) + -- the change of basis, and the bound on the determinant of the lattice basis + have hchain : b₀.det ⇑bR * bR.det ⇑bv = ∏ j : Fin n, successiveMinimum L B (j : ℕ) := by + rw [Basis.det_mul_det, hprod] + have hprodpos : 0 < ∏ j : Fin n, successiveMinimum L B (j : ℕ) := + Finset.prod_pos fun j _ ↦ hpos j + have hdetle : |b₀.det ⇑bR| ≤ ∏ j : Fin n, successiveMinimum L B (j : ℕ) := by + have h1 : |b₀.det ⇑bR| * |bR.det ⇑bv| = ∏ j : Fin n, successiveMinimum L B (j : ℕ) := by + rw [← abs_mul, hchain, abs_of_pos hprodpos] + nlinarith [abs_nonneg (b₀.det ⇑bR)] + -- the covolume against the fundamental domain of the scaled basis + have hcov : covolume L μ = |b₀.det ((↑) ∘ bZ)| * μ.real (ZSpan.fundamentalDomain b₀) := + covolume_eq_det_mul_measureReal L μ bZ b₀ + have hcoe : ((↑) ∘ bZ : Fin n → E) = ⇑bR := by + funext j + rw [hbR, Function.comp_apply, Basis.ofZLatticeBasis_apply] + -- the cross-polytope inside the body + have hBfin : μ B ≠ ⊤ := hB₃.measure_lt_top.ne + have hfdfin : μ (ZSpan.fundamentalDomain b₀) ≠ ⊤ := + (ZSpan.fundamentalDomain_isBounded b₀).measure_lt_top.ne + have hCB : μ.real (crossPolytope b₀) ≤ μ.real B := + measureReal_mono (crossPolytope_subset b₀ hB₀ h₀ hB₁ hgw) hBfin + have hCvol : μ.real (crossPolytope b₀) + = 2 ^ n / (Nat.factorial n) * μ.real (ZSpan.fundamentalDomain b₀) := by + rw [measureReal_def, measureReal_def, measure_crossPolytope b₀ μ, ENNReal.toReal_mul, + ENNReal.toReal_ofReal (by positivity)] + have hfdnn : 0 ≤ μ.real (ZSpan.fundamentalDomain b₀) := measureReal_nonneg + have hkey : 2 ^ n / (Nat.factorial n : ℝ) * μ.real (ZSpan.fundamentalDomain b₀) ≤ μ.real B := by + rw [← hCvol]; exact hCB + rw [Fin.prod_univ_eq_prod_range (fun i ↦ successiveMinimum L B i) n] at hdetle hprodpos + have hfac : (0 : ℝ) < 2 ^ n / (Nat.factorial n) := by positivity + have hstep : |b₀.det ⇑bR| * (2 ^ n / (Nat.factorial n : ℝ) * + μ.real (ZSpan.fundamentalDomain b₀)) ≤ + (∏ i ∈ Finset.range n, successiveMinimum L B i) * μ.real B := + le_trans (mul_le_mul_of_nonneg_right hdetle (mul_nonneg hfac.le hfdnn)) + (mul_le_mul_of_nonneg_left hkey hprodpos.le) + rw [hcov, hcoe, ← measureReal_def] + refine le_trans (le_of_eq ?_) hstep + ring + +/-! ### The upper bound for the zeroth minimum -/ + +/-- **Minkowski's first theorem, read homogeneously** (Cassels, Chapter VIII, the inequality (11) +of VIII.1): the zeroth minimum obeys `λ 0 ^ n * volume B ≤ 2 ^ n * covolume L`. This is the upper +bound of Minkowski's second theorem with `∏ i < n, λ i` weakened to `λ 0 ^ n`, and it is all that +the first theorem gives: the substantial half of Layer 4.2 is a different argument. -/ +theorem pow_successiveMinimum_zero_mul_measure_le [Nontrivial E] (L : Submodule ℤ E) + [DiscreteTopology L] [IsZLattice ℝ L] (μ : Measure E) [μ.IsAddHaarMeasure] (hB₀ : Convex ℝ B) + (hB₁ : ∀ x ∈ B, -x ∈ B) (hB₂ : (interior B).Nonempty) (hB₅ : IsCompact B) : + successiveMinimum L B 0 ^ finrank ℝ E * (μ B).toReal ≤ + 2 ^ finrank ℝ E * covolume L μ := by + set n := finrank ℝ E with hn + have hn0 : n ≠ 0 := by + rw [hn] + exact (Module.finrank_pos (R := ℝ) (M := E)).ne' + have hOpenPos : μ.IsOpenPosMeasure := inferInstance + have hBpos : 0 < μ.real B := by + obtain ⟨x, hx⟩ := hB₂ + have : 0 < μ (interior B) := (isOpen_interior).measure_pos μ ⟨x, hx⟩ + have hle : μ (interior B) ≤ μ B := measure_mono interior_subset + have : 0 < μ B := lt_of_lt_of_le this hle + rw [measureReal_def] + exact ENNReal.toReal_pos this.ne' hB₅.measure_lt_top.ne + have hcovpos : 0 < covolume L μ := covolume_pos L μ + set c : ℝ := (2 ^ n * covolume L μ / μ.real B) ^ ((n : ℝ)⁻¹) with hc + have hbase : 0 < 2 ^ n * covolume L μ / μ.real B := by positivity + have hcpos : 0 < c := Real.rpow_pos_of_pos hbase _ + have hcn : c ^ n = 2 ^ n * covolume L μ / μ.real B := + Real.rpow_inv_natCast_pow hbase.le hn0 + -- apply Minkowski's first theorem to the dilated body + have hmeas : μ.real (c • B) = c ^ n * μ.real B := by + rw [measureReal_def, measureReal_def, Measure.addHaar_smul, ENNReal.toReal_mul, + ENNReal.toReal_ofReal (abs_nonneg _), abs_of_nonneg (by positivity), hn] + have hle : 2 ^ n * covolume L μ ≤ (μ (c • B)).toReal := by + rw [← measureReal_def, hmeas, hcn, div_mul_cancel₀ _ hBpos.ne'] + have hmin := successiveMinimum_zero_le_one L μ (hB₀.smul c) + (fun x hx ↦ by + obtain ⟨y, hy, rfl⟩ := hx + exact ⟨-y, hB₁ y hy, by simp⟩) + (hB₅.smul c) (by rw [← hn]; exact hle) + rw [successiveMinimum_smul L B 0 hcpos] at hmin + have hlec : successiveMinimum L B 0 ≤ c := by + rw [inv_mul_le_iff₀ hcpos] at hmin + simpa using hmin + have h0 : 0 ≤ successiveMinimum L B 0 := + le_of_lt (successiveMinimum_pos L hB₀ hB₁ hB₂ hB₅.isBounded Module.finrank_pos) + calc successiveMinimum L B 0 ^ n * (μ B).toReal + ≤ c ^ n * (μ B).toReal := + mul_le_mul_of_nonneg_right (pow_le_pow_left₀ h0 hlec n) ENNReal.toReal_nonneg + _ = 2 ^ n * covolume L μ := by + rw [hcn, ← measureReal_def, div_mul_cancel₀ _ hBpos.ne'] + +/-! ### Cassels' Lemma 2: a basis adapted to the minima -/ + +omit [MeasurableSpace E] [BorelSpace E] in +/-- **Cassels' Chapter VIII, Lemma 2.** Some `ℤ`-basis `b` of `L` has, for every `j`, every lattice +point of gauge below the `j`-th minimum an *integer* combination of `b 0, …, b (j - 1)`. + +This is the first of the two pieces Weyl's proof of the upper bound needs, and it is exactly the +combination of two things proved elsewhere: the dependence half of Cassels' Lemma 1 +(`ZLattice.mem_span_of_gauge_lt_successiveMinimum`, Layer 4.1), which puts such a point in the +`ℝ`-span of the first `j` vectors realizing the minima, and the adapted basis +(`ZLattice.exists_basis_mem_span_int_of_mem_span_real`), which makes the lattice points of that +span the *integer* combinations of the first `j` basis vectors. Cassels proves the second by his +Chapter I, Theorem I; neither half is in Mathlib. + +⚠ The basis is not the family realizing the minima, and in general cannot be: Layer 4.6 is the +statement of how far apart the two can be. What survives is the flag. -/ +theorem exists_basis_mem_span_int_of_gauge_lt (L : Submodule ℤ E) [DiscreteTopology L] + [IsZLattice ℝ L] (hB₀ : Convex ℝ B) (hB₁ : ∀ x ∈ B, -x ∈ B) (hB₂ : (interior B).Nonempty) + (hB₃ : Bornology.IsBounded B) : + ∃ b : Basis (Fin (finrank ℝ E)) ℤ L, ∀ (j : ℕ) (x : E), x ∈ L → + gauge B x < successiveMinimum L B j → + x ∈ Submodule.span ℤ + ((fun i ↦ ((b i : L) : E)) '' {i : Fin (finrank ℝ E) | (i : ℕ) < j}) := by + obtain ⟨v, hvL, hvind, hvg⟩ := + exists_linearIndependent_gauge_eq_successiveMinimum L hB₀ hB₁ hB₂ hB₃ + obtain ⟨b, hb⟩ := exists_basis_mem_span_int_of_mem_span_real L rfl (fun i ↦ hvL i) hvind + exact ⟨b, fun j x hxL hgx ↦ hb j x hxL + (mem_span_of_gauge_lt_successiveMinimum L hB₀ hB₁ hB₂ hvL hvind hvg hxL hgx)⟩ + +/-! ### The upper bound + +Weyl's argument, as Cassels carries it: the measure of the image of the open dilate +`{gauge B < t}` in `E ⧸ L` grows at least like `s ^ (n - J)` when `t` is scaled by `s ≥ 1` inside +the range where congruence modulo `L` is congruence modulo `L ∩ span (v 0, …, v (J - 1))`, and +chaining that from the first minimum to the last multiplies the measure of the body by the product +of the minima. The estimate itself is `ZLattice.pow_mul_measure_inter_add_le` of +`ArithmeticHeights/QuotientFubini.lean`; what is here is the chain and the gauge bookkeeping. -/ + +omit [NormedSpace ℝ E] [FiniteDimensional ℝ E] [MeasurableSpace E] [BorelSpace E] in +/-- Transporting the defining property of a fundamental domain along an equality of lattices. -/ +private theorem existsUnique_of_eq {M N : Submodule ℤ E} (h : M = N) {F : Set E} {x : E} + (hM : ∃! v : M, (v : E) + x ∈ F) : ∃! v : N, (v : E) + x ∈ F := by + obtain ⟨u, hu, huq⟩ := hM + refine ⟨⟨(u : E), h ▸ u.2⟩, hu, fun w hw ↦ ?_⟩ + have hwm : (w : E) ∈ M := h ▸ w.2 + have hwu : (⟨(w : E), hwm⟩ : M) = u := huq _ hw + exact Subtype.ext (congrArg (fun z : M ↦ (z : E)) hwu) + +/-- A fundamental domain for `L` in the exact form the quotient-measure estimates ask for — +`∀ x, ∃! v, ↑v + x ∈ F`, not the almost-everywhere `MeasureTheory.IsAddFundamentalDomain` — +together with the two facts that make it usable: it has finite measure, and that measure is the +covolume. The domain is the fundamental parallelepiped of a `ℤ`-basis. -/ +private theorem exists_fundamentalDomain (L : Submodule ℤ E) [DiscreteTopology L] [IsZLattice ℝ L] + (μ : Measure E) [μ.IsAddHaarMeasure] : + ∃ F : Set E, MeasurableSet F ∧ (∀ x : E, ∃! v : L, (v : E) + x ∈ F) ∧ μ F ≠ ⊤ ∧ + (μ F).toReal = covolume L μ := by + have hfin : Module.Finite ℤ L := ZLattice.module_finite ℝ L + have hfree : Module.Free ℤ L := ZLattice.module_free ℝ L + set bZ := Module.Free.chooseBasis ℤ L with hbZ + set bR := bZ.ofZLatticeBasis ℝ L with hbR + have key : ∀ x : E, ∃! w : L, (w : E) + x ∈ ZSpan.fundamentalDomain bR := fun x ↦ + existsUnique_of_eq (bZ.ofZLatticeBasis_span ℝ) + (ZSpan.exist_unique_vadd_mem_fundamentalDomain bR x) + refine ⟨ZSpan.fundamentalDomain bR, ZSpan.fundamentalDomain_measurableSet bR, key, + (ZSpan.fundamentalDomain_isBounded bR).measure_lt_top.ne, ?_⟩ + rw [covolume_eq_measure_fundamentalDomain L μ (F := ZSpan.fundamentalDomain bR) + (MeasureTheory.IsAddFundamentalDomain.mk' + (ZSpan.fundamentalDomain_measurableSet bR).nullMeasurableSet key), measureReal_def] + +omit [FiniteDimensional ℝ E] [MeasurableSpace E] [BorelSpace E] in +/-- The span of the first `j` members of a family has dimension at most `j`. This is the whole of +what the chain needs about the flag, and it is why Cassels' Lemma 2 does not appear below. -/ +private theorem finrank_span_image_lt_le {m : ℕ} (v : Fin m → E) (j : ℕ) : + finrank ℝ (Submodule.span ℝ (v '' {i : Fin m | (i : ℕ) < j})) ≤ j := by + classical + have himg : v '' {i : Fin m | (i : ℕ) < j} + = (((Finset.univ.filter (fun i : Fin m ↦ (i : ℕ) < j)).image v : Finset E) : Set E) := by + ext x + simp [Set.mem_image] + rw [himg] + refine le_trans (finrank_span_finset_le_card _) (le_trans Finset.card_image_le ?_) + refine le_trans (Finset.card_le_card_of_injOn (fun i : Fin m ↦ (i : ℕ)) + (fun i hi ↦ Finset.mem_range.2 (by simpa using hi)) Fin.val_injective.injOn) ?_ + simp + +/-- A weighted lower bound propagates through successive ratios of a positive sequence. -/ +private theorem successiveMinimum_measure_chain (n : ℕ) (hnpos : 0 < n) + (lam : ℕ → ℝ) (hpos : ∀ i, i < n → 0 < lam i) + (bodyMeasure : ℝ≥0∞) (M : ℕ → ℝ≥0∞) + (hbase : M 0 = ENNReal.ofReal ((lam 0 / 2) ^ n) * bodyMeasure) + (hstep : ∀ j, 1 ≤ j → j < n → + ENNReal.ofReal ((lam j / lam (j - 1)) ^ (n - j)) * M (j - 1) ≤ M j) : + ∀ j, j < n → + ENNReal.ofReal ((∏ i ∈ Finset.range (j + 1), lam i) * lam j ^ (n - 1 - j)) * + bodyMeasure ≤ ENNReal.ofReal (2 ^ n) * M j := by + intro j + induction j with + | zero => + intro _ + have h0 : 0 < lam 0 := hpos 0 hnpos + have hlhs : lam 0 * lam 0 ^ (n - 1 - 0) + = 2 ^ n * (lam 0 / 2) ^ n := by + rw [Nat.sub_zero, ← pow_succ', show n - 1 + 1 = n from by omega, + div_pow] + field_simp + rw [hbase, Finset.prod_range_one, hlhs, ENNReal.ofReal_mul (by positivity), mul_assoc] + | succ k ih => + intro hk + have hkn : k < n := by omega + have hkpos : 0 < lam k := hpos k hkn + have hk1pos : 0 < lam (k + 1) := hpos (k + 1) hk + have hprodnn : (0:ℝ) ≤ ∏ i ∈ Finset.range (k + 1), lam i := + Finset.prod_nonneg fun i hi ↦ (hpos i (by have := Finset.mem_range.1 hi; omega)).le + have e2 : n - 1 - (k + 1) + 1 = n - (k + 1) := by omega + have hL2 : (∏ i ∈ Finset.range (k + 1 + 1), lam i) * + lam (k + 1) ^ (n - 1 - (k + 1)) + = (∏ i ∈ Finset.range (k + 1), lam i) * lam (k + 1) ^ (n - (k + 1)) := by + rw [Finset.prod_range_succ, mul_assoc, ← pow_succ', e2] + have hR2 : ((∏ i ∈ Finset.range (k + 1), lam i) * lam k ^ (n - 1 - k)) * + (lam (k + 1) / lam k) ^ (n - (k + 1)) + = (∏ i ∈ Finset.range (k + 1), lam i) * lam (k + 1) ^ (n - (k + 1)) := by + rw [show n - 1 - k = n - (k + 1) from by omega, div_pow] + field_simp + have hQ : (∏ i ∈ Finset.range (k + 1 + 1), lam i) * + lam (k + 1) ^ (n - 1 - (k + 1)) + = ((∏ i ∈ Finset.range (k + 1), lam i) * lam k ^ (n - 1 - k)) * + (lam (k + 1) / lam k) ^ (n - (k + 1)) := hL2.trans hR2.symm + have hstep' : ENNReal.ofReal ((lam (k + 1) / lam k) ^ (n - (k + 1))) * M k + ≤ M (k + 1) := by simpa using hstep (k + 1) (by omega) hk + calc ENNReal.ofReal ((∏ i ∈ Finset.range (k + 1 + 1), lam i) * + lam (k + 1) ^ (n - 1 - (k + 1))) * bodyMeasure + = ENNReal.ofReal ((lam (k + 1) / lam k) ^ (n - (k + 1))) * + (ENNReal.ofReal ((∏ i ∈ Finset.range (k + 1), lam i) * + lam k ^ (n - 1 - k)) * bodyMeasure) := by + rw [hQ, ENNReal.ofReal_mul (by positivity)] + ring + _ ≤ ENNReal.ofReal ((lam (k + 1) / lam k) ^ (n - (k + 1))) * + (ENNReal.ofReal (2 ^ n) * M k) := mul_le_mul_right (ih hkn) _ + _ = ENNReal.ofReal (2 ^ n) * + (ENNReal.ofReal ((lam (k + 1) / lam k) ^ (n - (k + 1))) * M k) := by ring + _ ≤ ENNReal.ofReal (2 ^ n) * M (k + 1) := mul_le_mul_right hstep' _ + +/-- **Minkowski's second theorem, the upper bound** (Cassels, Chapter VIII, Theorem V, the +inequality (12) of VIII.1): `(∏ i < n, λ i) * volume B ≤ 2 ^ n * covolume L`. This is the +substantial half, the one Layer 5 and 6.3 consume. + +The proof is Weyl's, through Cassels' Theorem IV, which is +`ZLattice.pow_mul_measure_inter_add_le`. Write `S t` for the image of the open dilate +`{gauge B < t}` in `E ⧸ L`. At `t = λ 0 / 2` the dilate injects into the quotient, so +`m (S t) = t ^ n * volume B`; and for `1 ≤ J < n` and `s = λ J / λ (J - 1)` the estimate gives +`m (S (s t)) ≥ s ^ (n - J) * m (S t)` at `t = λ (J - 1) / 2`, because there congruence modulo `L` +inside the dilate is congruence modulo `L ∩ span (v 0, …, v (J - 1))`, a sublattice of `ℤ`-rank at +most `J`. Chaining and bounding `m (S (λ (n - 1) / 2))` by the covolume multiplies `volume B` by +the telescoping product `λ 0 ^ n * ∏_{J = 1}^{n - 1} (λ J / λ (J - 1)) ^ (n - J) = ∏ i < n, λ i`. + +⚠ **The chain runs on open dilates, and that is what removes closedness.** For a closed body the +dilate contains points of gauge exactly `t`, and the separation hypothesis would hold only for +`2 t < λ J` strictly; for `{gauge B < t}` it holds at `2 t = λ J`, which is where the chain needs +it. The body and its interior have the same measure because a convex set has null frontier. + +⚠ **Cassels' Lemma 2 is not used.** He needs the adapted basis because he works in coordinates: +the integrality of the first `J` coordinates is how he sees that the translation stays in the +sublattice. Coordinate-free the sublattice `L ∩ span (v 0, …, v (J - 1))` is at hand and the only +thing wanted of it is its rank, which is the `ℝ`-dimension of its span by +`ZLattice.finrank_int_eq_finrank_real`. -/ +theorem prod_successiveMinimum_mul_measure_le (L : Submodule ℤ E) [DiscreteTopology L] + [IsZLattice ℝ L] (μ : Measure E) [μ.IsAddHaarMeasure] (hB₀ : Convex ℝ B) + (hB₁ : ∀ x ∈ B, -x ∈ B) (hB₂ : (interior B).Nonempty) (hB₃ : Bornology.IsBounded B) : + (∏ i ∈ Finset.range (finrank ℝ E), successiveMinimum L B i) * (μ B).toReal ≤ + 2 ^ finrank ℝ E * covolume L μ := by + classical + obtain ⟨FL, hFLm, hFLfd, hFLfin, hFLcov⟩ := exists_fundamentalDomain L μ + have h₀ : B ∈ 𝓝 (0 : E) := hB₀.mem_nhds_zero_of_symmetric hB₁ hB₂ + obtain ⟨v, hvL, hvind, hvg⟩ := + exists_linearIndependent_gauge_eq_successiveMinimum L hB₀ hB₁ hB₂ hB₃ + set lam : ℕ → ℝ := successiveMinimum L B + rcases Nat.eq_zero_or_pos (finrank ℝ E) with hn0 | hnpos + · -- a zero-dimensional space: the body and the fundamental domain are both the whole of `E` + have hsub : Subsingleton E := by + rw [← Module.finrank_zero_iff (R := ℝ)] + exact hn0 + obtain ⟨w, hw, -⟩ := hFLfd 0 + have h0F : (0 : E) ∈ FL := by + rwa [show (w : E) + (0 : E) = 0 from Subsingleton.elim _ _] at hw + have hBF : B ⊆ FL := fun x _ ↦ by + rwa [show x = (0 : E) from Subsingleton.elim _ _] + rw [hn0] + simp only [Finset.range_zero, Finset.prod_empty, one_mul, pow_zero] + rw [← hFLcov] + exact ENNReal.toReal_mono hFLfin (measure_mono hBF) + -- the open body, its dilates, and its measure + have hCm : MeasurableSet (interior B) := isOpen_interior.measurableSet + have hCc : Convex ℝ (interior B) := hB₀.interior + have hmemC : ∀ t : ℝ, 0 < t → ∀ x : E, x ∈ t • interior B ↔ gauge B x < t := by + intro t ht x + rw [Set.mem_smul_set_iff_inv_smul_mem₀ ht.ne', ← gauge_lt_one_iff_mem_interior hB₀ h₀, + gauge_smul_of_nonneg (inv_nonneg.2 ht.le), smul_eq_mul, inv_mul_lt_one₀ ht] + have hvol : μ (interior B) = μ B := by + refine le_antisymm (measure_mono interior_subset) ?_ + calc μ B ≤ μ (interior B ∪ frontier B) := + measure_mono fun x hx ↦ (em (x ∈ interior B)).imp id fun h ↦ ⟨subset_closure hx, h⟩ + _ ≤ μ (interior B) + μ (frontier B) := measure_union_le _ _ + _ = μ (interior B) := by rw [hB₀.addHaar_frontier μ, add_zero] + have hAm : ∀ t : ℝ, MeasurableSet (t • interior B) := fun t ↦ hCm.const_smul₀ t + -- positivity and monotonicity of the minima + have hpos : ∀ i, i < finrank ℝ E → 0 < lam i := + fun i hi ↦ successiveMinimum_pos L hB₀ hB₁ hB₂ hB₃ hi + have hmono : ∀ i j : ℕ, i ≤ j → j < finrank ℝ E → lam i ≤ lam j := + fun i j hij hj ↦ successiveMinimum_le_of_le hij hj hB₀ hB₁ hB₂ + -- the sublattices cut out by the flag of the family realizing the minima + obtain ⟨Λ, hΛ⟩ : ∃ Λ : ℕ → Submodule ℤ E, ∀ j, Λ j = + L ⊓ (Submodule.span ℝ (v '' {i : Fin (finrank ℝ E) | (i : ℕ) < j})).restrictScalars ℤ := + ⟨_, fun _ ↦ rfl⟩ + have hΛle : ∀ j, Λ j ≤ L := fun j ↦ (hΛ j).le.trans inf_le_left + have hΛdisc : ∀ j, DiscreteTopology (Λ j) := fun j ↦ discreteTopology_of_le (hΛle j) + have hΛrank : ∀ j, finrank ℤ (Λ j) ≤ j := by + intro j + rw [finrank_int_eq_finrank_real (hΛle j)] + refine le_trans (Submodule.finrank_mono ?_) (finrank_span_image_lt_le v j) + refine Submodule.span_le.2 fun x hx ↦ ?_ + rw [hΛ j] at hx + exact (Submodule.mem_inf.1 hx).2 + have hΛ0 : Λ 0 = ⊥ := by + rw [hΛ 0] + simp + -- the separation hypothesis, straight from the dependence half of Cassels' Lemma 1 + have hsep : ∀ (j : ℕ) (t : ℝ), 0 < t → 2 * t ≤ lam j → + ∀ x ∈ t • interior B, ∀ y ∈ t • interior B, x - y ∈ L → x - y ∈ Λ j := by + intro j t ht hlt x hx y hy hxy + have hgx : gauge B x < t := (hmemC t ht x).1 hx + have hgy : gauge B y < t := (hmemC t ht y).1 hy + have hadd := gauge_add_le hB₀ (absorbent_nhds_zero h₀) x (-y) + rw [gauge_neg hB₁] at hadd + have hg : gauge B (x - y) < lam j := by + rw [sub_eq_add_neg] + linarith + have hspan : x - y ∈ Submodule.span ℝ (v '' {i : Fin (finrank ℝ E) | (i : ℕ) < j}) := + mem_span_of_gauge_lt_successiveMinimum L hB₀ hB₁ hB₂ hvL hvind hvg (j := j) hxy hg + rw [hΛ j] + exact Submodule.mem_inf.2 ⟨hxy, (Submodule.restrictScalars_mem ℤ _ _).2 hspan⟩ + -- the quotient measure of the dilates, one per minimum + have hLcount : Countable L := countable_of_discreteTopology L + obtain ⟨M, hM⟩ : ∃ M : ℕ → ℝ≥0∞, ∀ j, + M j = μ (FL ∩ ((lam j / 2) • interior B + (L : Set E))) := ⟨_, fun _ ↦ rfl⟩ + have hbotfd : ∀ x : E, ∃! w : (⊥ : Submodule ℤ E), (w : E) + x ∈ (univ : Set E) := + fun _ ↦ ⟨0, Set.mem_univ _, fun _ _ ↦ Subsingleton.elim _ _⟩ + -- the base: below the first minimum the dilate injects into the quotient + have hbase : M 0 = ENNReal.ofReal ((lam 0 / 2) ^ finrank ℝ E) * μ (interior B) := by + have h0 : 0 < lam 0 := hpos 0 hnpos + have hsep0 : ∀ x ∈ (lam 0 / 2) • interior B, ∀ y ∈ (lam 0 / 2) • interior B, + x - y ∈ L → x - y ∈ (⊥ : Submodule ℤ E) := by + intro x hx y hy hxy + rw [← hΛ0] + exact hsep 0 (lam 0 / 2) (by positivity) (by linarith) x hx y hy hxy + have heq := measure_inter_add_eq_of_separated μ (bot_le : (⊥ : Submodule ℤ E) ≤ L) + hbotfd hFLfd MeasurableSet.univ hFLm hFLfin (hAm (lam 0 / 2)) hsep0 + have hz : (lam 0 / 2) • interior B + ((⊥ : Submodule ℤ E) : Set E) + = (lam 0 / 2) • interior B := by simp + rw [hM, ← heq, Set.univ_inter, hz, Measure.addHaar_smul, + abs_of_nonneg (by positivity : (0:ℝ) ≤ (lam 0 / 2) ^ finrank ℝ E)] + -- the inductive step: Cassels' Theorem IV applied at the `j`-th minimum + have hstep : ∀ j, 1 ≤ j → j < finrank ℝ E → + ENNReal.ofReal ((lam j / lam (j - 1)) ^ (finrank ℝ E - j)) * M (j - 1) ≤ M j := by + intro j hj1 hjn + have hjm : j - 1 < finrank ℝ E := by omega + have hp1 : 0 < lam (j - 1) := hpos _ hjm + have hpj : 0 < lam j := hpos _ hjn + have hle : lam (j - 1) ≤ lam j := hmono _ _ (by omega) hjn + have hs1 : 1 ≤ lam j / lam (j - 1) := (one_le_div hp1).2 hle + have hsA : (lam j / lam (j - 1)) • ((lam (j - 1) / 2) • interior B) + = (lam j / 2) • interior B := by + rw [smul_smul] + congr 1 + field_simp + have hd : DiscreteTopology (Λ j) := hΛdisc j + have hsepA : ∀ x ∈ (lam (j - 1) / 2) • interior B, ∀ y ∈ (lam (j - 1) / 2) • interior B, + x - y ∈ L → x - y ∈ Λ j := + hsep j (lam (j - 1) / 2) (by positivity) (by linarith) + have hsepB : ∀ x ∈ (lam j / lam (j - 1)) • ((lam (j - 1) / 2) • interior B), + ∀ y ∈ (lam j / lam (j - 1)) • ((lam (j - 1) / 2) • interior B), + x - y ∈ L → x - y ∈ Λ j := by + rw [hsA] + exact hsep j (lam j / 2) (by positivity) (by linarith) + have hkey := pow_mul_measure_inter_add_le μ (hΛle j) hFLfd hFLm hFLfin + (hAm (lam (j - 1) / 2)) (hCc.smul _) hs1 hsepA hsepB + rw [hsA] at hkey + rw [hM (j - 1), hM j] + refine le_trans (mul_le_mul_left ?_ _) hkey + have hrk := hΛrank j + exact ENNReal.ofReal_le_ofReal (pow_le_pow_right₀ hs1 (by omega)) + -- the chain, carrying `λ 0 ⋯ λ j * λ j ^ (n - 1 - j)` up the minima + have hchain := successiveMinimum_measure_chain (finrank ℝ E) hnpos lam hpos + (μ (interior B)) M hbase hstep + -- reading the chain off at the last minimum, against the covolume + have hfin := hchain (finrank ℝ E - 1) (by omega) + rw [show finrank ℝ E - 1 + 1 = finrank ℝ E from by omega, Nat.sub_self, pow_zero, mul_one] at hfin + have hMle : M (finrank ℝ E - 1) ≤ μ FL := by + rw [hM] + exact measure_mono Set.inter_subset_left + have hprodnn : (0:ℝ) ≤ ∏ i ∈ Finset.range (finrank ℝ E), lam i := + Finset.prod_nonneg fun i hi ↦ (hpos i (Finset.mem_range.1 hi)).le + have hle2 : ENNReal.ofReal (∏ i ∈ Finset.range (finrank ℝ E), lam i) * μ B + ≤ ENNReal.ofReal (2 ^ finrank ℝ E) * μ FL := by + rw [← hvol] + exact le_trans hfin (mul_le_mul_right hMle _) + have hBfin : μ B ≠ ⊤ := hB₃.measure_lt_top.ne + have hcalc := ENNReal.toReal_mono (ENNReal.mul_ne_top ENNReal.ofReal_ne_top hFLfin) hle2 + rw [ENNReal.toReal_mul, ENNReal.toReal_mul, ENNReal.toReal_ofReal hprodnn, + ENNReal.toReal_ofReal (by positivity : (0:ℝ) ≤ 2 ^ finrank ℝ E), hFLcov] at hcalc + exact hcalc + +end ZLattice + +/-! ### Why the upper bound is not a packing statement + +The upper bound of Minkowski's second theorem would follow at once from the following false +statement: for a symmetric convex body `B` and `0 < c ≤ 1`, shrinking one coordinate of a point +of `B` — in the basis realizing the minima, and shrinking the *earlier* coordinates, whose minima +are the smaller ones — keeps it inside `B`. It fails already in the plane. -/ + +section Examples + +/-- ⚠ **Shrinking a coordinate can leave a symmetric convex body**, so the linear map whose +determinant is the product of the minima does not map the body into itself. The body here is the +thin parallelogram `|x| ≤ 1`, `|19 x - 20 y| ≤ 1` along the diagonal: it contains `(1, 19/20)`, +but not `(9/10, 19/20)`, which the segment to `(0, 19/20)` has already left. -/ +example : ∃ (B : Set (ℝ × ℝ)) (x : ℝ × ℝ) (c : ℝ), Convex ℝ B ∧ (∀ y ∈ B, -y ∈ B) ∧ + Bornology.IsBounded B ∧ (interior B).Nonempty ∧ 0 < c ∧ c < 1 ∧ x ∈ B ∧ + ((c * x.1, x.2) : ℝ × ℝ) ∉ B := by + have combo : ∀ {a b s t r : ℝ}, |a| ≤ r → |b| ≤ r → 0 ≤ s → 0 ≤ t → s + t = 1 → + |s * a + t * b| ≤ r := by + intro a b s t r ha hb hs ht hst + calc |s * a + t * b| ≤ |s * a| + |t * b| := abs_add_le _ _ + _ = s * |a| + t * |b| := by rw [abs_mul, abs_mul, abs_of_nonneg hs, abs_of_nonneg ht] + _ ≤ s * r + t * r := by nlinarith + _ = r := by rw [← add_mul, hst, one_mul] + refine ⟨{p : ℝ × ℝ | |p.1| ≤ 1 ∧ |19 * p.1 - 20 * p.2| ≤ 1}, (1, 19 / 20), 9 / 10, ?_, ?_, ?_, + ?_, by norm_num, by norm_num, by norm_num, by norm_num [abs_le]⟩ + · rintro x ⟨hx1, hx2⟩ y ⟨hy1, hy2⟩ s t hs ht hst + refine ⟨?_, ?_⟩ + · change |s * x.1 + t * y.1| ≤ 1 + exact combo hx1 hy1 hs ht hst + · change |19 * (s * x.1 + t * y.1) - 20 * (s * x.2 + t * y.2)| ≤ 1 + have h : 19 * (s * x.1 + t * y.1) - 20 * (s * x.2 + t * y.2) + = s * (19 * x.1 - 20 * x.2) + t * (19 * y.1 - 20 * y.2) := by ring + rw [h] + exact combo hx2 hy2 hs ht hst + · rintro y ⟨hy1, hy2⟩ + refine ⟨?_, ?_⟩ + · change |(-y.1 : ℝ)| ≤ 1 + simpa using hy1 + · change |19 * (-y.1) - 20 * (-y.2)| ≤ 1 + have h : 19 * (-y.1) - 20 * (-y.2) = -(19 * y.1 - 20 * y.2) := by ring + rw [h, abs_neg] + exact hy2 + · refine Bornology.IsBounded.subset + (Metric.isBounded_closedBall (x := (0 : ℝ × ℝ)) (r := 1)) ?_ + rintro p ⟨h1, h2⟩ + rw [abs_le] at h1 h2 + obtain ⟨h1a, h1b⟩ := h1 + obtain ⟨h2a, h2b⟩ := h2 + rw [mem_closedBall_zero_iff, Prod.norm_def, Real.norm_eq_abs, Real.norm_eq_abs, max_le_iff, + abs_le, abs_le] + refine ⟨⟨h1a, h1b⟩, ?_, ?_⟩ <;> linarith + · refine ⟨0, ?_⟩ + have hsub : ∀ p : ℝ × ℝ, p ∈ Metric.ball (0 : ℝ × ℝ) (1 / 39) → + p ∈ {p : ℝ × ℝ | |p.1| ≤ 1 ∧ |19 * p.1 - 20 * p.2| ≤ 1} := by + intro p hp + rw [mem_ball_zero_iff, Prod.norm_def, Real.norm_eq_abs, Real.norm_eq_abs, max_lt_iff] at hp + obtain ⟨hp1, hp2⟩ := hp + rw [abs_lt] at hp1 hp2 + obtain ⟨h1a, h1b⟩ := hp1 + obtain ⟨h2a, h2b⟩ := hp2 + exact ⟨by rw [abs_le]; constructor <;> linarith, + by rw [abs_le]; constructor <;> linarith⟩ + exact interior_maximal hsub Metric.isOpen_ball (Metric.mem_ball_self (by norm_num)) + +end Examples diff --git a/LeanPool/CCDN/ArithmeticHeights/Plucker.lean b/LeanPool/CCDN/ArithmeticHeights/Plucker.lean new file mode 100644 index 0000000000..2a796308a2 --- /dev/null +++ b/LeanPool/CCDN/ArithmeticHeights/Plucker.lean @@ -0,0 +1,383 @@ +/- +Copyright (c) 2026 Ralf Stephan. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Ralf Stephan +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import Mathlib.LinearAlgebra.ExteriorPower.Basis +public import Mathlib.LinearAlgebra.FiniteDimensional.Basic +public import Mathlib.LinearAlgebra.LinearIndependent.Lemmas +public import Mathlib.LinearAlgebra.Projectivization.Basic + +/-! +# The Plücker point of a subspace + +A `k`-dimensional subspace `V` of `ι → K` determines a point of the projective space on +`Set.powersetCard ι k → K`, the tuple of its **Plücker coordinates**: wedge a basis of `V` together +inside `⋀[K]^k (ι → K)` and read the result in the basis that the standard basis of `ι → K` +induces there. A change of basis of `V` multiplies the wedge by a determinant, hence by a unit, so +the point of projective space depends on `V` alone. + +This file builds that point and proves it is an embedding of the `k`-dimensional subspaces into +projective space. Layer 3.2 defines the height of a subspace as the height of this point, and +everything the height inherits — Northcott for subspaces above all — rests on the injectivity +proved here. + +## Main definitions + +* `exteriorPower.plucker k v` for `v : Fin k → (ι → K)`: the coordinate tuple of the wedge + `v 0 ∧ ⋯ ∧ v (k-1)`, indexed by the `k`-element subsets of `ι`. +* `Submodule.pluckerPoint V hV` for `hV : Module.finrank K V = k`: the induced point of + `Projectivization K (Set.powersetCard ι k → K)`. + +## Main results + +* `exteriorPower.plucker_apply`: the Plücker coordinate at `s` is the maximal minor on the columns + `s` of the matrix whose rows are the `v i`. `exteriorPower.plucker_fin_eq_det` is the top-rank + case, where the single coordinate is the determinant, and + `exteriorPower.plucker_one_ofSingleton` the rank-one case, where the coordinates are the + coordinates of the vector. +* `exteriorPower.plucker_eq_zero_iff`: the coordinate tuple vanishes exactly when the family is + linearly dependent, so a basis of `V` produces a nonzero tuple. +* `Submodule.pluckerPoint_eq_mk` and `Submodule.pluckerPoint_span_range`: the value of the Plücker + point on *any* basis of `V`, and on any linearly independent family spanning it. This is the + basis-independence, stated in the form that is actually used. + `Submodule.pluckerPoint_span_singleton` is the rank-one case: the Plücker point of a line is the + point itself. +* `Submodule.pluckerPoint_injective`: distinct subspaces of the same rank have distinct Plücker + points. + +Two lemmas are stated here because Mathlib lacks them and the proofs need them: +`exteriorPower.ιMulti_eq_zero_iff`, that over a field a wedge vanishes exactly on linearly +dependent families, and `linearIndependent_fin1`, the `Fin 1` companion of Mathlib's +`linearIndependent_fin2`. + +## Implementation notes + +The index type is Mathlib's `Set.powersetCard ι k`, the type of `k`-element finsets of `ι`, because +that is what `Module.Basis.exteriorPower` is indexed by; using it verbatim is what lets +`exteriorPower.plucker_apply` come out of Mathlib's `exteriorPower.ιMultiDual_apply_ιMulti` with no +index bookkeeping of our own. It is also why `[LinearOrder ι]` is a standing hypothesis: an order +on `ι` is needed to say *which* wedge of basis vectors a `k`-element subset names. A different +order changes each coordinate by a sign only, so nothing downstream depends on the choice. + +The Plücker point is *not* built by `Projectivization.lift`. There is no projective space to lift +from: the data that varies is a basis of `V`, and bases do not form a projectivization. What takes +its place is `Submodule.pluckerPoint_eq_mk`, which says the value on an arbitrary basis is the +value on the chosen one, and which is the only thing a proof ever needs from well-definedness. +The definition itself picks `Module.finBasisOfFinrankEq`. + +Injectivity is proved through the wedge criterion `exteriorPower.ιMulti_snoc_eq_zero_iff`: a vector +lies in the span of a linearly independent family exactly when wedging it on gives `0`. Since the +wedge of a basis of `V` is recovered from the Plücker point up to a nonzero scalar, and scaling +does not change what the criterion detects, the point determines the subspace. The criterion itself +is proved in `ExteriorAlgebra K (ι → K)`, where wedging on is multiplication. + +## References + +E. Bombieri and W. Gubler, *Heights in Diophantine Geometry*, Cambridge University Press (2006), +§2.8.4. + +M. Hindry and J. H. Silverman, *Diophantine Geometry: An Introduction*, Springer GTM 201 (2000), +Exercise A.1.11(a)–(b). + +This is Layer 3.1 of the `ArithmeticHeights` roadmap. +-/ + +public section + +open Module + +section LinearIndependent + +variable {K V : Type*} [DivisionRing K] [AddCommGroup V] [Module K V] + +/-- A one-element family is linearly independent exactly when its single vector is nonzero. This +is the `Fin 1` companion of Mathlib's `linearIndependent_fin2`, which Mathlib does not have. -/ +theorem linearIndependent_fin1 {f : Fin 1 → V} : LinearIndependent K f ↔ f 0 ≠ 0 := + linearIndependent_unique_iff + +end LinearIndependent + +namespace exteriorPower + +/-- `Fin k` has exactly one subset of `k` elements, and its increasing enumeration is the +identity. -/ +private lemma ofFinEmbEquiv_symm_fin {k : ℕ} (s : Set.powersetCard (Fin k) k) : + ⇑(Set.powersetCard.ofFinEmbEquiv.symm s) = (id : Fin k → Fin k) := by + have hs : (s : Finset (Fin k)) = Finset.univ := Finset.eq_univ_of_card _ (by simp) + rw [Set.powersetCard.ofFinEmbEquiv_symm_apply] + exact (Finset.orderEmbOfFin_unique _ (fun x ↦ hs ▸ Finset.mem_univ x) strictMono_id).symm + +section Field + +variable {K E : Type*} [Field K] [AddCommGroup E] [Module K E] {k : ℕ} + +/-- Over a field, the wedge of a linearly independent family is nonzero. -/ +theorem ιMulti_ne_zero {v : Fin k → E} (hv : LinearIndependent K v) : ιMulti K k v ≠ 0 := by + have h := (ιMulti_family_linearIndependent_field k hv).ne_zero + (⟨Finset.univ, by simp⟩ : Set.powersetCard (Fin k) k) + rwa [ιMulti_family, ofFinEmbEquiv_symm_fin, Function.comp_id] at h + +/-- Over a field, a wedge of `k` vectors vanishes exactly when they are linearly dependent. -/ +theorem ιMulti_eq_zero_iff (v : Fin k → E) : ιMulti K k v = 0 ↔ ¬ LinearIndependent K v := + ⟨fun h hv ↦ ιMulti_ne_zero hv h, AlternatingMap.map_linearDependent _ v⟩ + +/-- **The wedge criterion for membership in a span.** A vector lies in the span of a linearly +independent family exactly when wedging it onto that family gives `0`. -/ +theorem ιMulti_snoc_eq_zero_iff {v : Fin k → E} (hv : LinearIndependent K v) (x : E) : + ιMulti K (k + 1) (Fin.snoc v x) = 0 ↔ x ∈ Submodule.span K (Set.range v) := by + rw [ιMulti_eq_zero_iff, linearIndependent_finSnoc] + simp [hv] + +end Field + +section Snoc + +variable {R M : Type*} [CommRing R] [AddCommGroup M] [Module R M] {k : ℕ} + +/-- **Wedging a vector onto a wedge is multiplication in the exterior algebra.** -/ +theorem coe_ιMulti_snoc (v : Fin k → M) (x : M) : + ((ιMulti R (k + 1) (Fin.snoc v x) : ⋀[R]^(k + 1) M) : ExteriorAlgebra R M) + = (ιMulti R k v : ExteriorAlgebra R M) * ExteriorAlgebra.ι R x := by + have hap : Fin.append v ![x] = Fin.snoc v x := by + rw [Fin.append_right_eq_snoc] + simp + rw [ιMulti_apply_coe, ιMulti_apply_coe, ← hap, ← ExteriorAlgebra.ιMulti_mul_ιMulti] + simp [ExteriorAlgebra.ιMulti_apply] + +/-- The wedge criterion sees a wedge only up to a scalar: proportional wedges give proportional +results when a further vector is wedged on. -/ +theorem ιMulti_snoc_smul {c : R} {v w : Fin k → M} (h : ιMulti R k v = c • ιMulti R k w) (x : M) : + ιMulti R (k + 1) (Fin.snoc v x) = c • ιMulti R (k + 1) (Fin.snoc w x) := by + apply Subtype.val_injective + rw [coe_ιMulti_snoc, SetLike.val_smul, coe_ιMulti_snoc, h, SetLike.val_smul, smul_mul_assoc] + +end Snoc + +section Plucker + +variable {R : Type*} [CommRing R] {ι : Type*} [Fintype ι] [LinearOrder ι] + +/-- The **Plücker coordinates** of a family `v : Fin k → (ι → R)`: the coordinate tuple of the +wedge `v 0 ∧ ⋯ ∧ v (k-1)` in the basis of `⋀[R]^k (ι → R)` induced by the standard basis of +`ι → R`, indexed by the `k`-element subsets of `ι`. -/ +@[expose] noncomputable def plucker (k : ℕ) (v : Fin k → (ι → R)) : Set.powersetCard ι k → R := + ((Pi.basisFun R ι).exteriorPower k).equivFun (ιMulti R k v) + +/-- **The Plücker coordinates are the maximal minors.** The coordinate at a `k`-element subset `s` +of `ι` is the determinant of the `k × k` matrix cut out of the rows `v` by the columns `s`, the +columns taken in the order of `ι`. -/ +theorem plucker_apply (k : ℕ) (v : Fin k → (ι → R)) (s : Set.powersetCard ι k) : + plucker k v s = (Matrix.of fun i j ↦ v i (Set.powersetCard.ofFinEmbEquiv.symm s j)).det := by + rw [plucker, Basis.equivFun_apply, basis_repr_apply, ιMultiDual_apply_ιMulti] + simp + +/-- **The Plücker coordinates commute with a ring homomorphism.** They are determinants, so this +is `RingHom.map_det`; it is what carries the Plücker point of a subspace along a base change. -/ +theorem plucker_comp_ringHom {S : Type*} [CommRing S] (f : R →+* S) (k : ℕ) + (v : Fin k → (ι → R)) : + plucker k (fun l ↦ f ∘ v l) = f ∘ plucker k v := by + funext s + rw [Function.comp_apply, plucker_apply, plucker_apply, RingHom.map_det] + rfl + +/-- The empty wedge: in rank `0` there is one Plücker coordinate and it is `1`. -/ +@[simp] theorem plucker_zero (v : Fin 0 → (ι → R)) : plucker 0 v = 1 := by + funext s + rw [plucker_apply, Matrix.det_fin_zero, Pi.one_apply] + +/-- In rank `1` the Plücker coordinate at the singleton `{i}` is the `i`-th coordinate: the +Plücker embedding extends the identity on points. -/ +theorem plucker_one_ofSingleton (x : ι → R) (i : ι) : + plucker 1 ![x] (Set.powersetCard.ofSingleton i) = x i := by + rw [plucker_apply, Matrix.det_fin_one] + simp [Set.powersetCard.ofSingleton, Set.powersetCard.ofFinEmbEquiv_symm_apply] + +/-- In rank `1` the Plücker coordinates are the coordinates, re-indexed along +`Set.powersetCard.ofSingleton`. -/ +theorem plucker_one_comp (x : ι → R) : plucker 1 ![x] ∘ Set.powersetCard.ofSingleton = x := + funext (plucker_one_ofSingleton x) + +/-- In top rank there is one Plücker coordinate and it is the determinant. -/ +theorem plucker_fin_eq_det {n : ℕ} (v : Fin n → (Fin n → R)) (s : Set.powersetCard (Fin n) n) : + plucker n v s = (Matrix.of v).det := by + rw [plucker_apply, ofFinEmbEquiv_symm_fin] + rfl + +/-- The Plücker coordinates determine the wedge: proportionality of the coordinate tuples is +proportionality of the wedges. -/ +theorem plucker_eq_smul_iff (k : ℕ) (c : R) (v w : Fin k → (ι → R)) : + plucker k v = c • plucker k w ↔ ιMulti R k v = c • ιMulti R k w := by + rw [plucker, plucker, ← map_smul] + exact (LinearEquiv.injective _).eq_iff + +/-- **Scaling the vectors scales the Plücker coordinates by the product of the scalars.** The +Plücker coordinates are a multilinear function of the family, so rescaling each vector multiplies +every coordinate by the product of the scalars — the statement that a determinant is multilinear +in its rows, read on the maximal minors at once. -/ +theorem plucker_smul (k : ℕ) (c : Fin k → R) (v : Fin k → (ι → R)) + (s : Set.powersetCard ι k) : + plucker k (fun i ↦ c i • v i) s = (∏ i, c i) * plucker k v s := by + rw [plucker, plucker, + show ((ιMulti R k) fun i ↦ c i • v i) = (∏ i, c i) • (ιMulti R k) v from + MultilinearMap.map_smul_univ (ιMulti R k).toMultilinearMap c v] + simp + +variable {K : Type*} [Field K] {k : ℕ} {v : Fin k → (ι → K)} + +/-- Over a field, the Plücker coordinates of a family vanish exactly when it is linearly +dependent. -/ +theorem plucker_eq_zero_iff (k : ℕ) (v : Fin k → (ι → K)) : + plucker k v = 0 ↔ ¬ LinearIndependent K v := by + rw [← ιMulti_eq_zero_iff (K := K) v, plucker, + map_eq_zero_iff _ (LinearEquiv.injective _)] + +/-- Over a field, the Plücker coordinates of a linearly independent family are not all zero. -/ +theorem plucker_ne_zero (hv : LinearIndependent K v) : plucker k v ≠ 0 := + fun h ↦ (plucker_eq_zero_iff k v).1 h hv + +/-- The Plücker coordinates of a nonzero vector, read as a one-element family, are not all zero. -/ +theorem plucker_one_ne_zero {x : ι → K} (hx : x ≠ 0) : plucker 1 ![x] ≠ 0 := + plucker_ne_zero (linearIndependent_fin1.2 (by simpa using hx)) + +end Plucker + +end exteriorPower + +namespace Submodule + +open exteriorPower + +section Basis + +variable {R M : Type*} [Ring R] [AddCommGroup M] [Module R M] {κ : Type*} + {V : Submodule R M} + +/-- The family of vectors of `M` underlying a basis of a submodule is linearly independent. -/ +theorem linearIndependent_coe_basis (b : Basis κ R V) : LinearIndependent R fun i ↦ ((b i : M)) := + b.linearIndependent.map' V.subtype V.ker_subtype + +/-- The span of the vectors underlying a basis of a submodule is that submodule. -/ +theorem span_range_coe_basis (b : Basis κ R V) : + Submodule.span R (Set.range fun i ↦ ((b i : M))) = V := by + rw [show (fun i ↦ ((b i : M))) = V.subtype ∘ ⇑b from rfl, Set.range_comp, ← Submodule.map_span, + b.span_eq, Submodule.map_top, Submodule.range_subtype] + +end Basis + +variable {K : Type*} [Field K] {ι : Type*} [Fintype ι] [LinearOrder ι] {k : ℕ} + {V : Submodule K (ι → K)} + +/-- Two bases of the same `k`-dimensional subspace have proportional Plücker coordinates: the +scalar is the determinant of the change-of-basis matrix, and in particular a unit. -/ +private lemma exists_plucker_eq_smul (hV : finrank K V = k) (b b' : Basis (Fin k) K V) : + ∃ c : K, plucker k (fun i ↦ ((b' i : ι → K))) = c • plucker k fun i ↦ ((b i : ι → K)) := by + have h1 : finrank K (⋀[K]^k V) = 1 := by rw [exteriorPower.finrank_eq, hV, Nat.choose_self] + obtain ⟨c, hc⟩ := (finrank_eq_one_iff_of_nonzero' (ιMulti K k ⇑b) + (ιMulti_ne_zero b.linearIndependent)).1 h1 (ιMulti K k ⇑b') + refine ⟨c, (plucker_eq_smul_iff ..).2 ?_⟩ + have := congrArg (exteriorPower.map k V.subtype) hc + rwa [map_smul, map_apply_ιMulti, map_apply_ιMulti, eq_comm] at this + +/-- **The Plücker point of a `k`-dimensional subspace of `ι → K`** (Bombieri–Gubler 2.8.4; +Hindry–Silverman, Exercise A.1.11(a)–(b)): the point of projective space on the `k`-element subsets +of `ι` given by the Plücker coordinates of any basis of `V`. It does not depend on the basis; see +`Submodule.pluckerPoint_eq_mk`. -/ +@[expose] noncomputable def pluckerPoint (V : Submodule K (ι → K)) (hV : finrank K V = k) : + Projectivization K (Set.powersetCard ι k → K) := + Projectivization.mk K (plucker k fun i ↦ (((finBasisOfFinrankEq K V hV) i : ι → K))) + (plucker_ne_zero (linearIndependent_coe_basis _)) + +/-- **The Plücker point is computed by every basis, not only by the chosen one.** -/ +theorem pluckerPoint_eq_mk (hV : finrank K V = k) (b : Basis (Fin k) K V) + (hb : plucker k (fun i ↦ ((b i : ι → K))) ≠ 0) : + pluckerPoint V hV = Projectivization.mk K (plucker k fun i ↦ ((b i : ι → K))) hb := by + obtain ⟨c, hc⟩ := exists_plucker_eq_smul hV b (finBasisOfFinrankEq K V hV) + refine ((Projectivization.mk_eq_mk_iff' K _ _ _ hb).2 ⟨c, hc.symm⟩) + +/-- **The Plücker point of a span.** For a linearly independent family, the Plücker point of the +subspace it spans is the point defined by its Plücker coordinates. -/ +theorem pluckerPoint_span_range {v : Fin k → (ι → K)} (hv : LinearIndependent K v) + (hV : finrank K (Submodule.span K (Set.range v)) = k) : + pluckerPoint (Submodule.span K (Set.range v)) hV = + Projectivization.mk K (plucker k v) (plucker_ne_zero hv) := by + have hfun : (fun i ↦ ((Basis.span hv i : ι → K))) = v := + funext fun i ↦ congrArg Subtype.val (Basis.span_apply hv i) + rw [pluckerPoint_eq_mk hV (Basis.span hv) (by rw [hfun]; exact plucker_ne_zero hv)] + simp only [hfun] + +/-- Equal subspaces have equal Plücker points, whatever rank proofs they are given. -/ +theorem pluckerPoint_congr {V W : Submodule K (ι → K)} (h : V = W) (hV : finrank K V = k) + (hW : finrank K W = k) : pluckerPoint V hV = pluckerPoint W hW := by + subst h + rfl + +/-- **The Plücker point of a line is the point itself**, re-indexed along +`Set.powersetCard.ofSingleton` (`exteriorPower.plucker_one_comp`). This is the compatibility that +makes Layer 3.2's height of a subspace agree with Mathlib's height of a point on a line. -/ +theorem pluckerPoint_span_singleton {x : ι → K} (hx : x ≠ 0) + (hV : finrank K (Submodule.span K {x}) = 1) : + pluckerPoint (Submodule.span K {x}) hV = + Projectivization.mk K (plucker 1 ![x]) (plucker_one_ne_zero hx) := by + have hr : Submodule.span K (Set.range ![x]) = Submodule.span K {x} := by simp + have hV' : finrank K (Submodule.span K (Set.range ![x])) = 1 := by rw [hr]; exact hV + rw [← pluckerPoint_congr hr hV' hV] + exact pluckerPoint_span_range (linearIndependent_fin1.2 (by simpa using hx)) hV' + +/-- **The Plücker map is an embedding** (Hindry–Silverman, Exercise A.1.11(b)): distinct subspaces +of the same rank have distinct Plücker points. This is what Layer 3.7 turns into Northcott for +subspaces. -/ +theorem pluckerPoint_injective (k : ℕ) : + Function.Injective fun V : {V : Submodule K (ι → K) // finrank K V = k} ↦ + pluckerPoint V.1 V.2 := by + rintro ⟨V, hV⟩ ⟨W, hW⟩ h + set v : Fin k → (ι → K) := fun i ↦ (((finBasisOfFinrankEq K V hV) i : ι → K)) with hvdef + set w : Fin k → (ι → K) := fun i ↦ (((finBasisOfFinrankEq K W hW) i : ι → K)) with hwdef + have hv : LinearIndependent K v := linearIndependent_coe_basis _ + have hw : LinearIndependent K w := linearIndependent_coe_basis _ + simp only [pluckerPoint, Projectivization.mk_eq_mk_iff'] at h + obtain ⟨c, hc⟩ := h + have hcw : ιMulti K k v = c • ιMulti K k w := (plucker_eq_smul_iff ..).1 hc.symm + have hc0 : c ≠ 0 := by + rintro rfl + exact ιMulti_ne_zero hv (by simpa using hcw) + have hVs : V = Submodule.span K (Set.range v) := (span_range_coe_basis _).symm + have hWs : W = Submodule.span K (Set.range w) := (span_range_coe_basis _).symm + refine Subtype.ext ?_ + change V = W + rw [hVs, hWs] + refine Submodule.ext fun x ↦ ?_ + rw [← ιMulti_snoc_eq_zero_iff hv, ← ιMulti_snoc_eq_zero_iff hw, ιMulti_snoc_smul hcw x, + smul_eq_zero, or_iff_right hc0] + +end Submodule + +section Examples + +open Submodule exteriorPower + +variable {K : Type*} [Field K] {ι : Type*} [Fintype ι] [LinearOrder ι] + +/-- The Plücker point of the zero subspace is the point of the constant tuple `1`: the empty wedge +is `1`, and in rank `0` there is a single Plücker coordinate. -/ +example (hV : finrank K (⊥ : Submodule K (ι → K)) = 0) : + pluckerPoint (⊥ : Submodule K (ι → K)) hV = + Projectivization.mk K 1 fun h ↦ one_ne_zero (congrFun h ⟨∅, by simp⟩) := by + simp [pluckerPoint] + +/-- On a line the Plücker coordinates are the coordinates of the point. -/ +example : plucker 1 ![(![1, 3] : Fin 2 → ℚ)] (Set.powersetCard.ofSingleton 0) = 1 ∧ + plucker 1 ![(![1, 3] : Fin 2 → ℚ)] (Set.powersetCard.ofSingleton 1) = 3 := by + constructor <;> rw [plucker_one_ofSingleton] <;> norm_num + +/-- In top rank the single Plücker coordinate is the determinant. -/ +example : plucker 2 ![(![1, 2] : Fin 2 → ℚ), ![3, 4]] ⟨Finset.univ, by simp⟩ = -2 := by + rw [plucker_fin_eq_det, Matrix.det_fin_two_of] + norm_num + +end Examples diff --git a/LeanPool/CCDN/ArithmeticHeights/PrekopaLeindler.lean b/LeanPool/CCDN/ArithmeticHeights/PrekopaLeindler.lean new file mode 100644 index 0000000000..cee81d1957 --- /dev/null +++ b/LeanPool/CCDN/ArithmeticHeights/PrekopaLeindler.lean @@ -0,0 +1,611 @@ +/- +Copyright (c) 2026 Ralf Stephan. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Ralf Stephan +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import Mathlib.Analysis.MeanInequalities +public import Mathlib.LinearAlgebra.Pi +public import Mathlib.MeasureTheory.Integral.Prod +public import Mathlib.MeasureTheory.Measure.Haar.InnerProductSpace +public import Mathlib.MeasureTheory.Measure.Lebesgue.EqHaar + +/-! +Modified for Lean/Mathlib 4.35.0: rename the real-weight AM-GM lemma to avoid a new +Mathlib declaration. + +# The Prékopa–Leindler inequality + +Let `a, b > 0` with `a + b = 1` and let `f`, `g`, `h` be measurable `ℝ≥0∞`-valued functions on a +real vector space carrying a measure, subject to + +`f x ^ a * g y ^ b ≤ h (a • x + b • y)` for all `x`, `y`. + +The Prékopa–Leindler inequality says that then + +`(∫⁻ f) ^ a * (∫⁻ g) ^ b ≤ ∫⁻ h`. + +It is the reverse of Hölder's inequality, it contains the Brunn–Minkowski inequality, and it is +the one analytic ingredient Bombieri–Gubler import rather than prove in their treatment of +Vaaler's cube-slicing theorem (their Lemma C.3.3, quoted from Prékopa). Mathlib has neither this +inequality nor Brunn–Minkowski nor log-concavity, so this file starts from the one-dimensional +Brunn–Minkowski inequality for sets. + +## Main definitions + +* `HasPrekopaLeindler μ`: the Prékopa–Leindler inequality holds for the measure `μ` on a real + vector space. The predicate exists so that the passage from one dimension to `n` has something + to induct on. + +## Main results + +* `Real.volume_add_volume_le_volume_add`: the **one-dimensional Brunn–Minkowski inequality**, + `volume s + volume t ≤ volume (s + t)` for nonempty measurable `s`, `t ⊆ ℝ`. +* `MeasureTheory.lintegral_eq_lintegral_measure_ofReal_lt`: the **layer cake formula** for an + `ℝ≥0∞`-valued function, `∫⁻ f ∂μ = ∫⁻ t in Ioi 0, μ {x | ofReal t < f x}`. +* `Real.prekopaLeindler`: the inequality on `ℝ`. +* `HasPrekopaLeindler.prod`: the inequality passes to a product measure. This is the induction + step, and it is Tonelli's theorem and nothing else. +* `HasPrekopaLeindler.of_measurePreserving`: transport along a measure-preserving linear + equivalence. +* `hasPrekopaLeindler_pi`, `hasPrekopaLeindler_euclideanSpace`: the inequality on `Fin n → ℝ` and + on `EuclideanSpace ℝ (Fin n)`. + +## Implementation notes + +⚠ **The `ℝ≥0∞` codomain and the hypothesis form of the statement remove every side condition.** +Bombieri–Gubler state C.3.3 with `r t := ⨆ {f x * g y | a • x + b • y = t}` on the right, and +Prékopa's own erratum records that this sup-convolution is only guaranteed to be Lebesgue +measurable. +Taking `h` as *given*, with `f x ^ a * g y ^ b ≤ h (a • x + b • y)` as a hypothesis, avoids that +question entirely; and because `∫⁻` is defined for every measurable `ℝ≥0∞`-valued function, there +is no integrability hypothesis either. Every statement below is therefore hypothesis-free beyond +measurability. + +⚠ **Prékopa–Leindler tensorizes, so the induction is on a product and not on coordinates.** +`HasPrekopaLeindler.prod` is Tonelli applied twice — integrate out the second factor to get three +functions on the first, whose hypothesis is the original one read at `(x₁, x₂)` and `(y₁, y₂)` — +and it mentions neither `ℝ` nor any dimension. So the `n`-dimensional theorem is the +one-dimensional one, this closure property, and transport along a linear measure-preserving +equivalence: `hasPrekopaLeindler_pi` is three lines. That is why the file is written around a +predicate rather than around `ℝ ^ n`. + +⚠ **All the geometry is in one dimension, and it is Brunn–Minkowski for sets.** After normalizing +`f` and `g` to have supremum `1`, the level sets `{f > t}` and `{g > t}` are nonempty for +`0 < t < 1` and satisfy `a • {f > t} + b • {g > t} ⊆ {h > t}`; the inequality then follows from +`volume s + volume t ≤ volume (s + t)` on `ℝ` and the layer cake formula. The Brunn–Minkowski step +is proved for compact sets by translating `s` by `sInf t` and `t` by `sSup s` — the two translates +lie in `s + t` and meet in a single point — and extended to measurable sets by inner regularity. + +⚠ **The normalization is what forces the truncation.** The level-set argument needs `⨆ f` finite +and nonzero; `⨆ f = ∞` is a genuine case, since an unbounded function can have a finite integral. +The general statement is therefore obtained from the bounded one by replacing `f` with +`min f (n + 1)` and letting `n → ∞`, which needs `(⨆ n, u n) ^ p = ⨆ n, u n ^ p` for `p > 0` — +`ENNReal.iSup_rpow` below, which is `ENNReal.orderIsoRpow` and nothing more. + +## References + +A. Prékopa, *On logarithmic concave measures and functions*, Acta Sci. Math. **34** (1973), +Theorem 3. This is the statement below, and it is the single step Bombieri–Gubler import. + +E. Bombieri and W. Gubler, *Heights in Diophantine Geometry*, Cambridge University Press (2006), +Lemma C.3.3, in the appendix that proves Vaaler's cube-slicing theorem. Their C.3.4 — the +log-concavity of a marginal — is in `ArithmeticHeights.LogConcave`, and is the only consumer of +this file inside the roadmap. + +This is Layer 4.5 (infrastructure) of the `ArithmeticHeights` roadmap. +-/ + +public section + +open MeasureTheory Set Pointwise + +/-- **Weighted AM–GM in `ℝ≥0∞`**, for two terms: the `ℝ≥0∞`-valued companion of +`NNReal.geom_mean_le_arith_mean2_weighted`, stated with real weights because the exponents of +`ENNReal.rpow` are real. -/ +theorem ENNReal.geom_mean_le_arith_mean2_weighted_real_weights {w₁ w₂ : ℝ} + (hw₁ : 0 ≤ w₁) (hw₂ : 0 ≤ w₂) + (hw : w₁ + w₂ = 1) (p₁ p₂ : ENNReal) : + p₁ ^ w₁ * p₂ ^ w₂ ≤ ENNReal.ofReal w₁ * p₁ + ENNReal.ofReal w₂ * p₂ := by + rcases eq_or_lt_of_le hw₁ with hz | hpos₁ + · have h₂ : w₂ = 1 := by rw [← hw, ← hz, zero_add] + rw [← hz, h₂] + simp + rcases eq_or_lt_of_le hw₂ with hz | hpos₂ + · have h₁ : w₁ = 1 := by rw [← hw, ← hz, add_zero] + rw [← hz, h₁] + simp + have hc₁ : ENNReal.ofReal w₁ ≠ 0 := by simp [ENNReal.ofReal_eq_zero, not_le.mpr hpos₁] + have hc₂ : ENNReal.ofReal w₂ ≠ 0 := by simp [ENNReal.ofReal_eq_zero, not_le.mpr hpos₂] + rcases eq_or_ne p₁ ⊤ with hp | hp + · rw [hp] + simp [ENNReal.mul_top hc₁] + rcases eq_or_ne p₂ ⊤ with hp' | hp' + · rw [hp'] + simp [ENNReal.mul_top hc₂] + lift p₁ to NNReal using hp + lift p₂ to NNReal using hp' + have e₁ : (w₁.toNNReal : ℝ) = w₁ := Real.coe_toNNReal w₁ hw₁ + have e₂ : (w₂.toNNReal : ℝ) = w₂ := Real.coe_toNNReal w₂ hw₂ + have hsum : w₁.toNNReal + w₂.toNNReal = 1 := by + apply NNReal.coe_injective + push_cast [e₁, e₂] + exact hw + have hmain := NNReal.geom_mean_le_arith_mean2_weighted w₁.toNNReal w₂.toNNReal p₁ p₂ hsum + rw [← ENNReal.coe_rpow_of_nonneg _ hw₁, ← ENNReal.coe_rpow_of_nonneg _ hw₂] + calc ((p₁ ^ w₁ : NNReal) : ENNReal) * ((p₂ ^ w₂ : NNReal) : ENNReal) + = ((p₁ ^ w₁ * p₂ ^ w₂ : NNReal) : ENNReal) := (ENNReal.coe_mul _ _).symm + _ ≤ ((w₁.toNNReal * p₁ + w₂.toNNReal * p₂ : NNReal) : ENNReal) := by + rw [ENNReal.coe_le_coe] + calc p₁ ^ w₁ * p₂ ^ w₂ + = p₁ ^ (w₁.toNNReal : ℝ) * p₂ ^ (w₂.toNNReal : ℝ) := by rw [e₁, e₂] + _ ≤ w₁.toNNReal * p₁ + w₂.toNNReal * p₂ := hmain + _ = ENNReal.ofReal w₁ * p₁ + ENNReal.ofReal w₂ * p₂ := by + rw [ENNReal.coe_add, ENNReal.coe_mul, ENNReal.coe_mul] + rfl + +/-- Raising to a positive real power commutes with suprema in `ℝ≥0∞`, because it is an order +isomorphism. -/ +theorem ENNReal.iSup_rpow {ι : Sort*} (u : ι → ENNReal) {p : ℝ} (hp : 0 < p) : + (⨆ i, u i) ^ p = ⨆ i, u i ^ p := by + have h := (ENNReal.orderIsoRpow p hp).map_iSup u + simp only [ENNReal.orderIsoRpow_apply] at h + exact h + +/-- The one-dimensional Brunn–Minkowski inequality for compact sets: translate `s` by `sInf t` and +`t` by `sSup s`; the two translates lie in `s + t` and meet only at `sSup s + sInf t`. -/ +private theorem Real.volume_add_volume_le_volume_add_of_isCompact {s t : Set ℝ} (hs : IsCompact s) + (ht : IsCompact t) (hsne : s.Nonempty) (htne : t.Nonempty) : + volume s + volume t ≤ volume (s + t) := by + set a := sSup s with hadef + set b := sInf t with hbdef + have ha : a ∈ s := hs.sSup_mem hsne + have hb : b ∈ t := ht.sInf_mem htne + set A : Set ℝ := (· + b) '' s with hA + set B : Set ℝ := (a + ·) '' t with hB + have hBm : MeasurableSet B := (ht.image (by fun_prop)).measurableSet + have hAsub : A ⊆ s + t := by rintro _ ⟨u, hu, rfl⟩; exact Set.add_mem_add hu hb + have hBsub : B ⊆ s + t := by rintro _ ⟨v, hv, rfl⟩; exact Set.add_mem_add ha hv + have hinter : A ∩ B ⊆ {a + b} := by + rintro x ⟨⟨u, hu, rfl⟩, ⟨v, hv, hx⟩⟩ + have hx' : a + v = u + b := hx + have h1 : u ≤ a := le_csSup hs.bddAbove hu + have h2 : b ≤ v := csInf_le ht.bddBelow hv + change u + b = a + b + linarith + have h0 : volume (A ∩ B) = 0 := measure_mono_null hinter (measure_singleton _) + have hvol : volume (A ∪ B) + volume (A ∩ B) = volume A + volume B := + measure_union_add_inter A hBm + have hAv : volume A = volume s := by simp [hA] + have hBv : volume B = volume t := by simp [hB] + calc volume s + volume t = volume A + volume B := by rw [hAv, hBv] + _ = volume (A ∪ B) := by rw [← hvol, h0, add_zero] + _ ≤ volume (s + t) := measure_mono (union_subset hAsub hBsub) + +/-- **The one-dimensional Brunn–Minkowski inequality.** For nonempty measurable sets `s`, `t ⊆ ℝ`, +`volume s + volume t ≤ volume (s + t)`. The sumset need not be measurable: `volume` is applied to +it as an outer measure. -/ +theorem Real.volume_add_volume_le_volume_add {s t : Set ℝ} (hs : MeasurableSet s) + (ht : MeasurableSet t) (hsne : s.Nonempty) (htne : t.Nonempty) : + volume s + volume t ≤ volume (s + t) := by + obtain ⟨x₀, hx₀⟩ := hsne + obtain ⟨y₀, hy₀⟩ := htne + rcases eq_or_ne (volume (s + t)) ⊤ with hst | hst + · simp [hst] + have hsv : volume s ≠ ⊤ := by + intro h + refine hst (top_le_iff.mp ?_) + have hle : volume ((· + y₀) '' s) ≤ volume (s + t) := + measure_mono (by rintro _ ⟨u, hu, rfl⟩; exact Set.add_mem_add hu hy₀) + simpa [h] using hle + have htv : volume t ≠ ⊤ := by + intro h + refine hst (top_le_iff.mp ?_) + have hle : volume ((x₀ + ·) '' t) ≤ volume (s + t) := + measure_mono (by rintro _ ⟨v, hv, rfl⟩; exact Set.add_mem_add hx₀ hv) + simpa [h] using hle + refine ENNReal.le_of_forall_pos_le_add fun ε hε _ ↦ ?_ + have hhalf : (ε : ENNReal) / 2 ≠ 0 := by simp [ENNReal.div_eq_zero_iff, hε.ne'] + obtain ⟨K, hKs, hKc, hK⟩ := hs.exists_isCompact_lt_add hsv hhalf + obtain ⟨L, hLt, hLc, hL⟩ := ht.exists_isCompact_lt_add htv hhalf + set K' : Set ℝ := insert x₀ K with hK' + set L' : Set ℝ := insert y₀ L with hL' + have hKK' : volume K ≤ volume K' := measure_mono (Set.subset_insert _ _) + have hLL' : volume L ≤ volume L' := measure_mono (Set.subset_insert _ _) + have hcomp := Real.volume_add_volume_le_volume_add_of_isCompact (hKc.insert x₀) (hLc.insert y₀) + ⟨x₀, Set.mem_insert _ _⟩ ⟨y₀, Set.mem_insert _ _⟩ + have hsub : K' + L' ⊆ s + t := + Set.add_subset_add (Set.insert_subset hx₀ hKs) (Set.insert_subset hy₀ hLt) + calc volume s + volume t ≤ (volume K + (ε : ENNReal) / 2) + (volume L + (ε : ENNReal) / 2) := + add_le_add hK.le hL.le + _ = (volume K + volume L) + ((ε : ENNReal) / 2 + (ε : ENNReal) / 2) := by ring + _ ≤ (volume K' + volume L') + (ε : ENNReal) := by + rw [ENNReal.add_halves] + exact add_le_add (add_le_add hKK' hLL') le_rfl + _ ≤ volume (K' + L') + (ε : ENNReal) := add_le_add hcomp le_rfl + _ ≤ volume (s + t) + (ε : ENNReal) := add_le_add (measure_mono hsub) le_rfl + +/-- The Lebesgue measure of `{t | 0 < t ∧ ENNReal.ofReal t < c}` is `c`, for every `c : ℝ≥0∞`: +the interval `Ioo 0 c.toReal` when `c` is finite, and `Ioi 0` when it is not. -/ +private theorem Real.volume_setOf_ofReal_lt (c : ENNReal) : + volume {t : ℝ | 0 < t ∧ ENNReal.ofReal t < c} = c := by + rcases eq_or_ne c ⊤ with hc | hc + · subst hc + have h : {t : ℝ | 0 < t ∧ ENNReal.ofReal t < ⊤} = Ioi (0 : ℝ) := by + ext t; simp [ENNReal.ofReal_lt_top] + rw [h, Real.volume_Ioi] + · have h : {t : ℝ | 0 < t ∧ ENNReal.ofReal t < c} = Ioo 0 c.toReal := by + ext t + constructor + · rintro ⟨h1, h2⟩ + exact ⟨h1, (ENNReal.ofReal_lt_iff_lt_toReal h1.le hc).mp h2⟩ + · rintro ⟨h1, h2⟩ + exact ⟨h1, (ENNReal.ofReal_lt_iff_lt_toReal h1.le hc).mpr h2⟩ + rw [h, Real.volume_Ioo, sub_zero, ENNReal.ofReal_toReal hc] + +/-- **The layer cake formula for `ℝ≥0∞`-valued functions.** Mathlib's `Layercake` file states this +for real-valued functions; the `ℝ≥0∞` form is what the Prékopa–Leindler proof needs, since the +level sets there may well have infinite measure. -/ +theorem MeasureTheory.lintegral_eq_lintegral_measure_ofReal_lt {α : Type*} [MeasurableSpace α] + (μ : Measure α) [SFinite μ] {f : α → ENNReal} (hf : Measurable f) : + ∫⁻ x, f x ∂μ = ∫⁻ t in Ioi (0 : ℝ), μ {x | ENNReal.ofReal t < f x} := by + have hS : MeasurableSet {p : α × ℝ | 0 < p.2 ∧ ENNReal.ofReal p.2 < f p.1} := + (measurableSet_lt measurable_const measurable_snd).inter + (measurableSet_lt (ENNReal.measurable_ofReal.comp measurable_snd) (hf.comp measurable_fst)) + set F : α → ℝ → ENNReal := fun x t => + Set.indicator {p : α × ℝ | 0 < p.2 ∧ ENNReal.ofReal p.2 < f p.1} 1 (x, t) with hF + have hFm : Measurable (Function.uncurry F) := measurable_one.indicator hS + have h1 : ∀ x, ∫⁻ t, F x t = f x := by + intro x + have he : (fun t => F x t) = Set.indicator {t : ℝ | 0 < t ∧ ENNReal.ofReal t < f x} 1 := by + ext t; simp [hF, Set.indicator_apply] + have hm : MeasurableSet {t : ℝ | 0 < t ∧ ENNReal.ofReal t < f x} := + (measurableSet_lt measurable_const measurable_id).inter + (measurableSet_lt ENNReal.measurable_ofReal measurable_const) + rw [he, lintegral_indicator_one hm, Real.volume_setOf_ofReal_lt] + have h2 : ∀ t : ℝ, 0 < t → ∫⁻ x, F x t ∂μ = μ {x | ENNReal.ofReal t < f x} := by + intro t ht + have he : (fun x => F x t) = Set.indicator {x | ENNReal.ofReal t < f x} 1 := by + ext x; simp [hF, Set.indicator_apply, ht] + rw [he, lintegral_indicator_one (measurableSet_lt measurable_const hf)] + have h3 : ∀ t : ℝ, t ≤ 0 → ∫⁻ x, F x t ∂μ = 0 := by + intro t ht + have he : (fun x => F x t) = fun _ => (0 : ENNReal) := by + ext x; simp [hF, Set.indicator_apply, not_lt.mpr ht] + rw [he, lintegral_zero] + calc ∫⁻ x, f x ∂μ = ∫⁻ x, (∫⁻ t, F x t) ∂μ := by simp_rw [h1] + _ = ∫⁻ t, (∫⁻ x, F x t ∂μ) := lintegral_lintegral_swap hFm.aemeasurable + _ = ∫⁻ t in Ioi (0 : ℝ), μ {x | ENNReal.ofReal t < f x} := by + rw [← lintegral_indicator measurableSet_Ioi] + refine lintegral_congr fun t => ?_ + rcases lt_or_ge 0 t with ht | ht + · rw [h2 t ht, Set.indicator_of_mem (mem_Ioi.mpr ht)] + · rw [h3 t ht, Set.indicator_of_notMem (by simpa using ht)] + +/-- Prékopa–Leindler on `ℝ` for functions normalized to have supremum `1`, in the stronger +*arithmetic mean* form `a * ∫⁻ f + b * ∫⁻ g ≤ ∫⁻ h`. This is where the one-dimensional +Brunn–Minkowski inequality and the layer cake formula are used; everything else in the file is +bookkeeping around it. -/ +private theorem Real.prekopaLeindler_of_le_one {a b : ℝ} (ha : 0 < a) (hb : 0 < b) + (hab : a + b = 1) {f g h : ℝ → ENNReal} (hf : Measurable f) (hg : Measurable g) + (hh : Measurable h) (hf1 : ∀ x, f x ≤ 1) (hg1 : ∀ y, g y ≤ 1) + (hfne : ∀ t : ℝ, 0 < t → t < 1 → {x | ENNReal.ofReal t < f x}.Nonempty) + (hgne : ∀ t : ℝ, 0 < t → t < 1 → {y | ENNReal.ofReal t < g y}.Nonempty) + (key : ∀ x y, f x ^ a * g y ^ b ≤ h (a • x + b • y)) : + ENNReal.ofReal a * (∫⁻ x, f x) + ENNReal.ofReal b * (∫⁻ y, g y) ≤ ∫⁻ z, h z := by + have hstep : ∀ t : ℝ, 0 < t → + ENNReal.ofReal a * volume {x | ENNReal.ofReal t < f x} + + ENNReal.ofReal b * volume {y | ENNReal.ofReal t < g y} + ≤ volume {z | ENNReal.ofReal t < h z} := by + intro t ht + rcases lt_or_ge t 1 with ht1 | ht1 + · have hAm : MeasurableSet {x | ENNReal.ofReal t < f x} := measurableSet_lt measurable_const hf + have hBm : MeasurableSet {y | ENNReal.ofReal t < g y} := measurableSet_lt measurable_const hg + obtain ⟨x₀, hx₀⟩ := hfne t ht ht1 + obtain ⟨y₀, hy₀⟩ := hgne t ht ht1 + have hc0 : ENNReal.ofReal t ≠ 0 := by simp [ENNReal.ofReal_eq_zero, not_le.mpr ht] + have hcT : ENNReal.ofReal t ≠ ⊤ := ENNReal.ofReal_ne_top + have hcpos : 0 < ENNReal.ofReal t := pos_iff_ne_zero.mpr hc0 + have hsub : a • {x | ENNReal.ofReal t < f x} + b • {y | ENNReal.ofReal t < g y} + ⊆ {z | ENNReal.ofReal t < h z} := by + rintro z ⟨p, hp, q, hq, rfl⟩ + obtain ⟨u, hu, rfl⟩ := hp + obtain ⟨v, hv, rfl⟩ := hq + change ENNReal.ofReal t < h (a • u + b • v) + refine lt_of_lt_of_le ?_ (key u v) + have hsplit : ENNReal.ofReal t = ENNReal.ofReal t ^ a * ENNReal.ofReal t ^ b := by + rw [← ENNReal.rpow_add _ _ hc0 hcT, hab, ENNReal.rpow_one] + rw [hsplit] + have h1 : ENNReal.ofReal t ^ a < f u ^ a := ENNReal.rpow_lt_rpow hu ha + have h2 : ENNReal.ofReal t ^ b ≤ g v ^ b := ENNReal.rpow_le_rpow hv.le hb.le + calc ENNReal.ofReal t ^ a * ENNReal.ofReal t ^ b + = ENNReal.ofReal t ^ b * ENNReal.ofReal t ^ a := mul_comm _ _ + _ < ENNReal.ofReal t ^ b * f u ^ a := + ENNReal.mul_lt_mul_right (ENNReal.rpow_pos hcpos hcT).ne' + (ENNReal.rpow_ne_top_of_nonneg hb.le hcT) h1 + _ = f u ^ a * ENNReal.ofReal t ^ b := mul_comm _ _ + _ ≤ f u ^ a * g v ^ b := by gcongr + have hBM := Real.volume_add_volume_le_volume_add (hAm.const_smul₀ a) (hBm.const_smul₀ b) + ⟨a • x₀, ⟨x₀, hx₀, rfl⟩⟩ ⟨b • y₀, ⟨y₀, hy₀, rfl⟩⟩ + have hvA : volume (a • {x | ENNReal.ofReal t < f x}) + = ENNReal.ofReal a * volume {x | ENNReal.ofReal t < f x} := by + rw [Measure.addHaar_smul]; simp [abs_of_pos ha] + have hvB : volume (b • {y | ENNReal.ofReal t < g y}) + = ENNReal.ofReal b * volume {y | ENNReal.ofReal t < g y} := by + rw [Measure.addHaar_smul]; simp [abs_of_pos hb] + calc ENNReal.ofReal a * volume {x | ENNReal.ofReal t < f x} + + ENNReal.ofReal b * volume {y | ENNReal.ofReal t < g y} + = volume (a • {x | ENNReal.ofReal t < f x}) + + volume (b • {y | ENNReal.ofReal t < g y}) := by rw [hvA, hvB] + _ ≤ volume (a • {x | ENNReal.ofReal t < f x} + b • {y | ENNReal.ofReal t < g y}) := hBM + _ ≤ volume {z | ENNReal.ofReal t < h z} := measure_mono hsub + · have h1t : (1 : ENNReal) ≤ ENNReal.ofReal t := ENNReal.one_le_ofReal.mpr ht1 + have hAe : {x | ENNReal.ofReal t < f x} = ∅ := + Set.eq_empty_iff_forall_notMem.mpr fun x hx => + absurd hx (not_lt.mpr (le_trans (hf1 x) h1t)) + have hBe : {y | ENNReal.ofReal t < g y} = ∅ := + Set.eq_empty_iff_forall_notMem.mpr fun y hy => + absurd hy (not_lt.mpr (le_trans (hg1 y) h1t)) + simp [hAe, hBe] + rw [MeasureTheory.lintegral_eq_lintegral_measure_ofReal_lt volume hf, + MeasureTheory.lintegral_eq_lintegral_measure_ofReal_lt volume hg, + MeasureTheory.lintegral_eq_lintegral_measure_ofReal_lt volume hh, + ← lintegral_const_mul' _ _ ENNReal.ofReal_ne_top, + ← lintegral_const_mul' _ _ ENNReal.ofReal_ne_top] + refine le_trans (le_lintegral_add _ _) (lintegral_mono_ae ?_) + filter_upwards [self_mem_ae_restrict measurableSet_Ioi] with t ht + exact hstep t ht + +/-- Prékopa–Leindler on `ℝ` for functions bounded by `M` and `N` whose suprema are `M` and `N`: +normalize by `M` and `N` and appeal to `Real.prekopaLeindler_of_le_one`. -/ +private theorem Real.prekopaLeindler_of_le {a b : ℝ} (ha : 0 < a) (hb : 0 < b) (hab : a + b = 1) + {f g h : ℝ → ENNReal} (hf : Measurable f) (hg : Measurable g) (hh : Measurable h) + {M N : ENNReal} (hM0 : M ≠ 0) (hMT : M ≠ ⊤) (hN0 : N ≠ 0) (hNT : N ≠ ⊤) + (hfM : ∀ x, f x ≤ M) (hgN : ∀ y, g y ≤ N) + (hfsup : ∀ t : ℝ, 0 < t → t < 1 → ∃ x, ENNReal.ofReal t * M < f x) + (hgsup : ∀ t : ℝ, 0 < t → t < 1 → ∃ y, ENNReal.ofReal t * N < g y) + (key : ∀ x y, f x ^ a * g y ^ b ≤ h (a • x + b • y)) : + (∫⁻ x, f x) ^ a * (∫⁻ y, g y) ^ b ≤ ∫⁻ z, h z := by + have hMi : M⁻¹ ≠ 0 := ENNReal.inv_ne_zero.mpr hMT + have hMiT : M⁻¹ ≠ ⊤ := ENNReal.inv_ne_top.mpr hM0 + have hNi : N⁻¹ ≠ 0 := ENNReal.inv_ne_zero.mpr hNT + have hNiT : N⁻¹ ≠ ⊤ := ENNReal.inv_ne_top.mpr hN0 + have hMa0 : M ^ a ≠ 0 := (ENNReal.rpow_pos (pos_iff_ne_zero.mpr hM0) hMT).ne' + have hMaT : M ^ a ≠ ⊤ := ENNReal.rpow_ne_top_of_nonneg ha.le hMT + have hNb0 : N ^ b ≠ 0 := (ENNReal.rpow_pos (pos_iff_ne_zero.mpr hN0) hNT).ne' + have hNbT : N ^ b ≠ ⊤ := ENNReal.rpow_ne_top_of_nonneg hb.le hNT + set c : ENNReal := (M ^ a * N ^ b)⁻¹ with hc + have hc0 : c ≠ 0 := ENNReal.inv_ne_zero.mpr (ENNReal.mul_ne_top hMaT hNbT) + have hcT : c ≠ ⊤ := ENNReal.inv_ne_top.mpr (by simp [hMa0, hNb0]) + have hcomp : ∀ u v : ENNReal, (M⁻¹ * u) ^ a * (N⁻¹ * v) ^ b = c * (u ^ a * v ^ b) := by + intro u v + rw [ENNReal.mul_rpow_of_nonneg _ _ ha.le, ENNReal.mul_rpow_of_nonneg _ _ hb.le, + ENNReal.inv_rpow, ENNReal.inv_rpow, hc, ENNReal.mul_inv (Or.inl hMa0) (Or.inl hMaT)] + ring + have hunit := Real.prekopaLeindler_of_le_one ha hb hab (f := fun x => M⁻¹ * f x) + (g := fun y => N⁻¹ * g y) (h := fun z => c * h z) + (measurable_const.mul hf) (measurable_const.mul hg) (measurable_const.mul hh) + (fun x => by + calc M⁻¹ * f x ≤ M⁻¹ * M := by gcongr; exact hfM x + _ = 1 := ENNReal.inv_mul_cancel hM0 hMT) + (fun y => by + calc N⁻¹ * g y ≤ N⁻¹ * N := by gcongr; exact hgN y + _ = 1 := ENNReal.inv_mul_cancel hN0 hNT) + (fun t ht ht1 => by + obtain ⟨x, hx⟩ := hfsup t ht ht1 + refine ⟨x, ?_⟩ + have hlt : M⁻¹ * (ENNReal.ofReal t * M) < M⁻¹ * f x := + ENNReal.mul_lt_mul_right hMi hMiT hx + calc ENNReal.ofReal t = M⁻¹ * (ENNReal.ofReal t * M) := by + rw [mul_comm (ENNReal.ofReal t) M, ← mul_assoc, ENNReal.inv_mul_cancel hM0 hMT, + one_mul] + _ < M⁻¹ * f x := hlt) + (fun t ht ht1 => by + obtain ⟨y, hy⟩ := hgsup t ht ht1 + refine ⟨y, ?_⟩ + have hlt : N⁻¹ * (ENNReal.ofReal t * N) < N⁻¹ * g y := + ENNReal.mul_lt_mul_right hNi hNiT hy + calc ENNReal.ofReal t = N⁻¹ * (ENNReal.ofReal t * N) := by + rw [mul_comm (ENNReal.ofReal t) N, ← mul_assoc, ENNReal.inv_mul_cancel hN0 hNT, + one_mul] + _ < N⁻¹ * g y := hlt) + (fun x y => by rw [hcomp]; gcongr; exact key x y) + rw [lintegral_const_mul' _ _ hMiT, lintegral_const_mul' _ _ hNiT, + lintegral_const_mul' _ _ hcT] at hunit + have hAM := ENNReal.geom_mean_le_arith_mean2_weighted_real_weights ha.le hb.le hab + (M⁻¹ * ∫⁻ x, f x) (N⁻¹ * ∫⁻ y, g y) + rw [hcomp] at hAM + exact (ENNReal.mul_le_mul_iff_right hc0 hcT).mp (hAM.trans hunit) + +/-- **The Prékopa–Leindler inequality on `ℝ`.** -/ +theorem Real.prekopaLeindler {a b : ℝ} (ha : 0 < a) (hb : 0 < b) (hab : a + b = 1) + {f g h : ℝ → ENNReal} (hf : Measurable f) (hg : Measurable g) (hh : Measurable h) + (key : ∀ x y, f x ^ a * g y ^ b ≤ h (a • x + b • y)) : + (∫⁻ x, f x) ^ a * (∫⁻ y, g y) ^ b ≤ ∫⁻ z, h z := by + rcases eq_or_ne (∫⁻ x, f x) 0 with hF0 | hF0 + · rw [hF0, ENNReal.zero_rpow_of_pos ha, zero_mul]; exact zero_le + rcases eq_or_ne (∫⁻ y, g y) 0 with hG0 | hG0 + · rw [hG0, ENNReal.zero_rpow_of_pos hb, mul_zero]; exact zero_le + obtain ⟨x₀, hx₀⟩ : ∃ x₀, f x₀ ≠ 0 := by + by_contra hcon + push Not at hcon + exact hF0 (by simp [hcon]) + obtain ⟨y₀, hy₀⟩ : ∃ y₀, g y₀ ≠ 0 := by + by_contra hcon + push Not at hcon + exact hG0 (by simp [hcon]) + set F : ℕ → ℝ → ENNReal := fun n x => min (f x) ((n : ENNReal) + 1) with hFdef + set G : ℕ → ℝ → ENNReal := fun n y => min (g y) ((n : ENNReal) + 1) with hGdef + have htopsup : ⨆ n : ℕ, ((n : ENNReal) + 1) = ⊤ := by + refine top_le_iff.mp ?_ + rw [← ENNReal.iSup_natCast] + exact iSup_mono fun n => le_self_add + have hFlim : ∀ x, ⨆ n, F n x = f x := by + intro x + refine le_antisymm (iSup_le fun n => min_le_left _ _) (le_of_forall_lt fun c hc => ?_) + obtain ⟨n, hn⟩ := lt_iSup_iff.mp (htopsup ▸ lt_of_lt_of_le hc le_top : + c < ⨆ n : ℕ, ((n : ENNReal) + 1)) + exact lt_of_lt_of_le (lt_min hc hn) (le_iSup (fun n => F n x) n) + have hGlim : ∀ y, ⨆ n, G n y = g y := by + intro y + refine le_antisymm (iSup_le fun n => min_le_left _ _) (le_of_forall_lt fun c hc => ?_) + obtain ⟨n, hn⟩ := lt_iSup_iff.mp (htopsup ▸ lt_of_lt_of_le hc le_top : + c < ⨆ n : ℕ, ((n : ENNReal) + 1)) + exact lt_of_lt_of_le (lt_min hc hn) (le_iSup (fun n => G n y) n) + have hFmono : Monotone F := fun n m hnm x => by + simp only [hFdef] + exact min_le_min le_rfl (by gcongr) + have hGmono : Monotone G := fun n m hnm y => by + simp only [hGdef] + exact min_le_min le_rfl (by gcongr) + have hFmeas : ∀ n, Measurable (F n) := fun n => hf.min measurable_const + have hGmeas : ∀ n, Measurable (G n) := fun n => hg.min measurable_const + have hFint : (∫⁻ x, f x) = ⨆ n, ∫⁻ x, F n x := by + rw [← lintegral_iSup hFmeas hFmono] + exact lintegral_congr fun x => (hFlim x).symm + have hGint : (∫⁻ y, g y) = ⨆ n, ∫⁻ y, G n y := by + rw [← lintegral_iSup hGmeas hGmono] + exact lintegral_congr fun y => (hGlim y).symm + have hbdd : ∀ n : ℕ, (∫⁻ x, F n x) ^ a * (∫⁻ y, G n y) ^ b ≤ ∫⁻ z, h z := by + intro n + set M : ENNReal := ⨆ x, F n x with hM + set N : ENNReal := ⨆ y, G n y with hN + have hMT : M ≠ ⊤ := by + refine ne_top_of_le_ne_top (by simp) (iSup_le fun x => ?_ : M ≤ (n : ENNReal) + 1) + exact min_le_right _ _ + have hNT : N ≠ ⊤ := by + refine ne_top_of_le_ne_top (by simp) (iSup_le fun y => ?_ : N ≤ (n : ENNReal) + 1) + exact min_le_right _ _ + have hM0 : M ≠ 0 := by + refine ne_of_gt (lt_of_lt_of_le ?_ (le_iSup (fun x => F n x) x₀)) + simp only [hFdef, lt_min_iff, pos_iff_ne_zero] + exact ⟨hx₀, by simp⟩ + have hN0 : N ≠ 0 := by + refine ne_of_gt (lt_of_lt_of_le ?_ (le_iSup (fun y => G n y) y₀)) + simp only [hGdef, lt_min_iff, pos_iff_ne_zero] + exact ⟨hy₀, by simp⟩ + refine Real.prekopaLeindler_of_le ha hb hab (hFmeas n) (hGmeas n) hh hM0 hMT hN0 hNT + (fun x => le_iSup (fun x => F n x) x) (fun y => le_iSup (fun y => G n y) y) + (fun t ht ht1 => ?_) (fun t ht ht1 => ?_) (fun x y => ?_) + · have hlt : ENNReal.ofReal t * M < M := by + calc ENNReal.ofReal t * M = M * ENNReal.ofReal t := mul_comm _ _ + _ < M * 1 := ENNReal.mul_lt_mul_right hM0 hMT (ENNReal.ofReal_lt_one.mpr ht1) + _ = M := mul_one M + exact lt_iSup_iff.mp hlt + · have hlt : ENNReal.ofReal t * N < N := by + calc ENNReal.ofReal t * N = N * ENNReal.ofReal t := mul_comm _ _ + _ < N * 1 := ENNReal.mul_lt_mul_right hN0 hNT (ENNReal.ofReal_lt_one.mpr ht1) + _ = N := mul_one N + exact lt_iSup_iff.mp hlt + · refine le_trans ?_ (key x y) + gcongr + · exact min_le_left _ _ + · exact min_le_left _ _ + rw [hFint, hGint, ENNReal.iSup_rpow _ ha, ENNReal.iSup_rpow _ hb, ENNReal.iSup_mul] + refine iSup_le fun n => ?_ + rw [ENNReal.mul_iSup] + refine iSup_le fun m => ?_ + refine le_trans ?_ (hbdd (max n m)) + gcongr + · exact hFmono (le_max_left n m) _ + · exact hGmono (le_max_right n m) _ + +/-- `HasPrekopaLeindler μ` says that the Prékopa–Leindler inequality holds for the measure `μ` on +the real vector space `E`: whenever `f x ^ a * g y ^ b ≤ h (a • x + b • y)` pointwise, with +`a, b > 0` and `a + b = 1`, the same inequality holds for the integrals. -/ +@[expose] def HasPrekopaLeindler {E : Type*} [MeasurableSpace E] [AddCommGroup E] [Module ℝ E] + (μ : Measure E) : Prop := + ∀ (a b : ℝ), 0 < a → 0 < b → a + b = 1 → ∀ (f g h : E → ENNReal), + Measurable f → Measurable g → Measurable h → + (∀ x y, f x ^ a * g y ^ b ≤ h (a • x + b • y)) → + (∫⁻ x, f x ∂μ) ^ a * (∫⁻ y, g y ∂μ) ^ b ≤ ∫⁻ z, h z ∂μ + +/-- Prékopa–Leindler holds for Lebesgue measure on `ℝ`. -/ +theorem hasPrekopaLeindler_real : HasPrekopaLeindler (volume : Measure ℝ) := + fun _ _ ha hb hab _ _ _ hf hg hh key => Real.prekopaLeindler ha hb hab hf hg hh key + +/-- **Prékopa–Leindler tensorizes.** Integrating out the second factor turns the hypothesis on +`E × F` into the hypothesis on `E` for the three marginals, and Tonelli's theorem identifies the +marginals' integrals with the original ones. -/ +theorem HasPrekopaLeindler.prod {E F : Type*} [MeasurableSpace E] [AddCommGroup E] [Module ℝ E] + [MeasurableSpace F] [AddCommGroup F] [Module ℝ F] {μ : Measure E} {ν : Measure F} + [SFinite ν] (hE : HasPrekopaLeindler μ) (hF : HasPrekopaLeindler ν) : + HasPrekopaLeindler (μ.prod ν) := by + intro a b ha hb hab f g h hf hg hh key + have hkey1 : ∀ x₁ y₁ : E, (∫⁻ x₂, f (x₁, x₂) ∂ν) ^ a * (∫⁻ y₂, g (y₁, y₂) ∂ν) ^ b + ≤ ∫⁻ z₂, h (a • x₁ + b • y₁, z₂) ∂ν := by + intro x₁ y₁ + refine hF a b ha hb hab _ _ _ (hf.comp measurable_prodMk_left) + (hg.comp measurable_prodMk_left) (hh.comp measurable_prodMk_left) fun x₂ y₂ => ?_ + exact key (x₁, x₂) (y₁, y₂) + have hmf : Measurable fun x₁ => ∫⁻ x₂, f (x₁, x₂) ∂ν := hf.lintegral_prod_right' + have hmg : Measurable fun y₁ => ∫⁻ y₂, g (y₁, y₂) ∂ν := hg.lintegral_prod_right' + have hmh : Measurable fun z₁ => ∫⁻ z₂, h (z₁, z₂) ∂ν := hh.lintegral_prod_right' + have hmain := hE a b ha hb hab _ _ _ hmf hmg hmh hkey1 + rwa [← lintegral_prod _ hf.aemeasurable, ← lintegral_prod _ hg.aemeasurable, + ← lintegral_prod _ hh.aemeasurable] at hmain + +/-- Prékopa–Leindler transports along a measure-preserving linear equivalence. -/ +theorem HasPrekopaLeindler.of_measurePreserving {E F : Type*} [MeasurableSpace E] + [AddCommGroup E] [Module ℝ E] [MeasurableSpace F] [AddCommGroup F] [Module ℝ F] + {μ : Measure E} {ν : Measure F} (e : E ≃ₗ[ℝ] F) + (he : MeasurePreserving e μ ν) (hsymm : Measurable e.symm) + (hF : HasPrekopaLeindler ν) : HasPrekopaLeindler μ := by + intro a b ha hb hab f g h hf hg hh key + have htrans : ∀ u : E → ENNReal, Measurable u → + ∫⁻ x : F, (u ∘ e.symm) x ∂ν = ∫⁻ x : E, u x ∂μ := by + intro u hu + rw [← he.lintegral_comp (hu.comp hsymm)] + exact lintegral_congr fun x => by simp + have hkey : ∀ u v : F, (f ∘ e.symm) u ^ a * (g ∘ e.symm) v ^ b + ≤ (h ∘ e.symm) (a • u + b • v) := by + intro u v + have hlin : e.symm (a • u + b • v) = a • e.symm u + b • e.symm v := by + simp [map_add, map_smul] + simpa [Function.comp_apply, hlin] using key (e.symm u) (e.symm v) + have hmain := hF a b ha hb hab (f ∘ e.symm) (g ∘ e.symm) (h ∘ e.symm) (hf.comp hsymm) + (hg.comp hsymm) (hh.comp hsymm) hkey + rwa [htrans f hf, htrans g hg, htrans h hh] at hmain + +/-- **The Prékopa–Leindler inequality on `Fin n → ℝ`**, by induction on `n` off +`hasPrekopaLeindler_real`, `HasPrekopaLeindler.prod` and the measure-preserving linear equivalence +`(Fin (n + 1) → ℝ) ≃ₗ[ℝ] ℝ × (Fin n → ℝ)`. -/ +theorem hasPrekopaLeindler_pi : ∀ n : ℕ, HasPrekopaLeindler (volume : Measure (Fin n → ℝ)) + | 0 => by + intro a b ha hb hab f g h _ _ _ key + have hvol : (volume : Measure (Fin 0 → ℝ)) Set.univ = 1 := by + rw [volume_pi, Measure.pi_univ]; simp + have hint : ∀ u : (Fin 0 → ℝ) → ENNReal, ∫⁻ x, u x = u 0 := by + intro u + have hc : (fun x : Fin 0 → ℝ => u x) = fun _ => u 0 := + funext fun x => congrArg u (funext fun i => i.elim0) + rw [hc, lintegral_const, hvol, mul_one] + rw [hint f, hint g, hint h] + simpa using key 0 0 + | (n + 1) => by + have hprod : HasPrekopaLeindler (volume : Measure (ℝ × (Fin n → ℝ))) := by + rw [Measure.volume_eq_prod] + exact hasPrekopaLeindler_real.prod (hasPrekopaLeindler_pi n) + refine HasPrekopaLeindler.of_measurePreserving + (Fin.consLinearEquiv ℝ (fun _ : Fin (n + 1) => ℝ)).symm ?_ ?_ hprod + · exact (volume_preserving_piFinSuccAbove (fun _ : Fin (n + 1) => ℝ) 0 : + MeasurePreserving (MeasurableEquiv.piFinSuccAbove (fun _ : Fin (n + 1) => ℝ) 0) _ _) + · simp only [LinearEquiv.symm_symm] + refine Measurable.of_eval fun i => ?_ + induction i using Fin.cases with + | zero => exact measurable_fst + | succ j => exact (measurable_pi_apply j).comp measurable_snd + +/-- **The Prékopa–Leindler inequality on `EuclideanSpace ℝ (Fin n)`**, the space Layer 4.5 states +cube slicing on. -/ +theorem hasPrekopaLeindler_euclideanSpace (n : ℕ) : + HasPrekopaLeindler (volume : Measure (EuclideanSpace ℝ (Fin n))) := + HasPrekopaLeindler.of_measurePreserving (WithLp.linearEquiv 2 ℝ (Fin n → ℝ)) + (PiLp.volume_preserving_ofLp (Fin n)) (PiLp.volume_preserving_toLp (Fin n)).measurable + (hasPrekopaLeindler_pi n) + +end diff --git a/LeanPool/CCDN/ArithmeticHeights/ProductOfBalls.lean b/LeanPool/CCDN/ArithmeticHeights/ProductOfBalls.lean new file mode 100644 index 0000000000..e25ecf6d20 --- /dev/null +++ b/LeanPool/CCDN/ArithmeticHeights/ProductOfBalls.lean @@ -0,0 +1,395 @@ +/- +Copyright (c) 2026 Ralf Stephan. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Ralf Stephan +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.ArithmeticHeights.SliceBound + +/-! +# The slice bound for a product of balls of volume one + +**Bombieri–Gubler, Lemma C.3.7.** Partition the coordinates of `ι → ℝ` into blocks by a map +`blk : ι → κ`, and let `prodBallPi blk` be the product over the blocks of the euclidean ball of +volume one in that block's coordinates. Then for every measurable convex symmetric `A`, the Gauss +measure of `A` is at most `vol (A ∩ prodBallPi blk)`. + +The induction is on the number of coordinates, splitting off the block of a chosen coordinate; the +remaining blocks keep the same label type `κ`, with one label whose block has become empty — and an +empty block imposes no condition, so no re-indexing of the labels is needed. + +## Main definitions + +* `gaussPi`, the Gauss density on `ι → ℝ` for the euclidean norm. +* `ballRadius n`, the radius of the ball of volume one in `n` dimensions. +* `prodBallPi blk`, the product of the balls of volume one over the blocks of `blk`. + +## Main results + +* `hasSliceBound_ballPi`: the case of a single block. +* `hasSliceBound_prodBallPi`: **Bombieri–Gubler, Lemma C.3.7**, on `ι → ℝ`. +* `hasSliceBound_prodBallEuclidean`: the same on `EuclideanSpace ℝ ι`. +* `hasPrekopaLeindler_innerProductSpace`: Prékopa–Leindler on any finite-dimensional real inner + product space, which is what Lemma C.3.4 is applied to in Layer 4.5's endgame. + +## Implementation notes + +⚠ **The induction is on coordinates, not on blocks, and that removes all re-indexing.** Splitting +off the block of one coordinate leaves a smaller coordinate set with the *same* label type; the +label just peeled off now has an empty block, and the condition attached to an empty block — +`0 ≤ ballRadius 0 ^ 2` — is vacuous. Inducting on the number of blocks instead would force the +labels to be renumbered at every step. + +⚠ **The radius is indexed by the cardinality of the block, not by the block.** `ballRadius n` is +the radius of the ball of volume one in `EuclideanSpace ℝ (Fin n)`; a block is a subtype, and +`unitVolumeRadius_euclideanSpace` identifies its radius with `ballRadius` of its cardinality. This +is the one place where the index type is changed, and it is done once rather than at every step of +the induction. + +## References + +E. Bombieri and W. Gubler, *Heights in Diophantine Geometry*, Cambridge University Press (2006), +Lemma C.3.7. + +This is Layer 4.5 (infrastructure) of the `ArithmeticHeights` roadmap. +-/ + +public section + +open MeasureTheory Measure Set ENNReal +open scoped Real + +/-- The Gauss density on `ι → ℝ` for the euclidean norm. -/ +@[expose] noncomputable def gaussPi {ι : Type*} [Fintype ι] (x : ι → ℝ) : ℝ≥0∞ := + ENNReal.ofReal (Real.exp (-π * ∑ i, (x i) ^ 2)) + +@[fun_prop] +theorem measurable_gaussPi {ι : Type*} [Fintype ι] : Measurable (gaussPi : (ι → ℝ) → ℝ≥0∞) := by + unfold gaussPi; fun_prop + +theorem gaussPi_le_one {ι : Type*} [Fintype ι] (x : ι → ℝ) : gaussPi x ≤ 1 := by + rw [gaussPi, ← ENNReal.ofReal_one] + refine ENNReal.ofReal_le_ofReal ?_ + rw [Real.exp_le_one_iff] + have h : (0 : ℝ) ≤ ∑ i, (x i) ^ 2 := Finset.sum_nonneg fun i _ => sq_nonneg _ + nlinarith [Real.pi_pos] + +theorem gaussPi_neg {ι : Type*} [Fintype ι] (x : ι → ℝ) : gaussPi (-x) = gaussPi x := by + have h : ∑ i, ((-x) i) ^ 2 = ∑ i, (x i) ^ 2 := Finset.sum_congr rfl fun i _ => by simp + rw [gaussPi, gaussPi, h] + +theorem gaussPi_eq_gaussDensity {ι : Type*} [Fintype ι] (x : ι → ℝ) : + gaussPi x = gaussDensity (WithLp.toLp 2 x : EuclideanSpace ℝ ι) := by + rw [gaussPi, gaussDensity_def, EuclideanSpace.norm_eq, Real.sq_sqrt (by positivity)] + simp + +/-- The Gauss density is log-concave: the quadratic form `∑ x i ^ 2` is convex. -/ +theorem logConcave_gaussPi {ι : Type*} [Fintype ι] : LogConcave (gaussPi : (ι → ℝ) → ℝ≥0∞) := by + intro a b ha hb hab x y + have hkey : ∑ i, ((a • x + b • y) i) ^ 2 ≤ a * ∑ i, (x i) ^ 2 + b * ∑ i, (y i) ^ 2 := by + rw [Finset.mul_sum, Finset.mul_sum, ← Finset.sum_add_distrib] + refine Finset.sum_le_sum fun i _ => ?_ + simp only [Pi.add_apply, Pi.smul_apply, smul_eq_mul] + nlinarith [sq_nonneg (x i - y i), mul_nonneg ha.le hb.le] + rw [gaussPi, gaussPi, gaussPi, ENNReal.ofReal_rpow_of_pos (Real.exp_pos _), + ENNReal.ofReal_rpow_of_pos (Real.exp_pos _), ← ENNReal.ofReal_mul (by positivity)] + refine ENNReal.ofReal_le_ofReal ?_ + rw [← Real.exp_mul, ← Real.exp_mul, ← Real.exp_add, Real.exp_le_exp] + nlinarith [Real.pi_pos] + +/-- The radius of the ball of volume one in `n` dimensions: Bombieri–Gubler's `ρ(n)`. -/ +@[expose] noncomputable def ballRadius (n : ℕ) : ℝ := unitVolumeRadius (EuclideanSpace ℝ (Fin n)) + +theorem ballRadius_pos (n : ℕ) : 0 < ballRadius n := unitVolumeRadius_pos + +theorem volume_ball_euclideanSpace_congr {α β : Type*} [Fintype α] [Fintype β] (e : α ≃ β) : + volume (Metric.ball (0 : EuclideanSpace ℝ α) 1) + = volume (Metric.ball (0 : EuclideanSpace ℝ β) 1) := by + let f : EuclideanSpace ℝ α ≃ₗᵢ[ℝ] EuclideanSpace ℝ β := + LinearIsometryEquiv.piLpCongrLeft 2 ℝ ℝ e + have hpre : f ⁻¹' (Metric.ball (0 : EuclideanSpace ℝ β) 1) + = Metric.ball (0 : EuclideanSpace ℝ α) 1 := by + ext x + simp [Metric.mem_ball, dist_zero_right] + rw [← hpre, f.measurePreserving.measure_preimage measurableSet_ball.nullMeasurableSet] + +theorem unitVolumeRadius_euclideanSpace (α : Type*) [Fintype α] : + unitVolumeRadius (EuclideanSpace ℝ α) = ballRadius (Fintype.card α) := by + rw [ballRadius, unitVolumeRadius, unitVolumeRadius, + volume_ball_euclideanSpace_congr (Fintype.equivFin α)] + simp + +/-- **The slice bound for a single block**: the ball of volume one in `α → ℝ`. -/ +theorem hasSliceBound_ballPi (α : Type*) [Fintype α] : + HasSliceBound (volume : Measure (α → ℝ)) gaussPi + {y : α → ℝ | ∑ j, (y j) ^ 2 ≤ ballRadius (Fintype.card α) ^ 2} := by + have h := HasSliceBound.of_measurePreserving (WithLp.linearEquiv 2 ℝ (α → ℝ)).symm + (PiLp.volume_preserving_toLp α) (PiLp.volume_preserving_ofLp α).measurable + measurable_gaussDensity measurableSet_closedBall + (hasSliceBound_unitVolumeBall (E := EuclideanSpace ℝ α)) + have hdens : (fun x : α → ℝ => + gaussDensity ((WithLp.linearEquiv 2 ℝ (α → ℝ)).symm x)) = gaussPi := + funext fun x => (gaussPi_eq_gaussDensity x).symm + have hset : (WithLp.linearEquiv 2 ℝ (α → ℝ)).symm ⁻¹' + Metric.closedBall (0 : EuclideanSpace ℝ α) (unitVolumeRadius (EuclideanSpace ℝ α)) + = {y : α → ℝ | ∑ j, (y j) ^ 2 ≤ ballRadius (Fintype.card α) ^ 2} := by + ext y + rw [Set.mem_preimage, Metric.mem_closedBall, dist_zero_right, EuclideanSpace.norm_eq, + unitVolumeRadius_euclideanSpace, + Real.sqrt_le_left (unitVolumeRadius_euclideanSpace α ▸ + (unitVolumeRadius_pos (E := EuclideanSpace ℝ α)).le)] + simp + rw [hdens, hset] at h + exact h + +theorem hasSliceBound_univ_of_le_one {E : Type*} [MeasurableSpace E] [AddCommGroup E] [Module ℝ E] + {μ : Measure E} {g : E → ℝ≥0∞} (hg : ∀ x, g x ≤ 1) {Q : Set E} (hQ : ∀ x, x ∈ Q) : + HasSliceBound μ g Q := by + intro A _ _ _ + calc ∫⁻ x in A, g x ∂μ ≤ ∫⁻ _ in A, (1 : ℝ≥0∞) ∂μ := lintegral_mono fun x => hg x + _ = μ A := setLIntegral_one A + _ = μ (A ∩ Q) := by rw [Set.inter_eq_self_of_subset_left fun x _ => hQ x] + +theorem convex_setOf_sum_sq_le {ι S : Type*} [Fintype S] (f : S → ι) (c : ℝ) : + Convex ℝ {x : ι → ℝ | ∑ j : S, (x (f j)) ^ 2 ≤ c} := by + intro x hx y hy a b ha hb hab + have hpt : ∑ j : S, ((a • x + b • y) (f j)) ^ 2 + ≤ a * ∑ j : S, (x (f j)) ^ 2 + b * ∑ j : S, (y (f j)) ^ 2 := by + rw [Finset.mul_sum, Finset.mul_sum, ← Finset.sum_add_distrib] + refine Finset.sum_le_sum fun j _ => ?_ + simp only [Pi.add_apply, Pi.smul_apply, smul_eq_mul] + nlinarith [sq_nonneg (x (f j) - y (f j)), mul_nonneg ha hb] + calc ∑ j : S, ((a • x + b • y) (f j)) ^ 2 + ≤ a * ∑ j : S, (x (f j)) ^ 2 + b * ∑ j : S, (y (f j)) ^ 2 := hpt + _ ≤ a * c + b * c := by gcongr <;> [exact hx; exact hy] + _ = c := by rw [← add_mul, hab, one_mul] + +theorem measurableSet_setOf_sum_sq_le {ι S : Type*} [Fintype S] (f : S → ι) (c : ℝ) : + MeasurableSet {x : ι → ℝ | ∑ j : S, (x (f j)) ^ 2 ≤ c} := + measurableSet_le (by fun_prop) measurable_const + +theorem neg_mem_setOf_sum_sq_le {ι S : Type*} [Fintype S] (f : S → ι) (c : ℝ) + {x : ι → ℝ} (hx : x ∈ {x : ι → ℝ | ∑ j : S, (x (f j)) ^ 2 ≤ c}) : + -x ∈ {x : ι → ℝ | ∑ j : S, (x (f j)) ^ 2 ≤ c} := by + have h : ∑ j : S, ((-x) (f j)) ^ 2 = ∑ j : S, (x (f j)) ^ 2 := + Finset.sum_congr rfl fun j _ => by simp + change ∑ j : S, ((-x) (f j)) ^ 2 ≤ c + rw [h]; exact hx + +/-- The product over the blocks of `blk` of the euclidean ball of volume one in that block's +coordinates: Bombieri–Gubler's `Q_N = B_{ρ(n₁)} × ⋯ × B_{ρ(n_r)}`. -/ +@[expose] def prodBallPi {ι κ : Type*} [Fintype ι] [DecidableEq κ] (blk : ι → κ) : + Set (ι → ℝ) := + {x | ∀ k : κ, ∑ j : {j : ι // blk j = k}, (x j) ^ 2 + ≤ ballRadius (Fintype.card {j : ι // blk j = k}) ^ 2} + +theorem prodBallPi_eq_iInter {ι κ : Type*} [Fintype ι] [DecidableEq κ] (blk : ι → κ) : + prodBallPi blk = ⋂ k : κ, {x : ι → ℝ | ∑ j : {j : ι // blk j = k}, (x (Subtype.val j)) ^ 2 + ≤ ballRadius (Fintype.card {j : ι // blk j = k}) ^ 2} := by + ext x; simp [prodBallPi] + +theorem measurableSet_prodBallPi {ι κ : Type*} [Fintype ι] [DecidableEq κ] [Countable κ] + (blk : ι → κ) : MeasurableSet (prodBallPi blk) := by + rw [prodBallPi_eq_iInter] + exact MeasurableSet.iInter fun k => measurableSet_setOf_sum_sq_le _ _ + +theorem convex_prodBallPi {ι κ : Type*} [Fintype ι] [DecidableEq κ] (blk : ι → κ) : + Convex ℝ (prodBallPi blk) := by + rw [prodBallPi_eq_iInter] + exact convex_iInter fun k => convex_setOf_sum_sq_le _ _ + +theorem neg_mem_prodBallPi {ι κ : Type*} [Fintype ι] [DecidableEq κ] (blk : ι → κ) + {x : ι → ℝ} (hx : x ∈ prodBallPi blk) : + -x ∈ prodBallPi blk := by + rw [prodBallPi_eq_iInter] at hx ⊢ + simp only [Set.mem_iInter] at hx ⊢ + exact fun k => neg_mem_setOf_sum_sq_le _ _ (hx k) + +/-- Prékopa–Leindler on `EuclideanSpace ℝ α` for an arbitrary finite index type. -/ +theorem hasPrekopaLeindler_euclideanSpace' (α : Type*) [Fintype α] : + HasPrekopaLeindler (volume : Measure (EuclideanSpace ℝ α)) := by + let f : EuclideanSpace ℝ α ≃ₗᵢ[ℝ] EuclideanSpace ℝ (Fin (Fintype.card α)) := + LinearIsometryEquiv.piLpCongrLeft 2 ℝ ℝ (Fintype.equivFin α) + exact HasPrekopaLeindler.of_measurePreserving f.toLinearEquiv f.measurePreserving + f.symm.continuous.measurable (hasPrekopaLeindler_euclideanSpace _) + +/-- Prékopa–Leindler on `α → ℝ` for an arbitrary finite index type. -/ +theorem hasPrekopaLeindler_pi' (α : Type*) [Fintype α] : + HasPrekopaLeindler (volume : Measure (α → ℝ)) := + HasPrekopaLeindler.of_measurePreserving (WithLp.linearEquiv 2 ℝ (α → ℝ)).symm + (PiLp.volume_preserving_toLp α) (PiLp.volume_preserving_ofLp α).measurable + (hasPrekopaLeindler_euclideanSpace' α) + +/-- **Prékopa–Leindler on a finite-dimensional real inner product space.** -/ +theorem hasPrekopaLeindler_innerProductSpace {E : Type*} [NormedAddCommGroup E] + [InnerProductSpace ℝ E] [FiniteDimensional ℝ E] [MeasurableSpace E] [BorelSpace E] : + HasPrekopaLeindler (volume : Measure E) := by + let f : E ≃ₗᵢ[ℝ] EuclideanSpace ℝ (Fin (Module.finrank ℝ E)) := (stdOrthonormalBasis ℝ E).repr + exact HasPrekopaLeindler.of_measurePreserving f.toLinearEquiv f.measurePreserving + f.symm.continuous.measurable (hasPrekopaLeindler_euclideanSpace _) + +private theorem hasSliceBound_prodBallPi_aux.{u} {κ : Type*} [DecidableEq κ] [Countable κ] + (n : ℕ) : + ∀ (α : Type u) [Fintype α] (blk : α → κ), + Fintype.card α ≤ n → + HasSliceBound (volume : Measure (α → ℝ)) gaussPi (prodBallPi blk) := by + have hempty : ∀ (α : Type u) [Fintype α] (blk : α → κ), IsEmpty α → + HasSliceBound (volume : Measure (α → ℝ)) gaussPi (prodBallPi blk) := by + intro α _ blk hα + refine hasSliceBound_univ_of_le_one gaussPi_le_one fun x k => ?_ + have h : (Finset.univ : Finset {j : α // blk j = k}) = ∅ := + Finset.eq_empty_iff_forall_notMem.mpr fun j _ => hα.elim j.1 + change ∑ j : {j : α // blk j = k}, (x j) ^ 2 ≤ _ + rw [h, Finset.sum_empty] + exact sq_nonneg _ + induction n with + | zero => + intro α _ blk hcard + exact hempty α blk (Fintype.card_eq_zero_iff.mp (Nat.le_zero.mp hcard)) + | succ n ih => + intro α _ blk hcard + rcases isEmpty_or_nonempty α with hα | hα + · exact hempty α blk hα + obtain ⟨j₀⟩ := hα + have hcards : 0 < Fintype.card {j : α // blk j = blk j₀} := + Fintype.card_pos_iff.mpr ⟨⟨j₀, rfl⟩⟩ + have hcardt : Fintype.card {j : α // ¬ (blk j = blk j₀)} ≤ n := by + have h := Fintype.card_subtype_compl (fun j : α => blk j = blk j₀) + omega + let L : (α → ℝ) ≃ₗ[ℝ] + (({j : α // blk j = blk j₀} → ℝ) × ({j : α // ¬ (blk j = blk j₀)} → ℝ)) := + { Equiv.piEquivPiSubtypeProd (fun j : α => blk j = blk j₀) (fun _ => ℝ) with + map_add' := fun _ _ => rfl + map_smul' := fun _ _ => rfl } + let blkt : {j : α // ¬ (blk j = blk j₀)} → κ := fun j => blk j.val + have hMP : MeasurePreserving L (volume : Measure (α → ℝ)) + (volume : Measure ((({j : α // blk j = blk j₀}) → ℝ) + × (({j : α // ¬ (blk j = blk j₀)}) → ℝ))) := by + rw [Measure.volume_eq_prod] + exact measurePreserving_piEquivPiSubtypeProd (fun _ : α => (volume : Measure ℝ)) + (fun j : α => blk j = blk j₀) + have hsymm : Measurable L.symm := + (MeasurableEquiv.piEquivPiSubtypeProd (fun _ : α => ℝ) + (fun j : α => blk j = blk j₀)).symm.measurable + have hprod : HasSliceBound (volume : Measure ((({j : α // blk j = blk j₀}) → ℝ) + × (({j : α // ¬ (blk j = blk j₀)}) → ℝ))) + (fun q => gaussPi q.1 * gaussPi q.2) + ({y : {j : α // blk j = blk j₀} → ℝ | + ∑ j, (y j) ^ 2 ≤ ballRadius (Fintype.card {j : α // blk j = blk j₀}) ^ 2} + ×ˢ prodBallPi blkt) := by + rw [Measure.volume_eq_prod] + exact HasSliceBound.prod measurable_gaussPi gaussPi_neg logConcave_gaussPi + measurable_gaussPi (measurableSet_prodBallPi blkt) (convex_prodBallPi blkt) + (fun _ hz => neg_mem_prodBallPi blkt hz) + (hasPrekopaLeindler_pi' _) (hasPrekopaLeindler_pi' _) + (hasSliceBound_ballPi _) (ih _ blkt hcardt) + have hdens : (fun x : α → ℝ => gaussPi (L x).1 * gaussPi (L x).2) = gaussPi := by + funext x + have h1 : ∑ j : {j : α // blk j = blk j₀}, ((L x).1 j) ^ 2 + = ∑ j : {j : α // blk j = blk j₀}, (x j.val) ^ 2 := rfl + have h2 : ∑ j : {j : α // ¬ (blk j = blk j₀)}, ((L x).2 j) ^ 2 + = ∑ j : {j : α // ¬ (blk j = blk j₀)}, (x j.val) ^ 2 := rfl + have hsum : ∑ i, (x i) ^ 2 + = (∑ j : {j : α // blk j = blk j₀}, (x j.val) ^ 2) + + ∑ j : {j : α // ¬ (blk j = blk j₀)}, (x j.val) ^ 2 := + (Fintype.sum_subtype_add_sum_subtype _ _).symm + rw [gaussPi, gaussPi, gaussPi, ← ENNReal.ofReal_mul (Real.exp_pos _).le, ← Real.exp_add, + h1, h2, hsum] + congr 2 + ring + have hiff : ∀ (x : α → ℝ) (k : κ), k ≠ blk j₀ → + ((∑ j : {j : {j : α // ¬ (blk j = blk j₀)} // blkt j = k}, (x j.1.1) ^ 2 + ≤ ballRadius (Fintype.card {j : {j : α // ¬ (blk j = blk j₀)} // blkt j = k}) ^ 2) + ↔ (∑ j : {j : α // blk j = k}, (x j.1) ^ 2 + ≤ ballRadius (Fintype.card {j : α // blk j = k}) ^ 2)) := by + intro x k hk + let e : {j : {j : α // ¬ (blk j = blk j₀)} // blkt j = k} ≃ {j : α // blk j = k} := + { toFun := fun j => ⟨j.1.1, j.2⟩ + invFun := fun j => ⟨⟨j.1, fun hcon => hk (by rw [← j.2, hcon])⟩, j.2⟩ + left_inv := fun _ => rfl + right_inv := fun _ => rfl } + rw [Fintype.sum_equiv e (fun j => (x j.1.1) ^ 2) (fun j => (x j.1) ^ 2) + (fun _ => rfl), Fintype.card_congr e] + have hset : (L : (α → ℝ) → _) ⁻¹' ({y : {j : α // blk j = blk j₀} → ℝ | + ∑ j, (y j) ^ 2 ≤ ballRadius (Fintype.card {j : α // blk j = blk j₀}) ^ 2} + ×ˢ prodBallPi blkt) = prodBallPi blk := by + ext x + constructor + · rintro ⟨h1, h2⟩ k + by_cases hk : k = blk j₀ + · subst hk; exact h1 + · exact (hiff x k hk).mp (h2 k) + · intro h + exact ⟨h (blk j₀), fun k => by + by_cases hk : k = blk j₀ + · subst hk + have hz : (Finset.univ : + Finset {j : {j : α // ¬ (blk j = blk j₀)} // blkt j = blk j₀}) = ∅ := + Finset.eq_empty_iff_forall_notMem.mpr fun j _ => j.1.2 j.2 + change ∑ j : {j : {j : α // ¬ (blk j = blk j₀)} // blkt j = blk j₀}, + ((L x).2 j) ^ 2 ≤ _ + rw [hz, Finset.sum_empty] + exact sq_nonneg _ + · exact (hiff x k hk).mpr (h k)⟩ + have hmeasg : Measurable (fun q : (({j : α // blk j = blk j₀}) → ℝ) + × (({j : α // ¬ (blk j = blk j₀)}) → ℝ) => gaussPi q.1 * gaussPi q.2) := + (measurable_gaussPi.comp measurable_fst).mul (measurable_gaussPi.comp measurable_snd) + have hfin := HasSliceBound.of_measurePreserving L hMP hsymm hmeasg + ((measurableSet_setOf_sum_sq_le (fun j : {j : α // blk j = blk j₀} => j) _).prod + (measurableSet_prodBallPi blkt)) hprod + rw [hdens, hset] at hfin + exact hfin + +/-- **Bombieri–Gubler, Lemma C.3.7**: the Gauss measure of a measurable convex symmetric set is at +most its volume inside a product of euclidean balls of volume one. -/ +theorem hasSliceBound_prodBallPi {ι κ : Type*} [Fintype ι] [DecidableEq κ] [Countable κ] + (blk : ι → κ) : + HasSliceBound (volume : Measure (ι → ℝ)) gaussPi (prodBallPi blk) := + hasSliceBound_prodBallPi_aux (Fintype.card ι) ι blk le_rfl + +/-- The product of balls of volume one, in `EuclideanSpace ℝ ι`. -/ +@[expose] def prodBall {ι κ : Type*} [Fintype ι] [DecidableEq κ] (blk : ι → κ) : + Set (EuclideanSpace ℝ ι) := + {x | ∀ k : κ, ∑ j : {j : ι // blk j = k}, (x j) ^ 2 + ≤ ballRadius (Fintype.card {j : ι // blk j = k}) ^ 2} + +theorem prodBall_eq_preimage {ι κ : Type*} [Fintype ι] [DecidableEq κ] (blk : ι → κ) : + prodBall blk = (WithLp.linearEquiv 2 ℝ (ι → ℝ)) ⁻¹' prodBallPi blk := rfl + +theorem measurableSet_prodBall {ι κ : Type*} [Fintype ι] [DecidableEq κ] [Countable κ] + (blk : ι → κ) : MeasurableSet (prodBall blk) := by + rw [prodBall_eq_preimage] + exact (PiLp.volume_preserving_ofLp ι).measurable (measurableSet_prodBallPi blk) + +theorem convex_prodBall {ι κ : Type*} [Fintype ι] [DecidableEq κ] (blk : ι → κ) : + Convex ℝ (prodBall blk) := by + rw [prodBall_eq_preimage] + exact (convex_prodBallPi blk).linear_preimage + (WithLp.linearEquiv 2 ℝ (ι → ℝ) : EuclideanSpace ℝ ι →ₗ[ℝ] (ι → ℝ)) + +theorem neg_mem_prodBall {ι κ : Type*} [Fintype ι] [DecidableEq κ] (blk : ι → κ) + {x : EuclideanSpace ℝ ι} (hx : x ∈ prodBall blk) : -x ∈ prodBall blk := by + rw [prodBall_eq_preimage] at hx ⊢ + have hneg : (WithLp.linearEquiv 2 ℝ (ι → ℝ)) (-x) = -((WithLp.linearEquiv 2 ℝ (ι → ℝ)) x) := + map_neg _ _ + simpa [Set.mem_preimage, hneg] using neg_mem_prodBallPi blk hx + +/-- **Bombieri–Gubler, Lemma C.3.7 on `EuclideanSpace ℝ ι`.** -/ +theorem hasSliceBound_prodBall {ι κ : Type*} [Fintype ι] [DecidableEq κ] [Countable κ] + (blk : ι → κ) : + HasSliceBound (volume : Measure (EuclideanSpace ℝ ι)) gaussDensity (prodBall blk) := by + have h := HasSliceBound.of_measurePreserving (WithLp.linearEquiv 2 ℝ (ι → ℝ)) + (PiLp.volume_preserving_ofLp ι) (PiLp.volume_preserving_toLp ι).measurable + measurable_gaussPi (measurableSet_prodBallPi blk) (hasSliceBound_prodBallPi blk) + have hdens : (fun x : EuclideanSpace ℝ ι => + gaussPi ((WithLp.linearEquiv 2 ℝ (ι → ℝ)) x)) = gaussDensity := by + funext x + rw [gaussPi_eq_gaussDensity] + rfl + have hset : (WithLp.linearEquiv 2 ℝ (ι → ℝ)) ⁻¹' prodBallPi blk = prodBall blk := rfl + rw [hdens, hset] at h + exact h diff --git a/LeanPool/CCDN/ArithmeticHeights/QuotientFubini.lean b/LeanPool/CCDN/ArithmeticHeights/QuotientFubini.lean new file mode 100644 index 0000000000..ffdc5f122a --- /dev/null +++ b/LeanPool/CCDN/ArithmeticHeights/QuotientFubini.lean @@ -0,0 +1,652 @@ +/- +Copyright (c) 2026 Ralf Stephan. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Ralf Stephan +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import Mathlib.Algebra.Module.ZLattice.Basic +public import Mathlib.MeasureTheory.Measure.Haar.Unique +public import Mathlib.MeasureTheory.Measure.Lebesgue.EqHaar +public import Mathlib.MeasureTheory.Measure.Prod + +/-! +# A Fubini decomposition of the quotient measure over a subspace + +For a lattice `Λ` in a real vector space `V` with fundamental domain `F`, and a second space `W`, +the slab `F ×ˢ univ` is a fundamental domain in `V × W` for the sublattice `Λ × 0`, so that for a +`Λ × 0`-invariant set `S` the number `(μV.prod μW) ((F ×ˢ univ) ∩ S)` *is* the measure of the image +of `S` in the quotient `(V × W) ⧸ (Λ × 0)`. The main result decomposes that number as an integral +over `W` of the quotient measures in `V ⧸ Λ` of the slices of `S`. + +This is the identity (10) in Cassels' proof of his Chapter VIII, Theorem IV, which is the kernel of +Weyl's proof of the upper bound in Minkowski's second theorem. Two further results carry the rest +of that proof: the descent `ZLattice.measure_inter_add_eq_of_separated`, which says that for a set +whose points are congruent modulo the big lattice `L` only when they are congruent modulo the small +one `Λ`, the quotient measures over `L` and over `Λ` agree; and the scaling estimate +`ZLattice.measure_inter_add_prod_smul_le`, which says that dilating a convex set by `s ≥ 1` +multiplies its quotient measure over `Λ × 0` by at least `s ^ dim W`. + +## Main results + +* `ZLattice.measure_inter_add_prod_eq_lintegral`: the Fubini decomposition, Cassels' (10). +* `ZLattice.isAddFundamentalDomain_prod_univ`: the slab `F ×ˢ univ` is a fundamental domain for + `Λ × 0`, which is what makes the left-hand side of the decomposition a quotient measure. +* `ZLattice.measure_inter_add_prod_smul_le`: the scaling estimate, Cassels' (12) and (14). +* `ZLattice.exists_isAddFundamentalDomain_measure_smul_le`: the scaling estimate for a lattice of + arbitrary rank in a single space, with the slab produced rather than assumed. +* `ZLattice.measure_inter_add_eq_of_separated`: the descent from `L` to `Λ`. +* `ZLattice.pow_mul_measure_inter_add_le`: the two combined — Cassels' Theorem IV in one step, the + form Minkowski's second theorem chains over the successive minima. +* `ZLattice.countable_of_discreteTopology`: a discrete `ℤ`-submodule of a finite-dimensional real + normed space is countable. Mathlib has this only for a lattice of full rank. + +## Implementation notes + +⚠ **The scaling estimate is not a statement about one set being a packing.** Its proof translates +each slice of the body by a *different* vector — `(s - 1) • y₀` for some `y₀` in that slice — and +what makes that legitimate is exactly that the quotient measure of a slice is unchanged by +translation. There is no single map of `V × W` doing this, which is why the Fubini decomposition, +and not a change of variables, is the tool. + +⚠ **The descent needs the covolume to be finite, and that is the only place any finiteness enters.** +The proof exhibits `(FΛ ∩ (A + Λ)) ∪ (FL \ (A + L))` as a fundamental domain for `L`, so it has +the measure of `FL`; cancelling the second piece off both sides is what needs `μ FL ≠ ⊤`. No +compactness, closedness or boundedness of the body is used anywhere in this file. + +⚠ **`IsZLattice` is not a hypothesis of the product results, and `Countable` is all they want.** +A fundamental domain is passed in as the hypothesis `∀ x, ∃! v : Λ, ↑v + x ∈ F` rather than +constructed, so the product statements are about an arbitrary countable `ℤ`-submodule. Only +`ZLattice.exists_isAddFundamentalDomain_measure_smul_le`, which builds the slab, needs the +submodule to be discrete — and there it is a `ZLattice` in its own span, not in the ambient space. + +## References + +J. W. S. Cassels, *An Introduction to the Geometry of Numbers*, Springer (1959), Chapter VIII §4.2, +Theorem IV, and the displays (10), (12) and (14) of its proof. Cassels normalizes the lattice to +`ℤ ^ n` by a linear transformation first; the `V × W` form here is that normalization made +coordinate-free, and `ZLattice.exists_isAddFundamentalDomain_measure_smul_le` performs it. + +⚠ What is *not* here is the chaining of Theorem IV over the successive minima, which is Minkowski's +second theorem itself. Beside the chain it wants three pieces of gauge bookkeeping, all specific to +the body rather than to the quotient measure: that `L ∩ span (v 0, …, v (J - 1))` has rank `J`, +that Cassels' Lemma 2 (`ZLattice.exists_basis_mem_span_int_of_gauge_lt`) supplies the separation +hypothesis for `t • B` when `2 t` is below the `J`-th minimum, and that a dilate of the body has +the expected measure. + +This is infrastructure for Layer 4.2 of the `ArithmeticHeights` roadmap. +-/ + +public section + +open MeasureTheory Measure Module Set + +open scoped ENNReal Pointwise + +namespace ZLattice + +section Lattice + +variable {G : Type*} [NormedAddCommGroup G] [MeasurableSpace G] [BorelSpace G] + +/-- A set enlarged by a countable lattice is a countable union of its translates, hence +measurable. -/ +private theorem measurableSet_add_lattice (M : Submodule ℤ G) [Countable M] {A : Set G} + (hA : MeasurableSet A) : MeasurableSet (A + (M : Set G)) := by + have h : A + (M : Set G) = ⋃ l : M, (fun x ↦ x - (l : G)) ⁻¹' A := by + ext x + simp only [Set.mem_add, Set.mem_iUnion, Set.mem_preimage] + exact ⟨fun ⟨a, ha, l, hl, he⟩ ↦ ⟨⟨l, hl⟩, by simp only [← he]; simpa using ha⟩, + fun ⟨l, hx⟩ ↦ ⟨x - (l : G), hx, l, l.2, by abel⟩⟩ + rw [h] + exact MeasurableSet.iUnion fun l ↦ hA.preimage (measurable_id.sub_const _) + +omit [MeasurableSpace G] [BorelSpace G] in +/-- A set enlarged by a lattice is invariant under that lattice. -/ +private theorem preimage_vadd_add_lattice (M : Submodule ℤ G) {Y : Set G} (g : M) : + (fun x ↦ g +ᵥ x) ⁻¹' (Y + (M : Set G)) = Y + (M : Set G) := by + ext x + constructor + · rintro ⟨a, ha, l, hl, he⟩ + have he' : a + l = (g : G) + x := he + refine ⟨a, ha, l - (g : G), Submodule.sub_mem _ hl g.2, ?_⟩ + change a + (l - (g : G)) = x + linear_combination (norm := abel) he' + · rintro ⟨a, ha, l, hl, rfl⟩ + refine ⟨a, ha, (g : G) + l, Submodule.add_mem _ g.2 hl, ?_⟩ + change a + ((g : G) + l) = (g : G) + (a + l) + abel + +omit [MeasurableSpace G] [BorelSpace G] in +/-- Membership in a set enlarged by a lattice is unchanged by a lattice translation. -/ +private theorem mem_add_lattice_iff (M : Submodule ℤ G) {A : Set G} {v : G} (hv : v ∈ M) (x : G) : + v + x ∈ A + (M : Set G) ↔ x ∈ A + (M : Set G) := by + constructor + · rintro ⟨a, ha, l, hl, h⟩ + refine ⟨a, ha, l - v, Submodule.sub_mem _ hl hv, ?_⟩ + simp only at h ⊢ + linear_combination (norm := abel) h + · rintro ⟨a, ha, l, hl, rfl⟩ + exact ⟨a, ha, v + l, Submodule.add_mem _ hv hl, by simp; abel⟩ + +omit [MeasurableSpace G] [BorelSpace G] in +/-- Transporting the defining property of a fundamental domain along an equality of lattices. -/ +private theorem existsUnique_of_eq {M N : Submodule ℤ G} (h : M = N) {F : Set G} {x : G} + (hM : ∃! v : M, (v : G) + x ∈ F) : ∃! v : N, (v : G) + x ∈ F := by + obtain ⟨u, hu, huq⟩ := hM + refine ⟨⟨(u : G), h ▸ u.2⟩, hu, fun w hw ↦ ?_⟩ + have hwm : (w : G) ∈ M := h ▸ w.2 + have hwu : (⟨(w : G), hwm⟩ : M) = u := huq _ hw + exact Subtype.ext (congrArg (fun z : M ↦ (z : G)) hwu) + +end Lattice + +section Descent + +variable {E : Type*} [NormedAddCommGroup E] [MeasurableSpace E] [BorelSpace E] + +/-- **The descent from a lattice to a sublattice** (the equivalence of (6) with (7) and (8) in +Cassels' proof of his Chapter VIII, Theorem IV). If two points of `A` are congruent modulo `L` only +when they are congruent modulo the sublattice `Λ`, then the measure of the image of `A` in `E ⧸ L` +equals the measure of its image in `E ⧸ Λ`: the covering `E ⧸ Λ → E ⧸ L` is injective there. + +Both measures are read off against fundamental domains — `FΛ` for `Λ`, `FL` for `L`, each given by +its exact form `∀ x, ∃! v, ↑v + x ∈ ·` — and the proof exhibits `(FΛ ∩ (A + Λ)) ∪ (FL \ (A + L))` +as a third fundamental domain, for `L`. ⚠ This is where `μ FL ≠ ⊤` is used, and it is the only +finiteness hypothesis in the file. -/ +theorem measure_inter_add_eq_of_separated (μ : Measure E) [μ.IsAddLeftInvariant] + {Λ L : Submodule ℤ E} [Countable Λ] [Countable L] (hΛL : Λ ≤ L) {FΛ FL : Set E} + (hΛ : ∀ x : E, ∃! v : Λ, (v : E) + x ∈ FΛ) (hL : ∀ x : E, ∃! v : L, (v : E) + x ∈ FL) + (hmΛ : MeasurableSet FΛ) (hmL : MeasurableSet FL) (hfin : μ FL ≠ ⊤) {A : Set E} + (hA : MeasurableSet A) (hsep : ∀ x ∈ A, ∀ y ∈ A, x - y ∈ L → x - y ∈ Λ) : + μ (FΛ ∩ (A + (Λ : Set E))) = μ (FL ∩ (A + (L : Set E))) := by + have : VAddInvariantMeasure L E μ := inferInstanceAs (VAddInvariantMeasure L.toAddSubgroup E μ) + set SΛ : Set E := A + (Λ : Set E) with hSΛ + set SL : Set E := A + (L : Set E) with hSL + have hsub : SΛ ⊆ SL := Set.add_subset_add_left fun l hl ↦ hΛL hl + have hmSΛ : MeasurableSet SΛ := measurableSet_add_lattice Λ hA + have hmSL : MeasurableSet SL := measurableSet_add_lattice L hA + set D : Set E := (FΛ ∩ SΛ) ∪ (FL \ SL) with hD + have hfd : IsAddFundamentalDomain L D μ := by + refine IsAddFundamentalDomain.mk' ((hmΛ.inter hmSΛ).union (hmL.diff hmSL)).nullMeasurableSet + fun x ↦ ?_ + by_cases hx : x ∈ SL + · have hmemL : ∀ v : L, (v : E) + x ∈ SL := fun v ↦ (mem_add_lattice_iff L v.2 x).2 hx + have hiff : ∀ y : E, y ∈ SL → (y ∈ D ↔ y ∈ FΛ ∩ SΛ) := fun y hy ↦ by + simp only [hD, Set.mem_union, Set.mem_sdiff, hy, not_true_eq_false, and_false, or_false] + have huniq2 : ∀ v v' : L, (v : E) + x ∈ FΛ ∩ SΛ → (v' : E) + x ∈ FΛ ∩ SΛ → v = v' := by + intro v v' h1 h2 + obtain ⟨a₁, ha₁, c₁, hc₁, he₁⟩ := h1.2 + obtain ⟨a₂, ha₂, c₂, hc₂, he₂⟩ := h2.2 + have he₁' : a₁ + c₁ = (v : E) + x := he₁ + have he₂' : a₂ + c₂ = (v' : E) + x := he₂ + have hd1 : a₁ - a₂ = ((v : E) - (v' : E)) - (c₁ - c₂) := by + linear_combination (norm := abel) he₁' - he₂' + have hdiff : a₁ - a₂ ∈ L := by + rw [hd1] + exact Submodule.sub_mem _ (Submodule.sub_mem _ v.2 v'.2) + (hΛL (Submodule.sub_mem _ hc₁ hc₂)) + have hs := hsep a₁ ha₁ a₂ ha₂ hdiff + have hw : (v : E) - (v' : E) ∈ Λ := by + have hd2 : (v : E) - (v' : E) = (a₁ - a₂) + (c₁ - c₂) := by + linear_combination (norm := abel) he₂' - he₁' + rw [hd2] + exact Submodule.add_mem _ hs (Submodule.sub_mem _ hc₁ hc₂) + obtain ⟨u', -, huq⟩ := hΛ ((v' : E) + x) + have e1 : (⟨(v : E) - (v' : E), hw⟩ : Λ) = u' := by + refine huq _ ?_ + change ((v : E) - (v' : E)) + ((v' : E) + x) ∈ FΛ + rw [show ((v : E) - (v' : E)) + ((v' : E) + x) = (v : E) + x from by abel] + exact h1.1 + have e2 : (0 : Λ) = u' := by + refine huq _ ?_ + change ((0 : Λ) : E) + ((v' : E) + x) ∈ FΛ + simpa using h2.1 + have h4 : (v : E) - (v' : E) = 0 := by + have h5 := congrArg (fun z : Λ ↦ (z : E)) (e1.trans e2.symm) + simpa using h5 + exact Subtype.ext (sub_eq_zero.1 h4) + obtain ⟨a, ha, l, hl, hx'⟩ := hx + have hx'' : a + l = x := hx' + obtain ⟨u, hu, -⟩ := hΛ a + have hex : ∃ v : L, (v : E) + x ∈ FΛ ∩ SΛ := by + refine ⟨⟨(u : E) - l, Submodule.sub_mem _ (hΛL u.2) hl⟩, ?_⟩ + change ((u : E) - l) + x ∈ FΛ ∩ SΛ + rw [show ((u : E) - l) + x = (u : E) + a from by rw [← hx'']; abel] + exact ⟨hu, ⟨a, ha, (u : E), u.2, add_comm _ _⟩⟩ + obtain ⟨v₀, hv₀⟩ := hex + exact ⟨v₀, (hiff _ (hmemL _)).2 hv₀, fun v hv ↦ huniq2 v v₀ ((hiff _ (hmemL v)).1 hv) hv₀⟩ + · have hmemL : ∀ v : L, (v : E) + x ∉ SL := fun v h ↦ hx ((mem_add_lattice_iff L v.2 x).1 h) + have hiff : ∀ v : L, ((v : E) + x ∈ D ↔ (v : E) + x ∈ FL) := by + intro v + have h1 : (v : E) + x ∉ SΛ := fun h ↦ hmemL v (hsub h) + rw [hD] + simp only [Set.mem_union, Set.mem_sdiff] + constructor + · rintro (⟨-, h⟩ | ⟨h, -⟩) + · exact absurd h h1 + · exact h + · exact fun h ↦ Or.inr ⟨h, hmemL v⟩ + obtain ⟨u, hu, huniq⟩ := hL x + exact ⟨u, (hiff u).2 hu, fun v hv ↦ huniq v ((hiff v).1 hv)⟩ + have hdisj : Disjoint (FΛ ∩ SΛ) (FL \ SL) := + Set.disjoint_left.2 fun y hy hy' ↦ hy'.2 (hsub hy.2) + have hunion : μ D = μ (FΛ ∩ SΛ) + μ (FL \ SL) := measure_union hdisj (hmL.diff hmSL) + have hFL : μ FL = μ (FL ∩ SL) + μ (FL \ SL) := (measure_inter_add_sdiff FL hmSL).symm + have hDeq : μ D = μ FL := + hfd.measure_eq (IsAddFundamentalDomain.mk' hmL.nullMeasurableSet hL) + have hlt : μ (FL \ SL) ≠ ⊤ := ne_top_of_le_ne_top hfin (measure_mono Set.sdiff_subset) + rw [hunion, hFL] at hDeq + exact (ENNReal.add_left_inj hlt).mp hDeq + +end Descent + +section Product + +variable {V W : Type*} [NormedAddCommGroup V] [NormedSpace ℝ V] [MeasurableSpace V] [BorelSpace V] + [FiniteDimensional ℝ V] [NormedAddCommGroup W] [NormedSpace ℝ W] [MeasurableSpace W] + [BorelSpace W] [FiniteDimensional ℝ W] + +/-- A lattice in `V` sits in `V × W` as the sublattice `Λ × 0`, and it is countable there. -/ +private instance countable_prod_bot (Λ : Submodule ℤ V) [Countable Λ] : + Countable (Λ.prod (⊥ : Submodule ℤ W)) := by + refine Function.Injective.countable + (f := fun p : Λ.prod (⊥ : Submodule ℤ W) ↦ (⟨p.1.1, p.2.1⟩ : Λ)) ?_ + intro p q hpq + have h1 : p.1.1 = q.1.1 := congrArg Subtype.val hpq + have h2 : p.1.2 = 0 := p.2.2 + have h3 : q.1.2 = 0 := q.2.2 + exact Subtype.ext (Prod.ext h1 (h2.trans h3.symm)) + +omit [NormedSpace ℝ V] [BorelSpace V] [FiniteDimensional ℝ V] [NormedSpace ℝ W] [BorelSpace W] + [FiniteDimensional ℝ W] in +/-- **The slab is a fundamental domain.** If `F` is a fundamental domain for `Λ` in `V`, then +`F ×ˢ univ` is one for `Λ × 0` in `V × W`. This is what makes the left-hand side of +`ZLattice.measure_inter_add_prod_eq_lintegral` the measure of an image in the quotient. -/ +theorem isAddFundamentalDomain_prod_univ (μV : Measure V) (μW : Measure W) (Λ : Submodule ℤ V) + {F : Set V} (hF : ∀ x : V, ∃! v : Λ, (v : V) + x ∈ F) (hFm : MeasurableSet F) : + IsAddFundamentalDomain (Λ.prod (⊥ : Submodule ℤ W)) (F ×ˢ (univ : Set W)) (μV.prod μW) := by + refine IsAddFundamentalDomain.mk' (hFm.prod MeasurableSet.univ).nullMeasurableSet fun x ↦ ?_ + obtain ⟨v, hv, hu⟩ := hF x.1 + refine ⟨⟨((v : V), 0), ⟨v.2, Submodule.zero_mem _⟩⟩, ⟨hv, mem_univ _⟩, ?_⟩ + rintro ⟨⟨w₁, w₂⟩, hw⟩ hmem + have hw₂ : w₂ = 0 := by simpa using hw.2 + subst hw₂ + have hvw := hu ⟨w₁, hw.1⟩ hmem.1 + exact Subtype.ext (Prod.ext (congrArg (fun z : Λ ↦ (z : V)) hvw) rfl) + +omit [NormedSpace ℝ V] [MeasurableSpace V] [BorelSpace V] [FiniteDimensional ℝ V] + [NormedSpace ℝ W] [MeasurableSpace W] [BorelSpace W] [FiniteDimensional ℝ W] in +/-- The slice of a slab enlarged by `Λ × 0` is the corresponding slice enlarged by `Λ`. -/ +private theorem preimage_prodMk_inter (Λ : Submodule ℤ V) (X : Set (V × W)) (z : W) {F : Set V} : + (fun y ↦ (y, z)) ⁻¹' ((F ×ˢ (univ : Set W)) ∩ + (X + ((Λ.prod (⊥ : Submodule ℤ W)) : Set (V × W)))) + = F ∩ ({y | (y, z) ∈ X} + (Λ : Set V)) := by + ext y + simp only [Set.mem_preimage, Set.mem_inter_iff, Set.mem_prod, Set.mem_univ, and_true, + Set.mem_add, Set.mem_ofPred_eq, SetLike.mem_coe, Submodule.mem_prod, Submodule.mem_bot] + refine and_congr Iff.rfl ⟨?_, ?_⟩ + · rintro ⟨p, hp, l, ⟨hl₁, hl₂⟩, he⟩ + refine ⟨p.1, ?_, l.1, hl₁, ?_⟩ + · have hz : p.2 = z := by + have := congrArg Prod.snd he + simpa [hl₂] using this + rwa [← hz] + · have := congrArg Prod.fst he + simpa using this + · rintro ⟨a, ha, l, hl, he⟩ + exact ⟨(a, z), ha, (l, 0), ⟨hl, rfl⟩, by simp [he]⟩ + +/-- **The Fubini decomposition of the quotient measure** (Cassels, Chapter VIII, §4.2, the display +(10) in the proof of Theorem IV). The measure of the image of `X` in `(V × W) ⧸ (Λ × 0)` is the +integral over `W` of the measures of the images of the slices of `X` in `V ⧸ Λ`. + +Both sides are quotient measures read off against fundamental domains: the slab `F ×ˢ univ` on the +left, by `ZLattice.isAddFundamentalDomain_prod_univ`, and `F` itself on the right. -/ +theorem measure_inter_add_prod_eq_lintegral (μV : Measure V) [μV.IsAddHaarMeasure] (μW : Measure W) + [μW.IsAddHaarMeasure] (Λ : Submodule ℤ V) [Countable Λ] {F : Set V} (hFm : MeasurableSet F) + {X : Set (V × W)} (hX : MeasurableSet X) : + (μV.prod μW) ((F ×ˢ (univ : Set W)) ∩ (X + ((Λ.prod (⊥ : Submodule ℤ W)) : Set (V × W)))) + = ∫⁻ z, μV (F ∩ ({y | (y, z) ∈ X} + (Λ : Set V))) ∂μW := by + rw [Measure.prod_apply_symm ((hFm.prod MeasurableSet.univ).inter + (measurableSet_add_lattice _ hX))] + exact lintegral_congr fun z ↦ by rw [preimage_prodMk_inter Λ X z] + +omit [NormedSpace ℝ V] [FiniteDimensional ℝ V] in +/-- Translation invariance and monotonicity of the quotient measure, in one step: if every point of +`X` lands in `Y` after a translation by the *fixed* vector `c`, the quotient measure of `X` is at +most that of `Y`. -/ +private theorem measure_inter_add_le (μ : Measure V) [μ.IsAddHaarMeasure] (Λ : Submodule ℤ V) + [Countable Λ] {F : Set V} (hF : ∀ x : V, ∃! v : Λ, (v : V) + x ∈ F) (hFm : MeasurableSet F) + {X Y : Set V} (hY : MeasurableSet Y) {c : V} (hsub : ∀ y ∈ X, y + c ∈ Y) : + μ (F ∩ (X + (Λ : Set V))) ≤ μ (F ∩ (Y + (Λ : Set V))) := by + have : VAddInvariantMeasure Λ V μ := inferInstanceAs (VAddInvariantMeasure Λ.toAddSubgroup V μ) + set Fc : Set V := (fun y ↦ y + (-c)) ⁻¹' F with hFc + have hFcm : MeasurableSet Fc := hFm.preimage (measurable_id.add_const _) + have hFcfd : ∀ x : V, ∃! v : Λ, (v : V) + x ∈ Fc := by + intro x + obtain ⟨u, hu, huq⟩ := hF (x + (-c)) + refine ⟨u, ?_, fun w hw ↦ huq w ?_⟩ + · change ((u : V) + x) + (-c) ∈ F + rw [show ((u : V) + x) + (-c) = (u : V) + (x + (-c)) from by abel] + exact hu + · change (w : V) + (x + (-c)) ∈ F + rw [show (w : V) + (x + (-c)) = ((w : V) + x) + (-c) from by abel] + exact hw + have hstep : μ (F ∩ (X + (Λ : Set V))) + = μ (Fc ∩ ((fun y ↦ y + (-c)) ⁻¹' (X + (Λ : Set V)))) := by + rw [hFc, ← Set.preimage_inter] + exact (measure_preimage_add_right μ (-c) _).symm + rw [hstep] + have hsub' : (fun y ↦ y + (-c)) ⁻¹' (X + (Λ : Set V)) ⊆ Y + (Λ : Set V) := by + rintro y ⟨a, ha, l, hl, he⟩ + refine ⟨a + c, hsub a ha, l, hl, ?_⟩ + simp only at he ⊢ + linear_combination (norm := abel) he + refine le_trans (measure_mono (Set.inter_subset_inter_right _ hsub')) (le_of_eq ?_) + have h1 : IsAddFundamentalDomain Λ F μ := IsAddFundamentalDomain.mk' hFm.nullMeasurableSet hF + have h2 : IsAddFundamentalDomain Λ Fc μ := + IsAddFundamentalDomain.mk' hFcm.nullMeasurableSet hFcfd + rw [Set.inter_comm Fc, Set.inter_comm F] + exact h2.measure_set_eq h1 (measurableSet_add_lattice Λ hY) (preimage_vadd_add_lattice Λ) + +omit [MeasurableSpace V] [BorelSpace V] [FiniteDimensional ℝ V] [MeasurableSpace W] [BorelSpace W] + [FiniteDimensional ℝ W] in +/-- Dilating a set dilates its slices, the slice being taken at the dilated point. -/ +private theorem slice_smul (A : Set (V × W)) {s : ℝ} (hs : s ≠ 0) (z : W) : + {y | (y, s • z) ∈ s • A} = s • {y | (y, z) ∈ A} := by + ext y + simp only [Set.mem_ofPred_eq, Set.mem_smul_set] + constructor + · rintro ⟨p, hp, he⟩ + have h₂ : s • p.2 = s • z := congrArg Prod.snd he + have h₂' : p.2 = z := smul_right_injective W hs h₂ + exact ⟨p.1, by rwa [← h₂'], congrArg Prod.fst he⟩ + · rintro ⟨x, hx, rfl⟩ + exact ⟨(x, z), hx, rfl⟩ + +/-- **The scaling estimate** (Cassels, Chapter VIII, §4.2, the displays (12) and (14) in the proof +of Theorem IV). For a convex set `A` and `s ≥ 1`, the quotient measure of `s • A` over `Λ × 0` is at +least `s ^ dim W` times that of `A`. + +The factor is `s ^ dim W` and not `s ^ dim (V × W)` because the Fubini decomposition scales only +the `W`-direction: on each slice the dilation is replaced by a translation, which the quotient +measure over `Λ` does not see. ⚠ The translating vector depends on the slice, so no map of `V × W` +realizes this comparison; see the implementation notes. -/ +theorem measure_inter_add_prod_smul_le (μV : Measure V) [μV.IsAddHaarMeasure] (μW : Measure W) + [μW.IsAddHaarMeasure] (Λ : Submodule ℤ V) [Countable Λ] {F : Set V} + (hF : ∀ x : V, ∃! v : Λ, (v : V) + x ∈ F) (hFm : MeasurableSet F) {A : Set (V × W)} + (hAm : MeasurableSet A) (hAc : Convex ℝ A) {s : ℝ} (hs : 1 ≤ s) : + ENNReal.ofReal (s ^ finrank ℝ W) * (μV.prod μW) + ((F ×ˢ (univ : Set W)) ∩ (A + ((Λ.prod (⊥ : Submodule ℤ W)) : Set (V × W)))) ≤ + (μV.prod μW) ((F ×ˢ (univ : Set W)) ∩ + ((s • A) + ((Λ.prod (⊥ : Submodule ℤ W)) : Set (V × W)))) := by + have hs0 : (0 : ℝ) < s := lt_of_lt_of_le one_pos hs + have hsAm : MeasurableSet (s • A) := hAm.const_smul₀ s + rw [measure_inter_add_prod_eq_lintegral μV μW Λ hFm hAm, + measure_inter_add_prod_eq_lintegral μV μW Λ hFm hsAm] + set f : W → ℝ≥0∞ := fun z ↦ μV (F ∩ ({y | (y, z) ∈ s • A} + (Λ : Set V))) with hf + have hsubst : ∫⁻ z, f z ∂μW = ENNReal.ofReal (s ^ finrank ℝ W) * ∫⁻ z, f (s • z) ∂μW := by + have hne : s ≠ 0 := hs0.ne' + have hmap : μW.map (fun z : W ↦ s • z) = ENNReal.ofReal |(s ^ finrank ℝ W)⁻¹| • μW := + map_addHaar_smul μW hne + have h1 : ∫⁻ z, f z ∂(μW.map (fun z : W ↦ s • z)) = ∫⁻ z, f (s • z) ∂μW := by + simpa using lintegral_map_equiv (μ := μW) f (MeasurableEquiv.smul₀ s hne) + rw [hmap, lintegral_smul_measure, + abs_of_nonneg (by positivity : (0 : ℝ) ≤ (s ^ finrank ℝ W)⁻¹)] at h1 + rw [← h1, smul_eq_mul, ← mul_assoc, ← ENNReal.ofReal_mul (by positivity), + mul_inv_cancel₀ (by positivity)] + simp + rw [hsubst] + refine mul_le_mul_right (lintegral_mono fun z ↦ ?_) _ + set X : Set V := {y | (y, z) ∈ A} with hX + have hXm : MeasurableSet X := hAm.preimage measurable_prodMk_right + have hXc : Convex ℝ X := by + intro y₁ h₁ y₂ h₂ a b ha hb hab + change (a • y₁ + b • y₂, z) ∈ A + rw [show (a • y₁ + b • y₂, z) = a • (y₁, z) + b • (y₂, z) from + Prod.ext rfl (show z = a • z + b • z by rw [← add_smul, hab, one_smul])] + exact hAc h₁ h₂ ha hb hab + have hfz : f (s • z) = μV (F ∩ ((s • X) + (Λ : Set V))) := by + rw [hf] + simp only + rw [slice_smul A hs0.ne' z, ← hX] + rw [hfz] + rcases Set.eq_empty_or_nonempty X with hemp | ⟨y₀, hy₀⟩ + · rw [hemp, Set.empty_add, Set.smul_set_empty, Set.empty_add] + · refine measure_inter_add_le μV Λ hF hFm (hXm.const_smul₀ s) (c := (s - 1) • y₀) ?_ + intro y hy + rw [show y + (s - 1) • y₀ = s • (s⁻¹ • y + (1 - s⁻¹) • y₀) from by + rw [smul_add, smul_smul, mul_inv_cancel₀ hs0.ne', one_smul, smul_smul, + show s * (1 - s⁻¹) = s - 1 from by field_simp]] + refine Set.smul_mem_smul_set (hXc hy hy₀ (by positivity) ?_ (by ring)) + simp only [sub_nonneg] + rw [inv_le_one₀ hs0] + exact hs + +end Product + +section Space + +variable {E : Type*} [NormedAddCommGroup E] [NormedSpace ℝ E] [MeasurableSpace E] [BorelSpace E] + [FiniteDimensional ℝ E] + +omit [MeasurableSpace E] [BorelSpace E] [FiniteDimensional ℝ E] in +/-- A lattice lies in its own `ℝ`-span. -/ +private theorem le_restrictScalars_span (Λ : Submodule ℤ E) : + Λ ≤ (Submodule.span ℝ (Λ : Set E)).restrictScalars ℤ := fun _ hx ↦ Submodule.subset_span hx + +omit [MeasurableSpace E] [BorelSpace E] [FiniteDimensional ℝ E] in +/-- A `ℤ`-submodule spans its own `ℝ`-span: the `span_top` half of being a lattice there. -/ +private theorem span_comap_span_eq_top (Λ : Submodule ℤ E) : + Submodule.span ℝ ((ZLattice.comap ℝ Λ (Submodule.span ℝ (Λ : Set E)).subtype) : + Set ↥(Submodule.span ℝ (Λ : Set E))) = ⊤ := by + set V : Submodule ℝ E := Submodule.span ℝ (Λ : Set E) with hV + have himg : V.subtype '' ((ZLattice.comap ℝ Λ V.subtype) : Set ↥V) = (Λ : Set E) := by + ext x + exact ⟨fun ⟨y, hy, hyx⟩ ↦ hyx ▸ hy, fun hx ↦ ⟨⟨x, Submodule.subset_span hx⟩, hx, rfl⟩⟩ + have hmap : Submodule.map V.subtype + (Submodule.span ℝ ((ZLattice.comap ℝ Λ V.subtype) : Set ↥V)) = V := by + rw [Submodule.map_span, himg, ← hV] + exact Submodule.map_injective_of_injective V.subtype_injective + (hmap.trans (Submodule.map_subtype_top V).symm) + +omit [MeasurableSpace E] [BorelSpace E] [FiniteDimensional ℝ E] in +/-- The lattice and its copy inside its own span are `ℤ`-linearly equivalent. -/ +private def comapSpanEquiv (Λ : Submodule ℤ E) : + (ZLattice.comap ℝ Λ (Submodule.span ℝ (Λ : Set E)).subtype) ≃ₗ[ℤ] Λ := + Submodule.comapSubtypeEquivOfLe (le_restrictScalars_span Λ) + +omit [MeasurableSpace E] [BorelSpace E] in +/-- **A discrete `ℤ`-submodule of a finite-dimensional real normed space is countable.** Mathlib +has this for a `ZLattice`; a discrete submodule of smaller rank is a `ZLattice` in its own span. -/ +theorem countable_of_discreteTopology (Λ : Submodule ℤ E) [DiscreteTopology Λ] : Countable Λ := by + have hdisc : DiscreteTopology (ZLattice.comap ℝ Λ (Submodule.span ℝ (Λ : Set E)).subtype) := + ZLattice.comap_discreteTopology ℝ Λ continuous_subtype_val Subtype.val_injective + have hzl : IsZLattice ℝ (ZLattice.comap ℝ Λ (Submodule.span ℝ (Λ : Set E)).subtype) := + ⟨span_comap_span_eq_top Λ⟩ + exact Countable.of_equiv _ (comapSpanEquiv Λ).toEquiv + +/-- **The scaling estimate for a lattice of arbitrary rank in a single space**, with the slab +produced rather than assumed: some measurable set `F` is a fundamental domain for `Λ` and, for +every convex `A` and every `s ≥ 1`, the quotient measure of `s • A` modulo `Λ` is at least +`s ^ (n - rank Λ)` times that of `A`. + +This is Cassels' normalization of his Chapter VIII, Theorem IV made intrinsic: he transforms the +lattice to `ℤ ^ n` linearly, and the transformation is here the choice of a complement `W` of the +span of `Λ`, along which the fundamental parallelepiped of `Λ` in that span is extended to the +slab `F`. The exponent is `dim W`. -/ +theorem exists_isAddFundamentalDomain_measure_smul_le (μ : Measure E) [μ.IsAddHaarMeasure] + (Λ : Submodule ℤ E) [DiscreteTopology Λ] : + ∃ F : Set E, MeasurableSet F ∧ (∀ x : E, ∃! v : Λ, (v : E) + x ∈ F) ∧ + ∀ A : Set E, MeasurableSet A → Convex ℝ A → ∀ s : ℝ, 1 ≤ s → + ENNReal.ofReal (s ^ (finrank ℝ E - finrank ℤ Λ)) * μ (F ∩ (A + (Λ : Set E))) ≤ + μ (F ∩ ((s • A) + (Λ : Set E))) := by + have hcount : Countable Λ := countable_of_discreteTopology Λ + set V : Submodule ℝ E := Submodule.span ℝ (Λ : Set E) with hV + obtain ⟨W, hcompl⟩ := V.exists_isCompl + set e : (↥V × ↥W) ≃L[ℝ] E := + (Submodule.prodEquivOfIsCompl V W hcompl).toContinuousLinearEquiv with he + have hea : ∀ p : ↥V × ↥W, e p = (p.1 : E) + (p.2 : E) := fun _ ↦ rfl + set Λ' : Submodule ℤ ↥V := ZLattice.comap ℝ Λ V.subtype with hΛ' + have hmemΛ' : ∀ y : ↥V, y ∈ Λ' ↔ (y : E) ∈ Λ := fun _ ↦ Iff.rfl + have hΛV : ∀ v : E, v ∈ Λ → v ∈ V := fun _ hv ↦ Submodule.subset_span hv + have hdisc : DiscreteTopology Λ' := + ZLattice.comap_discreteTopology ℝ Λ continuous_subtype_val Subtype.val_injective + have hzl : IsZLattice ℝ Λ' := ⟨span_comap_span_eq_top Λ⟩ + have hcount' : Countable Λ' := inferInstance + -- the fundamental parallelepiped of `Λ` inside its span + have hmodfin : Module.Finite ℤ Λ' := ZLattice.module_finite ℝ Λ' + have hfree : Module.Free ℤ Λ' := ZLattice.module_free ℝ Λ' + set bZ : Basis (Module.Free.ChooseBasisIndex ℤ Λ') ℤ Λ' := Module.Free.chooseBasis ℤ Λ' with hbZ + set bR : Basis (Module.Free.ChooseBasisIndex ℤ Λ') ℝ ↥V := bZ.ofZLatticeBasis ℝ Λ' with hbR + set FV : Set ↥V := ZSpan.fundamentalDomain bR with hFVdef + have hFVm : MeasurableSet FV := ZSpan.fundamentalDomain_measurableSet bR + have hFVfd : ∀ y : ↥V, ∃! v : Λ', (v : ↥V) + y ∈ FV := fun y ↦ + existsUnique_of_eq (bZ.ofZLatticeBasis_span ℝ) + (ZSpan.exist_unique_vadd_mem_fundamentalDomain bR y) + -- the slab in `E` + set F : Set E := e '' (FV ×ˢ (univ : Set ↥W)) with hFdef + have hmemF : ∀ y : E, y ∈ F ↔ (e.symm y).1 ∈ FV := by + intro y + rw [hFdef] + exact ⟨fun ⟨p, hp, hpy⟩ ↦ by simpa [← hpy] using hp.1, + fun hy ↦ ⟨e.symm y, ⟨hy, Set.mem_univ _⟩, by simp⟩⟩ + have hFm : MeasurableSet F := by + rw [show F = (e.symm : E → ↥V × ↥W) ⁻¹' (FV ×ˢ (univ : Set ↥W)) from by + ext y; rw [hmemF y]; simp] + exact (hFVm.prod MeasurableSet.univ).preimage e.symm.continuous.measurable + have hπ : ∀ (v : Λ) (x : E), + (e.symm ((v : E) + x)).1 = (⟨(v : E), hΛV _ v.2⟩ : ↥V) + (e.symm x).1 := by + intro v x + have hev : e.symm (v : E) = ((⟨(v : E), hΛV _ v.2⟩ : ↥V), (0 : ↥W)) := by + refine e.injective ?_ + rw [ContinuousLinearEquiv.apply_symm_apply, hea] + simp + rw [map_add, hev] + rfl + have hFfd : ∀ x : E, ∃! v : Λ, (v : E) + x ∈ F := by + intro x + obtain ⟨u, hu, huq⟩ := hFVfd (e.symm x).1 + have huΛ : ((u : ↥V) : E) ∈ Λ := u.2 + refine ⟨⟨((u : ↥V) : E), huΛ⟩, ?_, ?_⟩ + · change ((u : ↥V) : E) + x ∈ F + rw [hmemF, hπ ⟨((u : ↥V) : E), huΛ⟩ x, + show (⟨((u : ↥V) : E), hΛV _ huΛ⟩ : ↥V) = (u : ↥V) from Subtype.ext rfl] + exact hu + · intro w hw + have hw2 : (w : E) + x ∈ F := hw + rw [hmemF, hπ w x] at hw2 + have hw' : (⟨⟨(w : E), hΛV _ w.2⟩, w.2⟩ : Λ') = u := huq _ hw2 + exact Subtype.ext (congrArg (fun z : Λ' ↦ ((z : ↥V) : E)) hw') + refine ⟨F, hFm, hFfd, ?_⟩ + intro A hAm hAc s hs + -- the transport of the Haar measure to the product + obtain ⟨c, hc⟩ : ∃ c : ℝ≥0∞, ∀ S : Set E, MeasurableSet S → + μ S = c * ((addHaar : Measure ↥V).prod (addHaar : Measure ↥W)) (e ⁻¹' S) := by + set ν : Measure (↥V × ↥W) := μ.map e.symm with hν + have hνHaar : ν.IsAddHaarMeasure := e.symm.isAddHaarMeasure_map μ + obtain ⟨c, hcν⟩ : ∃ c : ℝ≥0∞, ν = c • ((addHaar : Measure ↥V).prod (addHaar : Measure ↥W)) := + ⟨addHaarScalarFactor ν ((addHaar : Measure ↥V).prod (addHaar : Measure ↥W)), + isAddLeftInvariant_eq_smul _ _⟩ + refine ⟨c, fun S hS ↦ ?_⟩ + have h1 : ν (e ⁻¹' S) = μ S := by + rw [hν, Measure.map_apply e.symm.continuous.measurable + (hS.preimage e.continuous.measurable)] + congr 1 + ext y + simp + rw [← h1, hcν] + simp + -- the transport of the sets + have hpreadd : ∀ X Y : Set E, e ⁻¹' (X + Y) = (e ⁻¹' X) + (e ⁻¹' Y) := by + intro X Y + ext p + simp only [Set.mem_preimage, Set.mem_add] + constructor + · rintro ⟨x, hx, y, hy, hxy⟩ + refine ⟨e.symm x, by simpa using hx, e.symm y, by simpa using hy, ?_⟩ + rw [← map_add, hxy] + exact e.symm_apply_apply p + · rintro ⟨p₁, h₁, p₂, h₂, rfl⟩ + exact ⟨e p₁, h₁, e p₂, h₂, (map_add e p₁ p₂).symm⟩ + have hpresmul : ∀ (t : ℝ) (X : Set E), e ⁻¹' (t • X) = t • (e ⁻¹' X) := by + intro t X + ext p + simp only [Set.mem_preimage, Set.mem_smul_set] + constructor + · rintro ⟨a, ha, hae⟩ + refine ⟨e.symm a, by simpa using ha, ?_⟩ + rw [← map_smul, hae] + exact e.symm_apply_apply p + · rintro ⟨q, hq, rfl⟩ + exact ⟨e q, hq, (map_smul e t q).symm⟩ + have hpreΛ : e ⁻¹' (Λ : Set E) = ((Λ'.prod (⊥ : Submodule ℤ ↥W)) : Set (↥V × ↥W)) := by + ext p + simp only [Set.mem_preimage, SetLike.mem_coe, Submodule.mem_prod, Submodule.mem_bot, hea] + constructor + · intro hp + have hpV : (p.1 : E) + (p.2 : E) ∈ V := hΛV _ hp + have h2 : (p.2 : E) ∈ V := by + rw [show (p.2 : E) = ((p.1 : E) + (p.2 : E)) - (p.1 : E) from by abel] + exact Submodule.sub_mem _ hpV p.1.2 + have h3 : p.2 = 0 := by + have hmem : (p.2 : E) ∈ V ⊓ W := ⟨h2, p.2.2⟩ + rw [hcompl.inf_eq_bot] at hmem + exact Subtype.ext (by simpa using hmem) + refine ⟨(hmemΛ' p.1).2 ?_, h3⟩ + rw [show ((p.1 : ↥V) : E) = (p.1 : E) + (p.2 : E) from by + rw [show (p.2 : E) = 0 from by rw [h3]; simp]; abel] + exact hp + · rintro ⟨h1, h2⟩ + rw [show (p.2 : E) = 0 from by rw [h2]; simp, add_zero] + exact (hmemΛ' p.1).1 h1 + have hpreF : e ⁻¹' F = FV ×ˢ (univ : Set ↥W) := by + ext p + rw [Set.mem_preimage, hmemF] + simp + -- the rank bookkeeping + have hr1 : finrank ℤ Λ = finrank ℝ ↥V := by + rw [← ZLattice.rank ℝ Λ'] + exact ((comapSpanEquiv Λ).finrank_eq).symm + have hr2 : finrank ℝ ↥V + finrank ℝ ↥W = finrank ℝ E := + Submodule.finrank_add_eq_of_isCompl hcompl + have hrank : finrank ℝ E - finrank ℤ Λ = finrank ℝ ↥W := by omega + -- the comparison, transported + have hsAm : MeasurableSet (s • A) := hAm.const_smul₀ s + rw [hc _ (hFm.inter (measurableSet_add_lattice Λ hAm)), + hc _ (hFm.inter (measurableSet_add_lattice Λ hsAm)), Set.preimage_inter, Set.preimage_inter, + hpreF, hpreadd, hpreΛ, hpreadd, hpreΛ, hpresmul, hrank, mul_left_comm] + exact mul_le_mul_right (measure_inter_add_prod_smul_le _ _ Λ' hFVfd hFVm + (hAm.preimage e.continuous.measurable) (hAc.linear_preimage e.toLinearEquiv.toLinearMap) hs) c + +/-- **Cassels' Chapter VIII, Theorem IV, in one step** (the display (14) of his proof). For a +convex set `A` on which congruence modulo the lattice `L` is congruence modulo the sublattice `Λ`, +dilating by `s ≥ 1` multiplies the measure of the image in `E ⧸ L` by at least +`s ^ (n - rank Λ)`. + +This is the inductive step of Weyl's proof of the upper bound in Minkowski's second theorem: the +separation hypotheses are Cassels' Lemma 2 applied to `A = t • B`, and the exponent decreases by +one each time `t` passes a successive minimum. -/ +theorem pow_mul_measure_inter_add_le (μ : Measure E) [μ.IsAddHaarMeasure] {Λ L : Submodule ℤ E} + [DiscreteTopology Λ] [DiscreteTopology L] (hΛL : Λ ≤ L) {FL : Set E} + (hL : ∀ x : E, ∃! v : L, (v : E) + x ∈ FL) (hmL : MeasurableSet FL) (hfin : μ FL ≠ ⊤) + {A : Set E} (hAm : MeasurableSet A) (hAc : Convex ℝ A) {s : ℝ} (hs : 1 ≤ s) + (hsepA : ∀ x ∈ A, ∀ y ∈ A, x - y ∈ L → x - y ∈ Λ) + (hsepB : ∀ x ∈ s • A, ∀ y ∈ s • A, x - y ∈ L → x - y ∈ Λ) : + ENNReal.ofReal (s ^ (finrank ℝ E - finrank ℤ Λ)) * μ (FL ∩ (A + (L : Set E))) ≤ + μ (FL ∩ ((s • A) + (L : Set E))) := by + have hcΛ : Countable Λ := countable_of_discreteTopology Λ + have hcL : Countable L := countable_of_discreteTopology L + obtain ⟨F, hFm, hFfd, hscale⟩ := exists_isAddFundamentalDomain_measure_smul_le μ Λ + have hsAm : MeasurableSet (s • A) := hAm.const_smul₀ s + rw [← measure_inter_add_eq_of_separated μ hΛL hFfd hL hFm hmL hfin hAm hsepA, + ← measure_inter_add_eq_of_separated μ hΛL hFfd hL hFm hmL hfin hsAm hsepB] + exact hscale A hAm hAc s hs + +end Space + +end ZLattice diff --git a/LeanPool/CCDN/ArithmeticHeights/RationalLattice.lean b/LeanPool/CCDN/ArithmeticHeights/RationalLattice.lean new file mode 100644 index 0000000000..5a554c1757 --- /dev/null +++ b/LeanPool/CCDN/ArithmeticHeights/RationalLattice.lean @@ -0,0 +1,551 @@ +/- +Copyright (c) 2026 Ralf Stephan. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Ralf Stephan +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.ArithmeticHeights.CauchyBinet +public import LeanPool.CCDN.ArithmeticHeights.GramCovolume +public import LeanPool.CCDN.ArithmeticHeights.Subspace +public import Mathlib.LinearAlgebra.FreeModule.PID +public import Mathlib.LinearAlgebra.LinearIndependent.BaseChange +public import Mathlib.RingTheory.Localization.Integer + +/-! +# The lattice of integral points of a rational subspace, and its covolume + +For a subspace `V ⊆ ℚ^ι` the integral points `V ∩ ℤ^ι` form a lattice of rank `dim V` in the real +span of `V`, and its covolume is the Arakelov height of `V`: + +```text +covolume (V ∩ ℤ^ι) = H_Ar V. +``` + +This is the `ℚ` case of the covolume identity of Layer 4.3 — W. M. Schmidt's `H'(S) = H(S)` — which +over a number field `K` reads `covol (V ∩ 𝓞_K^ι) = 2^{-r₂ k} |discr K|^{k/2} H_Ar(V)^d`. Over `ℚ` +the degree `d`, the discriminant and the number `r₂` of complex places are all `1`, `1` and `0`, so +the three constants vanish and the identity is the bare statement above. It is what converts the +lattice statement of Minkowski's second theorem (Layer 4.2) into a height statement, and it is the +only ingredient Siegel's lemma over `ℚ` takes from this milestone. + +## Main definitions + +* `Rat.piIntCast`, `Rat.piEuclidean`: the coordinatewise casts `(ι → ℤ) → (ι → ℚ)` and + `(ι → ℚ) → EuclideanSpace ℝ ι`. The second is the `ℚ` case of `NumberField.mixedEmbedding`. +* `Submodule.intPoints`: the integral points `V ∩ ℤ^ι` of a rational subspace, as a `ℤ`-submodule + of `ι → ℤ`. +* `Submodule.realSpan`: the real span of a rational subspace, inside `EuclideanSpace ℝ ι`. +* `Submodule.intLattice`: the integral points, as a `ℤ`-lattice inside `Submodule.realSpan`. This + is the object whose covolume the milestone computes. + +## Main results + +* `Submodule.covolume_intLattice`: the covolume identity, `covolume (V ∩ ℤ^ι) = H_Ar V`. +* `Submodule.covolume_intLattice_span_singleton`: its rank-one case — the covolume of the lattice + cut out by a line is the Arakelov height of any rational point spanning it, hence the euclidean + norm of the primitive integer point on it. +* `Submodule.exists_basis_intPoints`: the integral points are free of rank `dim V`, with a basis + that spans `V` over `ℚ` and generates *all* of `V ∩ ℤ^ι` — the saturation that makes the height + come out right. +* `Submodule.gcd_plucker_eq_one`: **the Plücker point of such a basis is a primitive integer + vector**: the maximal minors of a saturated integral basis are coprime. +* `NumberField.arakelovMulHeight_intCast_of_gcd_eq_one`: over `ℚ`, the Arakelov height of a tuple + of coprime integers is its euclidean norm. This is the Arakelov analogue of Mathlib's + `Rat.mulHeight_eq_max_abs_of_gcd_eq_one`, which Mathlib states for the sup norm only. +* `Submodule.finrank_realSpan`, `Submodule.mem_intLattice`, and the `DiscreteTopology` and + `IsZLattice` instances that make the covolume meaningful. + +## Implementation notes + +⚠ **Both sides of the identity are computed against the same sum of squares of maximal minors, and +that is the whole proof.** With `y` a saturated integral basis and `p` its tuple of Plücker +coordinates, `ZLattice.covolume_sq_eq_det_gram` and the Cauchy–Binet identity of Layer 3.4 give +`covolume² = det (Y Yᵀ) = ∑ₛ p ₛ²`, while the height is `∑ₛ p ₛ²` under a square root because the +finite places contribute `1` — which is exactly the primitivity of `p`. Neither computation knows +about the other; the identity is the statement that the archimedean factor of the height is a Gram +determinant and its finite factor is a saturation condition. + +⚠ **Saturation is not a convenience, it is the content.** For an arbitrary `ℤ`-basis of a +finite-index sublattice of `V ∩ ℤ^ι` the covolume is larger by the index, while the height does not +change: the Plücker coordinates are multiplied by the index and the extra factor is cancelled by +the finite places. `Submodule.gcd_plucker_eq_one` is where saturation is spent, and it is spent +exactly once. + +⚠ **The primitivity proof needs no adapted basis, no Smith normal form and no splitting of the +quotient.** If a prime `q` divided every maximal minor of `y`, then the reductions of the `y i` +would be linearly dependent over `ZMod q` — this is `exteriorPower.plucker_eq_zero_iff` of Layer +3.1, read over a finite field — so some integral combination `∑ cᵢ yᵢ` with a coefficient prime to +`q` would be divisible by `q`; its quotient by `q` is an integral point of `V`, hence a +`ℤ`-combination of the `y i` by saturation, and independence then forces `q ∣ cᵢ` for every `i`. +Cassels' Lemma 2 is not on this path either, just as it was not on the path of Layer 4.2. + +⚠ **The lattice lives in a subspace, and that is why the ambient space is `EuclideanSpace`.** A +sublattice of `ℤ^ι` of rank `k < #ι` has no covolume in `ℝ^ι`; the covolume it has is the one +measured inside its own real span, and a subspace of a real vector space carries a canonical +measure only when the space carries an inner product. `Submodule.realSpan` is therefore a submodule +of `EuclideanSpace ℝ ι` rather than of `ι → ℝ`, and `ZLattice.covolume` is taken against the +`MeasureSpace` instance that `Mathlib.MeasureTheory.Measure.Haar.OfBasis` puts on any +finite-dimensional real inner product space. + +⚠ **Only the `ℚ` case is here.** The number-field case of Layer 4.3 is Schmidt 1967, §3, Theorem 1, +whose two halves — a Lagrange expansion in blocks of `d` rows for the archimedean factor, and a +local counting lemma for `[σ(Λ) : Λ₀] = N(𝔞)` at the finite places — are not reproduced here. What +the `ℚ` case already settles is the shape of the statement and its normalization: the Arakelov +height, not the sup-norm height, is what a covolume equals. + +## References + +W. M. Schmidt, "On heights of algebraic subspaces and diophantine approximations", *Annals of +Mathematics* **85** (1967), 430–472, §3, Theorem 1: `H'(S) = H(S)`, of which the statement here is +the case `K = ℚ`. His Lemma 4 is the archimedean half — over `ℚ` it is the Cauchy–Binet identity +alone — and his Lemmas 5 and 6 the finite half, which over `ℚ` is the primitivity of the Plücker +point. + +I. Aliev and M. Henk, "Minkowski's successive minima in convex and discrete geometry", +*Communications in Mathematics* **31**(2) (2023), 35–59, §6 +(arXiv:2304.00120; doi:10.46298/cm.11155). The rational covolume identity is stated +immediately before Theorem 6.3 and used in the geometry-of-numbers proof of Siegel's lemma. + +This is Layer 4.3 of the `ArithmeticHeights` roadmap, over `ℚ`. +-/ + +public section + +open Module Submodule + +/-! +### The two coordinatewise casts +-/ + +/-- Coordinatewise `ℤ → ℚ` on tuples, as a `ℤ`-linear map. -/ +@[expose] def Rat.piIntCast (ι : Type*) : (ι → ℤ) →ₗ[ℤ] (ι → ℚ) where + toFun z i := (z i : ℚ) + map_add' a b := by funext i; simp + map_smul' c a := by funext i; simp + +@[simp] theorem Rat.piIntCast_apply {ι : Type*} (z : ι → ℤ) (i : ι) : + Rat.piIntCast ι z i = (z i : ℚ) := rfl + +theorem Rat.piIntCast_injective {ι : Type*} : Function.Injective (Rat.piIntCast ι) := + fun _ _ h ↦ funext fun i ↦ by + have hi := congrFun h i + rw [Rat.piIntCast_apply, Rat.piIntCast_apply] at hi + exact_mod_cast hi + +/-- Coordinatewise `ℚ → ℝ` on tuples, landing in `EuclideanSpace ℝ ι`, as a `ℚ`-linear map. This +is the `ℚ` case of `NumberField.mixedEmbedding`: there are no complex places and the one real +place is the inclusion. -/ +@[expose] def Rat.piEuclidean (ι : Type*) : (ι → ℚ) →ₗ[ℚ] EuclideanSpace ℝ ι where + toFun x := WithLp.toLp 2 fun i ↦ ((x i : ℝ)) + map_add' a b := by ext i; push_cast; simp + map_smul' c a := by ext i; push_cast; simp [Rat.smul_def] + +@[simp] theorem Rat.piEuclidean_apply {ι : Type*} (x : ι → ℚ) (i : ι) : + (Rat.piEuclidean ι x).ofLp i = (x i : ℝ) := rfl + +/-! +### The integral points of a rational subspace +-/ + +section IntPoints + +variable {ι : Type*} + +/-- The **integral points** `V ∩ ℤ^ι` of a rational subspace, as a `ℤ`-submodule of `ι → ℤ`. -/ +@[expose] def Submodule.intPoints (V : Submodule ℚ (ι → ℚ)) : Submodule ℤ (ι → ℤ) := + Submodule.comap (Rat.piIntCast ι) (V.restrictScalars ℤ) + +@[simp] theorem Submodule.mem_intPoints {V : Submodule ℚ (ι → ℚ)} {z : ι → ℤ} : + z ∈ V.intPoints ↔ Rat.piIntCast ι z ∈ V := Iff.rfl + +/-- A `ℤ`-basis of the integral points is linearly independent over `ℚ`. -/ +private theorem linearIndependent_of_basis_intPoints (V : Submodule ℚ (ι → ℚ)) {κ : Type*} + (b : Basis κ ℤ V.intPoints) : + LinearIndependent ℚ fun i ↦ Rat.piIntCast ι (b i : ι → ℤ) := by + rw [← LinearIndependent.iff_fractionRing (R := ℤ) (K := ℚ)] + exact b.linearIndependent.map' ((Rat.piIntCast ι).comp (V.intPoints.subtype)) + (LinearMap.ker_eq_bot.2 (Rat.piIntCast_injective.comp V.intPoints.injective_subtype)) + +/-- A `ℤ`-basis of the integral points generates all of them: saturation. -/ +private theorem span_int_of_basis_intPoints (V : Submodule ℚ (ι → ℚ)) {κ : Type*} + (b : Basis κ ℤ V.intPoints) : + Submodule.span ℤ (Set.range fun i ↦ (b i : ι → ℤ)) = V.intPoints := by + rw [show (fun i ↦ ((b i : ι → ℤ))) = (V.intPoints.subtype ∘ b) from rfl, Set.range_comp, + ← Submodule.map_span, b.span_eq, Submodule.map_subtype_top] + +variable [Finite ι] + +instance (V : Submodule ℚ (ι → ℚ)) : Module.Finite ℤ V.intPoints := + Module.Finite.of_injective V.intPoints.subtype V.intPoints.injective_subtype + +instance (V : Submodule ℚ (ι → ℚ)) : Module.Free ℤ V.intPoints := + Module.free_of_finite_type_torsion_free' + +/-- Clearing denominators: every rational tuple has a nonzero integral multiple. -/ +private theorem exists_intCast_smul (v : ι → ℚ) : + ∃ (N : ℤ) (z : ι → ℤ), N ≠ 0 ∧ (N : ℚ) • v = Rat.piIntCast ι z := by + classical + let _ : Fintype ι := Fintype.ofFinite ι + obtain ⟨b, hb⟩ := IsLocalization.exist_integer_multiples (nonZeroDivisors ℤ) + (Finset.univ : Finset ι) v + choose z hz using fun i ↦ hb i (Finset.mem_univ i) + refine ⟨(b : ℤ), z, nonZeroDivisors.coe_ne_zero b, funext fun i ↦ ?_⟩ + have h := hz i + simp only [Algebra.smul_def, algebraMap_int_eq, eq_intCast] at h + simpa using h.symm + +/-- A `ℤ`-basis of the integral points spans `V` over `ℚ`. -/ +private theorem span_of_basis_intPoints (V : Submodule ℚ (ι → ℚ)) {κ : Type*} + (b : Basis κ ℤ V.intPoints) : + Submodule.span ℚ (Set.range fun i ↦ Rat.piIntCast ι (b i : ι → ℤ)) = V := by + refine le_antisymm (Submodule.span_le.2 ?_) fun v hv ↦ ?_ + · rintro _ ⟨i, rfl⟩ + exact (b i).2 + · obtain ⟨N, z, hN, hz⟩ := exists_intCast_smul v + have hzΛ : z ∈ V.intPoints := by + rw [Submodule.mem_intPoints, ← hz] + exact V.smul_mem _ hv + have h1 : (⟨z, hzΛ⟩ : V.intPoints) ∈ Submodule.span ℤ (Set.range b) := by + rw [b.span_eq]; exact Submodule.mem_top + have h2 := Submodule.mem_map_of_mem (f := (Rat.piIntCast ι).comp (V.intPoints.subtype)) h1 + rw [Submodule.map_span, ← Set.range_comp] at h2 + have h3 : Rat.piIntCast ι z ∈ + Submodule.span ℚ (Set.range fun i ↦ Rat.piIntCast ι (b i : ι → ℤ)) := + Submodule.span_le_restrictScalars ℤ ℚ _ h2 + have hNQ : (N : ℚ) ≠ 0 := Int.cast_ne_zero.2 hN + rw [show v = (N : ℚ)⁻¹ • Rat.piIntCast ι z by + rw [← hz, smul_smul, inv_mul_cancel₀ hNQ, one_smul]] + exact Submodule.smul_mem _ _ h3 + +/-- **A saturated integral basis exists.** The integral points of a rational subspace of dimension +`k` are a free `ℤ`-module of rank `k`, and a basis of them spans `V` over `ℚ` while generating +*every* integral point of `V`. Both halves are needed: the first makes the basis a basis of the +real span, the second is what makes its Plücker point primitive. -/ +theorem Submodule.exists_basis_intPoints (V : Submodule ℚ (ι → ℚ)) {k : ℕ} + (hV : finrank ℚ V = k) : + ∃ y : Fin k → (ι → ℤ), + LinearIndependent ℚ (fun i ↦ Rat.piIntCast ι (y i)) ∧ + Submodule.span ℚ (Set.range fun i ↦ Rat.piIntCast ι (y i)) = V ∧ + Submodule.span ℤ (Set.range y) = V.intPoints := by + classical + let b₀ := Module.Free.chooseBasis ℤ V.intPoints + have hcard : Fintype.card (Module.Free.ChooseBasisIndex ℤ V.intPoints) = k := by + have h := finrank_span_eq_card (linearIndependent_of_basis_intPoints V b₀) + rw [span_of_basis_intPoints V b₀, hV] at h + exact h.symm + let b := b₀.reindex (Fintype.equivFinOfCardEq hcard) + exact ⟨fun i ↦ (b i : ι → ℤ), linearIndependent_of_basis_intPoints V b, + span_of_basis_intPoints V b, span_int_of_basis_intPoints V b⟩ + +end IntPoints + +/-! +### Primitivity of the Plücker point +-/ + +section Primitive + +open exteriorPower + +variable {ι : Type*} [Fintype ι] [LinearOrder ι] (V : Submodule ℚ (ι → ℚ)) + +/-- **The maximal minors of a saturated integral basis are coprime.** Equivalently: the Plücker +point of a rational subspace has a primitive integer representative, namely the one a basis of +`V ∩ ℤ^ι` produces. This is the finite half of the covolume identity — Schmidt's Lemmas 5 and 6 +over `ℚ` — and it is the only place the saturation hypothesis is used. -/ +theorem Submodule.gcd_plucker_eq_one {k : ℕ} {y : Fin k → (ι → ℤ)} + (hy : LinearIndependent ℚ (fun i ↦ Rat.piIntCast ι (y i))) + (hspan : Submodule.span ℚ (Set.range fun i ↦ Rat.piIntCast ι (y i)) = V) + (hsat : Submodule.span ℤ (Set.range y) = V.intPoints) : + (Finset.univ : Finset (Set.powersetCard ι k)).gcd (plucker k y) = 1 := by + classical + have hyZ : LinearIndependent ℤ y := + LinearIndependent.of_comp (Rat.piIntCast ι) + (hy.restrict_scalars (by simpa using Int.cast_injective (α := ℚ))) + have key : ∀ q : ℤ, Prime q → ¬ (∀ s, q ∣ plucker k y s) := by + intro q hq hdvd + set P := q.natAbs with hP + have hPp : P.Prime := Int.prime_iff_natAbs_prime.1 hq + have : Fact P.Prime := ⟨hPp⟩ + have hPdvd : ∀ s, ((P : ℤ)) ∣ plucker k y s := fun s ↦ + (Int.natAbs_dvd.2 dvd_rfl).trans (hdvd s) + have hzero : plucker k (fun i ↦ (Int.castRingHom (ZMod P)) ∘ y i) = 0 := by + funext s + rw [plucker_map] + simpa using (ZMod.intCast_zmod_eq_zero_iff_dvd _ P).2 (hPdvd s) + obtain ⟨c, hcsum, i₀, hi₀⟩ := + Fintype.not_linearIndependent_iff.1 ((plucker_eq_zero_iff (K := ZMod P) k _).1 hzero) + choose c' hc' using fun i ↦ ZMod.intCast_surjective (n := P) (c i) + set w : ι → ℤ := fun l ↦ ∑ i, c' i * y i l with hw + have hwdvd : ∀ l, ((P : ℤ)) ∣ w l := by + intro l + rw [← ZMod.intCast_zmod_eq_zero_iff_dvd] + have hl := congrFun hcsum l + simp only [Finset.sum_apply, Pi.smul_apply, smul_eq_mul, Pi.zero_apply, + Function.comp_apply, eq_intCast] at hl + have hcast : ((w l : ℤ) : ZMod P) = ∑ i, c i * ((y i l : ℤ) : ZMod P) := by + rw [hw] + push_cast + exact Finset.sum_congr rfl fun i _ ↦ by rw [hc' i] + rw [hcast, hl] + choose z hz using hwdvd + have hwz : w = (P : ℤ) • z := funext fun l ↦ by simp [hz l] + have hwV : Rat.piIntCast ι w ∈ V := by + rw [← hspan, show Rat.piIntCast ι w = ∑ i, (c' i : ℚ) • Rat.piIntCast ι (y i) from + funext fun l ↦ by simp [hw, Finset.sum_apply]] + exact Submodule.sum_mem _ fun i _ ↦ + Submodule.smul_mem _ _ (Submodule.subset_span ⟨i, rfl⟩) + have hPQ : ((P : ℚ)) ≠ 0 := Nat.cast_ne_zero.2 hPp.ne_zero + have hzV : Rat.piIntCast ι z ∈ V := by + have h1 : Rat.piIntCast ι w = (P : ℚ) • Rat.piIntCast ι z := by + rw [hwz, map_zsmul]; funext l; simp + have h2 := V.smul_mem ((P : ℚ)⁻¹) hwV + rwa [h1, smul_smul, inv_mul_cancel₀ hPQ, one_smul] at h2 + obtain ⟨d, hd⟩ := (mem_span_range_iff_exists_fun ℤ).1 (by rw [hsat]; exact hzV : + z ∈ Submodule.span ℤ (Set.range y)) + have hcoef : ∀ i, c' i - (P : ℤ) * d i = 0 := by + refine Fintype.linearIndependent_iff.1 hyZ _ ?_ + have h1 : ∑ i, c' i • y i = w := funext fun l ↦ by simp [hw, Finset.sum_apply] + have h2 : ∑ i, ((P : ℤ) * d i) • y i = w := by + rw [hwz, ← hd] + exact funext fun l ↦ by simp [Finset.mul_sum, Finset.sum_apply, mul_assoc] + simp only [sub_smul, Finset.sum_sub_distrib, h1, h2, sub_self] + refine hi₀ ?_ + rw [← hc' i₀, ZMod.intCast_zmod_eq_zero_iff_dvd] + exact ⟨d i₀, by linarith [hcoef i₀]⟩ + have hgu : IsUnit ((Finset.univ : Finset (Set.powersetCard ι k)).gcd (plucker k y)) := by + rw [Int.isUnit_iff_natAbs_eq] + by_contra hne + obtain ⟨q, hq, hqd⟩ := Int.exists_prime_and_dvd hne + exact key q hq fun s ↦ hqd.trans (Finset.gcd_dvd (Finset.mem_univ s)) + calc (Finset.univ : Finset (Set.powersetCard ι k)).gcd (plucker k y) + = normalize ((Finset.univ : Finset (Set.powersetCard ι k)).gcd (plucker k y)) := + Finset.normalize_gcd.symm + _ = 1 := normalize_eq_one.2 hgu + +end Primitive + +/-! +### The real span and the lattice inside it +-/ + +section RealSpan + +variable {ι : Type*} + +/-- The **real span** of a rational subspace, inside `EuclideanSpace ℝ ι`. -/ +@[expose] def Submodule.realSpan (V : Submodule ℚ (ι → ℚ)) : Submodule ℝ (EuclideanSpace ℝ ι) := + Submodule.span ℝ (Rat.piEuclidean ι '' (V : Set (ι → ℚ))) + +/-- The **integral points of `V` as a lattice in its real span**: the object whose covolume is the +Arakelov height of `V`. -/ +@[expose] noncomputable def Submodule.intLattice (V : Submodule ℚ (ι → ℚ)) : + Submodule ℤ ↥V.realSpan := + Submodule.comap ((V.realSpan.subtype).restrictScalars ℤ) + (Submodule.map (((Rat.piEuclidean ι).restrictScalars ℤ).comp (Rat.piIntCast ι)) V.intPoints) + +theorem Submodule.mem_intLattice {V : Submodule ℚ (ι → ℚ)} {w : ↥V.realSpan} : + w ∈ V.intLattice ↔ ∃ z : ι → ℤ, Rat.piIntCast ι z ∈ V ∧ + (w : EuclideanSpace ℝ ι) = Rat.piEuclidean ι (Rat.piIntCast ι z) := by + simp only [Submodule.intLattice, Submodule.mem_comap, Submodule.mem_map, + LinearMap.restrictScalars_apply, LinearMap.coe_comp, Function.comp_apply, + Submodule.coe_subtype, Submodule.mem_intPoints] + exact ⟨fun ⟨z, hz, h⟩ ↦ ⟨z, hz, h.symm⟩, fun ⟨z, hz, h⟩ ↦ ⟨z, hz, h.symm⟩⟩ + +private theorem realSpan_eq (V : Submodule ℚ (ι → ℚ)) {k : ℕ} {y : Fin k → (ι → ℤ)} + (hspan : Submodule.span ℚ (Set.range fun i ↦ Rat.piIntCast ι (y i)) = V) : + V.realSpan = + Submodule.span ℝ (Set.range fun i ↦ Rat.piEuclidean ι (Rat.piIntCast ι (y i))) := by + subst hspan + rw [Submodule.realSpan, ← Submodule.map_coe (Rat.piEuclidean ι), Submodule.map_span, + ← Set.range_comp, Submodule.span_span_of_tower ℚ ℝ] + rfl + +variable [Finite ι] + +private theorem linearIndependent_euclidean {k : ℕ} {y : Fin k → (ι → ℤ)} + (hy : LinearIndependent ℚ (fun i ↦ Rat.piIntCast ι (y i))) : + LinearIndependent ℝ fun i ↦ Rat.piEuclidean ι (Rat.piIntCast ι (y i)) := by + have h := (linearIndependent_algebraMap_comp_iff (R := ℚ) (S := ℝ) + (v := fun i ↦ Rat.piIntCast ι (y i))).2 hy + have h' : LinearIndependent ℝ fun i ↦ (fun l ↦ ((y i l : ℤ) : ℝ)) := by + convert h using 2 with i + funext l + simp + exact h'.map' (WithLp.linearEquiv 2 ℝ (ι → ℝ)).symm.toLinearMap + (LinearMap.ker_eq_bot.2 (WithLp.linearEquiv 2 ℝ (ι → ℝ)).symm.injective) + +/-- The construction behind every statement below: a saturated integral basis of `V ∩ ℤ^ι`, read +as an `ℝ`-basis of the real span whose `ℤ`-span is the lattice. -/ +private theorem exists_basis_intLattice (V : Submodule ℚ (ι → ℚ)) : + ∃ (y : Fin (finrank ℚ V) → (ι → ℤ)) (b : Basis (Fin (finrank ℚ V)) ℝ ↥V.realSpan), + (∀ i, (b i : EuclideanSpace ℝ ι) = Rat.piEuclidean ι (Rat.piIntCast ι (y i))) ∧ + V.intLattice = Submodule.span ℤ (Set.range b) ∧ + Submodule.span ℚ (Set.range fun i ↦ Rat.piIntCast ι (y i)) = V ∧ + LinearIndependent ℚ (fun i ↦ Rat.piIntCast ι (y i)) ∧ + Submodule.span ℤ (Set.range y) = V.intPoints := by + classical + obtain ⟨y, hyli, hyspan, hysat⟩ := V.exists_basis_intPoints rfl + have hE : LinearIndependent ℝ fun i ↦ Rat.piEuclidean ι (Rat.piIntCast ι (y i)) := + linearIndependent_euclidean hyli + have hW : V.realSpan = + Submodule.span ℝ (Set.range fun i ↦ Rat.piEuclidean ι (Rat.piIntCast ι (y i))) := + realSpan_eq V hyspan + have hmem : ∀ i, Rat.piEuclidean ι (Rat.piIntCast ι (y i)) ∈ V.realSpan := fun i ↦ by + rw [hW]; exact Submodule.subset_span ⟨i, rfl⟩ + have hliW : LinearIndependent ℝ fun i ↦ (⟨_, hmem i⟩ : ↥V.realSpan) := + LinearIndependent.of_comp V.realSpan.subtype hE + have hspW : ⊤ ≤ Submodule.span ℝ (Set.range fun i ↦ (⟨_, hmem i⟩ : ↥V.realSpan)) := by + rw [← Submodule.map_le_map_iff_of_injective V.realSpan.injective_subtype, + Submodule.map_span, ← Set.range_comp, Submodule.map_subtype_top] + exact le_of_eq hW + refine ⟨y, Basis.mk hliW hspW, fun i ↦ by rw [Basis.coe_mk], ?_, hyspan, hyli, hysat⟩ + rw [Basis.coe_mk, Submodule.intLattice, ← hysat, Submodule.map_span, ← Set.range_comp] + rw [show ((Submodule.span ℤ (Set.range (fun i ↦ (⟨_, hmem i⟩ : ↥V.realSpan))))) = + Submodule.comap ((V.realSpan.subtype).restrictScalars ℤ) + (Submodule.map ((V.realSpan.subtype).restrictScalars ℤ) + (Submodule.span ℤ (Set.range (fun i ↦ (⟨_, hmem i⟩ : ↥V.realSpan))))) from + (Submodule.comap_map_eq_self (by simp)).symm] + congr 1 + rw [Submodule.map_span, ← Set.range_comp] + rfl + +instance (V : Submodule ℚ (ι → ℚ)) : DiscreteTopology V.intLattice := by + let _ : Fintype ι := Fintype.ofFinite ι + obtain ⟨_, b, _, hL, _⟩ := exists_basis_intLattice V + rw [hL] + infer_instance + +/-- The real span of a rational subspace has the dimension of the subspace. -/ +theorem Submodule.finrank_realSpan (V : Submodule ℚ (ι → ℚ)) : + finrank ℝ ↥V.realSpan = finrank ℚ V := by + obtain ⟨_, b, _⟩ := exists_basis_intLattice V + simpa using finrank_eq_card_basis b + +end RealSpan + +section RealSpanZLattice + +variable {ι : Type*} [Fintype ι] + +instance (V : Submodule ℚ (ι → ℚ)) : IsZLattice ℝ V.intLattice := by + obtain ⟨_, b, _, hL, _⟩ := exists_basis_intLattice V + constructor + rw [hL] + exact ZSpan.span_top b + +end RealSpanZLattice + +/-! +### The Arakelov height of a primitive integer tuple +-/ + +/-- **Over `ℚ` the Arakelov height of a tuple of coprime integers is its euclidean norm.** The +finite places contribute `1` — this is Mathlib's `Rat.iSup_finitePlace_apply_eq_one_of_gcd_eq_one` +— and the single infinite place is real, so the archimedean factor is the ℓ² norm with exponent +`1`. Mathlib's `Rat.mulHeight_eq_max_abs_of_gcd_eq_one` is the same statement for the sup norm. -/ +theorem NumberField.arakelovMulHeight_intCast_of_gcd_eq_one {κ : Type*} [Fintype κ] {x : κ → ℤ} + (hx : (Finset.univ : Finset κ).gcd x = 1) : + NumberField.arakelovMulHeight (((↑) : ℤ → ℚ) ∘ x) = Real.sqrt (∑ i, ((x i : ℝ)) ^ 2) := by + have hne : Nonempty κ := by + by_contra h + simp only [not_nonempty_iff] at h + rw [Finset.univ_eq_empty, Finset.gcd_empty] at hx + exact zero_ne_one hx + have hx₀ : ((↑) : ℤ → ℚ) ∘ x ≠ 0 := by + contrapose! hx + rw [Function.comp_eq_zero_iff x Rat.intCast_injective Rat.intCast_zero] at hx + rw [hx, Finset.gcd_eq_zero_iff.mpr (by simp)] + exact zero_ne_one + rw [NumberField.arakelovMulHeight_eq hx₀] + simp only [Function.comp_apply] + have hmult : (default : NumberField.InfinitePlace ℚ).mult = 1 := by + rw [Subsingleton.elim (default : NumberField.InfinitePlace ℚ) Rat.infinitePlace] + exact NumberField.InfinitePlace.IsReal.mult_eq_one Rat.isReal_infinitePlace + rw [finprod_eq_one_of_forall_eq_one (Rat.iSup_finitePlace_apply_eq_one_of_gcd_eq_one · hx), + mul_one, Fintype.prod_unique, hmult] + have hterm : ∀ i : κ, (default : NumberField.InfinitePlace ℚ) ((x i : ℚ)) ^ 2 + = ((x i : ℝ)) ^ 2 := fun i ↦ by + rw [Rat.infinitePlace_apply] + push_cast + rw [sq_abs] + rw [Finset.sum_congr rfl fun i _ ↦ hterm i, Real.sqrt_eq_rpow] + norm_num + +/-! +### The covolume identity +-/ + +section Covolume + +open Matrix exteriorPower + +variable {ι : Type*} [Fintype ι] [LinearOrder ι] + +private theorem plucker_piIntCast (k : ℕ) (y : Fin k → (ι → ℤ)) (s : Set.powersetCard ι k) : + plucker k (fun i ↦ Rat.piIntCast ι (y i)) s = ((plucker k y s : ℤ) : ℚ) := by + rw [show (fun i ↦ Rat.piIntCast ι (y i)) = (fun i ↦ ((Int.castRingHom ℚ) ∘ y i)) from rfl] + exact plucker_map _ k y s + +private theorem plucker_intCast_real (k : ℕ) (y : Fin k → (ι → ℤ)) (s : Set.powersetCard ι k) : + plucker k (fun i ↦ (fun l ↦ ((y i l : ℤ) : ℝ))) s = ((plucker k y s : ℤ) : ℝ) := by + rw [show (fun i ↦ (fun l ↦ ((y i l : ℤ) : ℝ))) = (fun i ↦ ((Int.castRingHom ℝ) ∘ y i)) from rfl] + exact plucker_map _ k y s + +private theorem covolume_eq_of_basis {k : ℕ} {V : Submodule ℚ (ι → ℚ)} + {y : Fin k → (ι → ℤ)} {b : Basis (Fin k) ℝ ↥V.realSpan} + (hb : ∀ i, (b i : EuclideanSpace ℝ ι) = Rat.piEuclidean ι (Rat.piIntCast ι (y i))) + (hL : V.intLattice = Submodule.span ℤ (Set.range b)) + (hspan : Submodule.span ℚ (Set.range fun i ↦ Rat.piIntCast ι (y i)) = V) + (hli : LinearIndependent ℚ (fun i ↦ Rat.piIntCast ι (y i))) + (hgcd : (Finset.univ : Finset (Set.powersetCard ι k)).gcd (plucker k y) = 1) : + ZLattice.covolume V.intLattice = V.arakelovMulHeight := by + classical + have hcov : ZLattice.covolume V.intLattice ^ 2 + = ∑ s : Set.powersetCard ι k, ((plucker k y s : ℤ) : ℝ) ^ 2 := by + rw [hL, ZLattice.covolume_sq_eq_det_gram _ (b.restrictScalars ℤ)] + have hgram : (Matrix.of fun i j ↦ inner ℝ + ((b.restrictScalars ℤ) i : ↥V.realSpan) ((b.restrictScalars ℤ) j : ↥V.realSpan)) + = (Matrix.of fun i l ↦ ((y i l : ℤ) : ℝ)) * + (Matrix.of fun i l ↦ ((y i l : ℤ) : ℝ))ᵀ := by + ext i j + rw [Matrix.mul_apply] + simp only [Basis.restrictScalars_apply, Submodule.coe_inner, Matrix.of_apply, + Matrix.transpose_apply] + rw [PiLp.inner_apply] + refine Finset.sum_congr rfl fun l _ ↦ ?_ + rw [hb i, hb j] + simp [RCLike.inner_apply, mul_comm] + rw [hgram, Matrix.det_mul_transpose_self_eq_sum_sq] + refine Finset.sum_congr rfl fun s _ ↦ ?_ + rw [show (Matrix.of fun i l ↦ ((y i l : ℤ) : ℝ)).row + = (fun i ↦ (fun l ↦ ((y i l : ℤ) : ℝ))) from rfl, plucker_intCast_real] + have hH : V.arakelovMulHeight + = Real.sqrt (∑ s : Set.powersetCard ι k, ((plucker k y s : ℤ) : ℝ) ^ 2) := by + rw [← hspan, Submodule.arakelovMulHeight_span_range hli, + show plucker k (fun i ↦ Rat.piIntCast ι (y i)) = ((↑) : ℤ → ℚ) ∘ (plucker k y) from + funext fun s ↦ plucker_piIntCast k y s] + exact NumberField.arakelovMulHeight_intCast_of_gcd_eq_one hgcd + rw [hH, ← hcov, Real.sqrt_sq (ZLattice.covolume_pos _ _).le] + +/-- **The covolume identity over `ℚ`** (Layer 4.3, the case `K = ℚ`; W. M. Schmidt 1967, §3, +Theorem 1). The lattice of integral points of a rational subspace has covolume the Arakelov height +of the subspace. -/ +theorem Submodule.covolume_intLattice (V : Submodule ℚ (ι → ℚ)) : + ZLattice.covolume V.intLattice = V.arakelovMulHeight := by + obtain ⟨y, b, hb, hL, hspan, hli, hsat⟩ := exists_basis_intLattice V + exact covolume_eq_of_basis hb hL hspan hli (V.gcd_plucker_eq_one hli hspan hsat) + +/-- **The rank-one case.** The lattice cut out by the line through a nonzero rational point has +covolume the Arakelov height of that point — for a primitive integer point, its euclidean norm. -/ +theorem Submodule.covolume_intLattice_span_singleton {x : ι → ℚ} (hx : x ≠ 0) : + ZLattice.covolume (Submodule.span ℚ {x}).intLattice = NumberField.arakelovMulHeight x := by + rw [Submodule.covolume_intLattice, Submodule.arakelovMulHeight_span_singleton hx] + +end Covolume + +end diff --git a/LeanPool/CCDN/ArithmeticHeights/RowSpace.lean b/LeanPool/CCDN/ArithmeticHeights/RowSpace.lean new file mode 100644 index 0000000000..020574913f --- /dev/null +++ b/LeanPool/CCDN/ArithmeticHeights/RowSpace.lean @@ -0,0 +1,490 @@ +/- +Copyright (c) 2026 Ralf Stephan. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Ralf Stephan +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.ArithmeticHeights.Subspace +public import Mathlib.LinearAlgebra.Matrix.NonsingularInverse +public import Mathlib.LinearAlgebra.Matrix.Rank + +-- Only the worked examples below mention `Matrix.mulHeight`, the height of the entries of +-- Layer 2.5, and they mention it in order to distinguish it from the height of the row space. +import LeanPool.CCDN.ArithmeticHeights.Matrix + +/-! +# The row space of a matrix and the height of its maximal minors + +The dictionary between a matrix of full row rank and the subspace it spans: the Plücker +coordinates of the row space are the maximal minors, so the height of the row space is the height +of the tuple of minors, and left multiplication by an invertible matrix — a row operation — leaves +it unchanged. That invariance is what distinguishes the height of the row space from +`Matrix.mulHeight`, the height of the entries of Layer 2.5, and it is the reason Bombieri–Vaaler +can improve on the naïve Siegel bound. + +## Main definitions + +None, deliberately. The row space of `A` is `Submodule.span R (Set.range A.row)`, which is +Mathlib's `LinearMap.range A.vecMulLinear` by `range_vecMulLinear`, and the tuple of maximal minors +is `exteriorPower.plucker m A.row` of Layer 3.1. A third name for either would force every later +statement to choose between three spellings of one object. + +## Main results + +* `Matrix.plucker_row_eq_det_submatrix`: the Plücker coordinate of `A` at a set `s` of columns is + the determinant of the submatrix on those columns, taken in increasing order. This is the index + identification, and `Matrix.plucker_row_eq_det_submatrix_orderIso` restates it with + `Finset.orderIsoOfFin` in place of `Finset.orderEmbOfFin`. +* `Matrix.plucker_row_mul`: `plucker m (U * A).row = U.det • plucker m A.row`, for *every* square + `U` and over any commutative ring — the determinant identity underlying the height invariance + in Bombieri–Vaaler (2.5). +* `Matrix.span_range_row_mul`: for `U` of unit determinant the row space itself is unchanged, whence + `Matrix.mulHeight_span_range_row_mul` and its Arakelov and absolute companions. +* `Matrix.mulHeight_span_range_row` and `Matrix.arakelovMulHeight_span_range_row`: the height of the + row space is the height of the tuple of maximal minors in each chosen normalization. + The relative Arakelov version corresponds to Bombieri–Vaaler's absolute `H(A)` after raising + to the number-field degree. These row-space heights differ from `Matrix.mulHeight A`. +* `Matrix.linearIndependent_row_iff_rank_eq` and + `Matrix.linearIndependent_row_iff_plucker_row_ne_zero`: full row rank, in the two forms that the + statements above take as a hypothesis. + +## Implementation notes + +The invariance under row operations is not a theorem about heights. `Matrix.span_range_row_mul` is +an equality of submodules, and every height statement below is one `congrArg` away from it, +because Layer 3.2 defines the height of a subspace as a function of the subspace alone. The +mathematical content sits in that span equality and, quantitatively, in `Matrix.plucker_row_mul`. + +`Matrix.plucker_row_mul` needs no hypothesis on `U`, no field, and no expansion of an alternating +map along a matrix — which is fortunate, since Mathlib has no such expansion. The proof is Layer +3.1 used twice: `exteriorPower.plucker_fin_eq_det` says that the single Plücker coordinate of a +square family is its determinant, so the wedge of the rows of `U` is `U.det` times the wedge of the +standard basis; and `exteriorPower.plucker_eq_smul_iff` transports a proportionality of coordinate +tuples to one of wedges. `exteriorPower.map` then pushes the relation along `A.vecMulLinear`, +whose composition with the rows of `U` is the rows of `U * A` — by `rfl`. + +## References + +E. Bombieri and W. Gubler, *Heights in Diophantine Geometry*, Cambridge University Press (2006), +Remark 2.8.7 for the dictionary, Definition 2.8.11 for the Arakelov normalization of the row-space +height, and 2.9.8 for the height of the entries, which is a different number and is Layer 2.5 here. +The book's `h_Ar` is absolute logarithmic; the relative multiplicative Arakelov height here is +`exp([K : ℚ] · h_Ar(A))` in that convention. +E. Bombieri and J. Vaaler, "On Siegel's lemma", *Inventiones Mathematicae* **73** (1983), 11–32, +equation (2.5), where the invariance is the statement that the height is intrinsic on the +Grassmannian. W. M. Schmidt, "On heights of algebraic subspaces and diophantine approximations", +*Annals of Mathematics* **85** (1967), 430–472, §1. + +This is Layer 3.3 of the `ArithmeticHeights` roadmap. +-/ + +public section + +namespace Matrix + +open Module Submodule exteriorPower + +/-! +### The Plücker coordinates of a matrix are its maximal minors +-/ + +section Minors + +variable {R : Type*} [CommRing R] {ι : Type*} [Fintype ι] [LinearOrder ι] {m : ℕ} + +/-- **The Plücker coordinates of a matrix are its maximal minors.** The coordinate at a set `s` of +`m` columns is the determinant of the submatrix on those columns, enumerated in increasing order. +The enumeration is Mathlib's `Finset.orderEmbOfFin`, the one `Set.powersetCard.ofFinEmbEquiv` +already fixes, so no later statement has to choose it again. -/ +theorem plucker_row_eq_det_submatrix (A : Matrix (Fin m) ι R) (s : Set.powersetCard ι m) : + plucker m A.row s = + (A.submatrix id ((s : Finset ι).orderEmbOfFin (Set.powersetCard.card_eq s))).det := by + rw [plucker_apply, Set.powersetCard.ofFinEmbEquiv_symm_apply] + rfl + +/-- `Matrix.plucker_row_eq_det_submatrix` with `Finset.orderIsoOfFin` in place of +`Finset.orderEmbOfFin`; the two enumerations agree by `Finset.coe_orderIsoOfFin_apply`. -/ +theorem plucker_row_eq_det_submatrix_orderIso (A : Matrix (Fin m) ι R) + (s : Set.powersetCard ι m) : + plucker m A.row s = + (A.submatrix id fun j ↦ + (((s : Finset ι).orderIsoOfFin (Set.powersetCard.card_eq s) j : ι))).det := by + rw [plucker_row_eq_det_submatrix] + simp only [Finset.coe_orderIsoOfFin_apply] + +end Minors + +/-! +### Row operations multiply the Plücker coordinates by a determinant +-/ + +section RowOps + +variable {R : Type*} [CommRing R] {ι : Type*} {m : ℕ} + +/-- The rows of `U * A` are the rows of `U` read through `A`. -/ +theorem row_mul_eq_comp (U : Matrix (Fin m) (Fin m) R) (A : Matrix (Fin m) ι R) : + (U * A).row = ⇑A.vecMulLinear ∘ U.row := + rfl + +/-- The rows of the identity matrix are the standard basis. -/ +theorem row_one_eq_basisFun : + (1 : Matrix (Fin m) (Fin m) R).row = ⇑(Pi.basisFun R (Fin m)) := by + funext i j + simp [Matrix.one_apply, Pi.single_apply, eq_comm] + +/-- **The wedge of the rows of a square matrix is its determinant times the wedge of the standard +basis.** This is `exteriorPower.plucker_fin_eq_det` read through the injectivity of the coordinate +map on the top exterior power, which is a line. -/ +theorem ιMulti_row_eq_det_smul (U : Matrix (Fin m) (Fin m) R) : + ιMulti R m U.row = U.det • ιMulti R m (1 : Matrix (Fin m) (Fin m) R).row := by + refine (plucker_eq_smul_iff ..).1 ?_ + funext s + rw [plucker_fin_eq_det, Pi.smul_apply, plucker_fin_eq_det, smul_eq_mul, + show of U.row = U from rfl, show of (1 : Matrix (Fin m) (Fin m) R).row = 1 from rfl, + det_one, mul_one] + +/-- **A row operation multiplies the wedge of the rows by the determinant.** -/ +theorem ιMulti_row_mul (U : Matrix (Fin m) (Fin m) R) (A : Matrix (Fin m) ι R) : + ιMulti R m (U * A).row = U.det • ιMulti R m A.row := by + rw [row_mul_eq_comp U, ← map_apply_ιMulti, ιMulti_row_eq_det_smul U, map_smul, + map_apply_ιMulti, ← row_mul_eq_comp 1, Matrix.one_mul] + +end RowOps + +section Plucker + +variable {R : Type*} [CommRing R] {ι : Type*} [Fintype ι] [LinearOrder ι] {m : ℕ} + +/-- **A row operation multiplies the tuple of maximal minors by the determinant.** This identity +underlies the height invariance in Bombieri–Vaaler (2.5). No hypothesis on `U` is needed: +for `U` singular both sides vanish. -/ +theorem plucker_row_mul (U : Matrix (Fin m) (Fin m) R) (A : Matrix (Fin m) ι R) : + plucker m (U * A).row = U.det • plucker m A.row := + (plucker_eq_smul_iff ..).2 (ιMulti_row_mul U A) + +end Plucker + +/-! +### The row space +-/ + +section Span + +variable {R : Type*} [CommRing R] {ι : Type*} {m : ℕ} +variable {n : Type*} [Fintype n] [DecidableEq n] + +omit [DecidableEq n] in +/-- Every row of `U * A` lies in the row space of `A`. -/ +theorem span_range_row_mul_le (U : Matrix n n R) (A : Matrix n ι R) : + span R (Set.range (U * A).row) ≤ span R (Set.range A.row) := by + rw [← range_vecMulLinear A, Submodule.span_le] + rintro _ ⟨i, rfl⟩ + exact ⟨U.row i, rfl⟩ + +/-- **A row operation does not change the row space.** Everything below about the height of a row +space is this equality followed by a `congrArg`, the height of Layer 3.2 being a function of the +subspace alone. The row index is an arbitrary finite type: Layer 5.6 multiplies by a +block-diagonal matrix whose rows are indexed by a product. -/ +theorem span_range_row_mul {U : Matrix n n R} (hU : IsUnit U.det) + (A : Matrix n ι R) : + span R (Set.range (U * A).row) = span R (Set.range A.row) := by + refine le_antisymm (span_range_row_mul_le U A) ?_ + calc span R (Set.range A.row) + = span R (Set.range (U⁻¹ * (U * A)).row) := by + rw [← Matrix.mul_assoc, nonsing_inv_mul U hU, Matrix.one_mul] + _ ≤ span R (Set.range (U * A).row) := span_range_row_mul_le _ _ + +@[simp] +theorem span_range_row_one : span R (Set.range (1 : Matrix (Fin m) (Fin m) R).row) = ⊤ := by + rw [row_one_eq_basisFun] + exact (Pi.basisFun R (Fin m)).span_eq + +/-- A square matrix of unit determinant has the whole space as its row space. -/ +theorem span_range_row_eq_top_of_isUnit {U : Matrix (Fin m) (Fin m) R} (hU : IsUnit U.det) : + span R (Set.range U.row) = ⊤ := by + have h := span_range_row_mul hU (1 : Matrix (Fin m) (Fin m) R) + rwa [Matrix.mul_one, span_range_row_one] at h + +end Span + +/-! +### Full row rank +-/ + +section Rank + +variable {K : Type*} [Field K] {ι : Type*} [Fintype ι] {m : ℕ} + +/-- **Full row rank**, in the form Mathlib's `Matrix.rank` takes. -/ +theorem linearIndependent_row_iff_rank_eq (A : Matrix (Fin m) ι K) : + LinearIndependent K A.row ↔ A.rank = m := by + rw [rank_eq_finrank_span_row, linearIndependent_iff_card_eq_finrank_span, Fintype.card_fin, + Set.finrank] + exact eq_comm + +/-- **A matrix has a maximal independent family of rows, of size its rank**, spanning the same row +space: `f` picks out `R = rank A` of the rows, the submatrix on them has independent rows, and its +row space is that of `A`. This is what lets a bound in terms of the rows be paid `rank A` times +rather than once per row. -/ +theorem exists_submatrix_row_linearIndependent (A : Matrix (Fin m) ι K) : + ∃ (R : ℕ) (f : Fin R → Fin m), A.rank = R ∧ + LinearIndependent K (A.submatrix f id).row ∧ + span K (Set.range (A.submatrix f id).row) = span K (Set.range A.row) := by + classical + obtain ⟨b, -, -, hsp, hind⟩ := + exists_linearIndepOn_extension (K := K) (v := A.row) (linearIndepOn_empty K A.row) + (Set.empty_subset (Set.univ : Set (Fin m))) + set s : Finset (Fin m) := b.toFinset with hs + set R : ℕ := s.card with hR + set E : Fin R ≃ ↥b := + (s.orderIsoOfFin hR.symm).toEquiv.trans (Equiv.subtypeEquivRight fun x ↦ Set.mem_toFinset) + with hE + have hindB : LinearIndependent K (A.submatrix (fun i ↦ (E i : Fin m)) id).row := + hind.comp E E.injective + have hrange : Set.range (fun i : Fin R ↦ ((E i : Fin m))) = b := by + rw [show (fun i : Fin R ↦ ((E i : Fin m))) = Subtype.val ∘ E from rfl, + Set.range_comp, E.range_eq_univ, Set.image_univ, Subtype.range_coe] + have himg : Set.range (A.submatrix (fun i ↦ (E i : Fin m)) id).row = A.row '' b := by + rw [show (A.submatrix (fun i ↦ (E i : Fin m)) id).row + = A.row ∘ (fun i : Fin R ↦ ((E i : Fin m))) from rfl, Set.range_comp, hrange] + have hspanB : span K (Set.range (A.submatrix (fun i ↦ (E i : Fin m)) id).row) + = span K (Set.range A.row) := by + rw [himg] + refine le_antisymm (span_mono ?_) (span_le.2 ?_) + · rintro _ ⟨x, -, rfl⟩ + exact ⟨x, rfl⟩ + · rw [← Set.image_univ] + exact hsp + refine ⟨R, fun i ↦ (E i : Fin m), ?_, hindB, hspanB⟩ + rw [rank_eq_finrank_span_row, ← hspanB, finrank_span_eq_card hindB, Fintype.card_fin] + +/-- **Full row rank, in minors**: a matrix has independent rows exactly when some maximal minor is +nonzero. This is `exteriorPower.plucker_eq_zero_iff` of Layer 3.1, and it is what makes the height +of the row space a statement about a nonzero tuple. -/ +theorem linearIndependent_row_iff_plucker_row_ne_zero [LinearOrder ι] (A : Matrix (Fin m) ι K) : + LinearIndependent K A.row ↔ plucker m A.row ≠ 0 := by + rw [ne_eq, plucker_eq_zero_iff, not_not] + +end Rank + +/-! +### The height of the row space +-/ + +section Height + +variable {K : Type*} [Field K] [Height.AdmissibleAbsValues K] {ι : Type*} [Fintype ι] + [LinearOrder ι] {m : ℕ} {A : Matrix (Fin m) ι K} {U : Matrix (Fin m) (Fin m) K} + +/-- **The height of the row space is the height of the tuple of maximal minors** (Bombieri–Gubler, +Remark 2.8.7). This uses sup norms, while Bombieri–Vaaler's `H(A)` uses the absolute Arakelov +height of the minors. `Matrix.mulHeight A` instead measures the entries. -/ +theorem mulHeight_span_range_row (hA : LinearIndependent K A.row) : + (span K (Set.range A.row)).mulHeight = + Height.mulHeight fun s : Set.powersetCard ι m ↦ + (A.submatrix id ((s : Finset ι).orderEmbOfFin (Set.powersetCard.card_eq s))).det := by + rw [Submodule.mulHeight_span_range hA] + exact congrArg _ (funext (plucker_row_eq_det_submatrix A)) + +/-- The logarithmic form of `Matrix.mulHeight_span_range_row`. -/ +theorem logHeight_span_range_row (hA : LinearIndependent K A.row) : + (span K (Set.range A.row)).logHeight = + Height.logHeight fun s : Set.powersetCard ι m ↦ + (A.submatrix id ((s : Finset ι).orderEmbOfFin (Set.powersetCard.card_eq s))).det := by + rw [Submodule.logHeight_span_range hA] + exact congrArg _ (funext (plucker_row_eq_det_submatrix A)) + +/-- **The sup-norm height of the row space is invariant under row operations.** This is the +sup-norm counterpart of the Arakelov height invariance in Bombieri–Vaaler (2.5). -/ +theorem mulHeight_span_range_row_mul (hU : IsUnit U.det) (A : Matrix (Fin m) ι K) : + (span K (Set.range (U * A).row)).mulHeight = (span K (Set.range A.row)).mulHeight := + congrArg _ (span_range_row_mul hU A) + +/-- The logarithmic form of `Matrix.mulHeight_span_range_row_mul`. -/ +theorem logHeight_span_range_row_mul (hU : IsUnit U.det) (A : Matrix (Fin m) ι K) : + (span K (Set.range (U * A).row)).logHeight = (span K (Set.range A.row)).logHeight := + congrArg _ (span_range_row_mul hU A) + +/-- The same invariance read on the tuple of minors rather than on the subspace: a row operation +scales it by `U.det`, and the projective height does not see a scalar. -/ +theorem mulHeight_plucker_row_mul (hU : IsUnit U.det) (A : Matrix (Fin m) ι K) : + Height.mulHeight (plucker m (U * A).row) = Height.mulHeight (plucker m A.row) := by + rw [plucker_row_mul, Height.mulHeight_smul_eq_mulHeight _ hU.ne_zero] + +/-- The logarithmic form of `Matrix.mulHeight_plucker_row_mul`. -/ +theorem logHeight_plucker_row_mul (hU : IsUnit U.det) (A : Matrix (Fin m) ι K) : + Height.logHeight (plucker m (U * A).row) = Height.logHeight (plucker m A.row) := + congrArg Real.log (mulHeight_plucker_row_mul hU A) + +end Height + +section ArakelovHeight + +variable {K : Type*} [Field K] [NumberField K] {ι : Type*} [Fintype ι] [LinearOrder ι] {m : ℕ} + {A : Matrix (Fin m) ι K} {U : Matrix (Fin m) (Fin m) K} + +/-- **The relative Arakelov height of the row space is that of its tuple of maximal minors.** +This is `exp([K : ℚ] · h_Ar(A))` for the absolute logarithmic matrix height of +Bombieri–Gubler Definition 2.8.11. The Siegel-lemma constants of Layer 5 use the relative +multiplicative normalization here. -/ +theorem arakelovMulHeight_span_range_row (hA : LinearIndependent K A.row) : + (span K (Set.range A.row)).arakelovMulHeight = + NumberField.arakelovMulHeight fun s : Set.powersetCard ι m ↦ + (A.submatrix id ((s : Finset ι).orderEmbOfFin (Set.powersetCard.card_eq s))).det := by + rw [Submodule.arakelovMulHeight_span_range hA] + exact congrArg _ (funext (plucker_row_eq_det_submatrix A)) + +/-- The logarithmic form of `Matrix.arakelovMulHeight_span_range_row`. -/ +theorem arakelovLogHeight_span_range_row (hA : LinearIndependent K A.row) : + (span K (Set.range A.row)).arakelovLogHeight = + NumberField.arakelovLogHeight fun s : Set.powersetCard ι m ↦ + (A.submatrix id ((s : Finset ι).orderEmbOfFin (Set.powersetCard.card_eq s))).det := by + rw [Submodule.arakelovLogHeight_span_range hA] + exact congrArg _ (funext (plucker_row_eq_det_submatrix A)) + +theorem arakelovMulHeight_span_range_row_mul (hU : IsUnit U.det) (A : Matrix (Fin m) ι K) : + (span K (Set.range (U * A).row)).arakelovMulHeight = + (span K (Set.range A.row)).arakelovMulHeight := + congrArg _ (span_range_row_mul hU A) + +theorem arakelovLogHeight_span_range_row_mul (hU : IsUnit U.det) (A : Matrix (Fin m) ι K) : + (span K (Set.range (U * A).row)).arakelovLogHeight = + (span K (Set.range A.row)).arakelovLogHeight := + congrArg _ (span_range_row_mul hU A) + +end ArakelovHeight + +section AbsoluteHeight + +variable {K : Type*} [Field K] [CharZero K] [Algebra.IsAlgebraic ℚ K] {ι : Type*} [Fintype ι] + [LinearOrder ι] {m : ℕ} {A : Matrix (Fin m) ι K} {U : Matrix (Fin m) (Fin m) K} + +/-- **The absolute sup-norm height of the row space is that of its tuple of maximal minors.** +Bombieri–Vaaler's `H(A)` uses the Arakelov normalization instead. -/ +theorem absMulHeight_span_range_row (hA : LinearIndependent K A.row) : + (span K (Set.range A.row)).absMulHeight = + NumberField.absMulHeight fun s : Set.powersetCard ι m ↦ + (A.submatrix id ((s : Finset ι).orderEmbOfFin (Set.powersetCard.card_eq s))).det := by + rw [Submodule.absMulHeight_span_range hA] + exact congrArg _ (funext (plucker_row_eq_det_submatrix A)) + +/-- The logarithmic form of `Matrix.absMulHeight_span_range_row`. -/ +theorem absLogHeight_span_range_row (hA : LinearIndependent K A.row) : + (span K (Set.range A.row)).absLogHeight = + NumberField.absLogHeight fun s : Set.powersetCard ι m ↦ + (A.submatrix id ((s : Finset ι).orderEmbOfFin (Set.powersetCard.card_eq s))).det := by + rw [Submodule.absLogHeight_span_range hA] + exact congrArg _ (funext (plucker_row_eq_det_submatrix A)) + +theorem absMulHeight_span_range_row_mul (hU : IsUnit U.det) (A : Matrix (Fin m) ι K) : + (span K (Set.range (U * A).row)).absMulHeight = (span K (Set.range A.row)).absMulHeight := + congrArg _ (span_range_row_mul hU A) + +theorem absLogHeight_span_range_row_mul (hU : IsUnit U.det) (A : Matrix (Fin m) ι K) : + (span K (Set.range (U * A).row)).absLogHeight = (span K (Set.range A.row)).absLogHeight := + congrArg _ (span_range_row_mul hU A) + +end AbsoluteHeight + +end Matrix + +/-! +### Worked examples +-/ + +section Examples + +open Matrix Submodule exteriorPower + +/-- **Acceptance test: the index identification.** The three maximal minors of a `2 × 3` matrix, +at the three two-element column sets. The columns of each set are taken in increasing order, so +the minor at `{1, 2}` is `0 · 3 − 2 · 1 = −2` and not its negative; a construction that enumerated +a column set in any other order is refuted here. -/ +example : + plucker 2 (Matrix.of ![![1, 0, 2], ![0, 1, 3]] : Matrix (Fin 2) (Fin 3) ℚ).row + ⟨{0, 1}, Set.powersetCard.mem_iff.2 (by decide)⟩ = 1 ∧ + plucker 2 (Matrix.of ![![1, 0, 2], ![0, 1, 3]] : Matrix (Fin 2) (Fin 3) ℚ).row + ⟨{0, 2}, Set.powersetCard.mem_iff.2 (by decide)⟩ = 3 ∧ + plucker 2 (Matrix.of ![![1, 0, 2], ![0, 1, 3]] : Matrix (Fin 2) (Fin 3) ℚ).row + ⟨{1, 2}, Set.powersetCard.mem_iff.2 (by decide)⟩ = -2 := by + have h01 : ⇑(({0, 1} : Finset (Fin 3)).orderEmbOfFin (by decide)) = ![0, 1] := + (Finset.orderEmbOfFin_unique _ (by decide) (by decide)).symm + have h02 : ⇑(({0, 2} : Finset (Fin 3)).orderEmbOfFin (by decide)) = ![0, 2] := + (Finset.orderEmbOfFin_unique _ (by decide) (by decide)).symm + have h12 : ⇑(({1, 2} : Finset (Fin 3)).orderEmbOfFin (by decide)) = ![1, 2] := + (Finset.orderEmbOfFin_unique _ (by decide) (by decide)).symm + refine ⟨?_, ?_, ?_⟩ + · rw [Matrix.plucker_row_eq_det_submatrix, h01, Matrix.det_fin_two] + norm_num + · rw [Matrix.plucker_row_eq_det_submatrix, h02, Matrix.det_fin_two] + norm_num + · rw [Matrix.plucker_row_eq_det_submatrix, h12, Matrix.det_fin_two] + norm_num + +/-- **Rejection test: the height of the entries is not a height on the Grassmannian.** A row +operation leaves the row space, and hence its height, alone, while it changes the height of the +entries: adding three times the first row of the identity to the second turns a matrix of height +`1` into one of height `3`. This is why the naïve Siegel bound, which is stated in terms of the +entries, is not invariant, and why Bombieri–Vaaler state theirs in terms of the row space. -/ +example : ∃ U A : Matrix (Fin 2) (Fin 2) ℚ, IsUnit U.det ∧ + (span ℚ (Set.range (U * A).row)).mulHeight = (span ℚ (Set.range A.row)).mulHeight ∧ + Matrix.mulHeight (U * A) ≠ Matrix.mulHeight A := by + obtain ⟨U, hUdef⟩ : ∃ U : Matrix (Fin 2) (Fin 2) ℚ, ∀ i j, U i j = ![![1, 0], ![3, 1]] i j := + ⟨Matrix.of ![![1, 0], ![3, 1]], fun _ _ ↦ rfl⟩ + have hU : IsUnit U.det := by + rw [Matrix.det_fin_two] + simp only [hUdef] + norm_num + have h1 : Matrix.mulHeight (1 : Matrix (Fin 2) (Fin 2) ℚ) = 1 := by + have hx : Matrix.mulHeight (1 : Matrix (Fin 2) (Fin 2) ℚ) + = Height.mulHeight (((↑) : ℤ → ℚ) ∘ + fun q : Fin 2 × Fin 2 ↦ (1 : Matrix (Fin 2) (Fin 2) ℤ) q.1 q.2) := by + refine congrArg Height.mulHeight (funext fun q ↦ ?_) + obtain ⟨i, j⟩ := q + fin_cases i <;> fin_cases j <;> simp + have hs : (⨆ q : Fin 2 × Fin 2, |(1 : Matrix (Fin 2) (Fin 2) ℤ) q.1 q.2|) = 1 := by + refine le_antisymm (ciSup_le fun q ↦ ?_) + (le_ciSup_of_le (Finite.bddAbove_range _) (0, 0) (by simp)) + obtain ⟨i, j⟩ := q + fin_cases i <;> fin_cases j <;> simp + rw [hx, Rat.mulHeight_eq_max_abs_of_gcd_eq_one (by decide), hs] + norm_num + have h3 : Matrix.mulHeight U = 3 := by + have hx : Matrix.mulHeight U = Height.mulHeight (((↑) : ℤ → ℚ) ∘ + fun q : Fin 2 × Fin 2 ↦ (![![1, 0], ![3, 1]] : Matrix (Fin 2) (Fin 2) ℤ) q.1 q.2) := by + refine congrArg Height.mulHeight (funext fun q ↦ ?_) + obtain ⟨i, j⟩ := q + rw [hUdef] + fin_cases i <;> fin_cases j <;> simp + have hs : (⨆ q : Fin 2 × Fin 2, |(![![1, 0], ![3, 1]] : Matrix (Fin 2) (Fin 2) ℤ) q.1 q.2|) + = 3 := by + refine le_antisymm (ciSup_le fun q ↦ ?_) + (le_ciSup_of_le (Finite.bddAbove_range _) (1, 0) (by norm_num)) + obtain ⟨i, j⟩ := q + fin_cases i <;> fin_cases j <;> norm_num + rw [hx, Rat.mulHeight_eq_max_abs_of_gcd_eq_one (by decide), hs] + norm_num + refine ⟨U, 1, hU, Matrix.mulHeight_span_range_row_mul hU 1, ?_⟩ + rw [Matrix.mul_one, h1, h3] + norm_num + +/-- **Conformance.** The milestone's three statements: the height of the row space is the height of +the tuple of maximal minors, it is invariant under row operations, and the two Mathlib spellings of +the row space agree. -/ +example {K : Type*} [Field K] [Height.AdmissibleAbsValues K] {ι : Type*} [Fintype ι] + [LinearOrder ι] {m : ℕ} (A : Matrix (Fin m) ι K) (hA : LinearIndependent K A.row) + {U : Matrix (Fin m) (Fin m) K} (hU : IsUnit U.det) : + (span K (Set.range A.row)).mulHeight = Height.mulHeight (plucker m A.row) ∧ + (span K (Set.range (U * A).row)).mulHeight = (span K (Set.range A.row)).mulHeight ∧ + LinearMap.range A.vecMulLinear = span K (Set.range A.row) := + ⟨Submodule.mulHeight_span_range hA, Matrix.mulHeight_span_range_row_mul hU A, + range_vecMulLinear A⟩ + +end Examples + +end diff --git a/LeanPool/CCDN/ArithmeticHeights/Siegel.lean b/LeanPool/CCDN/ArithmeticHeights/Siegel.lean new file mode 100644 index 0000000000..e2e466e49f --- /dev/null +++ b/LeanPool/CCDN/ArithmeticHeights/Siegel.lean @@ -0,0 +1,342 @@ +/- +Copyright (c) 2026 Ralf Stephan. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Ralf Stephan +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.ArithmeticHeights.Absolute +public import Mathlib.NumberTheory.NumberField.House +public import Mathlib.NumberTheory.SiegelsLemma + +/-! +# Siegel's lemma, in height form + +Mathlib proves the classical Siegel lemma as a statement about the **sup norm**: a nonzero +`M × N` integer matrix with `M < N` kills a nonzero integer vector of norm at most +`(N ‖A‖) ^ (M / (N - M))`. This file restates it in the vocabulary of heights, which is what the +rest of the roadmap consumes; the translation between the two normalizations is isolated as a +lemma of its own, the exponent is shown to be sharp, and the number-field corollary is read off +Mathlib's `house`-normalized version the same way. + +## Main results + +* `Rat.gcd_mul_mulHeight_intCast`: the translation. For a nonzero integer tuple, + `gcd x * H(x) = max i, |x i|`; `Rat.mulHeight_intCast_le` is the inequality it is used through. +* `Int.Matrix.exists_ne_zero_mulVec_eq_zero_iSup_abs_le` and + `Int.Matrix.exists_ne_zero_mulVec_eq_zero_mulHeight_le`: Siegel's lemma with an explicit bound + `B` on the entries, in the sup norm and in the height, both at `(N B) ^ (M / (N - M))`. +* `Int.Matrix.exists_forall_pow_le_iSup_abs`: the exponent `M / (N - M)` is sharp. +* `NumberField.absMulHeight_le_iSup_house`: the absolute height of a tuple of algebraic integers + is at most the largest house of its entries. +* `NumberField.exists_forall_exists_ne_zero_mulVec_eq_zero_absMulHeight_le`: Siegel's lemma over a + number field in height form, Bombieri–Gubler's Corollary 2.9.2. + +## Implementation notes + +⚠ **The height form is not a strengthening, because the content can always be divided out.** The +height of an integer tuple is its sup norm divided by the greatest common divisor of its entries, +so `H(x) ≤ max i, |x i|` with equality exactly on primitive tuples. That makes the height bound +formally weaker-looking hypotheses and formally stronger conclusions, but nothing is gained: the +primitive multiple of a kernel vector is again a kernel vector, and it is the one on which the two +statements coincide. What the translation buys is the *vocabulary*: everything downstream of +Layer 1 is stated for `Height.mulHeight`, and a sup norm cannot be fed to it. + +⚠ **The exponent is sharp already at `N = M + 1`, and only the factor `N ^ (M / (N - M))` is +slack.** The equations `B xᵢ = xᵢ₊₁` have kernel the line through `(1, B, B², …, B ^ M)`, whose +nonzero integer points all have sup norm at least `B ^ M`, against the bound `((M + 1) B) ^ M`. +Taking `k` such blocks side by side realizes every exponent `M = k M / (k (M + 1) - k M)`, so the +sharpness is not an artifact of `N - M = 1`. The bound is nevertheless not *invariant*: it changes +under `A ↦ U A` for `U ∈ GL_M(ℤ)`, which leaves the kernel alone, and removing that defect is the +whole content of Layer 5.2. + +⚠ **Passing from the house to the height costs no constant at all.** For a tuple of algebraic +integers every finite local factor is at most `1` — the finite part of the height is the inverse +norm of the ideal the entries generate, and that norm is a positive integer — while each infinite +place is the modulus of a conjugate and so is dominated by the house. The infinite factors carry +the weights `mult v`, which sum to `[K : ℚ]`, and that is exactly the root the absolute +normalization takes. So `H_abs(x) ≤ max l, house (x l)` with no degree factor, and Mathlib's +number-field Siegel bound transfers verbatim. + +⚠ **Mathlib's number-field constant is `private`.** `c₁ K` of +`Mathlib/NumberTheory/NumberField/House.lean` cannot be named from outside that file, so the +restatement has to quantify over it existentially — which is also the shape Bombieri–Gubler state +the corollary in — and the witness is supplied by unification against the theorem it is proved +from. + +## References + +E. Bombieri and W. Gubler, *Heights in Diophantine Geometry*, Cambridge University Press (2006), +Lemma 2.9.1 and Corollary 2.9.2. + +M. Hindry and J. H. Silverman, *Diophantine Geometry: An Introduction*, Springer (2000), +Lemma D.4.1, which is the statement Mathlib's file is proved against. + +This is Layer 5.1 of the `ArithmeticHeights` roadmap. +-/ + +public section + +open Finset Height Matrix + +/-! ### The sup norm of an integer tuple -/ + +/-- The sup norm of an integer tuple is the maximum of the absolute values of its entries. The +left-hand side is the shape Mathlib's Siegel lemma is stated in, the right-hand side the shape +every height statement is stated in. -/ +theorem Int.pi_norm_eq_iSup_abs {ι : Type*} [Fintype ι] [Nonempty ι] (x : ι → ℤ) : + ‖x‖ = ((⨆ i, |x i| : ℤ) : ℝ) := by + obtain ⟨i₀, hi₀⟩ := exists_eq_ciSup_of_finite (f := fun i ↦ |x i|) + have hb : ∀ i, |x i| ≤ ⨆ j, |x j| := fun i ↦ + le_ciSup (f := fun j ↦ |x j|) (Finite.bddAbove_range _) i + refine le_antisymm (pi_norm_le_iff_of_nonneg ?_ |>.2 fun i ↦ ?_) ?_ + · exact_mod_cast (abs_nonneg (x i₀)).trans (hb i₀) + · rw [Int.norm_eq_abs, ← Int.cast_abs, Int.cast_le] + exact hb i + · rw [← hi₀, Int.cast_abs, ← Int.norm_eq_abs] + exact norm_le_pi_norm x i₀ + +namespace Rat + +variable {ι : Type*} [Nonempty ι] {x : ι → ℤ} + +/-- Multiplication by a nonnegative integer commutes with a finite supremum. -/ +private lemma iSup_const_mul [Finite ι] {g : ℤ} (hg : 0 ≤ g) (f : ι → ℤ) : + ⨆ i, g * f i = g * ⨆ i, f i := by + obtain ⟨i₀, hi₀⟩ := exists_eq_ciSup_of_finite (f := fun i ↦ g * f i) + obtain ⟨j₀, hj₀⟩ := exists_eq_ciSup_of_finite (f := f) + have h1 : f i₀ ≤ f j₀ := (le_ciSup (Finite.bddAbove_range f) i₀).trans_eq hj₀.symm + have h2 : g * f j₀ ≤ g * f i₀ := + (le_ciSup (Finite.bddAbove_range fun i ↦ g * f i) j₀).trans_eq hi₀.symm + rw [← hi₀, ← hj₀] + exact le_antisymm (mul_le_mul_of_nonneg_left h1 hg) h2 + +/-- **The sup-norm-to-height translation.** The multiplicative height of a nonzero tuple of +integers, read as a tuple of rationals, is the maximum of the absolute values of its entries +divided by their greatest common divisor. Mathlib's `Rat.mulHeight_eq_max_abs_of_gcd_eq_one` is +the primitive case; the content is what the projective height divides out. -/ +theorem gcd_mul_mulHeight_intCast [Fintype ι] (hx : x ≠ 0) : + ((univ.gcd x : ℤ) : ℝ) * mulHeight (((↑) : ℤ → ℚ) ∘ x) = ((⨆ i, |x i| : ℤ) : ℝ) := by + obtain ⟨i₀, hi₀⟩ := Function.ne_iff.mp hx + have hg0 : univ.gcd x ≠ 0 := fun h ↦ hi₀ (Finset.gcd_eq_zero_iff.1 h i₀ (mem_univ i₀)) + have hgnn : 0 ≤ univ.gcd x := by + rw [← Finset.normalize_gcd, ← Int.abs_eq_normalize] + exact abs_nonneg _ + have hdvd : ∀ i, univ.gcd x ∣ x i := fun i ↦ Finset.gcd_dvd (mem_univ i) + set y : ι → ℤ := fun i ↦ x i / univ.gcd x + have hxy : ∀ i, x i = univ.gcd x * y i := fun i ↦ (Int.mul_ediv_cancel' (hdvd i)).symm + have hy1 : univ.gcd y = 1 := Finset.gcd_div_eq_one (mem_univ i₀) hi₀ + have hsmul : (((↑) : ℤ → ℚ) ∘ x) = ((univ.gcd x : ℤ) : ℚ) • (((↑) : ℤ → ℚ) ∘ y) := by + funext i + simp only [Function.comp_apply, Pi.smul_apply, smul_eq_mul] + exact_mod_cast congrArg (Int.cast : ℤ → ℚ) (hxy i) + rw [hsmul, mulHeight_smul_eq_mulHeight _ (by exact_mod_cast hg0), + mulHeight_eq_max_abs_of_gcd_eq_one hy1, ← Int.cast_mul, ← iSup_const_mul hgnn] + congr 1 + exact congrArg _ (funext fun i ↦ by rw [hxy i, abs_mul, abs_of_nonneg hgnn]) + +/-- The height of an integer tuple is at most its sup norm, with equality exactly when the tuple +is primitive. This is the direction Siegel's lemma is read in. -/ +theorem mulHeight_intCast_le [Finite ι] (hx : x ≠ 0) : + mulHeight (((↑) : ℤ → ℚ) ∘ x) ≤ ((⨆ i, |x i| : ℤ) : ℝ) := by + have : Fintype ι := Fintype.ofFinite ι + obtain ⟨i₀, hi₀⟩ := Function.ne_iff.mp hx + have hg0 : univ.gcd x ≠ 0 := fun h ↦ hi₀ (Finset.gcd_eq_zero_iff.1 h i₀ (mem_univ i₀)) + have hgnn : 0 ≤ univ.gcd x := by + rw [← Finset.normalize_gcd, ← Int.abs_eq_normalize] + exact abs_nonneg _ + have hg1 : (1 : ℝ) ≤ ((univ.gcd x : ℤ) : ℝ) := by + rw [← Int.cast_one, Int.cast_le] + omega + rw [← gcd_mul_mulHeight_intCast hx] + exact le_mul_of_one_le_left (mulHeight_pos _).le hg1 + +end Rat + +/-! ### Siegel's lemma in height form -/ + +attribute [local instance] Matrix.seminormedAddCommGroup + +namespace Int.Matrix + +variable {α β : Type*} [Fintype α] [Fintype β] (A : Matrix α β ℤ) + +/-- **Siegel's lemma, with an explicit bound on the entries.** A system of `M` linear equations in +`N > M` unknowns with integer coefficients of absolute value at most `B` has a nonzero integer +solution of sup norm at most `(N B) ^ (M / (N - M))`. + +This is Mathlib's `Int.Matrix.exists_ne_zero_int_vec_norm_le` with the matrix norm traded for a +bound on the entries; the hypothesis `1 ≤ B` is what removes the `max 1 ‖A‖` there, and costs +nothing, since a nonzero integer matrix has an entry of absolute value at least `1`. -/ +theorem exists_ne_zero_mulVec_eq_zero_iSup_abs_le {B : ℤ} (hB : 1 ≤ B) (hA : ∀ i j, |A i j| ≤ B) + (hn : Fintype.card α < Fintype.card β) (hm : 0 < Fintype.card α) : + ∃ x : β → ℤ, x ≠ 0 ∧ A *ᵥ x = 0 ∧ + ((⨆ j, |x j| : ℤ) : ℝ) ≤ ((Fintype.card β : ℝ) * B) ^ + ((Fintype.card α : ℝ) / ((Fintype.card β : ℝ) - Fintype.card α)) := by + have _ : Nonempty β := Fintype.card_pos_iff.1 (hm.trans hn) + have hB' : (0 : ℝ) ≤ (B : ℝ) := by exact_mod_cast hB.trans' zero_le_one + have hAB : ‖A‖ ≤ (B : ℝ) := by + rw [Matrix.norm_le_iff hB'] + intro i j + rw [Int.norm_eq_abs, ← Int.cast_abs, Int.cast_le] + exact hA i j + obtain ⟨x, hx0, hxA, hxb⟩ := exists_ne_zero_int_vec_norm_le A hn hm + refine ⟨x, hx0, hxA, ?_⟩ + rw [← Int.pi_norm_eq_iSup_abs] + refine hxb.trans (Real.rpow_le_rpow ?_ ?_ ?_) + · exact mul_nonneg (Nat.cast_nonneg _) (le_trans zero_le_one (le_max_left _ _)) + · exact mul_le_mul_of_nonneg_left (max_le (by exact_mod_cast hB) hAB) (Nat.cast_nonneg _) + · exact div_nonneg (Nat.cast_nonneg _) (by simpa using (Nat.cast_le (α := ℝ)).2 hn.le) + +/-- **Siegel's lemma in height form** (Bombieri–Gubler, Lemma 2.9.1; Hindry–Silverman, Lemma +D.4.1). The solution the previous statement produces has multiplicative height at most +`(N B) ^ (M / (N - M))` as well: by `Rat.gcd_mul_mulHeight_intCast` the height of an integer tuple +is at most its sup norm, and the two agree when the tuple is primitive — which the minimal +solution is, but which the statement does not need. -/ +theorem exists_ne_zero_mulVec_eq_zero_mulHeight_le {B : ℤ} (hB : 1 ≤ B) (hA : ∀ i j, |A i j| ≤ B) + (hn : Fintype.card α < Fintype.card β) (hm : 0 < Fintype.card α) : + ∃ x : β → ℤ, x ≠ 0 ∧ A *ᵥ x = 0 ∧ + mulHeight (((↑) : ℤ → ℚ) ∘ x) ≤ ((Fintype.card β : ℝ) * B) ^ + ((Fintype.card α : ℝ) / ((Fintype.card β : ℝ) - Fintype.card α)) := by + have _ : Nonempty β := Fintype.card_pos_iff.1 (hm.trans hn) + obtain ⟨x, hx0, hxA, hxb⟩ := exists_ne_zero_mulVec_eq_zero_iSup_abs_le A hB hA hn hm + exact ⟨x, hx0, hxA, (Rat.mulHeight_intCast_le hx0).trans hxb⟩ + +/-- **The exponent `M / (N - M)` is sharp**, already for `N = M + 1`, where it is `M`. The +`M × (M + 1)` matrix of the equations `B xᵢ = xᵢ₊₁` has entries of absolute value at most `B` and +kernel the line spanned by `(1, B, B², …, B ^ M)`, so every nonzero solution has sup norm at least +`B ^ M`, against a bound of `((M + 1) B) ^ M`: only the factor `(M + 1) ^ M` is slack. + +Taking `k` copies of this system side by side gives, for every `k`, an `k M × k (M + 1)` system +with the same conclusion and exponent `k M / k = M`, so no exponent below `M / (N - M)` can be +substituted in the statement above. -/ +theorem exists_forall_pow_le_iSup_abs (M : ℕ) {B : ℤ} (hB : 1 ≤ B) : + ∃ A : Matrix (Fin M) (Fin (M + 1)) ℤ, (∀ i j, |A i j| ≤ B) ∧ + ∀ x : Fin (M + 1) → ℤ, x ≠ 0 → A *ᵥ x = 0 → B ^ M ≤ ⨆ j, |x j| := by + classical + set A : Matrix (Fin M) (Fin (M + 1)) ℤ := Matrix.of fun i j ↦ + B * (if j = i.castSucc then 1 else 0) - (if j = i.succ then 1 else 0) with hAdef + have hne : ∀ i : Fin M, i.castSucc ≠ i.succ := fun i ↦ + ne_of_lt (Fin.castSucc_lt_succ : i.castSucc < i.succ) + have hmul : ∀ (x : Fin (M + 1) → ℤ) (i : Fin M), + (A *ᵥ x) i = B * x i.castSucc - x i.succ := by + intro x i + simp [hAdef, Matrix.mulVec, dotProduct, sub_mul, ite_mul, Finset.sum_sub_distrib, + Finset.sum_ite_eq'] + refine ⟨A, ?_, ?_⟩ + · intro i j + rcases eq_or_ne j i.castSucc with rfl | hj + · simp [hAdef, hne i, abs_of_nonneg (hB.trans' zero_le_one)] + · rcases eq_or_ne j i.succ with rfl | hj' + · simpa [hAdef, hj] using hB + · simpa [hAdef, hj, hj'] using hB.trans' zero_le_one + · intro x hx0 hxA + have hstep : ∀ i : Fin M, B * x i.castSucc = x i.succ := by + intro i + have h : B * x i.castSucc - x i.succ = 0 := by + rw [← hmul x i]; exact congrFun hxA i + exact eq_of_sub_eq_zero h + have hpow : ∀ j : Fin (M + 1), x j = B ^ (j : ℕ) * x 0 := by + intro j + induction j using Fin.induction with + | zero => simp + | succ i ih => + rw [← hstep i, ih, Fin.val_castSucc, Fin.val_succ, pow_succ] + ring + have hx00 : x 0 ≠ 0 := fun h ↦ hx0 (funext fun j ↦ by simp [hpow j, h]) + have hlast : B ^ M ≤ |x (Fin.last M)| := by + rw [hpow (Fin.last M), Fin.val_last, abs_mul, abs_pow, + abs_of_nonneg (hB.trans' zero_le_one)] + exact le_mul_of_one_le_right (pow_nonneg (hB.trans' zero_le_one) M) + (Int.one_le_abs hx00) + exact hlast.trans (le_ciSup (f := fun j ↦ |x j|) (Finite.bddAbove_range _) (Fin.last M)) + +end Int.Matrix + +/-! ### Siegel's lemma over a number field -/ + +namespace NumberField + +variable {K : Type*} [Field K] [NumberField K] {ι : Type*} [Finite ι] + +/-- **The house bounds the height.** For a nonzero tuple of algebraic integers the absolute +multiplicative height is at most the largest house of its entries: the finite local factors are at +most `1` because the entries are integral, and each infinite one is bounded by the house because an +infinite place is the modulus of a conjugate. + +This is the whole of the passage from Mathlib's `NumberField.house`-normalized Siegel lemma to a +height statement. -/ +theorem absMulHeight_le_iSup_house {x : ι → 𝓞 K} (hx : x ≠ 0) : + absMulHeight (fun i ↦ ((x i : K))) ≤ ⨆ i, house ((x i : K)) := by + obtain ⟨i₁, hi₁⟩ := Function.ne_iff.mp hx + have hx0 : (fun i ↦ ((x i : K))) ≠ 0 := Function.ne_iff.mpr ⟨i₁, by simpa using hi₁⟩ + have hHb : ∀ i, house ((x i : K)) ≤ ⨆ j, house ((x j : K)) := fun i ↦ + le_ciSup (f := fun j ↦ house ((x j : K))) (Finite.bddAbove_range _) i + have hH0 : 0 ≤ ⨆ j, house ((x j : K)) := (house_nonneg _).trans (hHb i₁) + have hple : ∀ (v : InfinitePlace K) (i : ι), v ((x i : K)) ≤ house ((x i : K)) := by + intro v i + have h := norm_le_pi_norm (canonicalEmbedding K ((x i : K))) v.embedding + rwa [canonicalEmbedding.apply_at, InfinitePlace.norm_embedding_eq] at h + have hfin : ∏ᶠ v : FinitePlace K, (⨆ i, v ((x i : K))) ≤ 1 := by + have hone := absNorm_mul_finprod_finitePlace_eq_one hx + have hn1 : (1 : ℝ) ≤ ((Ideal.span (Set.range x)).absNorm : ℝ) := by + rw [← Nat.cast_one, Nat.cast_le, Nat.one_le_iff_ne_zero] + intro h + rw [h, Nat.cast_zero, zero_mul] at hone + exact absurd hone (by norm_num) + refine le_of_mul_le_mul_left ?_ (lt_of_lt_of_le zero_lt_one hn1) + rw [hone, mul_one] + exact hn1 + have hmulH : Height.mulHeight (fun i ↦ ((x i : K))) ≤ + (⨆ j, house ((x j : K))) ^ Module.finrank ℚ K := by + rw [NumberField.mulHeight_eq hx0] + have hprod : ∏ v : InfinitePlace K, (⨆ i, v ((x i : K))) ^ v.mult ≤ + (⨆ j, house ((x j : K))) ^ Module.finrank ℚ K := by + calc ∏ v : InfinitePlace K, (⨆ i, v ((x i : K))) ^ v.mult + ≤ ∏ v : InfinitePlace K, (⨆ j, house ((x j : K))) ^ v.mult := by + gcongr <;> intros <;> + first + | exact pow_nonneg (Real.iSup_nonneg fun i ↦ apply_nonneg _ _) _ + | exact Real.iSup_nonneg fun i ↦ apply_nonneg _ _ + | exact Finite.bddAbove_range fun j ↦ house ((x j : K)) + | exact hple _ _ + _ = (⨆ j, house ((x j : K))) ^ ∑ v : InfinitePlace K, v.mult := + Finset.prod_pow_eq_pow_sum _ _ _ + _ = (⨆ j, house ((x j : K))) ^ Module.finrank ℚ K := by rw [InfinitePlace.sum_mult_eq] + refine le_trans (mul_le_mul hprod hfin ?_ (pow_nonneg hH0 _)) (le_of_eq (mul_one _)) + exact finprod_nonneg fun v ↦ Real.iSup_nonneg fun i ↦ apply_nonneg v _ + rw [absMulHeight_eq] + calc Height.mulHeight (fun i ↦ ((x i : K))) ^ ((Module.finrank ℚ K : ℝ))⁻¹ + ≤ ((⨆ j, house ((x j : K))) ^ Module.finrank ℚ K) ^ ((Module.finrank ℚ K : ℝ))⁻¹ := + Real.rpow_le_rpow (Height.mulHeight_pos _).le hmulH (by positivity) + _ = ⨆ j, house ((x j : K)) := Real.pow_rpow_inv_natCast hH0 Module.finrank_pos.ne' + +open scoped Classical in +/-- **Siegel's lemma over a number field, in height form** (Bombieri–Gubler, Corollary 2.9.2). +There is a constant `C`, depending only on `K`, such that a system of `p` linear equations in +`q > p` unknowns whose coefficients are algebraic integers of house at most `A` has a nonzero +solution in `𝓞 K ^ q` of absolute multiplicative height at most `C (C q A) ^ (p / (q - p))`. + +The constant is Mathlib's, and is `private` in `Mathlib/NumberTheory/NumberField/House.lean`; +quantifying over it existentially is therefore the only way to restate the theorem, and it is also +the shape the literature states it in. What is proved here is the left-hand side: Mathlib bounds +the *house* of each coordinate, and `absMulHeight_le_iSup_house` turns that into a bound on the +height of the tuple. -/ +theorem exists_forall_exists_ne_zero_mulVec_eq_zero_absMulHeight_le (K : Type*) [Field K] + [NumberField K] : + ∃ C : ℝ, ∀ (p q : ℕ) (a : Matrix (Fin p) (Fin q) (𝓞 K)) (A : ℝ), a ≠ 0 → 0 < p → p < q → + (∀ k l, house ((a k l : K)) ≤ A) → + ∃ ξ : Fin q → 𝓞 K, ξ ≠ 0 ∧ a *ᵥ ξ = 0 ∧ + absMulHeight (fun l ↦ ((ξ l : K))) ≤ C * ((C * q * A) ^ ((p : ℝ) / (q - p))) := + ⟨_, fun p q a A ha h0p hpq habs ↦ by + have : Nonempty (Fin q) := Fin.pos_iff_nonempty.1 (h0p.trans hpq) + obtain ⟨ξ, hξ0, hξa, hξb⟩ := + NumberField.house.exists_ne_zero_int_vec_house_le (K := K) (a := a) (ha := ha) + (h0p := h0p) (hpq := hpq) (cardβ := Fintype.card_fin q) (habs := habs) + (cardα := Fintype.card_fin p) + exact ⟨ξ, hξ0, hξa, (absMulHeight_le_iSup_house hξ0).trans (ciSup_le hξb)⟩⟩ + +end NumberField diff --git a/LeanPool/CCDN/ArithmeticHeights/SliceBound.lean b/LeanPool/CCDN/ArithmeticHeights/SliceBound.lean new file mode 100644 index 0000000000..387786107c --- /dev/null +++ b/LeanPool/CCDN/ArithmeticHeights/SliceBound.lean @@ -0,0 +1,428 @@ +/- +Copyright (c) 2026 Ralf Stephan. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Ralf Stephan +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.ArithmeticHeights.GaussMeasure +public import LeanPool.CCDN.ArithmeticHeights.LogConcave + +/-! +# The Gauss measure of a symmetric convex set is at most its volume inside a product of balls + +**Bombieri–Gubler, Lemma C.3.7.** Let `Q` be a product of euclidean balls each of volume one, one +factor per block of a partition of the coordinates. Then for every measurable convex symmetric +`A`, the Gauss measure of `A` is at most `vol (A ∩ Q)`. This is the inequality Vaaler's +cube-slicing theorem is extracted from by an `ε`-thickening. + +The predicate `HasSliceBound μ g Q` states the conclusion for a measure `μ`, a density `g` and a +set `Q`, in the form that both makes the induction on the number of blocks a `Measure.prod` +statement and makes the base case a one-dimensional comparison along rays. + +## Main definitions + +* `HasSliceBound μ g Q`: for every measurable convex symmetric `A`, + `∫⁻ x in A, g x ∂μ ≤ μ (A ∩ Q)`. + +## Main results + +* `HasSliceBound.lintegral_le`: the bound upgrades from sets to even log-concave functions, + by the layer cake. +* `hasSliceBound_unitVolumeBall`: **the base case, `r = 1`**, for the ball of volume one. +* `HasSliceBound.prod`: **the induction step**, the case of a product of two blocks. +* `HasSliceBound.of_measurePreserving`: transport along a measure-preserving linear equivalence. + +## Implementation notes + +⚠ **The base case needs no Gamma function and no closed form for the radius.** Polar decomposition +turns both sides into integrals over the unit sphere; on each ray, a symmetric convex set meets the +ray in an interval, so either the ray's segment of the volume-one ball lies in `A` or the reverse, +and the two cases need only `exp (-π r ^ 2) ≤ 1` and the *equality* of the two radial integrals. +That equality is forced by the two normalizations — the Gauss measure and the ball both have total +mass one — once the sphere measure, a positive finite constant, is cancelled. This is why +Bombieri–Gubler's Remark C.3.6 does not appear. + +⚠ **The induction step needs the bound twice, in both factors, because slices of a symmetric set +are not symmetric.** `A_y = {z | (y, z) ∈ A}` satisfies `A_{-y} = -A_y`, not `A_y = -A_y`, so the +bound cannot be applied slice by slice. Bombieri–Gubler's remedy, followed here, is to apply the +upgraded form `HasSliceBound.lintegral_le` once in each factor, with the layer cake of the first +density in between: the superlevel sets of a log-concave even density are convex *and* symmetric, +and the marginals over them are log-concave by Lemma C.3.4 and even because the density is. + +⚠ **Closedness of `A` is not needed, only convexity, symmetry and measurability.** Bombieri–Gubler +ask for a closed symmetric convex set, and their approximation argument in the induction step needs +closedness to know the superlevel sets are closed. The layer cake used here replaces that argument, +and the superlevel sets of a log-concave function are convex whether or not they are closed, so the +hypothesis disappears. It is worth having: in the induction step the sets the bound is applied to +are superlevel sets of a marginal, whose closedness would be an upper semicontinuity question. + +## References + +E. Bombieri and W. Gubler, *Heights in Diophantine Geometry*, Cambridge University Press (2006), +Lemma C.3.7. + +This is Layer 4.5 (infrastructure) of the `ArithmeticHeights` roadmap. +-/ + +public section + +open MeasureTheory Measure Set Metric ENNReal Pointwise +open scoped Real + +variable {E F : Type*} + +/-- `HasSliceBound μ g Q` says that the measure with density `g` of a measurable convex symmetric +set `A` never exceeds `μ (A ∩ Q)`. With `g` the Gauss density and `Q` a product of balls of volume +one this is Bombieri–Gubler's Lemma C.3.7. -/ +@[expose] def HasSliceBound [MeasurableSpace E] [AddCommGroup E] [Module ℝ E] + (μ : Measure E) (g : E → ℝ≥0∞) (Q : Set E) : Prop := + ∀ A : Set E, MeasurableSet A → Convex ℝ A → (∀ x ∈ A, -x ∈ A) → + ∫⁻ x in A, g x ∂μ ≤ μ (A ∩ Q) + +/-- A set closed under negation equals its own negation. -/ +theorem Set.neg_eq_self_of_neg_mem {G : Type*} [InvolutiveNeg G] {A : Set G} + (h : ∀ x ∈ A, -x ∈ A) : -A = A := by + ext x + simp only [Set.mem_neg] + exact ⟨fun hx => by simpa using h _ hx, fun hx => h x hx⟩ + +/-- **The slice bound upgrades from sets to even log-concave functions.** Writing `f` as a +superposition of the indicators of its superlevel sets — which are convex because `f` is +log-concave and symmetric because `f` is even — turns the bound for sets into a bound for +integrals. -/ +theorem HasSliceBound.lintegral_le [MeasurableSpace E] [AddCommGroup E] [Module ℝ E] + {μ : Measure E} [SFinite μ] {g : E → ℝ≥0∞} {Q : Set E} + (hb : HasSliceBound μ g Q) (hg : Measurable g) + {f : E → ℝ≥0∞} (hf : Measurable f) (hfe : ∀ x, f (-x) = f x) (hflc : LogConcave f) : + ∫⁻ x, f x * g x ∂μ ≤ ∫⁻ x in Q, f x ∂μ := by + have hK : ∀ t : ℝ, MeasurableSet {x : E | ENNReal.ofReal t < f x} := fun t => + measurableSet_lt measurable_const hf + calc ∫⁻ x, f x * g x ∂μ = ∫⁻ x, f x ∂(μ.withDensity g) := by + rw [lintegral_withDensity_eq_lintegral_mul _ hg hf] + exact lintegral_congr fun x => mul_comm _ _ + _ = ∫⁻ t in Ioi (0 : ℝ), (μ.withDensity g) {x | ENNReal.ofReal t < f x} := + lintegral_eq_lintegral_measure_ofReal_lt _ hf + _ = ∫⁻ t in Ioi (0 : ℝ), ∫⁻ x in {x | ENNReal.ofReal t < f x}, g x ∂μ := + lintegral_congr fun t => withDensity_apply g (hK t) + _ ≤ ∫⁻ t in Ioi (0 : ℝ), μ ({x | ENNReal.ofReal t < f x} ∩ Q) := + lintegral_mono fun t => hb _ (hK t) (hflc.convex_lt _) + (fun x hx => by change ENNReal.ofReal t < f (-x); rw [hfe x]; exact hx) + _ = ∫⁻ t in Ioi (0 : ℝ), (μ.restrict Q) {x | ENNReal.ofReal t < f x} := + lintegral_congr fun t => (Measure.restrict_apply (hK t)).symm + _ = ∫⁻ x, f x ∂(μ.restrict Q) := (lintegral_eq_lintegral_measure_ofReal_lt _ hf).symm + +/-- **The base case of Bombieri–Gubler's Lemma C.3.7**, stated for an arbitrary norm: if the Gauss +density has total mass one and the closed ball of radius `ρ` has measure one, then the Gauss +measure of a measurable convex symmetric set is at most its measure inside that ball. -/ +theorem hasSliceBound_closedBall [NormedAddCommGroup E] [NormedSpace ℝ E] + [MeasurableSpace E] [BorelSpace E] [FiniteDimensional ℝ E] (μ : Measure E) + [μ.IsAddHaarMeasure] {ρ : ℝ} (hρ : 0 < ρ) (hball : μ (closedBall 0 ρ) = 1) + (hgauss : ∫⁻ x, gaussDensity x ∂μ = 1) : + HasSliceBound μ gaussDensity (closedBall (0 : E) ρ) := by + rcases subsingleton_or_nontrivial E with hE | hE + · intro A hA _ _ + have hcb : closedBall (0 : E) ρ = univ := by + ext x; simp [Subsingleton.elim x (0 : E), hρ.le] + have hg : ∀ x : E, gaussDensity x = 1 := fun x => by + rw [Subsingleton.elim x (0 : E), gaussDensity_zero] + simp [hg, hcb] + intro A hA hAconv hAsymm + rcases A.eq_empty_or_nonempty with rfl | ⟨a, ha⟩ + · simp + have h0 : (0 : E) ∈ A := by + have := hAconv ha (hAsymm a ha) (by norm_num : (0 : ℝ) ≤ 1 / 2) + (by norm_num : (0 : ℝ) ≤ 1 / 2) (by norm_num) + simpa using this + set n := Module.finrank ℝ E with hn + set W := Measure.volumeIoiPow (n - 1) with hW + set c : ℝ≥0∞ := ∫⁻ r : Ioi (0 : ℝ), ENNReal.ofReal (Real.exp (-π * (r : ℝ) ^ 2)) ∂W with hcdef + set d : ℝ≥0∞ := W {r : Ioi (0 : ℝ) | (r : ℝ) ≤ ρ} with hddef + have hnorm : ∀ (u : sphere (0 : E) 1) (r : Ioi (0 : ℝ)), ‖(r : ℝ) • (u : E)‖ = (r : ℝ) := by + intro u r + rw [norm_smul, mem_sphere_zero_iff_norm.mp u.2, mul_one, Real.norm_eq_abs, abs_of_pos r.2] + have hgsmul : ∀ (u : sphere (0 : E) 1) (r : Ioi (0 : ℝ)), + gaussDensity ((r : ℝ) • (u : E)) = ENNReal.ofReal (Real.exp (-π * (r : ℝ) ^ 2)) := by + intro u r; rw [gaussDensity_def, hnorm] + have hSne : μ.toSphere univ ≠ 0 := by + simp only [ne_eq, measure_univ_eq_zero] + exact Measure.toSphere_ne_zero μ + have hStop : μ.toSphere univ ≠ ⊤ := measure_ne_top _ _ + have hdmeas : MeasurableSet {r : Ioi (0 : ℝ) | (r : ℝ) ≤ ρ} := + measurable_subtype_coe measurableSet_Iic + have claim1 : μ.toSphere univ * c = 1 := by + rw [← hgauss, lintegral_eq_lintegral_sphere' μ (f := gaussDensity) (by fun_prop), + show (fun u : sphere (0 : E) 1 => ∫⁻ r : Ioi (0 : ℝ), gaussDensity ((r : ℝ) • (u : E)) ∂W) + = fun _ => c from funext fun u => lintegral_congr fun r => hgsmul u r, + lintegral_const, mul_comm] + have hcbmem : ∀ (u : sphere (0 : E) 1) (r : Ioi (0 : ℝ)), + ((r : ℝ) • (u : E) ∈ closedBall (0 : E) ρ) ↔ (r : ℝ) ≤ ρ := by + intro u r; rw [mem_closedBall, dist_zero_right, hnorm] + have hd1 : ∀ u : sphere (0 : E) 1, ∫⁻ r : Ioi (0 : ℝ), + Set.indicator (closedBall (0 : E) ρ) (fun _ => (1 : ℝ≥0∞)) ((r : ℝ) • (u : E)) ∂W = d := by + intro u + have he : (fun r : Ioi (0 : ℝ) => + Set.indicator (closedBall (0 : E) ρ) (fun _ => (1 : ℝ≥0∞)) ((r : ℝ) • (u : E))) + = Set.indicator {r : Ioi (0 : ℝ) | (r : ℝ) ≤ ρ} (fun _ => (1 : ℝ≥0∞)) := by + funext r + by_cases h : (r : ℝ) ≤ ρ + · rw [Set.indicator_of_mem ((hcbmem u r).mpr h), + Set.indicator_of_mem (show r ∈ {r : Ioi (0 : ℝ) | (r : ℝ) ≤ ρ} from h)] + · rw [Set.indicator_of_notMem (fun hm => h ((hcbmem u r).mp hm)), + Set.indicator_of_notMem (show r ∉ {r : Ioi (0 : ℝ) | (r : ℝ) ≤ ρ} from h)] + rw [he, lintegral_indicator hdmeas, setLIntegral_one, hddef] + have claim2 : μ.toSphere univ * d = 1 := by + rw [← hball] + have h1 : μ (closedBall (0 : E) ρ) + = ∫⁻ x, Set.indicator (closedBall (0 : E) ρ) (fun _ => (1 : ℝ≥0∞)) x ∂μ := by + rw [lintegral_indicator measurableSet_closedBall, setLIntegral_one] + rw [h1, lintegral_eq_lintegral_sphere' μ + (f := Set.indicator (closedBall (0 : E) ρ) (fun _ => (1 : ℝ≥0∞))) + (measurable_one.indicator measurableSet_closedBall), + show (fun u : sphere (0 : E) 1 => ∫⁻ r : Ioi (0 : ℝ), + Set.indicator (closedBall (0 : E) ρ) (fun _ => (1 : ℝ≥0∞)) ((r : ℝ) • (u : E)) ∂W) + = fun _ => d from funext hd1, lintegral_const, mul_comm] + have hcd : c = d := (ENNReal.mul_right_inj hSne hStop).mp (claim1.trans claim2.symm) + have key : ∀ u : sphere (0 : E) 1, + ∫⁻ r : Ioi (0 : ℝ), Set.indicator A gaussDensity ((r : ℝ) • (u : E)) ∂W ≤ + ∫⁻ r : Ioi (0 : ℝ), + Set.indicator (A ∩ closedBall (0 : E) ρ) (fun _ => (1 : ℝ≥0∞)) ((r : ℝ) • (u : E)) ∂W := by + intro u + by_cases hc : (ρ • (u : E)) ∈ A + · have hsub : ∀ t : ℝ, 0 < t → t ≤ ρ → (t • (u : E)) ∈ A := by + intro t ht htρ + have h1 : (0 : ℝ) ≤ t / ρ := by positivity + have h2 : (0 : ℝ) ≤ 1 - t / ρ := by + have : t / ρ ≤ 1 := (div_le_one hρ).mpr htρ + linarith + have := hAconv hc h0 h1 h2 (by ring) + simpa [smul_smul, div_mul_cancel₀ _ hρ.ne'] using this + have hle : ∫⁻ r : Ioi (0 : ℝ), Set.indicator A gaussDensity ((r : ℝ) • (u : E)) ∂W ≤ c := + lintegral_mono fun r => (Set.indicator_le_self _ _ _).trans (hgsmul u r).le + refine hle.trans ?_ + rw [hcd, hddef, ← setLIntegral_one, ← lintegral_indicator hdmeas] + refine lintegral_mono fun r => ?_ + by_cases hr : (r : ℝ) ≤ ρ + · rw [Set.indicator_of_mem (show r ∈ {r : Ioi (0 : ℝ) | (r : ℝ) ≤ ρ} from hr), + Set.indicator_of_mem (show ((r : ℝ) • (u : E)) ∈ A ∩ closedBall (0 : E) ρ from + ⟨hsub r r.2 hr, (hcbmem u r).mpr hr⟩)] + · rw [Set.indicator_of_notMem (show r ∉ {r : Ioi (0 : ℝ) | (r : ℝ) ≤ ρ} from hr)] + exact zero_le + · refine lintegral_mono fun r => ?_ + have hr0 : (0 : ℝ) < (r : ℝ) := r.2 + by_cases hmem : ((r : ℝ) • (u : E)) ∈ A + · have hr : (r : ℝ) ≤ ρ := by + by_contra hgt + refine hc ?_ + have hgt' : ρ < (r : ℝ) := not_le.mp hgt + have h1 : (0 : ℝ) ≤ ρ / (r : ℝ) := le_of_lt (div_pos hρ hr0) + have h2 : (0 : ℝ) ≤ 1 - ρ / (r : ℝ) := by + have : ρ / (r : ℝ) ≤ 1 := (div_le_one hr0).mpr hgt'.le + linarith + have := hAconv hmem h0 h1 h2 (by ring) + simpa [smul_smul, div_mul_cancel₀ _ hr0.ne'] using this + rw [Set.indicator_of_mem hmem, + Set.indicator_of_mem (show ((r : ℝ) • (u : E)) ∈ A ∩ closedBall (0 : E) ρ from + ⟨hmem, (hcbmem u r).mpr hr⟩)] + exact gaussDensity_le_one _ + · rw [Set.indicator_of_notMem hmem]; exact zero_le + calc ∫⁻ x in A, gaussDensity x ∂μ + = ∫⁻ x, Set.indicator A gaussDensity x ∂μ := (lintegral_indicator hA _).symm + _ = ∫⁻ u : sphere (0 : E) 1, ∫⁻ r : Ioi (0 : ℝ), + Set.indicator A gaussDensity ((r : ℝ) • (u : E)) ∂W ∂μ.toSphere := + lintegral_eq_lintegral_sphere' μ (measurable_gaussDensity.indicator hA) + _ ≤ ∫⁻ u : sphere (0 : E) 1, ∫⁻ r : Ioi (0 : ℝ), + Set.indicator (A ∩ closedBall (0 : E) ρ) (fun _ => (1 : ℝ≥0∞)) ((r : ℝ) • (u : E)) ∂W + ∂μ.toSphere := lintegral_mono key + _ = ∫⁻ x, Set.indicator (A ∩ closedBall (0 : E) ρ) (fun _ => (1 : ℝ≥0∞)) x ∂μ := + (lintegral_eq_lintegral_sphere' μ + (measurable_one.indicator (hA.inter measurableSet_closedBall))).symm + _ = μ (A ∩ closedBall (0 : E) ρ) := by + rw [lintegral_indicator (hA.inter measurableSet_closedBall), setLIntegral_one] + +/-- **Bombieri–Gubler's Lemma C.3.7 for a single block**: the Gauss measure of a measurable convex +symmetric set is at most its volume inside the ball of volume one. -/ +theorem hasSliceBound_unitVolumeBall [NormedAddCommGroup E] [InnerProductSpace ℝ E] + [MeasurableSpace E] [BorelSpace E] [FiniteDimensional ℝ E] : + HasSliceBound (volume : Measure E) gaussDensity (closedBall (0 : E) (unitVolumeRadius E)) := + hasSliceBound_closedBall volume unitVolumeRadius_pos volume_closedBall_unitVolumeRadius + lintegral_gaussDensity_eq_one + +/-- **The induction step of Bombieri–Gubler's Lemma C.3.7.** If the slice bound holds in each +factor, for densities that are log-concave and even, then it holds for the product measure, the +product density and the product of the two sets. -/ +theorem HasSliceBound.prod + [MeasurableSpace E] [AddCommGroup E] [Module ℝ E] [MeasurableNeg E] + [MeasurableSpace F] [AddCommGroup F] [Module ℝ F] [MeasurableNeg F] + {μ : Measure E} {ν : Measure F} [SFinite μ] [SFinite ν] + [μ.IsNegInvariant] [ν.IsNegInvariant] + {g : E → ℝ≥0∞} {h : F → ℝ≥0∞} {Q : Set E} {R : Set F} + (hg : Measurable g) (hge : ∀ x, g (-x) = g x) (hglc : LogConcave g) + (hh : Measurable h) (hR : MeasurableSet R) + (hRc : Convex ℝ R) (hRs : ∀ z ∈ R, -z ∈ R) + (hPLμ : HasPrekopaLeindler μ) (hPLν : HasPrekopaLeindler ν) + (hE : HasSliceBound μ g Q) (hF : HasSliceBound ν h R) : + HasSliceBound (μ.prod ν) (fun p => g p.1 * h p.2) (Q ×ˢ R) := by + intro A hA hAconv hAsymm + set K : ℝ → Set E := fun s => {y | ENNReal.ofReal s < g y} with hKdef + have hKmeas : ∀ s, MeasurableSet (K s) := fun s => measurableSet_lt measurable_const hg + have hKconv : ∀ s, Convex ℝ (K s) := fun s => hglc.convex_lt _ + have hKsymm : ∀ s, ∀ y ∈ K s, -y ∈ K s := fun s y hy => by + change ENNReal.ofReal s < g (-y); rw [hge y]; exact hy + have hAsecE : ∀ z : F, MeasurableSet {y : E | (y, z) ∈ A} := fun z => hA.preimage (by fun_prop) + have hAsecF : ∀ y : E, MeasurableSet {z : F | (y, z) ∈ A} := fun y => hA.preimage (by fun_prop) + have hnegE : ∀ z : F, {y : E | (y, -z) ∈ A} = -{y : E | (y, z) ∈ A} := fun z => by + ext y + simp only [Set.mem_neg, Set.mem_ofPred_eq] + exact ⟨fun hy => by simpa using hAsymm _ hy, fun hy => by simpa using hAsymm _ hy⟩ + have hnegF : ∀ y : E, {z : F | (-y, z) ∈ A} = -{z : F | (y, z) ∈ A} := fun y => by + ext z + simp only [Set.mem_neg, Set.mem_ofPred_eq] + exact ⟨fun hz => by simpa using hAsymm _ hz, fun hz => by simpa using hAsymm _ hz⟩ + have hRneg : (-R : Set F) = R := Set.neg_eq_self_of_neg_mem hRs + have hKneg : ∀ s, (-(K s) : Set E) = K s := fun s => Set.neg_eq_self_of_neg_mem (hKsymm s) + set f : E → ℝ≥0∞ := fun y => (ν.restrict R) {z | (y, z) ∈ A} with hfdef + set θ : ℝ → F → ℝ≥0∞ := fun s z => (μ.restrict (K s)) {y | (y, z) ∈ A} with hθdef + have hfmeas : Measurable f := measurable_measure_prodMk_left hA + have hθmeas : ∀ s, Measurable (θ s) := fun s => measurable_measure_prodMk_right hA + have hΘmeas : Measurable fun p : F × ℝ => θ p.2 p.1 := by + have hS : MeasurableSet {q : E × (F × ℝ) | + ENNReal.ofReal q.2.2 < g q.1 ∧ (q.1, q.2.1) ∈ A} := + (measurableSet_lt (ENNReal.measurable_ofReal.comp (measurable_snd.comp measurable_snd)) + (hg.comp measurable_fst)).inter (hA.preimage (by fun_prop)) + have heq : (fun p : F × ℝ => θ p.2 p.1) + = fun p : F × ℝ => μ ((fun y => (y, p)) ⁻¹' + {q : E × (F × ℝ) | ENNReal.ofReal q.2.2 < g q.1 ∧ (q.1, q.2.1) ∈ A}) := by + funext p + rw [hθdef] + simp only + rw [Measure.restrict_apply' (hKmeas p.2)] + congr 1 + ext y + constructor + · rintro ⟨h1, h2⟩; exact ⟨h2, h1⟩ + · rintro ⟨h1, h2⟩; exact ⟨h2, h1⟩ + rw [heq] + exact measurable_measure_prodMk_right hS + have hfeven : ∀ y, f (-y) = f y := fun y => by + rw [hfdef] + simp only + rw [hnegF y, Measure.restrict_apply (hAsecF y).neg, Measure.restrict_apply (hAsecF y), + show (-{z : F | (y, z) ∈ A} ∩ R) = -({z : F | (y, z) ∈ A} ∩ R) by + rw [inter_neg, hRneg], measure_neg] + have hθeven : ∀ s z, θ s (-z) = θ s z := fun s z => by + rw [hθdef] + simp only + rw [hnegE z, Measure.restrict_apply' (hKmeas s), Measure.restrict_apply' (hKmeas s), + show (-{y : E | (y, z) ∈ A} ∩ K s) = -({y : E | (y, z) ∈ A} ∩ K s) by + rw [inter_neg, hKneg], measure_neg] + have hindA : LogConcave (Set.indicator A (fun _ => (1 : ℝ≥0∞))) := logConcave_indicator hAconv 1 + have hindAmeas : Measurable (Set.indicator A (fun _ => (1 : ℝ≥0∞))) := measurable_one.indicator hA + have hfeq : (fun y => ∫⁻ z in R, Set.indicator A (fun _ => (1 : ℝ≥0∞)) (y, z) ∂ν) = f := by + funext y + rw [show (fun z => Set.indicator A (fun _ => (1 : ℝ≥0∞)) (y, z)) + = Set.indicator {z | (y, z) ∈ A} (fun _ => (1 : ℝ≥0∞)) from rfl, + lintegral_indicator (hAsecF y), setLIntegral_one, hfdef] + have hflc : LogConcave f := + hfeq ▸ LogConcave.setLIntegral_prod_right hPLν hindAmeas hindA hR hRc + have hAswapconv : Convex ℝ {q : F × E | (q.2, q.1) ∈ A} := + fun _ h1 _ h2 _ _ ha hb hab => hAconv h1 h2 ha hb hab + have hindA' : LogConcave (Set.indicator {q : F × E | (q.2, q.1) ∈ A} (fun _ => (1 : ℝ≥0∞))) := + logConcave_indicator hAswapconv 1 + have hindA'meas : + Measurable (Set.indicator {q : F × E | (q.2, q.1) ∈ A} (fun _ => (1 : ℝ≥0∞))) := + measurable_one.indicator (hA.preimage (by fun_prop)) + have hθlc : ∀ s, LogConcave (θ s) := fun s => by + have heq : (fun z => ∫⁻ y in K s, + Set.indicator {q : F × E | (q.2, q.1) ∈ A} (fun _ => (1 : ℝ≥0∞)) (z, y) ∂μ) = θ s := by + funext z + rw [show (fun y => Set.indicator {q : F × E | (q.2, q.1) ∈ A} (fun _ => (1 : ℝ≥0∞)) (z, y)) + = Set.indicator {y | (y, z) ∈ A} (fun _ => (1 : ℝ≥0∞)) from rfl, + lintegral_indicator (hAsecE z), setLIntegral_one, hθdef] + exact heq ▸ LogConcave.setLIntegral_prod_right hPLμ hindA'meas hindA' (hKmeas s) (hKconv s) + have hlayer : ∀ S : Set E, MeasurableSet S → + ∫⁻ y in S, g y ∂μ = ∫⁻ s in Ioi (0 : ℝ), (μ.restrict (K s)) S := by + intro S _ + rw [lintegral_eq_lintegral_measure_ofReal_lt (μ.restrict S) hg] + refine lintegral_congr fun s => ?_ + rw [Measure.restrict_apply (hKmeas s), Measure.restrict_apply' (hKmeas s), Set.inter_comm] + have hW1 : Measurable (Set.indicator A (fun p : E × F => g p.1 * h p.2)) := + ((hg.comp measurable_fst).mul (hh.comp measurable_snd)).indicator hA + have hW2 : Measurable (Set.indicator A (fun p : E × F => g p.1)) := + (hg.comp measurable_fst).indicator hA + have hθs : ∀ z, Measurable fun s => θ s z := fun _ => hΘmeas.comp measurable_prodMk_left + calc ∫⁻ x in A, g x.1 * h x.2 ∂(μ.prod ν) + = ∫⁻ z, (∫⁻ y in {y | (y, z) ∈ A}, g y ∂μ) * h z ∂ν := by + rw [← lintegral_indicator hA, lintegral_prod _ hW1.aemeasurable, + lintegral_lintegral_swap + (f := fun (y : E) (z : F) => + Set.indicator A (fun p : E × F => g p.1 * h p.2) (y, z)) hW1.aemeasurable] + refine lintegral_congr fun z => ?_ + rw [show (fun y => Set.indicator A (fun p : E × F => g p.1 * h p.2) (y, z)) + = Set.indicator {y | (y, z) ∈ A} (fun y => g y * h z) from rfl, + lintegral_indicator (hAsecE z), lintegral_mul_const _ hg] + _ = ∫⁻ z, (∫⁻ s in Ioi (0 : ℝ), θ s z * h z) ∂ν := by + refine lintegral_congr fun z => ?_ + rw [hlayer _ (hAsecE z), ← lintegral_mul_const _ (hθs z)] + _ = ∫⁻ s in Ioi (0 : ℝ), ∫⁻ z, θ s z * h z ∂ν := + lintegral_lintegral_swap (f := fun (z : F) (s : ℝ) => θ s z * h z) + (hΘmeas.mul (hh.comp measurable_fst)).aemeasurable + _ ≤ ∫⁻ s in Ioi (0 : ℝ), ∫⁻ z in R, θ s z ∂ν := + lintegral_mono fun s => hF.lintegral_le hh (hθmeas s) (hθeven s) (hθlc s) + _ = ∫⁻ z in R, (∫⁻ s in Ioi (0 : ℝ), θ s z) ∂ν := + lintegral_lintegral_swap (f := fun (s : ℝ) (z : F) => θ s z) + (hΘmeas.comp measurable_swap).aemeasurable + _ = ∫⁻ z in R, (∫⁻ y in {y | (y, z) ∈ A}, g y ∂μ) ∂ν := + lintegral_congr fun z => (hlayer _ (hAsecE z)).symm + _ = ∫⁻ y, f y * g y ∂μ := by + have h1 : ∀ z : F, ∫⁻ y in {y | (y, z) ∈ A}, g y ∂μ + = ∫⁻ y, Set.indicator A (fun p : E × F => g p.1) (y, z) ∂μ := by + intro z + rw [show (fun y => Set.indicator A (fun p : E × F => g p.1) (y, z)) + = Set.indicator {y | (y, z) ∈ A} (fun y => g y) from rfl, + lintegral_indicator (hAsecE z)] + simp only [h1] + rw [lintegral_lintegral_swap (μ := ν.restrict R) (ν := μ) + (f := fun z y => Set.indicator A (fun p : E × F => g p.1) (y, z)) + (hW2.comp measurable_swap).aemeasurable] + refine lintegral_congr fun y => ?_ + rw [show (fun z => Set.indicator A (fun p : E × F => g p.1) (y, z)) + = Set.indicator {z | (y, z) ∈ A} (fun _ => g y) from rfl, + lintegral_indicator (hAsecF y), setLIntegral_const, hfdef, mul_comm] + _ ≤ ∫⁻ y in Q, f y ∂μ := hE.lintegral_le hg hfmeas hfeven hflc + _ = (μ.prod ν) (A ∩ (Q ×ˢ R)) := by + rw [← Measure.restrict_apply hA, ← Measure.prod_restrict, Measure.prod_apply hA] + rfl + +/-- The slice bound transports along a measure-preserving linear equivalence. -/ +theorem HasSliceBound.of_measurePreserving [MeasurableSpace E] [AddCommGroup E] [Module ℝ E] + [MeasurableSpace F] [AddCommGroup F] [Module ℝ F] {μ : Measure E} {ν : Measure F} + (e : E ≃ₗ[ℝ] F) (he : MeasurePreserving e μ ν) (hsymm : Measurable e.symm) + {g : F → ℝ≥0∞} (hg : Measurable g) {Q : Set F} (hQ : MeasurableSet Q) + (hb : HasSliceBound ν g Q) : + HasSliceBound μ (fun x => g (e x)) (e ⁻¹' Q) := by + intro A hA hAconv hAsymm + have hBA : e ⁻¹' (e.symm ⁻¹' A) = A := by ext x; simp + have hBmeas : MeasurableSet (e.symm ⁻¹' A) := hsymm hA + have hBconv : Convex ℝ (e.symm ⁻¹' A) := hAconv.linear_preimage (e.symm : F →ₗ[ℝ] E) + have hBsymm : ∀ y ∈ e.symm ⁻¹' A, -y ∈ e.symm ⁻¹' A := by + intro y hy + have : e.symm (-y) = -(e.symm y) := map_neg _ _ + simpa [Set.mem_preimage, this] using hAsymm _ hy + have h1 : ∫⁻ x in A, g (e x) ∂μ = ∫⁻ y in e.symm ⁻¹' A, g y ∂ν := by + rw [← lintegral_indicator hBmeas, ← he.lintegral_comp (hg.indicator hBmeas), + ← lintegral_indicator hA] + refine lintegral_congr fun x => ?_ + by_cases hx : x ∈ A + · rw [Set.indicator_of_mem hx, + Set.indicator_of_mem (show e x ∈ e.symm ⁻¹' A by simpa using hx)] + · rw [Set.indicator_of_notMem hx, + Set.indicator_of_notMem (show e x ∉ e.symm ⁻¹' A by simpa using hx)] + have h2 : μ (A ∩ e ⁻¹' Q) = ν (e.symm ⁻¹' A ∩ Q) := by + have hpre : A ∩ e ⁻¹' Q = e ⁻¹' (e.symm ⁻¹' A ∩ Q) := by rw [Set.preimage_inter, hBA] + rw [hpre, he.measure_preimage (hBmeas.inter hQ).nullMeasurableSet] + rw [h1, h2] + exact hb _ hBmeas hBconv hBsymm diff --git a/LeanPool/CCDN/ArithmeticHeights/Subspace.lean b/LeanPool/CCDN/ArithmeticHeights/Subspace.lean new file mode 100644 index 0000000000..b1a5e53b19 --- /dev/null +++ b/LeanPool/CCDN/ArithmeticHeights/Subspace.lean @@ -0,0 +1,537 @@ +/- +Copyright (c) 2026 Ralf Stephan. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Ralf Stephan +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.ArithmeticHeights.Absolute +public import LeanPool.CCDN.ArithmeticHeights.Arakelov +public import LeanPool.CCDN.ArithmeticHeights.Plucker + +/-! +# The height of a linear subspace + +Schmidt's height of a subspace `V : Submodule K (ι → K)`: the height of its Plücker point +`Submodule.pluckerPoint`, a point of `Projectivization K (Set.powersetCard ι k → K)`, in each of +the three normalizations the roadmap carries — Mathlib's sup-norm height, the Arakelov height of +Layer 0.1, and the absolute height of Layer 0.4 — with the logarithmic form of each alongside. + +The definition is Bombieri–Gubler 2.8.4 and Schmidt 1967, §1. What makes it the right definition +is `Submodule.mulHeight_span_singleton`: the height of a line is the projective height of the +point that spans it. + +## Main definitions + +* `Submodule.mulHeight`, `Submodule.logHeight`: the height of a subspace of `ι → K`, over any + field with `Height.AdmissibleAbsValues`. +* `Submodule.arakelovMulHeight`, `Submodule.arakelovLogHeight`: the ℓ²-at-infinity variant, over a + number field. +* `Submodule.absMulHeight`, `Submodule.absLogHeight`: the absolute variant, over a field of + algebraic numbers. + +## Main results + +* `Submodule.mulHeight_span_singleton`: `Submodule.mulHeight (span K {x}) = Height.mulHeight x` + for `x ≠ 0`, and the same for the other three heights. This is the compatibility that fixes the + definition; it is `exteriorPower.plucker_one_comp` of Layer 3.1 read through + `Height.mulHeight_comp_equiv`. +* `Submodule.mulHeight_span_range`: the height of the span of a linearly independent family is + the height of its tuple of Plücker coordinates, which by `exteriorPower.plucker_apply` is its + tuple of maximal minors. This is the form the matrix dictionary of 3.3 consumes. +* `Submodule.mulHeight_bot` and `Submodule.mulHeight_top`: the two degenerate ranks, where the + exterior power is a line, have height `1`. +* `Submodule.one_le_mulHeight`, and positivity and nonnegativity beside it. +* `Submodule.mulHeight_eq_mulHeight_pluckerPoint`: the definition, re-typed along any proof that + the rank of `V` is `k`. Every proof enters through this rather than through the definition. +* `Submodule.absMulHeight_eq`: over a number field the absolute height of a subspace is the + relative one normalized by the degree, as it is for tuples and for projective points. +* `Submodule.mulHeight_le_arakelovMulHeight`: the height of a subspace is at most its Arakelov + height, the comparison of Layer 0.2 read at the Plücker point. + +`Set.powersetCard.subsingleton_iff` and `Projectivization.mulHeight_eq_one_of_subsingleton` are +stated here because Mathlib has neither, although both are about Mathlib's own objects — the first +sits beside `Set.powersetCard.nontrivial_iff`, the second beside +`Projectivization.mulHeight`. They belong upstream rather than in this library. + +## Implementation notes + +The rank is not a parameter of the definition. `Submodule.mulHeight V` is +`Projectivization.mulHeight (V.pluckerPoint rfl)`, taking `k := Module.finrank K V`, so it is a +total function of `V` alone: `Submodule.mulHeight (V ⊓ W)` is well formed without producing a rank +for `V ⊓ W` first, which is what the submodularity of 3.6 needs. +`Submodule.mulHeight_eq_mulHeight_pluckerPoint` re-types it along an arbitrary +`hV : Module.finrank K V = k` — by `subst`, since `k` is a variable — and that lemma, not the +definition, is what every proof below uses. + +Both degenerate cases are one argument, and neither is a computation of Plücker coordinates: for +`Module.finrank K V = 0` and for `Module.finrank K V = Fintype.card ι` the index type +`Set.powersetCard ι (Module.finrank K V)` is a subsingleton — only `∅`, only `Finset.univ` — so +every tuple over it has height `1` by the product formula. In particular `mulHeight ⊤ = 1` does +not go through `exteriorPower.plucker_fin_eq_det`, which is stated for `ι = Fin n`. + +The four height families are separate sections rather than one definition parametrized by a +height, because their hypotheses differ: `Height.AdmissibleAbsValues K` for the first, +`NumberField K` for the Arakelov height, and `CharZero K` with `Algebra.IsAlgebraic ℚ K` for the +absolute one. As the roadmap's convention records, the duplication is in the statements only. + +## References + +E. Bombieri and W. Gubler, *Heights in Diophantine Geometry*, Cambridge University Press (2006), +2.8.4 for the Plücker construction, Definition 1.5.4 for the absolute sup-norm height, and +Definition 2.8.5 (p. 67) for the absolute Arakelov height of a subspace. +The relative variants here use unnormalized place-weighted products. `absMulHeight` and +`absLogHeight` give the absolute sup-norm variants; the Arakelov variants here remain relative. +W. M. Schmidt, "On heights of algebraic subspaces and diophantine approximations", *Annals of +Mathematics* **85** (1967), 430–472, §1, where the height of a subspace is defined through its +Grassmann coordinates. + +This is Layer 3.2 of the `ArithmeticHeights` roadmap. +-/ + +public section + +open Module + +/-! +### Two lemmas that belong upstream + +`Set.powersetCard.nontrivial_iff` is in Mathlib; its negation is not, and it is what makes the two +degenerate ranks of a subspace height degenerate. Mathlib's `Projectivization.mulHeight` has no +subsingleton lemma either, although the tuple-level `Height.mulHeight_eq_one_of_subsingleton` is +there. +-/ + +namespace Set.powersetCard + +/-- A type of `n`-element subsets is a subsingleton exactly when there is at most one such subset: +`n = 0`, when only `∅` qualifies, or `n` at least the cardinality, when only `Finset.univ` does. +This is the negation of `Set.powersetCard.nontrivial_iff`. -/ +theorem subsingleton_iff {α : Type*} [Finite α] {n : ℕ} : + Subsingleton (Set.powersetCard α n) ↔ n = 0 ∨ Nat.card α ≤ n := by + rw [← not_nontrivial_iff_subsingleton, nontrivial_iff] + omega + +end Set.powersetCard + +namespace Projectivization + +variable {K : Type*} [Field K] [Height.AdmissibleAbsValues K] {ι : Type*} [Finite ι] + +/-- On a subsingleton index type every point of projective space has height `1`; the projective +form of `Height.mulHeight_eq_one_of_subsingleton`. -/ +@[simp] +theorem mulHeight_eq_one_of_subsingleton [Subsingleton ι] (x : Projectivization K (ι → K)) : + mulHeight x = 1 := by + rw [← x.mk_rep, mulHeight_mk] + exact Height.mulHeight_eq_one_of_subsingleton _ + +@[simp] +theorem logHeight_eq_zero_of_subsingleton [Subsingleton ι] (x : Projectivization K (ι → K)) : + logHeight x = 0 := by + simp [logHeight_eq_log_mulHeight] + +end Projectivization + +namespace Submodule + +open exteriorPower + +section Degenerate + +variable {K : Type*} [Field K] {ι : Type*} [Fintype ι] {V : Submodule K (ι → K)} + +/-- The index type of the Plücker coordinates collapses to a point exactly in the two degenerate +ranks. Every `= 1` statement below is this lemma followed by the product formula. -/ +private lemma subsingleton_powersetCard_finrank + (h : finrank K V = 0 ∨ Fintype.card ι ≤ finrank K V) : + Subsingleton (Set.powersetCard ι (finrank K V)) := + Set.powersetCard.subsingleton_iff.2 (by rwa [Nat.card_eq_fintype_card]) + +end Degenerate + +variable {K : Type*} [Field K] {ι : Type*} [Fintype ι] [LinearOrder ι] {k : ℕ} + {V : Submodule K (ι → K)} + +/-! +### The height of a subspace +-/ + +section Relative + +variable [Height.AdmissibleAbsValues K] + +/-- **The multiplicative height of a linear subspace of `ι → K`**: the projective height of its +Plücker point, following Schmidt. The rank is read off `V` rather than supplied, so this is a +total function of `V`. -/ +noncomputable def mulHeight (V : Submodule K (ι → K)) : ℝ := + Projectivization.mulHeight (V.pluckerPoint rfl) + +/-- **The logarithmic height of a linear subspace of `ι → K`.** -/ +noncomputable def logHeight (V : Submodule K (ι → K)) : ℝ := + Projectivization.logHeight (V.pluckerPoint rfl) + +/-- The definition, re-typed along an arbitrary proof that `V` has rank `k`. Every proof enters +through this. -/ +theorem mulHeight_eq_mulHeight_pluckerPoint (hV : finrank K V = k) : + V.mulHeight = Projectivization.mulHeight (V.pluckerPoint hV) := by + subst hV + rfl + +/-- The logarithmic form of `Submodule.mulHeight_eq_mulHeight_pluckerPoint`. -/ +theorem logHeight_eq_logHeight_pluckerPoint (hV : finrank K V = k) : + V.logHeight = Projectivization.logHeight (V.pluckerPoint hV) := by + subst hV + rfl + +theorem logHeight_eq_log_mulHeight (V : Submodule K (ι → K)) : + V.logHeight = Real.log V.mulHeight := + Projectivization.logHeight_eq_log_mulHeight _ + +/-- The height of a subspace is at least `1`. -/ +theorem one_le_mulHeight (V : Submodule K (ι → K)) : 1 ≤ V.mulHeight := + Projectivization.one_le_mulHeight _ + +theorem mulHeight_pos (V : Submodule K (ι → K)) : 0 < V.mulHeight := + Projectivization.mulHeight_pos _ + +theorem mulHeight_ne_zero (V : Submodule K (ι → K)) : V.mulHeight ≠ 0 := + Projectivization.mulHeight_ne_zero _ + +theorem logHeight_nonneg (V : Submodule K (ι → K)) : 0 ≤ V.logHeight := + Projectivization.logHeight_nonneg _ + +/-- The zero subspace has height `1`: its exterior power is the line spanned by the empty wedge, +whose single coordinate is indexed by `∅`. -/ +@[simp] +theorem mulHeight_bot : (⊥ : Submodule K (ι → K)).mulHeight = 1 := + have := subsingleton_powersetCard_finrank (.inl (finrank_bot K (ι → K))) + Projectivization.mulHeight_eq_one_of_subsingleton _ + +@[simp] +theorem logHeight_bot : (⊥ : Submodule K (ι → K)).logHeight = 0 := by + rw [logHeight_eq_log_mulHeight, mulHeight_bot, Real.log_one] + +/-- The whole space has height `1`: its exterior power is the line spanned by the wedge of the +standard basis, whose single coordinate is indexed by `Finset.univ`. -/ +@[simp] +theorem mulHeight_top : (⊤ : Submodule K (ι → K)).mulHeight = 1 := + have := subsingleton_powersetCard_finrank (V := (⊤ : Submodule K (ι → K))) + (.inr (by rw [finrank_top, finrank_fintype_fun_eq_card])) + Projectivization.mulHeight_eq_one_of_subsingleton _ + +@[simp] +theorem logHeight_top : (⊤ : Submodule K (ι → K)).logHeight = 0 := by + rw [logHeight_eq_log_mulHeight, mulHeight_top, Real.log_one] + +/-- **The height of a line is the projective height of a point spanning it.** This is the +compatibility that makes the Plücker point the right carrier of the definition: in rank one the +coordinates are the coordinates of the vector itself, re-indexed along +`Set.powersetCard.ofSingleton`. -/ +theorem mulHeight_span_singleton {x : ι → K} (hx : x ≠ 0) : + (span K {x}).mulHeight = Height.mulHeight x := by + have hV : finrank K (span K {x} : Submodule K (ι → K)) = 1 := finrank_span_singleton hx + rw [mulHeight_eq_mulHeight_pluckerPoint hV, pluckerPoint_span_singleton hx hV, + Projectivization.mulHeight_mk, + ← Height.mulHeight_comp_equiv Set.powersetCard.ofSingleton (plucker 1 ![x]), plucker_one_comp] + +/-- The logarithmic form of `Submodule.mulHeight_span_singleton`. -/ +theorem logHeight_span_singleton {x : ι → K} (hx : x ≠ 0) : + (span K {x}).logHeight = Height.logHeight x := by + rw [logHeight_eq_log_mulHeight, mulHeight_span_singleton hx, + Height.logHeight_eq_log_mulHeight] + +/-- **The height of the span of a linearly independent family is the height of its tuple of +Plücker coordinates** — by `exteriorPower.plucker_apply`, of its tuple of maximal minors. This is +the form the matrix dictionary of 3.3 consumes. -/ +theorem mulHeight_span_range {v : Fin k → (ι → K)} (hv : LinearIndependent K v) : + (span K (Set.range v)).mulHeight = Height.mulHeight (plucker k v) := by + have hV : finrank K (span K (Set.range v)) = k := + (finrank_span_eq_card hv).trans (Fintype.card_fin k) + rw [mulHeight_eq_mulHeight_pluckerPoint hV, pluckerPoint_span_range hv hV, + Projectivization.mulHeight_mk] + +/-- The logarithmic form of `Submodule.mulHeight_span_range`. -/ +theorem logHeight_span_range {v : Fin k → (ι → K)} (hv : LinearIndependent K v) : + (span K (Set.range v)).logHeight = Height.logHeight (plucker k v) := by + rw [logHeight_eq_log_mulHeight, mulHeight_span_range hv, Height.logHeight_eq_log_mulHeight] + +end Relative + +/-! +### The Arakelov height of a subspace + +The same definition with the ℓ² norm at the archimedean places, which is the normalization the +Siegel-lemma literature states its constants in. +-/ + +section Arakelov + +variable [NumberField K] + +/-- **The relative multiplicative Arakelov height of a linear subspace of `ι → K`**, +`exp([K : ℚ] · h_Ar(V))` in the absolute logarithmic convention of +Bombieri–Gubler Definition 2.8.5. -/ +noncomputable def arakelovMulHeight (V : Submodule K (ι → K)) : ℝ := + Projectivization.arakelovMulHeight (V.pluckerPoint rfl) + +/-- **The relative logarithmic Arakelov height of a linear subspace of `ι → K`**, +`[K : ℚ] · h_Ar(V)` in the absolute convention of Bombieri–Gubler Definition 2.8.5. -/ +noncomputable def arakelovLogHeight (V : Submodule K (ι → K)) : ℝ := + Projectivization.arakelovLogHeight (V.pluckerPoint rfl) + +theorem arakelovMulHeight_eq_arakelovMulHeight_pluckerPoint (hV : finrank K V = k) : + V.arakelovMulHeight = Projectivization.arakelovMulHeight (V.pluckerPoint hV) := by + subst hV + rfl + +theorem arakelovLogHeight_eq_arakelovLogHeight_pluckerPoint (hV : finrank K V = k) : + V.arakelovLogHeight = Projectivization.arakelovLogHeight (V.pluckerPoint hV) := by + subst hV + rfl + +theorem arakelovLogHeight_eq_log_arakelovMulHeight (V : Submodule K (ι → K)) : + V.arakelovLogHeight = Real.log V.arakelovMulHeight := + Projectivization.arakelovLogHeight_eq_log_arakelovMulHeight _ + +theorem one_le_arakelovMulHeight (V : Submodule K (ι → K)) : 1 ≤ V.arakelovMulHeight := + Projectivization.one_le_arakelovMulHeight _ + +theorem arakelovMulHeight_pos (V : Submodule K (ι → K)) : 0 < V.arakelovMulHeight := + Projectivization.arakelovMulHeight_pos _ + +theorem arakelovMulHeight_ne_zero (V : Submodule K (ι → K)) : V.arakelovMulHeight ≠ 0 := + Projectivization.arakelovMulHeight_ne_zero _ + +theorem arakelovLogHeight_nonneg (V : Submodule K (ι → K)) : 0 ≤ V.arakelovLogHeight := + Projectivization.arakelovLogHeight_nonneg _ + +@[simp] +theorem arakelovMulHeight_bot : (⊥ : Submodule K (ι → K)).arakelovMulHeight = 1 := + have := subsingleton_powersetCard_finrank (.inl (finrank_bot K (ι → K))) + Projectivization.arakelovMulHeight_eq_one_of_subsingleton _ + +@[simp] +theorem arakelovLogHeight_bot : (⊥ : Submodule K (ι → K)).arakelovLogHeight = 0 := by + rw [arakelovLogHeight_eq_log_arakelovMulHeight, arakelovMulHeight_bot, Real.log_one] + +@[simp] +theorem arakelovMulHeight_top : (⊤ : Submodule K (ι → K)).arakelovMulHeight = 1 := + have := subsingleton_powersetCard_finrank (V := (⊤ : Submodule K (ι → K))) + (.inr (by rw [finrank_top, finrank_fintype_fun_eq_card])) + Projectivization.arakelovMulHeight_eq_one_of_subsingleton _ + +@[simp] +theorem arakelovLogHeight_top : (⊤ : Submodule K (ι → K)).arakelovLogHeight = 0 := by + rw [arakelovLogHeight_eq_log_arakelovMulHeight, arakelovMulHeight_top, Real.log_one] + +/-- **The Arakelov height of a line is the projective Arakelov height of a point spanning it.** -/ +theorem arakelovMulHeight_span_singleton {x : ι → K} (hx : x ≠ 0) : + (span K {x}).arakelovMulHeight = NumberField.arakelovMulHeight x := by + have hV : finrank K (span K {x} : Submodule K (ι → K)) = 1 := finrank_span_singleton hx + rw [arakelovMulHeight_eq_arakelovMulHeight_pluckerPoint hV, pluckerPoint_span_singleton hx hV, + Projectivization.arakelovMulHeight_mk, + ← NumberField.arakelovMulHeight_comp_equiv Set.powersetCard.ofSingleton (plucker 1 ![x]), + plucker_one_comp] + +/-- The logarithmic form of `Submodule.arakelovMulHeight_span_singleton`. -/ +theorem arakelovLogHeight_span_singleton {x : ι → K} (hx : x ≠ 0) : + (span K {x}).arakelovLogHeight = NumberField.arakelovLogHeight x := by + rw [arakelovLogHeight_eq_log_arakelovMulHeight, arakelovMulHeight_span_singleton hx, + NumberField.arakelovLogHeight_eq_log_arakelovMulHeight] + +/-- **The Arakelov height of the span of a linearly independent family is the Arakelov height of +its tuple of maximal minors.** -/ +theorem arakelovMulHeight_span_range {v : Fin k → (ι → K)} (hv : LinearIndependent K v) : + (span K (Set.range v)).arakelovMulHeight = NumberField.arakelovMulHeight (plucker k v) := by + have hV : finrank K (span K (Set.range v)) = k := + (finrank_span_eq_card hv).trans (Fintype.card_fin k) + rw [arakelovMulHeight_eq_arakelovMulHeight_pluckerPoint hV, pluckerPoint_span_range hv hV, + Projectivization.arakelovMulHeight_mk] + +/-- The logarithmic form of `Submodule.arakelovMulHeight_span_range`. -/ +theorem arakelovLogHeight_span_range {v : Fin k → (ι → K)} (hv : LinearIndependent K v) : + (span K (Set.range v)).arakelovLogHeight = NumberField.arakelovLogHeight (plucker k v) := by + rw [arakelovLogHeight_eq_log_arakelovMulHeight, arakelovMulHeight_span_range hv, + NumberField.arakelovLogHeight_eq_log_arakelovMulHeight] + +/-- **The height of a subspace is at most its Arakelov height**, with no constant: the two +normalizations differ only at the archimedean places, where `‖·‖_∞ ≤ ‖·‖_2`. This is +`Projectivization.mulHeight_le_arakelovMulHeight` at the Plücker point, and it is what carries +Northcott for subspaces (3.7) from one normalization to the other. + +The reverse bound is not stated: it carries the constant +`(Fintype.card ι).choose (finrank K V) ^ (Height.totalWeight K / 2)`, the number of Plücker +coordinates rather than the dimension of the ambient space, and nothing below needs it. -/ +theorem mulHeight_le_arakelovMulHeight (V : Submodule K (ι → K)) : + V.mulHeight ≤ V.arakelovMulHeight := by + rw [mulHeight_eq_mulHeight_pluckerPoint (k := finrank K V) rfl, + arakelovMulHeight_eq_arakelovMulHeight_pluckerPoint (k := finrank K V) rfl] + exact Projectivization.mulHeight_le_arakelovMulHeight _ + +/-- The logarithmic form of `Submodule.mulHeight_le_arakelovMulHeight`. -/ +theorem logHeight_le_arakelovLogHeight (V : Submodule K (ι → K)) : + V.logHeight ≤ V.arakelovLogHeight := by + rw [logHeight_eq_log_mulHeight, arakelovLogHeight_eq_log_arakelovMulHeight] + exact Real.log_le_log (mulHeight_pos V) (mulHeight_le_arakelovMulHeight V) + +end Arakelov + +/-! +### The absolute height of a subspace + +The sup-norm variant that does not depend on the field the subspace is written over. +`absLogHeight` is the literature's `h(V)`, and `absMulHeight` is its exponential. +This is one of the six Northcott statements of 3.7. +-/ + +section Absolute + +variable [CharZero K] [Algebra.IsAlgebraic ℚ K] + +/-- **The absolute multiplicative height of a linear subspace of `ι → K`**, +`exp(h(V))` via Bombieri–Gubler 2.8.4 and Definition 1.5.4. -/ +noncomputable def absMulHeight (V : Submodule K (ι → K)) : ℝ := + Projectivization.absMulHeight (V.pluckerPoint rfl) + +/-- **The absolute logarithmic height of a linear subspace of `ι → K`**, +`h(V)` via Bombieri–Gubler 2.8.4 and Definition 1.5.4. -/ +noncomputable def absLogHeight (V : Submodule K (ι → K)) : ℝ := + Projectivization.absLogHeight (V.pluckerPoint rfl) + +theorem absMulHeight_eq_absMulHeight_pluckerPoint (hV : finrank K V = k) : + V.absMulHeight = Projectivization.absMulHeight (V.pluckerPoint hV) := by + subst hV + rfl + +theorem absLogHeight_eq_absLogHeight_pluckerPoint (hV : finrank K V = k) : + V.absLogHeight = Projectivization.absLogHeight (V.pluckerPoint hV) := by + subst hV + rfl + +theorem absLogHeight_eq_log_absMulHeight (V : Submodule K (ι → K)) : + V.absLogHeight = Real.log V.absMulHeight := + Projectivization.absLogHeight_eq_log_absMulHeight _ + +theorem one_le_absMulHeight (V : Submodule K (ι → K)) : 1 ≤ V.absMulHeight := + Projectivization.one_le_absMulHeight _ + +theorem absMulHeight_pos (V : Submodule K (ι → K)) : 0 < V.absMulHeight := + Projectivization.absMulHeight_pos _ + +theorem absMulHeight_ne_zero (V : Submodule K (ι → K)) : V.absMulHeight ≠ 0 := + Projectivization.absMulHeight_ne_zero _ + +theorem absLogHeight_nonneg (V : Submodule K (ι → K)) : 0 ≤ V.absLogHeight := + Projectivization.absLogHeight_nonneg _ + +@[simp] +theorem absMulHeight_bot : (⊥ : Submodule K (ι → K)).absMulHeight = 1 := + have := subsingleton_powersetCard_finrank (.inl (finrank_bot K (ι → K))) + Projectivization.absMulHeight_eq_one_of_subsingleton _ + +@[simp] +theorem absLogHeight_bot : (⊥ : Submodule K (ι → K)).absLogHeight = 0 := by + rw [absLogHeight_eq_log_absMulHeight, absMulHeight_bot, Real.log_one] + +@[simp] +theorem absMulHeight_top : (⊤ : Submodule K (ι → K)).absMulHeight = 1 := + have := subsingleton_powersetCard_finrank (V := (⊤ : Submodule K (ι → K))) + (.inr (by rw [finrank_top, finrank_fintype_fun_eq_card])) + Projectivization.absMulHeight_eq_one_of_subsingleton _ + +@[simp] +theorem absLogHeight_top : (⊤ : Submodule K (ι → K)).absLogHeight = 0 := by + rw [absLogHeight_eq_log_absMulHeight, absMulHeight_top, Real.log_one] + +/-- **The absolute height of a line is the absolute height of a point spanning it.** -/ +theorem absMulHeight_span_singleton {x : ι → K} (hx : x ≠ 0) : + (span K {x}).absMulHeight = NumberField.absMulHeight x := by + have hV : finrank K (span K {x} : Submodule K (ι → K)) = 1 := finrank_span_singleton hx + rw [absMulHeight_eq_absMulHeight_pluckerPoint hV, pluckerPoint_span_singleton hx hV, + Projectivization.absMulHeight_mk, + ← NumberField.absMulHeight_comp_equiv Set.powersetCard.ofSingleton (plucker 1 ![x]), + plucker_one_comp] + +/-- The logarithmic form of `Submodule.absMulHeight_span_singleton`. -/ +theorem absLogHeight_span_singleton {x : ι → K} (hx : x ≠ 0) : + (span K {x}).absLogHeight = NumberField.absLogHeight x := by + rw [absLogHeight_eq_log_absMulHeight, absMulHeight_span_singleton hx, + NumberField.absLogHeight_eq_log_absMulHeight] + +/-- **The absolute height of the span of a linearly independent family is the absolute height of +its tuple of maximal minors.** -/ +theorem absMulHeight_span_range {v : Fin k → (ι → K)} (hv : LinearIndependent K v) : + (span K (Set.range v)).absMulHeight = NumberField.absMulHeight (plucker k v) := by + have hV : finrank K (span K (Set.range v)) = k := + (finrank_span_eq_card hv).trans (Fintype.card_fin k) + rw [absMulHeight_eq_absMulHeight_pluckerPoint hV, pluckerPoint_span_range hv hV, + Projectivization.absMulHeight_mk] + +/-- The logarithmic form of `Submodule.absMulHeight_span_range`. -/ +theorem absLogHeight_span_range {v : Fin k → (ι → K)} (hv : LinearIndependent K v) : + (span K (Set.range v)).absLogHeight = NumberField.absLogHeight (plucker k v) := by + rw [absLogHeight_eq_log_absMulHeight, absMulHeight_span_range hv, + NumberField.absLogHeight_eq_log_absMulHeight] + +/-- **Over a number field the absolute height of a subspace is the relative one normalized by the +degree**, exactly as for tuples and for projective points. -/ +theorem absMulHeight_eq [NumberField K] (V : Submodule K (ι → K)) : + V.absMulHeight = V.mulHeight ^ ((finrank ℚ K : ℝ))⁻¹ := + Projectivization.absMulHeight_eq _ + +/-- The logarithmic form of `Submodule.absMulHeight_eq`. -/ +theorem absLogHeight_eq [NumberField K] (V : Submodule K (ι → K)) : + V.absLogHeight = ((finrank ℚ K : ℝ))⁻¹ * V.logHeight := by + rw [absLogHeight_eq_log_absMulHeight, absMulHeight_eq, Real.log_rpow (mulHeight_pos V), + logHeight_eq_log_mulHeight, mul_comm] + +end Absolute + +end Submodule + +/-! +### Worked examples +-/ + +section Examples + +open Submodule + +/-- **Acceptance test.** The height of the line `ℚ · (1, 3)` in `ℚ²` is `3`: the Plücker +coordinates of a line are the coordinates of a vector spanning it, and the projective height of +`(1, 3)` is `3`. -/ +example : (span ℚ {![(1 : ℚ), 3]}).mulHeight = 3 := by + rw [mulHeight_span_singleton (by simp), Height.mulHeight_swap, + ← Height.mulHeight₁_eq_mulHeight, Rat.mulHeight₁_eq_max] + norm_num + +/-- **Rejection test: the height of a subspace is not monotone under inclusion.** The line above +is contained in `⊤`, whose height is `1`. So no bound on the height of a subspace of `V` in terms +of the height of `V` is available, and in particular the vectors of a basis of `V` are not bounded +by `Submodule.mulHeight V`. Layer 5 bounds the vectors it produces through the successive minima +and never through the height of the space they span. -/ +example : (⊤ : Submodule ℚ (Fin 2 → ℚ)).mulHeight = 1 ∧ + (⊤ : Submodule ℚ (Fin 2 → ℚ)).mulHeight < (span ℚ {![(1 : ℚ), 3]}).mulHeight := by + refine ⟨mulHeight_top, ?_⟩ + rw [mulHeight_top] + rw [show (span ℚ {![(1 : ℚ), 3]}).mulHeight = 3 from by + rw [mulHeight_span_singleton (by simp), Height.mulHeight_swap, + ← Height.mulHeight₁_eq_mulHeight, Rat.mulHeight₁_eq_max] + norm_num] + norm_num + +/-- **Conformance.** The two degenerate ranks of the roadmap's milestone, and the compatibility +that fixes the definition, over an arbitrary field with admissible absolute values. -/ +example {K : Type*} [Field K] [Height.AdmissibleAbsValues K] {ι : Type*} [Fintype ι] + [LinearOrder ι] {x : ι → K} (hx : x ≠ 0) : + (⊥ : Submodule K (ι → K)).mulHeight = 1 ∧ (⊤ : Submodule K (ι → K)).mulHeight = 1 ∧ + (span K {x}).mulHeight = Height.mulHeight x := + ⟨mulHeight_bot, mulHeight_top, mulHeight_span_singleton hx⟩ + +end Examples + +end diff --git a/LeanPool/CCDN/ArithmeticHeights/SuccessiveMinima.lean b/LeanPool/CCDN/ArithmeticHeights/SuccessiveMinima.lean new file mode 100644 index 0000000000..b765bca841 --- /dev/null +++ b/LeanPool/CCDN/ArithmeticHeights/SuccessiveMinima.lean @@ -0,0 +1,648 @@ +/- +Copyright (c) 2026 Ralf Stephan. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Ralf Stephan +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import Mathlib.Algebra.Module.ZLattice.Covolume +public import Mathlib.Analysis.Convex.Gauge +public import Mathlib.MeasureTheory.Group.GeometryOfNumbers + +/-! +# The successive minima of a symmetric convex body + +For a lattice `L` in a finite-dimensional real normed space `E` and a symmetric convex body `B`, +the `i`-th successive minimum is the least dilation of `B` that contains `i + 1` linearly +independent points of `L`. This file defines it, proves it positive, monotone and homogeneous of +degree `-1` in the body, and proves Cassels' Lemma 1: the minima are attained, by one family of +independent lattice vectors realizing all of them at once. + +## Main definitions + +* `ZLattice.successiveMinimum`: the `i`-th successive minimum, as the infimum of the admissible + dilations. For `i ≥ finrank ℝ E` there are none and the value is `sInf ∅ = 0`, so every + statement below carries `i < finrank ℝ E`. + +## Main results + +* `gauge_le_iff_mem_smul`: the dictionary between Cassels' distance functions and the convex-body + form pinned by the roadmap. Membership in `t • B` is having gauge at most `t`, for a **closed** + body; `mem_smul_of_gauge_lt` is the half that needs no closedness. +* `ZLattice.successiveMinimum_pos`: the minima are positive, which is where boundedness of the + body is used, and `ZLattice.successiveMinimum_eq_zero_of_le`: they vanish above the dimension. +* `ZLattice.successiveMinimum_le_of_le` and `ZLattice.successiveMinimum_smul`: monotone in the + index, homogeneous of degree `-1` in the body. +* `ZLattice.exists_linearIndependent_gauge_eq_successiveMinimum`: **Cassels' Lemma 1**. There is + one linearly independent family of lattice vectors whose gauges are the successive minima. This + is what every later proof consumes; its body form is + `ZLattice.exists_linearIndependent_mem_smul_successiveMinimum`, which is where closedness of + the body is used. +* `Finset.prod_pow_le_prod_range`: the regrouping that turns a bound on the product of all `d * k` + minima into a bound on a product of `k` `d`-th powers — what Layers 5.3 and 5.4 spend to pass + from the minima of the lattice of a `k`-dimensional `K`-subspace to `k` heights. +* `ZLattice.successiveMinimum_zero_eq`: the zeroth minimum is the least dilation containing a + nonzero lattice point — the quantity Minkowski's convex-body theorem bounds — and + `ZLattice.successiveMinimum_zero_le_one` is that theorem read as the case `i = 0`. + +## Implementation notes + +The body is carried as four separate hypotheses — convex, symmetric, with nonempty interior, +bounded — plus closedness where it is needed, rather than as one bundled structure, so that each +statement says which of them it uses. Cassels indexes the minima by a distance function `F` and +dilates `{x | F x ≤ 1}`; the convex-body form is the pinned one, and `gauge_le_iff_mem_smul` is +the translation, proved once here. + +⚠ **Attainment does not need the body to be closed; only its body form does.** The family of +`ZLattice.exists_linearIndependent_gauge_eq_successiveMinimum` realizes the minima as *gauges* +for any bounded symmetric convex body with nonempty interior, and that statement is the one the +proof produces. Turning `gauge B (v j) = λ j` into `v j ∈ λ j • B` is exactly the dictionary, and +exactly what fails for an open body: for the open unit ball no lattice vector lies in `λ 0 • B` +at all, since anything inside that dilation would already have shown a smaller dilation +admissible. The two statements are therefore kept apart, and only the second carries `IsClosed B`. + +⚠ **The minima are not monotone in the index without `i < finrank ℝ E`.** Above the dimension +there is no independent family at all, the set of admissible dilations is empty, and the value is +`sInf ∅ = 0`. So `Monotone (successiveMinimum L B)` is false whenever `E` is nontrivial, and a +statement of Minkowski's second theorem that ranged over all of `ℕ` would multiply by zero. + +## References + +J. W. S. Cassels, *An Introduction to the Geometry of Numbers*, Springer (1959), Chapter VIII §1: +the definition, Lemma 1 (attainment) and the elementary properties proved here. E. Bombieri and +W. Gubler, *Heights in Diophantine Geometry*, Cambridge University Press (2006), Appendix C.2. +Minkowski's first theorem is Mathlib's +`MeasureTheory.exists_ne_zero_mem_lattice_of_measure_mul_two_pow_le_measure`. + +This is Layer 4.1 of the `ArithmeticHeights` roadmap. +-/ + +public section + +open Metric Module MeasureTheory Set + +open scoped Pointwise Topology + +variable {E : Type*} [NormedAddCommGroup E] [NormedSpace ℝ E] {B : Set E} + +/-! ### The body and its gauge + +Three facts about a symmetric convex body, none of them about lattices: it is a neighbourhood of +the origin, and its dilations are the sublevel sets of its gauge — from below always, and exactly +when the body is closed. -/ + +/-- A symmetric convex set with nonempty interior is a neighbourhood of `0`: the midpoint of an +interior point and its negative is an interior point. This is what makes the body absorbent, and +it is the only use of the symmetry hypothesis in the elementary theory. -/ +theorem Convex.mem_nhds_zero_of_symmetric (hB₀ : Convex ℝ B) (hB₁ : ∀ x ∈ B, -x ∈ B) + (hB₂ : (interior B).Nonempty) : B ∈ 𝓝 (0 : E) := by + obtain ⟨x, hx⟩ := hB₂ + have hneg : -x ∈ B := hB₁ x (interior_subset hx) + have h0 : (0 : E) ∈ interior B := by + have := hB₀.combo_interior_self_mem_interior hx hneg (a := 1 / 2) (b := 1 / 2) + (by norm_num) (by norm_num) (by norm_num) + simpa using this + exact mem_interior_iff_mem_nhds.1 h0 + +/-- Half of the gauge dictionary, and the half that costs nothing: a point of gauge strictly less +than `t` lies in `t • B`. No closedness is needed, and this is what the existence statements below +use to exhibit an admissible dilation. -/ +theorem mem_smul_of_gauge_lt (hB₀ : Convex ℝ B) (h₀ : B ∈ 𝓝 (0 : E)) {t : ℝ} {x : E} + (h : gauge B x < t) : x ∈ t • B := by + have hx : x ∈ {y : E | gauge B y ≤ gauge B x} := by simp + rw [setOfPred_gauge_le_eq hB₀ (mem_of_mem_nhds h₀) (absorbent_nhds_zero h₀) + (gauge_nonneg x)] at hx + exact mem_iInter₂.1 hx t h + +/-- **The gauge dictionary.** For a closed convex body with `0` in its interior, lying in the +dilation `t • B` is the same as having gauge at most `t`. This is the translation between Cassels' +distance functions and the convex-body form of the successive minima, and closedness is exactly +what it costs: for an open body the two sides differ on the boundary. -/ +theorem gauge_le_iff_mem_smul (hB₀ : Convex ℝ B) (hB₄ : IsClosed B) (h₀ : B ∈ 𝓝 (0 : E)) {t : ℝ} + (ht : 0 < t) {x : E} : gauge B x ≤ t ↔ x ∈ t • B := by + refine ⟨fun h ↦ ?_, gauge_le_of_mem ht.le⟩ + have h1 : gauge B (t⁻¹ • x) ≤ 1 := by + rw [gauge_smul_of_nonneg (inv_nonneg.2 ht.le), smul_eq_mul, inv_mul_le_iff₀ ht, mul_one] + exact h + have h2 : t⁻¹ • x ∈ B := by + rw [← hB₄.closure_eq] + exact (gauge_le_one_iff_mem_closure hB₀ h₀).1 h1 + rwa [mem_smul_set_iff_inv_smul_mem₀ ht.ne'] + +namespace ZLattice + +variable {i : ℕ} + +/-- The `i`-th **successive minimum** of a set `B` with respect to a lattice `L`: the least +dilation of `B` containing `i + 1` linearly independent points of `L`. The case `i = 0` is the +quantity bounded by Minkowski's convex-body theorem. For `i ≥ finrank ℝ E` there is no such +family and the value is `sInf ∅ = 0`, so every statement about the minima carries +`i < finrank ℝ E`. -/ +noncomputable def successiveMinimum (L : Submodule ℤ E) (B : Set E) (i : ℕ) : ℝ := + sInf {t : ℝ | 0 < t ∧ ∃ v : Fin (i + 1) → E, + (∀ j, v j ∈ (t • B) ∩ (L : Set E)) ∧ LinearIndependent ℝ v} + +variable {L : Submodule ℤ E} + +/-- An admissible dilation bounds the minimum from above. This and `le_successiveMinimum` are the +only two ways the infimum is used below. -/ +theorem successiveMinimum_le {t : ℝ} (ht : 0 < t) {v : Fin (i + 1) → E} + (hv : ∀ j, v j ∈ (t • B) ∩ (L : Set E)) (hind : LinearIndependent ℝ v) : + successiveMinimum L B i ≤ t := + csInf_le ⟨0, fun _ hs ↦ hs.1.le⟩ ⟨ht, v, hv, hind⟩ + +/-- The minima are homogeneous of degree `-1` in the body: dilating the body by `c` divides every +minimum by `c`. -/ +theorem successiveMinimum_smul (L : Submodule ℤ E) (B : Set E) (i : ℕ) {c : ℝ} (hc : 0 < c) : + successiveMinimum L (c • B) i = c⁻¹ * successiveMinimum L B i := by + rw [successiveMinimum, successiveMinimum, ← smul_eq_mul, + ← Real.sInf_smul_of_nonneg (inv_nonneg.2 hc.le)] + congr 1 + ext t + simp only [mem_ofPred_eq, Set.mem_smul_set, smul_eq_mul] + constructor + · rintro ⟨ht, v, hv, hind⟩ + refine ⟨t * c, ⟨by positivity, v, fun j ↦ ⟨?_, (hv j).2⟩, hind⟩, by field_simp⟩ + have := (hv j).1 + rwa [smul_smul] at this + · rintro ⟨s, ⟨hs, v, hv, hind⟩, rfl⟩ + have heq : c⁻¹ * s * c = s := by field_simp + refine ⟨by positivity, v, fun j ↦ ⟨?_, (hv j).2⟩, hind⟩ + rw [smul_smul, heq] + exact (hv j).1 + +variable [FiniteDimensional ℝ E] + +section Elementary + +variable [DiscreteTopology L] [IsZLattice ℝ L] + +/-- A `ℤ`-basis of a lattice of `E`, read as an `ℝ`-basis of `E` indexed by `Fin (finrank ℝ E)`. +Every basis vector is a lattice point, which is what makes the set of admissible dilations +nonempty. -/ +private theorem exists_basis_mem (L : Submodule ℤ E) [DiscreteTopology L] [IsZLattice ℝ L] : + ∃ b : Basis (Fin (finrank ℝ E)) ℝ E, ∀ j, b j ∈ L := by + classical + have hcard : Fintype.card (Module.Free.ChooseBasisIndex ℤ L) = finrank ℝ E := by + rw [← finrank_eq_card_chooseBasisIndex, ZLattice.rank ℝ L] + refine ⟨((Module.Free.chooseBasis ℤ L).ofZLatticeBasis ℝ L).reindex + (Fintype.equivFinOfCardEq hcard), fun j ↦ ?_⟩ + simp only [Basis.reindex_apply, Basis.ofZLatticeBasis_apply] + exact SetLike.coe_mem _ + +/-- Below the dimension there is an admissible dilation: a basis of `E` made of lattice vectors +is independent, and the body absorbs its finitely many members. -/ +private theorem exists_mem_minimumSet (hi : i < finrank ℝ E) (hB₀ : Convex ℝ B) + (hB₁ : ∀ x ∈ B, -x ∈ B) (hB₂ : (interior B).Nonempty) : + ∃ t : ℝ, 0 < t ∧ ∃ v : Fin (i + 1) → E, + (∀ j, v j ∈ (t • B) ∩ (L : Set E)) ∧ LinearIndependent ℝ v := by + have h₀ : B ∈ 𝓝 (0 : E) := hB₀.mem_nhds_zero_of_symmetric hB₁ hB₂ + obtain ⟨b, hb⟩ := exists_basis_mem L + set v : Fin (i + 1) → E := fun j ↦ b (j.castLE hi) + set t : ℝ := 1 + ∑ j, gauge B (v j) + have hlt : ∀ j, gauge B (v j) < t := fun j ↦ by + have : gauge B (v j) ≤ ∑ k, gauge B (v k) := + Finset.single_le_sum (fun k _ ↦ gauge_nonneg (v k)) (Finset.mem_univ j) + linarith + refine ⟨t, ?_, v, fun j ↦ ⟨mem_smul_of_gauge_lt hB₀ h₀ (hlt j), ?_⟩, ?_⟩ + · have : (0 : ℝ) ≤ ∑ j, gauge B (v j) := + Finset.sum_nonneg fun j _ ↦ gauge_nonneg (v j) + linarith + · exact hb _ + · exact b.linearIndependent.comp _ fun a c h ↦ Fin.castLE_injective hi h + +/-- Below the dimension, a lower bound for every admissible dilation is a lower bound for the +minimum. -/ +theorem le_successiveMinimum (hi : i < finrank ℝ E) (hB₀ : Convex ℝ B) (hB₁ : ∀ x ∈ B, -x ∈ B) + (hB₂ : (interior B).Nonempty) {c : ℝ} + (H : ∀ t : ℝ, 0 < t → ∀ v : Fin (i + 1) → E, (∀ j, v j ∈ (t • B) ∩ (L : Set E)) → + LinearIndependent ℝ v → c ≤ t) : + c ≤ successiveMinimum L B i := + le_csInf (exists_mem_minimumSet hi hB₀ hB₁ hB₂) fun _ ht ↦ H _ ht.1 _ ht.2.choose_spec.1 + ht.2.choose_spec.2 + +end Elementary + +/-- Above the dimension the minima vanish: there is no independent family of `i + 1` vectors at +all, so the set of admissible dilations is empty and its infimum is the junk value `0`. Together +with `successiveMinimum_pos` this is why every statement about the minima is restricted to +`i < finrank ℝ E`. -/ +theorem successiveMinimum_eq_zero_of_le (L : Submodule ℤ E) (B : Set E) (hi : finrank ℝ E ≤ i) : + successiveMinimum L B i = 0 := by + have hempty : {t : ℝ | 0 < t ∧ ∃ v : Fin (i + 1) → E, + (∀ j, v j ∈ (t • B) ∩ (L : Set E)) ∧ LinearIndependent ℝ v} = ∅ := by + ext t + simp only [mem_ofPred_eq, mem_empty_iff_false, iff_false, not_and] + rintro - + rintro ⟨v, -, hind⟩ + have := hind.fintype_card_le_finrank + simp only [Fintype.card_fin] at this + omega + rw [successiveMinimum, hempty, Real.sInf_empty] + +/-- The minima are monotone in the index below the dimension: an independent family of `j + 1` +vectors contains one of `i + 1` vectors, so every dilation admissible at `j` is admissible at +`i`. The hypothesis `j < finrank ℝ E` is not removable — see `successiveMinimum_eq_zero_of_le`. -/ +theorem successiveMinimum_le_of_le [DiscreteTopology L] [IsZLattice ℝ L] {j : ℕ} (hij : i ≤ j) + (hj : j < finrank ℝ E) (hB₀ : Convex ℝ B) (hB₁ : ∀ x ∈ B, -x ∈ B) + (hB₂ : (interior B).Nonempty) : + successiveMinimum L B i ≤ successiveMinimum L B j := by + refine le_csInf (exists_mem_minimumSet hj hB₀ hB₁ hB₂) ?_ + rintro t ⟨ht, v, hv, hind⟩ + have hle : i + 1 ≤ j + 1 := by omega + refine successiveMinimum_le ht (v := fun k ↦ v (k.castLE hle)) (fun k ↦ hv _) ?_ + exact hind.comp _ fun a c h ↦ Fin.castLE_injective hle h + +section Discrete + +variable [DiscreteTopology L] + +/-- A bounded gauge slice meets the lattice in a finite set: the slice is bounded because the body +is, and the lattice is discrete and closed. This is the one place discreteness of the lattice is +used, and every attainment statement below rests on it. -/ +private theorem finite_gauge_le_inter (L : Submodule ℤ E) [DiscreteTopology L] + (h₀ : B ∈ 𝓝 (0 : E)) (hB₃ : Bornology.IsBounded B) (t : ℝ) : + ({x : E | gauge B x ≤ t} ∩ (L : Set E)).Finite := by + obtain ⟨R, hR, hRB⟩ := hB₃.subset_closedBall_lt 0 0 + refine Metric.finite_isBounded_inter_isClosed ?_ ?_ ?_ + · exact DiscreteTopology.isDiscrete + · refine (Metric.isBounded_closedBall (x := (0 : E)) (r := R * t)).subset fun x hx ↦ ?_ + have hx' : ‖x‖ / R ≤ t := + le_trans (le_gauge_of_subset_closedBall (absorbent_nhds_zero h₀) hR.le hRB) hx + rw [mem_closedBall_zero_iff] + rw [div_le_iff₀ hR] at hx' + linarith [hx', mul_comm t R] + · have : DiscreteTopology L.toAddSubgroup := inferInstanceAs (DiscreteTopology L) + rw [← Submodule.coe_toAddSubgroup] + exact AddSubgroup.isClosed_of_discreteTopology + +/-- The gauge attains its minimum on any nonempty set of lattice points: the points of gauge at +most that of a chosen member form a finite set. -/ +private theorem exists_gauge_min (L : Submodule ℤ E) [DiscreteTopology L] (h₀ : B ∈ 𝓝 (0 : E)) + (hB₃ : Bornology.IsBounded B) {S : Set E} (hS : S ⊆ (L : Set E)) (hne : S.Nonempty) : + ∃ w ∈ S, ∀ y ∈ S, gauge B w ≤ gauge B y := by + obtain ⟨x₀, hx₀⟩ := hne + have hfin : (S ∩ {x : E | gauge B x ≤ gauge B x₀}).Finite := by + refine (finite_gauge_le_inter L h₀ hB₃ (gauge B x₀)).subset fun x hx ↦ ⟨hx.2, hS hx.1⟩ + obtain ⟨w, hw, hmin⟩ := Set.exists_min_image _ (gauge B) hfin ⟨x₀, hx₀, by simp⟩ + refine ⟨w, hw.1, fun y hy ↦ ?_⟩ + by_cases hy' : gauge B y ≤ gauge B x₀ + · exact hmin y ⟨hy, hy'⟩ + · exact le_trans (hmin x₀ ⟨hx₀, by simp⟩) (not_le.1 hy').le + +variable [IsZLattice ℝ L] + +/-- Nonzero lattice points have gauge bounded away from zero. This is where boundedness of the +body is used: the body is inside a ball, so a point of small gauge is a point of small norm, and +the lattice has none but `0`. -/ +private theorem exists_pos_le_gauge (L : Submodule ℤ E) [DiscreteTopology L] [IsZLattice ℝ L] + (h₀ : B ∈ 𝓝 (0 : E)) (hB₃ : Bornology.IsBounded B) (hE : 0 < finrank ℝ E) : + ∃ δ : ℝ, 0 < δ ∧ ∀ x ∈ (L : Set E), x ≠ 0 → δ ≤ gauge B x := by + obtain ⟨b, hb⟩ := exists_basis_mem L + have hne : {x : E | x ∈ (L : Set E) ∧ x ≠ 0}.Nonempty := + ⟨b ⟨0, hE⟩, hb _, b.ne_zero _⟩ + obtain ⟨w, hw, hwmin⟩ := exists_gauge_min L h₀ hB₃ (fun y hy ↦ hy.1) hne + refine ⟨gauge B w, ?_, fun x hx hx0 ↦ hwmin x ⟨hx, hx0⟩⟩ + rw [gauge_pos (absorbent_nhds_zero h₀) ((NormedSpace.isVonNBounded_iff ℝ).2 hB₃)] + exact hw.2 + +/-- **The minima are positive.** A nonzero lattice point has gauge bounded away from zero, so no +small dilation of a bounded body can contain one. -/ +theorem successiveMinimum_pos (L : Submodule ℤ E) [DiscreteTopology L] [IsZLattice ℝ L] + (hB₀ : Convex ℝ B) (hB₁ : ∀ x ∈ B, -x ∈ B) (hB₂ : (interior B).Nonempty) + (hB₃ : Bornology.IsBounded B) (hi : i < finrank ℝ E) : 0 < successiveMinimum L B i := by + have h₀ : B ∈ 𝓝 (0 : E) := hB₀.mem_nhds_zero_of_symmetric hB₁ hB₂ + obtain ⟨δ, hδ, hlow⟩ := exists_pos_le_gauge L h₀ hB₃ (by omega) + refine lt_of_lt_of_le hδ (le_successiveMinimum hi hB₀ hB₁ hB₂ fun t ht v hv hind ↦ ?_) + exact (hlow (v 0) (hv 0).2 (hind.ne_zero 0)).trans (gauge_le_of_mem ht.le (hv 0).1) + +end Discrete + +section Attainment + +variable [DiscreteTopology L] [IsZLattice ℝ L] + +/-- The inductive step of Cassels' Lemma 1, carrying the minimality of the family over the +complement of its own span: that clause is what makes the gauges increase along the family, and +it is the reason the construction is greedy rather than a choice for each index separately. -/ +private theorem exists_gauge_eq_aux (L : Submodule ℤ E) [DiscreteTopology L] [IsZLattice ℝ L] + (hB₀ : Convex ℝ B) (hB₁ : ∀ x ∈ B, -x ∈ B) (hB₂ : (interior B).Nonempty) + (hB₃ : Bornology.IsBounded B) : + ∀ m : ℕ, m ≤ finrank ℝ E → ∃ v : Fin m → E, (∀ j, v j ∈ (L : Set E)) ∧ + LinearIndependent ℝ v ∧ (∀ j : Fin m, gauge B (v j) = successiveMinimum L B j) ∧ + ∀ y ∈ (L : Set E), y ∉ Submodule.span ℝ (Set.range v) → ∀ j, gauge B (v j) ≤ gauge B y := by + have h₀ : B ∈ 𝓝 (0 : E) := hB₀.mem_nhds_zero_of_symmetric hB₁ hB₂ + intro m + induction m with + | zero => + exact fun _ ↦ ⟨Fin.elim0, fun j ↦ j.elim0, linearIndependent_empty_type, fun j ↦ j.elim0, + fun _ _ _ j ↦ j.elim0⟩ + | succ m ih => + intro hm + obtain ⟨v, hvL, hvind, hvmin, hvlow⟩ := ih (by omega) + have hrank : finrank ℝ (Submodule.span ℝ (Set.range v)) = m := by + rw [finrank_span_eq_card hvind, Fintype.card_fin] + have hWlt : Submodule.span ℝ (Set.range v) < ⊤ := + Submodule.lt_top_of_finrank_lt_finrank (by rw [hrank]; omega) + have hne : {y : E | y ∈ (L : Set E) ∧ y ∉ Submodule.span ℝ (Set.range v)}.Nonempty := by + by_contra hcon + rw [not_nonempty_iff_eq_empty, eq_empty_iff_forall_notMem] at hcon + refine hWlt.ne (top_le_iff.1 ?_) + rw [← (IsZLattice.span_top (K := ℝ) (L := L) : Submodule.span ℝ (L : Set E) = ⊤)] + refine Submodule.span_le.2 fun y hy ↦ ?_ + by_contra hyW + exact hcon y ⟨hy, hyW⟩ + obtain ⟨w, hw, hwmin⟩ := exists_gauge_min L h₀ hB₃ (fun y hy ↦ hy.1) hne + obtain ⟨hwL, hwW⟩ := hw + have hgle : ∀ j : Fin (m + 1), gauge B ((Fin.snoc v w : Fin (m + 1) → E) j) ≤ gauge B w := by + refine Fin.lastCases ?_ fun k ↦ ?_ + · simp + · simpa using hvlow w hwL hwW k + have hv'ind : LinearIndependent ℝ (Fin.snoc v w : Fin (m + 1) → E) := hvind.finSnoc hwW + have hv'L : ∀ j : Fin (m + 1), (Fin.snoc v w : Fin (m + 1) → E) j ∈ (L : Set E) := by + refine Fin.lastCases ?_ fun k ↦ ?_ + · simpa using hwL + · simpa using hvL k + have hgw : gauge B w = successiveMinimum L B m := by + refine le_antisymm ?_ ?_ + · refine le_successiveMinimum (by omega) hB₀ hB₁ hB₂ fun t ht u hu huind ↦ ?_ + have hex : ∃ k, u k ∉ Submodule.span ℝ (Set.range v) := by + by_contra hcon + rw [not_exists] at hcon + have hle : Submodule.span ℝ (Set.range u) ≤ Submodule.span ℝ (Set.range v) := by + refine Submodule.span_le.2 ?_ + rintro _ ⟨k, rfl⟩ + exact not_not.1 (hcon k) + have := Submodule.finrank_mono hle + rw [finrank_span_eq_card huind, Fintype.card_fin, hrank] at this + omega + obtain ⟨k, hk⟩ := hex + exact (hwmin (u k) ⟨(hu k).2, hk⟩).trans (gauge_le_of_mem ht.le (hu k).1) + · refine le_of_forall_gt_imp_ge_of_dense fun t ht ↦ ?_ + refine successiveMinimum_le (lt_of_le_of_lt (gauge_nonneg w) ht) + (v := Fin.snoc v w) (fun j ↦ ⟨?_, hv'L j⟩) hv'ind + exact mem_smul_of_gauge_lt hB₀ h₀ (lt_of_le_of_lt (hgle j) ht) + refine ⟨Fin.snoc v w, hv'L, hv'ind, ?_, ?_⟩ + · refine Fin.lastCases ?_ fun k ↦ ?_ + · simpa using hgw + · simpa using hvmin k + · intro y hy hyW + have hsub : Set.range v ⊆ Set.range (Fin.snoc v w : Fin (m + 1) → E) := by + rw [Set.range_subset_iff] + exact fun k ↦ ⟨k.castSucc, by simp⟩ + have hyW' : y ∉ Submodule.span ℝ (Set.range v) := fun h ↦ + hyW (Submodule.span_mono hsub h) + refine Fin.lastCases ?_ fun k ↦ ?_ + · simpa using hwmin y ⟨hy, hyW'⟩ + · simpa using hvlow y hy hyW' k + +/-- **Cassels' Lemma 1: the successive minima are attained.** There is a single family of +`finrank ℝ E` linearly independent lattice vectors whose gauges are the successive minima — not +one family for each index. This is the statement every later proof consumes. + +⚠ No closedness of the body is needed here; see +`exists_linearIndependent_mem_smul_successiveMinimum` for the body form, which does need it. -/ +theorem exists_linearIndependent_gauge_eq_successiveMinimum (L : Submodule ℤ E) + [DiscreteTopology L] [IsZLattice ℝ L] (hB₀ : Convex ℝ B) (hB₁ : ∀ x ∈ B, -x ∈ B) + (hB₂ : (interior B).Nonempty) (hB₃ : Bornology.IsBounded B) : + ∃ v : Fin (finrank ℝ E) → E, (∀ j, v j ∈ (L : Set E)) ∧ LinearIndependent ℝ v ∧ + ∀ j : Fin (finrank ℝ E), gauge B (v j) = successiveMinimum L B j := + let ⟨v, hvL, hvind, hvmin, _⟩ := exists_gauge_eq_aux L hB₀ hB₁ hB₂ hB₃ (finrank ℝ E) le_rfl + ⟨v, hvL, hvind, hvmin⟩ + +/-- The body form of Cassels' Lemma 1: the independent lattice vectors realizing the minima lie in +the corresponding dilations of the body. This is the form Minkowski's second theorem and the basis +lemma of Layer 4.6 consume, and `IsClosed B` is exactly what it costs over the gauge form. -/ +theorem exists_linearIndependent_mem_smul_successiveMinimum (L : Submodule ℤ E) + [DiscreteTopology L] [IsZLattice ℝ L] (hB₀ : Convex ℝ B) (hB₁ : ∀ x ∈ B, -x ∈ B) + (hB₂ : (interior B).Nonempty) (hB₃ : Bornology.IsBounded B) (hB₄ : IsClosed B) : + ∃ v : Fin (finrank ℝ E) → E, (∀ j, v j ∈ (L : Set E)) ∧ LinearIndependent ℝ v ∧ + ∀ j : Fin (finrank ℝ E), v j ∈ (successiveMinimum L B j) • B := by + obtain ⟨v, hvL, hvind, hvg⟩ := + exists_linearIndependent_gauge_eq_successiveMinimum L hB₀ hB₁ hB₂ hB₃ + refine ⟨v, hvL, hvind, fun j ↦ ?_⟩ + have hpos : 0 < successiveMinimum L B j := + successiveMinimum_pos L hB₀ hB₁ hB₂ hB₃ j.isLt + rw [← gauge_le_iff_mem_smul hB₀ hB₄ (hB₀.mem_nhds_zero_of_symmetric hB₁ hB₂) hpos, hvg j] + +/-- **The dependence half of Cassels' Lemma 1.** Outside the span of the first `j` members of a +family realizing the minima there is nothing of gauge below the `j`-th minimum. The point would +extend those `j` members to an independent family of `j + 1` lattice points, which bounds the +`j`-th minimum by the largest gauge in the family; the induction on `j` is what identifies that +largest gauge as the gauge of the new point, since without it the bound is only by the maximum of +the new gauge and the `(j - 1)`-st minimum, and those can be equal. -/ +theorem successiveMinimum_le_gauge_of_notMem_span (L : Submodule ℤ E) [DiscreteTopology L] + [IsZLattice ℝ L] (hB₀ : Convex ℝ B) (hB₁ : ∀ x ∈ B, -x ∈ B) (hB₂ : (interior B).Nonempty) + {v : Fin (finrank ℝ E) → E} (hvL : ∀ i, v i ∈ (L : Set E)) (hvind : LinearIndependent ℝ v) + (hvg : ∀ i, gauge B (v i) = successiveMinimum L B i) : + ∀ j : ℕ, j ≤ finrank ℝ E → ∀ y ∈ (L : Set E), + y ∉ Submodule.span ℝ (v '' {i : Fin (finrank ℝ E) | (i : ℕ) < j}) → + successiveMinimum L B j ≤ gauge B y := by + have h₀ : B ∈ 𝓝 (0 : E) := hB₀.mem_nhds_zero_of_symmetric hB₁ hB₂ + intro j + induction j with + | zero => + intro _ y hy hspan + refine le_of_forall_gt_imp_ge_of_dense fun t ht ↦ ?_ + have htpos : 0 < t := lt_of_le_of_lt (gauge_nonneg _) ht + have : Subsingleton (Fin (0 + 1)) := inferInstanceAs (Subsingleton (Fin 1)) + refine successiveMinimum_le htpos (v := fun _ : Fin (0 + 1) ↦ y) + (fun _ ↦ ⟨mem_smul_of_gauge_lt hB₀ h₀ ht, hy⟩) ?_ + refine (linearIndependent_subsingleton_index_iff _).2 fun _ ↦ ?_ + intro hy0 + rw [hy0] at hspan + exact hspan (Submodule.zero_mem _) + | succ j ih => + intro hj y hy hspan + have hjn : j < finrank ℝ E := hj + have hprev : successiveMinimum L B j ≤ gauge B y := + ih hjn.le y hy fun h ↦ + hspan (Submodule.span_mono (Set.image_mono fun i hi ↦ Nat.lt_succ_of_lt hi) h) + have hrange : Set.range (v ∘ Fin.castLE hj) + = v '' {i : Fin (finrank ℝ E) | (i : ℕ) < j + 1} := by + rw [Set.range_comp, Fin.range_castLE] + refine le_of_forall_gt_imp_ge_of_dense fun t ht ↦ ?_ + have htpos : 0 < t := lt_of_le_of_lt (gauge_nonneg _) ht + refine successiveMinimum_le (i := j + 1) htpos (v := Fin.snoc (v ∘ Fin.castLE hj) y) ?_ ?_ + · refine Fin.lastCases ?_ (fun k ↦ ?_) + · rw [Fin.snoc_last] + exact ⟨mem_smul_of_gauge_lt hB₀ h₀ ht, hy⟩ + · rw [Fin.snoc_castSucc] + refine ⟨mem_smul_of_gauge_lt hB₀ h₀ ?_, hvL _⟩ + calc gauge B ((v ∘ Fin.castLE hj) k) = successiveMinimum L B (k.castLE hj) := hvg _ + _ ≤ successiveMinimum L B j := + successiveMinimum_le_of_le (by simpa using Fin.is_le k) hjn hB₀ hB₁ hB₂ + _ ≤ gauge B y := hprev + _ < t := ht + · rw [linearIndependent_finSnoc] + exact ⟨hvind.comp _ fun _ _ h ↦ Fin.castLE_injective hj h, fun hmem ↦ hspan (hrange ▸ hmem)⟩ + +/-- **Cassels' Lemma 1, completed.** A lattice point of gauge below the `j`-th minimum lies in the +span of the first `j` members of a family realizing the minima. No hypothesis on `j` is needed: +above the dimension the minima vanish and no gauge is negative. This is the input to Cassels' +Lemma 2, `ZLattice.exists_basis_mem_span_int_of_gauge_lt`. -/ +theorem mem_span_of_gauge_lt_successiveMinimum (L : Submodule ℤ E) [DiscreteTopology L] + [IsZLattice ℝ L] (hB₀ : Convex ℝ B) (hB₁ : ∀ x ∈ B, -x ∈ B) (hB₂ : (interior B).Nonempty) + {v : Fin (finrank ℝ E) → E} (hvL : ∀ i, v i ∈ (L : Set E)) (hvind : LinearIndependent ℝ v) + (hvg : ∀ i, gauge B (v i) = successiveMinimum L B i) {y : E} (hy : y ∈ (L : Set E)) {j : ℕ} + (hgy : gauge B y < successiveMinimum L B j) : + y ∈ Submodule.span ℝ (v '' {i : Fin (finrank ℝ E) | (i : ℕ) < j}) := by + by_contra hspan + rcases le_or_gt (finrank ℝ E) j with hj | hj + · rw [successiveMinimum_eq_zero_of_le L B hj] at hgy + exact absurd hgy (not_lt.2 (gauge_nonneg _)) + · exact absurd (successiveMinimum_le_gauge_of_notMem_span L hB₀ hB₁ hB₂ hvL hvind hvg j hj.le y + hy hspan) (not_le.2 hgy) + +end Attainment + +/-! ### Minkowski's first theorem as the case `i = 0` + +The zeroth minimum is the least dilation of the body containing a nonzero lattice point, which is +the quantity Minkowski's convex-body theorem bounds. The identity is measure-free; the bound is +the only statement in this file that mentions a measure. -/ + +omit [FiniteDimensional ℝ E] in +/-- The zeroth minimum asks only for a nonzero lattice point: a one-element family is linearly +independent exactly when its member is nonzero. -/ +theorem successiveMinimum_zero_eq (L : Submodule ℤ E) (B : Set E) : + successiveMinimum L B 0 = sInf {t : ℝ | 0 < t ∧ ∃ x ∈ (t • B) ∩ (L : Set E), x ≠ 0} := by + have : Subsingleton (Fin (0 + 1)) := inferInstanceAs (Subsingleton (Fin 1)) + rw [successiveMinimum] + congr 1 + ext t + simp only [mem_ofPred_eq, and_congr_right_iff] + intro _ + constructor + · rintro ⟨v, hv, hind⟩ + exact ⟨v 0, hv 0, hind.ne_zero 0⟩ + · rintro ⟨x, hx, hx0⟩ + exact ⟨fun _ ↦ x, fun _ ↦ hx, (linearIndependent_subsingleton_index_iff _).2 fun _ ↦ hx0⟩ + +/-- **Minkowski's convex-body theorem is the case `i = 0`.** A symmetric convex compact body whose +volume is at least `2 ^ n` times the covolume of the lattice has zeroth minimum at most one. -/ +theorem successiveMinimum_zero_le_one [MeasurableSpace E] [BorelSpace E] [Nontrivial E] + (L : Submodule ℤ E) [DiscreteTopology L] [IsZLattice ℝ L] (μ : Measure E) + [μ.IsAddHaarMeasure] (hB₀ : Convex ℝ B) (hB₁ : ∀ x ∈ B, -x ∈ B) (hB₅ : IsCompact B) + (h : 2 ^ finrank ℝ E * covolume L μ ≤ (μ B).toReal) : + successiveMinimum L B 0 ≤ 1 := by + classical + have : Subsingleton (Fin (0 + 1)) := inferInstanceAs (Subsingleton (Fin 1)) + have hcount : Countable L.toAddSubgroup := inferInstanceAs (Countable L) + have hdisc : DiscreteTopology L.toAddSubgroup := inferInstanceAs (DiscreteTopology L) + set b := Module.Free.chooseBasis ℤ L + set F := ZSpan.fundamentalDomain (b.ofZLatticeBasis ℝ) + have fund : IsAddFundamentalDomain L.toAddSubgroup F μ := ZLattice.isAddFundamentalDomain b μ + have hFfin : μ F ≠ ⊤ := (ZSpan.fundamentalDomain_isBounded _).measure_lt_top.ne + have hcov : covolume L μ = (μ F).toReal := + covolume_eq_measure_fundamentalDomain L μ (ZLattice.isAddFundamentalDomain b μ) + have key : μ F * 2 ^ finrank ℝ E ≤ μ B := by + rw [← ENNReal.toReal_le_toReal (ENNReal.mul_ne_top hFfin (by finiteness)) hB₅.measure_lt_top.ne, + ENNReal.toReal_mul, ENNReal.toReal_pow, ENNReal.toReal_ofNat] + rw [hcov] at h + linarith [h] + obtain ⟨x, hx0, hxB⟩ := + exists_ne_zero_mem_lattice_of_measure_mul_two_pow_le_measure fund hB₁ hB₀ hB₅ key + refine successiveMinimum_le one_pos (v := fun _ ↦ (x : E)) (fun _ ↦ ⟨?_, ?_⟩) + ((linearIndependent_subsingleton_index_iff _).2 fun _ hx ↦ hx0 (Subtype.ext hx)) + · rwa [one_smul] + · exact x.2 + +end ZLattice + +/-! ### Regrouping a product of minima -/ + +section Regrouping + +/-- **Every `d`-th term, to the power `d`, against the whole product.** For a nonnegative sequence +that is monotone below `N`, the product of `f (d * j) ^ d` over `j < k` is at most the product of +`f i` over `i < d * k`. This is what turns Minkowski's second theorem — a bound on the product of +all `d * k` successive minima — into a bound on a product of `k` heights, each of which costs a +`d`-th power. -/ +theorem Finset.prod_pow_le_prod_range {f : ℕ → ℝ} {N : ℕ} (hf0 : ∀ i, 0 ≤ f i) + (hmono : ∀ i j, i ≤ j → j < N → f i ≤ f j) (d : ℕ) : + ∀ k, d * k ≤ N → ∏ j ∈ Finset.range k, f (d * j) ^ d ≤ ∏ i ∈ Finset.range (d * k), f i := by + intro k + induction k with + | zero => intro _; simp + | succ k ih => + intro hle + have hk : d * k ≤ N := le_trans (Nat.mul_le_mul_left d (Nat.le_succ k)) hle + have hsplit : d * (k + 1) = d * k + d := by ring + rw [Finset.prod_range_succ, hsplit, Finset.prod_range_add] + refine mul_le_mul (ih hk) ?_ (pow_nonneg (hf0 _) _) + (Finset.prod_nonneg fun i _ ↦ hf0 i) + calc f (d * k) ^ d = ∏ _i ∈ Finset.range d, f (d * k) := by + rw [Finset.prod_const, Finset.card_range] + _ ≤ ∏ i ∈ Finset.range d, f (d * k + i) := by + refine Finset.prod_le_prod₀ (fun i _ ↦ hf0 _) fun i hi ↦ ?_ + rw [Finset.mem_range] at hi + exact hmono _ _ (Nat.le_add_right _ _) (by omega) + +end Regrouping + +/-! ### Worked examples -/ + +section Examples + +open ZLattice + +variable [FiniteDimensional ℝ E] {L : Submodule ℤ E} + +/-- The minima are **not** a monotone function of the index: above the dimension the value is the +junk `sInf ∅ = 0`, while below it the value is positive. Any statement about the minima that +ranged over all of `ℕ` — Minkowski's second theorem over `Finset.range` of anything larger than +the dimension, say — is refuted here. -/ +example [DiscreteTopology L] [IsZLattice ℝ L] (hB₀ : Convex ℝ B) (hB₁ : ∀ x ∈ B, -x ∈ B) + (hB₂ : (interior B).Nonempty) (hB₃ : Bornology.IsBounded B) (hE : 0 < finrank ℝ E) : + ¬ Monotone (successiveMinimum L B) := by + intro hmono + have h0 : 0 < successiveMinimum L B 0 := successiveMinimum_pos L hB₀ hB₁ hB₂ hB₃ hE + have hn : successiveMinimum L B (finrank ℝ E) = 0 := + successiveMinimum_eq_zero_of_le L B le_rfl + have := hmono (Nat.zero_le (finrank ℝ E)) + rw [hn] at this + linarith + +/-- **The body form of attainment fails for an open body.** For the open unit ball no lattice +vector lies in `λ 0 • B`: anything inside that dilation is inside a strictly smaller one, which +contradicts the definition of the infimum. So `IsClosed B` in +`exists_linearIndependent_mem_smul_successiveMinimum` is not a convenience, and the gauge form of +attainment is the sharp statement. -/ +example [Nontrivial E] [DiscreteTopology L] [IsZLattice ℝ L] : + ¬ ∃ v : Fin (finrank ℝ E) → E, (∀ j, v j ∈ (L : Set E)) ∧ LinearIndependent ℝ v ∧ + ∀ j : Fin (finrank ℝ E), v j ∈ (successiveMinimum L (ball 0 1) j) • ball (0 : E) 1 := by + have : Subsingleton (Fin (0 + 1)) := inferInstanceAs (Subsingleton (Fin 1)) + have hE : 0 < finrank ℝ E := finrank_pos + have hB₀ : Convex ℝ (ball (0 : E) 1) := convex_ball 0 1 + have hB₁ : ∀ x ∈ ball (0 : E) 1, -x ∈ ball (0 : E) 1 := by + intro x hx + rwa [mem_ball_zero_iff, norm_neg, ← mem_ball_zero_iff] + have hB₂ : (interior (ball (0 : E) 1)).Nonempty := by + rw [isOpen_ball.interior_eq] + exact ⟨0, by simp⟩ + have hB₃ : Bornology.IsBounded (ball (0 : E) 1) := Metric.isBounded_ball + set lam := successiveMinimum L (ball (0 : E) 1) 0 with hlam + have hpos : 0 < lam := successiveMinimum_pos L hB₀ hB₁ hB₂ hB₃ hE + rintro ⟨v, hvL, hvind, hvmem⟩ + set j : Fin (finrank ℝ E) := ⟨0, hE⟩ + have hx0 : v j ≠ 0 := hvind.ne_zero j + have hmem : v j ∈ lam • ball (0 : E) 1 := hvmem j + rw [mem_smul_set_iff_inv_smul_mem₀ hpos.ne', mem_ball_zero_iff, norm_smul, norm_inv, + Real.norm_eq_abs, abs_of_pos hpos, inv_mul_lt_one₀ hpos] at hmem + set t := (‖v j‖ + lam) / 2 with ht + have ht0 : 0 < t := by positivity + have htlt : t < lam := by rw [ht]; linarith + have hlow : lam ≤ t := by + refine successiveMinimum_le ht0 (v := fun _ ↦ v j) (fun _ ↦ ⟨?_, ?_⟩) + ((linearIndependent_subsingleton_index_iff _).2 fun _ ↦ hx0) + · rw [mem_smul_set_iff_inv_smul_mem₀ ht0.ne', mem_ball_zero_iff, norm_smul, norm_inv, + Real.norm_eq_abs, abs_of_pos ht0, inv_mul_lt_one₀ ht0] + rw [ht]; linarith + · exact hvL j + linarith + +end Examples diff --git a/LeanPool/CCDN/Attribution.lean b/LeanPool/CCDN/Attribution.lean new file mode 100644 index 0000000000..54e2ee7c1f --- /dev/null +++ b/LeanPool/CCDN/Attribution.lean @@ -0,0 +1,41 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/-! +# Source attribution + +This project imports the main-result dependency closure of Jason Davies's +CCDN Dimension Growth formalization at commit +`bb025e7207f3a35a21b898629f5678b72aa7e44f`. +Lean Pool changed module paths and ported the proofs to its pinned toolchain. +Original copyright and license notices are retained in the source files. + +The upstream NOTICE reads: + +CCDN Dimension Growth + +This project contains work from rwst/Subspace-Theorems, by Ralf Stephan, +licensed under Apache-2.0. The vendored transitive dependency of +ArithmeticHeights.BombieriVaaler (vendor/bombieri-vaaler) comes from commit +09835cb23557ae5ae0cf55a9c4ab9055f5d60338. +Original copyright and license notices are retained in all source files. +Two compatibility changes for Lean/Mathlib 4.35 are recorded in +vendor/bombieri-vaaler/lean-4.35.patch and upstream.json. + +This project contains work adapted from AlexKontorovich/PrimeNumberTheoremAnd, +by the PrimeNumberTheoremAnd contributors, licensed under Apache-2.0. +vendor/mertens/Mertens.lean adapts the first Mertens theorem from +PrimeNumberTheoremAnd/IEANTN/Mertens.lean at commit +c39a751132c88b6e8080b74c74023fd95b3d8be0. The upstream file carries no +copyright header; the adapted file records its source at the top, and the +changes are listed in vendor/mertens/upstream.json. A copy of the Apache +License 2.0 is in vendor/mertens/LICENSE. + +The cited paths refer to the pinned upstream repository: +https://github.com/jasondavies/ccdn-dimension-growth/tree/bb025e7207f3a35a21b898629f5678b72aa7e44f +The Apache-2.0 license is preserved in Lean Pool's root LICENSE. +-/ diff --git a/LeanPool/CCDN/AuxiliaryPolynomial/AbsoluteAuxiliary.lean b/LeanPool/CCDN/AuxiliaryPolynomial/AbsoluteAuxiliary.lean new file mode 100644 index 0000000000..f30b9b71f4 --- /dev/null +++ b/LeanPool/CCDN/AuxiliaryPolynomial/AbsoluteAuxiliary.lean @@ -0,0 +1,283 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.Algebra.ProjectiveTranslation +public import LeanPool.CCDN.AuxiliaryPolynomial.PositionedAuxiliary +import LeanPool.CCDN.AuxiliaryPolynomial.HypersurfaceBadPrimeCoordinates +import LeanPool.CCDN.AuxiliaryPolynomial.HypersurfacePositionHeight + +/-! +The coefficient-sensitive auxiliary theorem for arbitrary absolutely +irreducible homogeneous equations in arbitrary coordinates. + +The equation is moved into position by a homogeneous translation fixing the +homogenizing coordinate; zeros, primitivity, absolute irreducibility and +nondivisibility are transported along it, and the box grows from `B` to +`(d + 1) B`. `absolute_auxiliary_degree_of_comparison` does this for any finite +prime set whose normalized comparison holds for every translate. + +The geometric bad-prime predicate is invariant under integral shears. + +The box enlargement by d+1 costs only a dimension constant in the +auxiliary degree, with no added power of d in its leading term. +-/ + +@[expose] public section +namespace CCDN +noncomputable section + +/-- For positive `r` and `d`, the box enlargement factor +$(d + 1)^{(r + 1) / (r d^{1/r})}$ is at most $\exp((r + 1)^2 / r)$, a bound depending only +on `r`. -/ +lemma auxiliary_box_rpow_factor {r d : ℕ} (hr : 0 < r) (hd : 0 < d) : + ((d : ℝ) + 1) ^ (((r : ℝ) + 1) / ((r : ℝ) * (d : ℝ) ^ (1 / (r : ℝ)))) ≤ + Real.exp (((r : ℝ) + 1) ^ 2 / r) := by + have hr0 : (0 : ℝ) < r := by exact_mod_cast hr + have hd0 : (0 : ℝ) < d := by exact_mod_cast hd + have hroot : (0 : ℝ) < (d : ℝ) ^ (1 / (r : ℝ)) := by positivity + rw [Real.rpow_def_of_pos (by positivity)] + apply Real.exp_le_exp.mpr + have hh := mul_le_mul_of_nonneg_right (log_nat_add_one_le_root hr hd) + (show 0 ≤ ((r : ℝ) + 1) / ((r : ℝ) * (d : ℝ) ^ (1 / (r : ℝ))) by positivity) + convert hh using 1 + field_simp + +private lemma auxiliary_box_log_cost {r d : ℕ} (hr : 0 < r) (hd : 0 < d) : + (d : ℝ) ^ (1 - 1 / (r : ℝ)) * Real.log ((d : ℝ) + 1) ≤ ((r : ℝ) + 1) * d := by + have hd0 : (0 : ℝ) < d := by exact_mod_cast hd + have hh := mul_le_mul_of_nonneg_left (log_nat_add_one_le_root hr hd) + (Real.rpow_nonneg hd0.le (1 - 1 / (r : ℝ))) + have he : (d : ℝ) ^ (1 - 1 / (r : ℝ)) * (d : ℝ) ^ (1 / (r : ℝ)) = d := by + rw [← Real.rpow_add hd0, sub_add_cancel, Real.rpow_one] + calc + _ ≤ _ := hh + _ = ((r : ℝ) + 1) * d := by rw [← mul_left_comm, he] + +end +end CCDN + +namespace CCDN +noncomputable section +open MvPolynomial + +/-- A prime is bad for the homogeneous translate of a nonzero integral form `f` if and only +if it is bad for `f`. -/ +lemma hypersurfaceBadReduction_homogeneousTranslation {n d : ℕ} + (f : MvPolynomial (Fin (n + 1)) ℤ) (hf : f.IsHomogeneous d) (hf0 : f ≠ 0) + (a : Fin n → ℤ) (p : ℕ) : + HypersurfaceBadReduction (homogeneousTranslation ℤ a f) p ↔ HypersurfaceBadReduction f p := by + by_cases hp : p.Prime + · let : Fact p.Prime := ⟨hp⟩ + have hg0 : homogeneousTranslation ℤ a f ≠ 0 := by + exact (homogeneousTranslation ℤ a).injective.ne (by simpa using hf0) + rw [homogeneous_hypersurfaceBadReduction_iff _ (homogeneousTranslation_homogeneous a hf) hg0, + homogeneous_hypersurfaceBadReduction_iff _ hf hf0, homogeneousTranslation_map] + exact not_congr (MulEquiv.irreducible_iff + (f := homogeneousTranslation (AlgebraicClosure (ZMod p)) ((Int.castRingHom _) ∘ a))) + · constructor <;> intro h <;> exact (hp h.1).elim + +end +end CCDN + +namespace CCDN +noncomputable section +open MvPolynomial + +/-- The dimension constant $(5(n + 2) + 5)(n + 2)$ of the positioning step: after a suitable +homogeneous translation, the logarithm of the coefficient height exceeds that of the +lexicographic leading coefficient by at most this constant times $d^{1 + 1/(n + 1)}$. -/ +def hypersurfaceAuxiliaryPositionConstant (n : ℕ) : ℝ := (5 * (n + 2 : ℝ) + 5) * (n + 2 : ℝ) + +/-- The dimension constant in the absolutely irreducible case of CCDN Theorem 3.1.1 for forms +in `n + 3` variables: the positioned constant times a factor absorbing the enlargement of the +box from `B` to `(d + 1) B` and the passage to the original coefficient height. -/ +def hypersurfaceAbsoluteAuxiliaryConstant (n : ℕ) : ℝ := + hypersurfacePositionedAuxiliaryConstant n (hypersurfaceAuxiliaryPositionConstant n) * + (Real.exp ((n + 2 : ℝ) ^ 2 / (n + 1 : ℝ)) + (n + 3 : ℝ)) + +/-- The constant `hypersurfaceAbsoluteAuxiliaryConstant n` is positive. -/ +lemma hypersurfaceAbsoluteAuxiliaryConstant_pos (n : ℕ) : + 0 < hypersurfaceAbsoluteAuxiliaryConstant n := by + unfold hypersurfaceAbsoluteAuxiliaryConstant hypersurfacePositionedAuxiliaryConstant + positivity + +/-- The auxiliary degree bound of CCDN Theorem 3.1.1 with bad-prime factor `b` and +coefficient height `H`. -/ +def hypersurfaceAuxiliaryDegreeBound (n d : ℕ) (B b H : ℝ) : ℝ := + hypersurfaceAbsoluteAuxiliaryConstant n * auxDegreeBound n d B b H + +section Translation + +variable {n : ℕ} + +private lemma irreducible_map_homogeneousTranslation {K : Type*} [Field K] (a : Fin (n + 2) → ℤ) + {f : MvPolynomial (Fin (n + 3)) ℤ} (hir : Irreducible (map (Int.castRingHom K) f)) : + Irreducible (map (Int.castRingHom K) (homogeneousTranslation ℤ a f)) := by + rw [homogeneousTranslation_map] + exact (MulEquiv.irreducible_iff + (f := homogeneousTranslation K ((Int.castRingHom K) ∘ a))).mpr hir + +/-- Evaluating the homogeneous translate of `f` by `a` at the point translated by `-a` gives +the value of `f` at the original point. -/ +lemma eval_homogeneousTranslation_neg (a : Fin (n + 2) → ℤ) + (f : MvPolynomial (Fin (n + 3)) ℤ) (z : IntPoint (n + 3)) : + eval (homogeneousPointTranslation (-a) z) (homogeneousTranslation ℤ a f) = eval z f := by + rw [homogeneousTranslation_eval] + change eval (homogeneousPointTranslation a (homogeneousPointTranslation (-a) z)) f = _ + have he := homogeneousPointTranslation_inverse (-a) z + simp only [neg_neg] at he + rw [he] + +/-- A form `g` not divisible by the translate of `f` translates back to a +nonmultiple of `f`. -/ +private lemma not_dvd_homogeneousTranslation_neg (a : Fin (n + 2) → ℤ) + {f g : MvPolynomial (Fin (n + 3)) ℤ} (hgnd : ¬homogeneousTranslation ℤ a f ∣ g) : + ¬f ∣ homogeneousTranslation ℤ (-a) g := by + intro hdvd + apply hgnd + have hh := map_dvd (homogeneousTranslation ℤ a).toRingHom hdvd + change homogeneousTranslation ℤ a f ∣ + homogeneousTranslation ℤ a (homogeneousTranslation ℤ (-a) g) at hh + rwa [show homogeneousTranslation ℤ a (homogeneousTranslation ℤ (-a) g) = g from + (homogeneousTranslation ℤ a).apply_symm_apply g] at hh + +end Translation + +/-- Enlarging the box from `B` to `(d + 1) B` and lowering the coefficient height from +`H` to `h ≤ H` cost a factor depending only on the dimension. -/ +private lemma auxDegreeBound_translate_le {n d : ℕ} (hd : 0 < d) {B b H h : ℝ} (hB : 1 ≤ B) + (hb : 0 ≤ b) (hh0 : 0 < h) (hhH : h ≤ H) : + auxDegreeBound n d (((d : ℝ) + 1) * B) b H ≤ + (Real.exp ((n + 2 : ℝ) ^ 2 / (n + 1 : ℝ)) + (n + 3 : ℝ)) * auxDegreeBound n d B b h := by + have hd0 : (0 : ℝ) < d := by exact_mod_cast hd + have hnR : (n : ℝ) + 1 + 1 = n + 2 := by ring + let R := ((d : ℝ) + 1) * B + let D := (d : ℝ) ^ (4 - 1 / (n + 1 : ℝ)) + let α := boxExponent n d + let q := heightExponent n d + let Q := D * b * B ^ α / h ^ q + let l := (d : ℝ) ^ (1 - 1 / (n + 1 : ℝ)) * Real.log B + let E := Real.exp ((n + 2 : ℝ) ^ 2 / (n + 1 : ℝ)) + have hD : 0 ≤ D := by dsimp [D]; positivity + have hQ : 0 ≤ Q := by dsimp [Q]; positivity + have hl : 0 ≤ l := mul_nonneg (Real.rpow_nonneg hd0.le _) (Real.log_nonneg hB) + have hE : 0 ≤ E := (Real.exp_pos _).le + have hfactor : ((d : ℝ) + 1) ^ α ≤ E := by + simpa only [α, E, Nat.cast_add, Nat.cast_one, hnR] using + auxiliary_box_rpow_factor (r := n + 1) (by omega) hd + have hRpow : R ^ α ≤ E * B ^ α := by + rw [show R = ((d : ℝ) + 1) * B from rfl, Real.mul_rpow (by positivity) (by linarith)] + exact mul_le_mul_of_nonneg_right hfactor (Real.rpow_nonneg (by linarith) _) + have hQ' : D * b * R ^ α / H ^ q ≤ E * Q := by + calc + _ ≤ D * b * R ^ α / h ^ q := div_le_div_of_nonneg_left (by positivity) + (by positivity) (Real.rpow_le_rpow hh0.le hhH (by dsimp [q]; positivity)) + _ ≤ D * b * (E * B ^ α) / h ^ q := + div_le_div_of_nonneg_right (mul_le_mul_of_nonneg_left hRpow (mul_nonneg hD hb)) + (by positivity) + _ = _ := by dsimp [Q]; ring + have hlogcost : (d : ℝ) ^ (1 - 1 / (n + 1 : ℝ)) * Real.log ((d : ℝ) + 1) ≤ + (n + 2 : ℝ) * d := by + simpa only [Nat.cast_add, Nat.cast_one, hnR] using + auxiliary_box_log_cost (r := n + 1) (by omega) hd + have hdD : (d : ℝ) ≤ D := le_rpow_four_sub_inv hd + have hl' : (d : ℝ) ^ (1 - 1 / (n + 1 : ℝ)) * Real.log R ≤ l + (n + 2 : ℝ) * D := by + dsimp only [R, l] + rw [Real.log_mul (by positivity) (by linarith), mul_add] + have hh := mul_le_mul_of_nonneg_left hdD (show 0 ≤ (n + 2 : ℝ) by positivity) + linarith + change D * b * R ^ α / H ^ q + (d : ℝ) ^ (1 - 1 / (n + 1 : ℝ)) * Real.log R + D ≤ + (E + (n + 3 : ℝ)) * (Q + l + D) + have h1 := mul_nonneg (show 0 ≤ (n + 3 : ℝ) by positivity) hQ + have h2 := mul_nonneg (show 0 ≤ E + (n + 2 : ℝ) by positivity) hl + have h3 := mul_nonneg hE hD + nlinarith + +/-- The absolutely irreducible case of CCDN Theorem 3.1.1 for any finite prime set +`bad` whose normalized comparison holds for every homogeneous translate of `f` and the +translated points. The constant depends only on dimension; the coefficient height +belongs to the original equation. -/ +theorem absolute_auxiliary_degree_of_comparison + {K : Type*} [Field K] [CharZero K] [IsAlgClosed K] {n d t : ℕ} + (f : MvPolynomial (Fin (n + 3)) ℤ) (hf : f.IsHomogeneous d) (hd : 0 < d) + (hir : Irreducible (map (Int.castRingHom K) f)) + {B : ℝ} {z : Fin t → IntPoint (n + 3)} (hzB : IsPrimitiveBoxZeroFamily f B z) + (bad : Finset ℕ) + (hcomp : ∀ a : Fin (n + 2) → ℤ, AuxiliaryRankComparison MonomialOrder.lex + (homogeneousTranslation ℤ a f) d (fun i => homogeneousPointTranslation (-a) (z i)) bad) : + ∃ M : ℕ, + (M : ℝ) ≤ hypersurfaceAuxiliaryDegreeBound n d B (primeSetFactor bad) + (integerCoefficientHeight f) ∧ + ∃ g, IsPointAuxiliary f g M z := by + obtain ⟨⟨hB, hz, hbox⟩, hprim⟩ := hzB + have hf0 := ne_zero_of_irreducible_map hir + have hnR : (n : ℝ) + 1 + 1 = n + 2 := by ring + obtain ⟨a, H, ha, hH, hcf, hcg, hpos⟩ := exists_hypersurface_coefficient_position + (r := n + 1) (by omega) hd f hf hf0 + let model := homogeneousTranslation ℤ a f + let w := fun i => homogeneousPointTranslation (-a) (z i) + have hposition : Real.log H ≤ Real.log |((MonomialOrder.lex.leadingCoeff model : ℤ) : ℝ)| + + hypersurfaceAuxiliaryPositionConstant n * d * (d : ℝ) ^ (1 / (n + 1 : ℝ)) := by + simpa only [hypersurfaceAuxiliaryPositionConstant, Nat.cast_add, Nat.cast_one, + Nat.cast_ofNat, hnR] using hpos + obtain ⟨M, hbound, g, hg0, hgh, hgz, hgnd⟩ := positioned_auxiliary_degree_of_comparison + MonomialOrder.lex model (homogeneousTranslation_homogeneous a hf) hd + (irreducible_map_homogeneousTranslation a hir) H hH hcg (z := w) + ⟨⟨one_le_mul_of_one_le_of_one_le (by linarith [Nat.cast_nonneg (α := ℝ) d]) hB, + fun i => (eval_homogeneousTranslation_neg a f (z i)).trans (hz i), + fun i j => homogeneousPointTranslation_box hB a ha (z i) (hbox i) j⟩, + fun i => (homogeneousPointTranslation_gcd (-a) (z i)).trans (hprim i)⟩ bad (hcomp a) + (hypersurfaceAuxiliaryPositionConstant n) + (by unfold hypersurfaceAuxiliaryPositionConstant; positivity) hposition + refine ⟨M, ?_, homogeneousTranslation ℤ (-a) g, + (homogeneousTranslation ℤ (-a)).injective.ne (by simpa using hg0), + homogeneousTranslation_homogeneous (-a) hgh, + fun i => by rw [homogeneousTranslation_eval]; exact hgz i, + not_dvd_homogeneousTranslation_neg a hgnd⟩ + have hC : 0 ≤ hypersurfacePositionedAuxiliaryConstant n + (hypersurfaceAuxiliaryPositionConstant n) := by + unfold hypersurfacePositionedAuxiliaryConstant; positivity + have htr := auxDegreeBound_translate_le (n := n) hd hB (Real.exp_pos _).le + (by exact_mod_cast integerCoefficientHeight_pos f hf0) + (integerCoefficientHeight_le f (by linarith) hcf) (b := primeSetFactor bad) + refine hbound.trans ((mul_le_mul_of_nonneg_left htr hC).trans_eq ?_) + unfold hypersurfaceAuxiliaryDegreeBound hypersurfaceAbsoluteAuxiliaryConstant + ring + +/-- The absolutely irreducible case of CCDN Theorem 3.1.1. The constant depends +only on dimension. The coefficient height and bad-prime factor belong to the +original equation. -/ +theorem hypersurface_absolute_auxiliary_degree + {K : Type*} [Field K] [CharZero K] [IsAlgClosed K] {n d t : ℕ} + (f : MvPolynomial (Fin (n + 3)) ℤ) (hf : f.IsHomogeneous d) (hd : 0 < d) + (hir : Irreducible (map (Int.castRingHom K) f)) + (z : Fin t → IntPoint (n + 3)) (hprim : ∀ i, Finset.univ.gcd (z i) = 1) + (hz : ∀ i, eval (z i) f = 0) + {B : ℝ} (hB : 1 ≤ B) (hbox : ∀ i, InBox (z i) B) : + ∃ (bad : Finset ℕ) (M : ℕ), + (∀ p, p ∈ bad ↔ stepanovThreshold d < p ∧ HypersurfaceBadReduction f p) ∧ + (M : ℝ) ≤ hypersurfaceAuxiliaryDegreeBound n d B (primeSetFactor bad) + (integerCoefficientHeight f) ∧ + ∃ g : MvPolynomial (Fin (n + 3)) ℤ, g ≠ 0 ∧ g.IsHomogeneous M ∧ + (∀ i, eval (z i) g = 0) ∧ ¬ f ∣ g := by + have hf0 := ne_zero_of_irreducible_map hir + obtain ⟨bad, hbad⟩ := exists_hypersurface_bad_prime_finset f hf hd hir + obtain ⟨M, hM, g, hg⟩ := absolute_auxiliary_degree_of_comparison f hf hd hir + ⟨⟨hB, hz, hbox⟩, hprim⟩ bad + (fun a => hypersurface_auxiliaryRankComparison MonomialOrder.lex _ + (homogeneousTranslation_homogeneous a hf) + ((homogeneousTranslation ℤ a).injective.ne (by simpa using hf0)) hd _ + (fun i => (homogeneousPointTranslation_gcd (-a) (z i)).trans (hprim i)) + (fun i => (eval_homogeneousTranslation_neg a f (z i)).trans (hz i)) bad + fun p => by rw [hbad, hypersurfaceBadReduction_homogeneousTranslation f hf hf0]) + exact ⟨bad, M, hbad, hM, g, isPointAuxiliary_iff.mp hg⟩ + +end +end CCDN diff --git a/LeanPool/CCDN/AuxiliaryPolynomial/AffineAuxiliaryCases.lean b/LeanPool/CCDN/AuxiliaryPolynomial/AffineAuxiliaryCases.lean new file mode 100644 index 0000000000..0408291d37 --- /dev/null +++ b/LeanPool/CCDN/AuxiliaryPolynomial/AffineAuxiliaryCases.lean @@ -0,0 +1,161 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.AuxiliaryPolynomial.PrimeScaledAffineMinimum +import LeanPool.CCDN.AuxiliaryPolynomial.AffineHeightCutoff +import LeanPool.CCDN.AuxiliaryPolynomial.HypersurfaceAuxiliaryTheorem +import LeanPool.CCDN.AuxiliaryPolynomial.UnscaledAffineMinimum +import LeanPool.CCDN.AuxiliaryPolynomial.UnscaledHypersurfaceAuxiliary +import LeanPool.CCDN.AuxiliaryPolynomial.UnscaledHypersurfaceBadPrimes +import LeanPool.CCDN.Height.ScaledHypersurfaceHeight + +/-! All box and coefficient cases for the affine auxiliary theorem when +the constant coefficient is nonzero. -/ + +@[expose] public section +namespace CCDN +noncomputable section +open MvPolynomial + +/-- The affine auxiliary degree bound `affineMinimumAuxiliaryBound` is monotone in the +comparison constant `A`, for `1 ≤ B`, `1 ≤ L` and `0 ≤ b`. -/ +lemma affineMinimumAuxiliaryBound_mono_A (n d : ℕ) {A A' B L b : ℝ} + (hA : A ≤ A') (hB : 1 ≤ B) (hL : 1 ≤ L) (hb : 0 ≤ b) : + affineMinimumAuxiliaryBound n d A B L b ≤ affineMinimumAuxiliaryBound n d A' B L b := by + have hT : 0 ≤ Real.log L + d * Real.log B + (d : ℝ) ^ 2 := by + exact add_nonneg (add_nonneg (Real.log_nonneg hL) + (mul_nonneg (Nat.cast_nonneg d) (Real.log_nonneg hB))) (sq_nonneg _) + have hmin := le_min hT (mul_nonneg (sq_nonneg (d : ℝ)) hb) + have hK : 0 ≤ max (affineHeightTransferConstant n) (Real.exp 1) := + (Real.exp_pos 1).le.trans (le_max_right _ _) + have hC := (hypersurfaceAbsoluteAuxiliaryConstant_pos n).le + unfold affineMinimumAuxiliaryBound + gcongr + +/-- The affine auxiliary degree bound `affineMinimumAuxiliaryBound` is monotone in the +bad-prime factor `b`, for `0 ≤ A`, `1 ≤ B` and `1 ≤ L`. -/ +lemma affineMinimumAuxiliaryBound_mono_b (n d : ℕ) {A B L b b' : ℝ} + (hA : 0 ≤ A) (hB : 1 ≤ B) (hL : 1 ≤ L) (hb : b ≤ b') : + affineMinimumAuxiliaryBound n d A B L b ≤ affineMinimumAuxiliaryBound n d A B L b' := by + have hK : 0 ≤ max (affineHeightTransferConstant n) (Real.exp 1) := + (Real.exp_pos 1).le.trans (le_max_right _ _) + have hC := (hypersurfaceAbsoluteAuxiliaryConstant_pos n).le + unfold affineMinimumAuxiliaryBound + gcongr + +/-- The affine auxiliary degree bound is at least the degree `d` whenever `0 < d`, so the +equation itself always meets it. -/ +lemma degree_le_affineMinimumAuxiliaryBound (n : ℕ) {d : ℕ} (hd : 0 < d) + {A B L b : ℝ} (hA : 0 ≤ A) (hB : 1 ≤ B) (hL : 1 ≤ L) (hb : 0 ≤ b) : + (d : ℝ) ≤ affineMinimumAuxiliaryBound n d A B L b := by + have hd1 : (1 : ℝ) ≤ d := by exact_mod_cast hd + have hn1 : (1 : ℝ) ≤ n + 1 := by have := Nat.cast_nonneg (α := ℝ) n; linarith + have hdD : (d : ℝ) ≤ (d : ℝ) ^ (4 - 1 / (n + 1 : ℝ)) := le_rpow_four_sub_inv hd + have hT : 0 ≤ Real.log L + d * Real.log B + (d : ℝ) ^ 2 := + add_nonneg (add_nonneg (Real.log_nonneg hL) + (mul_nonneg (Nat.cast_nonneg d) (Real.log_nonneg hB))) (sq_nonneg _) + have hmin := le_min hT (mul_nonneg (sq_nonneg (d : ℝ)) hb) + have hK : 0 ≤ max (affineHeightTransferConstant n) (Real.exp 1) := + (Real.exp_pos 1).le.trans (le_max_right _ _) + have hlog := Real.log_nonneg hB + have hC := hypersurfaceAbsoluteAuxiliaryConstant_one_le n + let E := max (affineHeightTransferConstant n) (Real.exp 1) * A * + (d : ℝ) ^ (2 - 1 / (n + 1 : ℝ)) * + min (Real.log L + d * Real.log B + (d : ℝ) ^ 2) ((d : ℝ) ^ 2 * b) * + B ^ (1 / (d : ℝ) ^ (1 / (n + 1 : ℝ))) / + L ^ (1 / ((n + 1 : ℝ) * d * (d : ℝ) ^ (1 / (n + 1 : ℝ)))) + + (d : ℝ) ^ (1 - 1 / (n + 1 : ℝ)) * Real.log B + have hE : 0 ≤ E := by dsimp only [E]; positivity + unfold affineMinimumAuxiliaryBound + exact (hdD.trans (le_add_of_nonneg_left hE)).trans + (le_mul_of_one_le_left (by positivity) hC) + +/-- The scale $4 R^2$, with `R = scalingPrimeRatio`, of the small-box threshold +`affineAuxiliaryBoxScale * (d + 1) ^ affineAuxiliaryBoxExponent n` separating the unscaled +case of the affine auxiliary theorem from the prime-scaled cases. -/ +def affineAuxiliaryBoxScale : ℝ := 4 * scalingPrimeRatio ^ 2 + +/-- The exponent $4(n + 2) + 2$ of `d + 1` in the small-box threshold of the affine +auxiliary theorem for equations in `n + 2` variables. -/ +def affineAuxiliaryBoxExponent (n : ℕ) : ℕ := 4 * (n + 2) + 2 + +/-- The comparison constant used uniformly over all box radii in the affine auxiliary +theorem: the larger of `scalingPrimeRatio` and the small-box scale times +$e^{(4(n + 2) + 2)(n + 2)}$. -/ +def affineAuxiliaryComparisonConstant (n : ℕ) : ℝ := + max scalingPrimeRatio + (Real.exp (Real.log affineAuxiliaryBoxScale + (affineAuxiliaryBoxExponent n : ℝ) * (n + 2 : ℝ))) + +/-- The small-box scale `affineAuxiliaryBoxScale` is at least `1`. -/ +lemma affineAuxiliaryBoxScale_one_le : 1 ≤ affineAuxiliaryBoxScale := by + have h := scalingPrimeRatio_one_le + unfold affineAuxiliaryBoxScale + nlinarith + +/-- The comparison constant `affineAuxiliaryComparisonConstant n` is at least `1`. -/ +lemma affineAuxiliaryComparisonConstant_one_le (n : ℕ) : + 1 ≤ affineAuxiliaryComparisonConstant n := + scalingPrimeRatio_one_le.trans (le_max_left _ _) + +/-- Every primitive irreducible equation with nonzero constant coefficient +has the affine auxiliary, uniformly over all box radii. -/ +theorem affine_auxiliary_minimum_nonzero_constant {n : ℕ} + (f : MvPolynomial (Fin (n + 2)) ℤ) (hprim : PrimitiveIntegerPolynomial f) + (hir : Irreducible f) (hd : 0 < f.totalDegree) (hc : f.coeff 0 ≠ 0) + {B : ℝ} (hB : 1 ≤ B) : + ∃ g, IsBoxAuxiliary f g B ∧ + (g.totalDegree : ℝ) ≤ affineMinimumAuxiliaryBound n f.totalDegree + (affineAuxiliaryComparisonConstant n) B + (integerCoefficientHeight (homogeneousComponent f.totalDegree f) : ℝ) + (hypersurfaceBadPrimeFactor f) := by + let d := f.totalDegree + let L := (integerCoefficientHeight (homogeneousComponent d f) : ℝ) + let b := hypersurfaceBadPrimeFactor (scaledHomogenize d 1 f) + have hL : 1 ≤ L := leading_height_one_le f hir.ne_zero + have hb : 0 ≤ b := hypersurfaceBadPrimeFactor_nonneg _ + have hb' : b ≤ hypersurfaceBadPrimeFactor f := + hypersurfaceBadPrimeFactor_unscaledHomogenize_le f hir.ne_zero + have hA : 1 ≤ affineAuxiliaryComparisonConstant n := affineAuxiliaryComparisonConstant_one_le n + by_cases hsmall : B ≤ affineAuxiliaryBoxScale * ((d : ℝ) + 1) ^ affineAuxiliaryBoxExponent n + · obtain ⟨g, hg, hgdeg⟩ := unscaled_affine_auxiliary_minimum f hprim hir hd + affineAuxiliaryBoxScale_one_le hB hsmall + refine ⟨g, hg, hgdeg.trans ?_⟩ + exact (affineMinimumAuxiliaryBound_mono_A n d (le_max_right _ _) hB hL hb).trans + (affineMinimumAuxiliaryBound_mono_b n d (zero_le_one.trans hA) hB hL hb') + · have hBA : scalingPrimeRatio ≤ B := by + have hS : scalingPrimeRatio ≤ affineAuxiliaryBoxScale := by + have h := scalingPrimeRatio_one_le + unfold affineAuxiliaryBoxScale + nlinarith + have hpow : (1 : ℝ) ≤ ((d : ℝ) + 1) ^ affineAuxiliaryBoxExponent n := + one_le_pow₀ (by have := Nat.cast_nonneg (α := ℝ) d; linarith) + have hS0 := zero_le_one.trans affineAuxiliaryBoxScale_one_le + exact (hS.trans (le_mul_of_one_le_right hS0 hpow)).trans (le_of_not_ge hsmall) + by_cases hheight : Real.log |(f.coeff 0 : ℝ)| < B / scalingPrimeRatio + · obtain ⟨g, hg, hgdeg⟩ := affine_auxiliary_minimum_of_small_constant_coefficient + f hprim hir hd hc hBA hheight + refine ⟨g, hg, hgdeg.trans ?_⟩ + exact (affineMinimumAuxiliaryBound_mono_A n d (le_max_left _ _) hB hL hb).trans + (affineMinimumAuxiliaryBound_mono_b n d (zero_le_one.trans hA) hB hL hb') + · rcases affine_coefficient_height_dichotomy f hir.ne_zero hprim hB with hh | hg + · have hcoef : Real.log |(f.coeff 0 : ℝ)| ≤ Real.log (integerCoefficientHeight f : ℝ) := + Real.log_le_log (abs_pos.mpr (Int.cast_ne_zero.mpr hc)) + (coefficient_abs_le_integerCoefficientHeight f 0) + have hcut := affine_coefficient_height_cutoff (n + 2) d scalingPrimeRatio_one_le hB + ((le_of_not_gt hheight).trans (hcoef.trans hh)) + exact (hsmall hcut).elim + · obtain ⟨g, hg, hgdeg⟩ := hg + refine ⟨g, hg, ?_⟩ + have hgR : (g.totalDegree : ℝ) ≤ d := by exact_mod_cast hgdeg + exact hgR.trans (degree_le_affineMinimumAuxiliaryBound n hd (zero_le_one.trans hA) hB hL + (hypersurfaceBadPrimeFactor_nonneg f)) + +end +end CCDN diff --git a/LeanPool/CCDN/AuxiliaryPolynomial/AffineAuxiliaryTranslationBound.lean b/LeanPool/CCDN/AuxiliaryPolynomial/AffineAuxiliaryTranslationBound.lean new file mode 100644 index 0000000000..49375c89f8 --- /dev/null +++ b/LeanPool/CCDN/AuxiliaryPolynomial/AffineAuxiliaryTranslationBound.lean @@ -0,0 +1,147 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.AuxiliaryPolynomial.PrimeScaledAffineMinimum +import LeanPool.CCDN.AuxiliaryPolynomial.HypersurfacePositionHeight +import LeanPool.CCDN.ForMathlib.Log + +/-! Translating the affine box by a vector bounded by the degree costs only + a dimension constant in the coefficient-sensitive minimum bound. -/ + +@[expose] public section +namespace CCDN +noncomputable section + +private lemma affine_box_translation_power {r d : ℕ} (hr : 0 < r) (hd : 0 < d) + {B : ℝ} (hB : 1 ≤ B) : + (B + d) ^ (1 / (d : ℝ) ^ (1 / (r : ℝ))) ≤ + Real.exp ((r : ℝ) + 1) * B ^ (1 / (d : ℝ) ^ (1 / (r : ℝ))) := by + have hd0 : (0 : ℝ) < d := by exact_mod_cast hd + have hroot : (0 : ℝ) < (d : ℝ) ^ (1 / (r : ℝ)) := by positivity + have hfactor : ((d : ℝ) + 1) ^ (1 / (d : ℝ) ^ (1 / (r : ℝ))) ≤ + Real.exp ((r : ℝ) + 1) := by + rw [Real.rpow_def_of_pos (by positivity), Real.exp_le_exp, ← div_eq_mul_one_div] + exact (div_le_iff₀ hroot).mpr (log_nat_add_one_le_root hr hd) + calc + _ ≤ (((d : ℝ) + 1) * B) ^ (1 / (d : ℝ) ^ (1 / (r : ℝ))) := by + apply Real.rpow_le_rpow (by positivity) _ (by positivity) + nlinarith + _ = ((d : ℝ) + 1) ^ (1 / (d : ℝ) ^ (1 / (r : ℝ))) * + B ^ (1 / (d : ℝ) ^ (1 / (r : ℝ))) := Real.mul_rpow (by positivity) (by linarith) + _ ≤ _ := mul_le_mul_of_nonneg_right hfactor (by positivity) + +private lemma affine_box_translation_log {d : ℕ} (hd : 0 < d) {B : ℝ} (hB : 1 ≤ B) : + Real.log (B + d) ≤ Real.log B + Real.log ((d : ℝ) + 1) := by + have hd0 : (0 : ℝ) < d := by exact_mod_cast hd + have hlog := Real.log_le_log (by positivity : 0 < B + d) + (show B + d ≤ B * ((d : ℝ) + 1) by nlinarith) + rwa [Real.log_mul (by linarith) (by positivity)] at hlog + +private lemma affine_box_translation_min {d : ℕ} (hd : 0 < d) + {B L b : ℝ} (hB : 1 ≤ B) (hL : 1 ≤ L) (hb : 0 ≤ b) : + min (Real.log L + d * Real.log (B + d) + (d : ℝ) ^ 2) ((d : ℝ) ^ 2 * b) ≤ + 2 * min (Real.log L + d * Real.log B + (d : ℝ) ^ 2) ((d : ℝ) ^ 2 * b) := by + have hd0 : (0 : ℝ) < d := by exact_mod_cast hd + have hlogd : Real.log ((d : ℝ) + 1) ≤ d := Real.log_add_one_le_self d.cast_nonneg + have hnum : Real.log L + d * Real.log (B + d) + (d : ℝ) ^ 2 ≤ + 2 * (Real.log L + d * Real.log B + (d : ℝ) ^ 2) := by + have hlog := affine_box_translation_log hd hB + have hLlog := Real.log_nonneg hL + have hBlog := Real.log_nonneg hB + nlinarith [mul_le_mul_of_nonneg_left hlog hd0.le, + mul_le_mul_of_nonneg_left hlogd hd0.le] + rcases le_total (Real.log L + d * Real.log B + (d : ℝ) ^ 2) ((d : ℝ) ^ 2 * b) with h | h + · rw [min_eq_left h] + exact (min_le_left _ _).trans hnum + · rw [min_eq_right h] + exact (min_le_right _ _).trans (by nlinarith [sq_nonneg (d : ℝ)]) + +private lemma affine_box_translation_lower {r d : ℕ} (_hr : 0 < r) (hd : 0 < d) + {B : ℝ} (hB : 1 ≤ B) : + (d : ℝ) ^ (1 - 1 / (r : ℝ)) * Real.log (B + d) + + (d : ℝ) ^ (4 - 1 / (r : ℝ)) ≤ + 2 * ((d : ℝ) ^ (1 - 1 / (r : ℝ)) * Real.log B + + (d : ℝ) ^ (4 - 1 / (r : ℝ))) := by + have hd1 : (1 : ℝ) ≤ d := by exact_mod_cast hd + have hd0 : (0 : ℝ) < d := by linarith + have hlogd : Real.log ((d : ℝ) + 1) ≤ d := Real.log_add_one_le_self d.cast_nonneg + have hpow : (d : ℝ) ^ (1 - 1 / (r : ℝ)) * d ≤ (d : ℝ) ^ (4 - 1 / (r : ℝ)) := by + calc + _ = (d : ℝ) ^ (2 - 1 / (r : ℝ)) := by + calc + _ = (d : ℝ) ^ (1 - 1 / (r : ℝ)) * (d : ℝ) ^ (1 : ℝ) := by rw [Real.rpow_one] + _ = _ := by rw [← Real.rpow_add hd0]; congr 1; ring + _ ≤ _ := Real.rpow_le_rpow_of_exponent_le hd1 (by linarith) + have hlog := affine_box_translation_log hd hB + have hdlow : 0 ≤ (d : ℝ) ^ (1 - 1 / (r : ℝ)) := by positivity + have hBlog := Real.log_nonneg hB + nlinarith [mul_le_mul_of_nonneg_left hlog hdlow, + mul_le_mul_of_nonneg_left hlogd hdlow] + +/-- The full coefficient-sensitive affine expression is stable under the + bounded translation used to make the constant coefficient nonzero. -/ +private theorem affine_auxiliary_expression_translation_bound {r d : ℕ} + (hr : 0 < r) (hd : 0 < d) {K B L b : ℝ} + (hK : 0 ≤ K) (hB : 1 ≤ B) (hL : 1 ≤ L) (hb : 0 ≤ b) : + K * (d : ℝ) ^ (2 - 1 / (r : ℝ)) * + min (Real.log L + d * Real.log (B + d) + (d : ℝ) ^ 2) ((d : ℝ) ^ 2 * b) * + (B + d) ^ (1 / (d : ℝ) ^ (1 / (r : ℝ))) / + L ^ (1 / ((r : ℝ) * d * (d : ℝ) ^ (1 / (r : ℝ)))) + + (d : ℝ) ^ (1 - 1 / (r : ℝ)) * Real.log (B + d) + + (d : ℝ) ^ (4 - 1 / (r : ℝ)) ≤ + (2 * Real.exp ((r : ℝ) + 1)) * + (K * (d : ℝ) ^ (2 - 1 / (r : ℝ)) * + min (Real.log L + d * Real.log B + (d : ℝ) ^ 2) ((d : ℝ) ^ 2 * b) * + B ^ (1 / (d : ℝ) ^ (1 / (r : ℝ))) / + L ^ (1 / ((r : ℝ) * d * (d : ℝ) ^ (1 / (r : ℝ)))) + + (d : ℝ) ^ (1 - 1 / (r : ℝ)) * Real.log B + + (d : ℝ) ^ (4 - 1 / (r : ℝ))) := by + have hd0 : (0 : ℝ) < d := by exact_mod_cast hd + have hLlog := Real.log_nonneg hL + have hBlog := Real.log_nonneg hB + have hBp : 1 ≤ B + d := by linarith + have hBplog := Real.log_nonneg hBp + have hmin0 : 0 ≤ min (Real.log L + d * Real.log B + (d : ℝ) ^ 2) ((d : ℝ) ^ 2 * b) := by + positivity + have hminp0 : 0 ≤ min (Real.log L + d * Real.log (B + d) + (d : ℝ) ^ 2) + ((d : ℝ) ^ 2 * b) := by positivity + have hmain := mul_le_mul (affine_box_translation_min hd hB hL hb) + (affine_box_translation_power hr hd hB) (by positivity) + (show 0 ≤ 2 * min (Real.log L + d * Real.log B + (d : ℝ) ^ 2) + ((d : ℝ) ^ 2 * b) by positivity) + have hscaled := mul_le_mul_of_nonneg_left hmain + (show 0 ≤ K * (d : ℝ) ^ (2 - 1 / (r : ℝ)) / + L ^ (1 / ((r : ℝ) * d * (d : ℝ) ^ (1 / (r : ℝ)))) by positivity) + have hlower := affine_box_translation_lower hr hd hB + have hE : 1 ≤ Real.exp ((r : ℝ) + 1) := Real.one_le_exp (by positivity) + have hnonneg : 0 ≤ (d : ℝ) ^ (1 - 1 / (r : ℝ)) * Real.log B + + (d : ℝ) ^ (4 - 1 / (r : ℝ)) := by positivity + have hcost := mul_le_mul_of_nonneg_right hE hnonneg + simp only [one_mul] at hcost + have hcost2 := mul_le_mul_of_nonneg_left hcost (by norm_num : (0 : ℝ) ≤ 2) + convert add_le_add hscaled (hlower.trans hcost2) using 1 <;> ring + +/-- Specialization to the common minimum bound used by both affine branches. -/ +theorem affineMinimumAuxiliaryBound_translation {n d : ℕ} (hd : 0 < d) + {A B L b : ℝ} (hA : 0 ≤ A) (hB : 1 ≤ B) (hL : 1 ≤ L) (hb : 0 ≤ b) : + affineMinimumAuxiliaryBound n d A (B + d) L b ≤ + (2 * Real.exp ((n : ℝ) + 2)) * affineMinimumAuxiliaryBound n d A B L b := by + have hK : 0 ≤ max (affineHeightTransferConstant n) (Real.exp 1) * A := + mul_nonneg ((Real.exp_pos 1).le.trans (le_max_right _ _)) hA + have hh := affine_auxiliary_expression_translation_bound (r := n + 1) (by omega) hd hK hB hL hb + have hn : (n : ℝ) + 1 + 1 = n + 2 := by ring + simp only [Nat.cast_add, Nat.cast_one, hn] at hh + have hscaled := mul_le_mul_of_nonneg_left hh (hypersurfaceAbsoluteAuxiliaryConstant_pos n).le + unfold affineMinimumAuxiliaryBound + convert hscaled using 1 + ring + +end +end CCDN diff --git a/LeanPool/CCDN/AuxiliaryPolynomial/AffineBadPrimeTranslation.lean b/LeanPool/CCDN/AuxiliaryPolynomial/AffineBadPrimeTranslation.lean new file mode 100644 index 0000000000..04358a792d --- /dev/null +++ b/LeanPool/CCDN/AuxiliaryPolynomial/AffineBadPrimeTranslation.lean @@ -0,0 +1,82 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.Algebra.AffineTranslation +public import LeanPool.CCDN.AuxiliaryPolynomial.HypersurfaceBadPrimeFactor +public import LeanPool.CCDN.AuxiliaryPolynomial.Nonmultiple +import LeanPool.CCDN.AuxiliaryPolynomial.AffineTranslationCount + +/-! Integral translations preserve the affine bad-prime factor, including +degree-dropping reductions and the nonabsolute zero convention. -/ + +@[expose] public section +namespace CCDN +noncomputable section +open MvPolynomial + +private lemma hypersurfaceBadReduction_affineTranslation {n : ℕ} + (a : Fin n → ℤ) (f : MvPolynomial (Fin n) ℤ) (p : ℕ) : + HypersurfaceBadReduction (affineTranslation ℤ a f) p ↔ HypersurfaceBadReduction f p := by + unfold HypersurfaceBadReduction + apply exists_congr + intro hp + let : Fact p.Prime := ⟨hp⟩ + dsimp only + rw [affineTranslation_map, affineTranslation_totalDegree, affineTranslation_totalDegree, + affineTranslation_irreducible_iff] + +/-- The bad-prime factor of an integral polynomial is invariant under integral affine +translation of the variables. -/ +theorem hypersurfaceBadPrimeFactor_affineTranslation {n : ℕ} + (a : Fin (n + 1) → ℤ) (f : MvPolynomial (Fin (n + 1)) ℤ) : + hypersurfaceBadPrimeFactor (affineTranslation ℤ a f) = hypersurfaceBadPrimeFactor f := by + classical + have hir : AbsolutelyIrreducible (affineTranslation ℤ a f) ↔ AbsolutelyIrreducible f := by + dsimp only [AbsolutelyIrreducible] + rw [affineTranslation_map, affineTranslation_irreducible_iff] + by_cases hf : AbsolutelyIrreducible f + · let bad := (finite_fin_hypersurface_bad_primes f hf + (absolute_integer_polynomial_totalDegree_pos f hf)).toFinset + have hbad (p) : p ∈ bad ↔ stepanovThreshold f.totalDegree < p ∧ + HypersurfaceBadReduction f p := by + simp only [bad, Set.Finite.mem_toFinset, Set.mem_ofPred_eq] + rw [hypersurfaceBadPrimeFactor_eq_exp f hf rfl bad hbad] + exact hypersurfaceBadPrimeFactor_eq_exp _ (hir.mpr hf) (affineTranslation_totalDegree a f) + bad (by intro p; rw [hbad, hypersurfaceBadReduction_affineTranslation]) + · rw [hypersurfaceBadPrimeFactor_eq_zero f hf, + hypersurfaceBadPrimeFactor_eq_zero _ (fun h => hf (hir.mp h))] + +/-- Pull an auxiliary equation back through a bounded translation. -/ +theorem affine_auxiliary_translation_transport {n : ℕ} + (f g : MvPolynomial (Fin n) ℤ) (a : Fin n → ℤ) {B D : ℝ} + (hg : IsBoxAuxiliary (affineTranslation ℤ a f) g (B + D)) (ha : InBox a D) : + ∃ G, IsBoxAuxiliary f G B ∧ G.totalDegree = g.totalDegree := by + let G := (affineTranslation ℤ a).symm g + have hG0 : G ≠ 0 := (affineTranslation ℤ a).symm.injective.ne (by simpa using hg.ne_zero) + have hGnd : ¬ f ∣ G := by + intro hd + apply hg.not_dvd + have hh := map_dvd (affineTranslation ℤ a).toRingHom hd + change affineTranslation ℤ a f ∣ affineTranslation ℤ a ((affineTranslation ℤ a).symm g) at hh + simpa only [AlgEquiv.apply_symm_apply] using hh + refine ⟨G, ⟨hG0, hGnd, ?_⟩, affineTranslation_totalDegree (-a) g⟩ + intro x hx hxf + have hx' (j) : |((x - a) j : ℝ)| ≤ B + D := by + change |((x j - a j : ℤ) : ℝ)| ≤ _ + rw [Int.cast_sub] + exact (abs_sub _ _).trans (add_le_add (hx j) (ha j)) + have hf' : eval (x - a) (affineTranslation ℤ a f) = 0 := by + rw [affineTranslation_eval, sub_add_cancel, hxf] + change eval x (affineTranslation ℤ (-a) g) = 0 + rw [affineTranslation_eval] + exact hg.vanishes (x - a) hx' hf' + +end +end CCDN diff --git a/LeanPool/CCDN/AuxiliaryPolynomial/AffineCongruenceAuxiliary.lean b/LeanPool/CCDN/AuxiliaryPolynomial/AffineCongruenceAuxiliary.lean new file mode 100644 index 0000000000..d6c0f01601 --- /dev/null +++ b/LeanPool/CCDN/AuxiliaryPolynomial/AffineCongruenceAuxiliary.lean @@ -0,0 +1,115 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.AuxiliaryPolynomial.AffineCongruenceHeight +public import LeanPool.CCDN.ForMathlib.StandardExponents +import LeanPool.CCDN.AuxiliaryPolynomial.StandardMonomialAuxiliary +import LeanPool.CCDN.AuxiliaryPolynomial.WeightedMonomialDeterminant + +/-! Auxiliary polynomials from the competing congruence determinant +lower bound and weighted archimedean upper bound. -/ + +@[expose] public section +namespace CCDN +noncomputable section +open MvPolynomial Finset + +private theorem square_minorGcd_eq_abs_det {s : ℕ} (A : Matrix (Fin s) (Fin s) ℤ) : + Int.Matrix.minorGcd A = |A.det| := by + classical + let : Nonempty (Set.powersetCard (Fin s) s) := + ⟨⟨univ, by simp⟩⟩ + rw [minorGcd_eq_gcd_plucker] + have hp : exteriorPower.plucker s A.row = fun _ => A.det := + funext (fun S => exteriorPower.plucker_fin_eq_det A.row S) + rw [hp] + rw [Finset.gcd_eq_gcd_image, Finset.image_const univ_nonempty] + simp only [Finset.gcd_singleton, id_eq, ← Int.abs_eq_normalize] + +/-- The lower bound for the logarithm of a nonzero `s × s` determinant of monomials evaluated +at integral zeros of an absolutely irreducible affine equation of degree `d` in `n + 2` +variables with coefficient height at most `H`, when the zeros lie in smooth residue classes +modulo primes whose logarithms sum to `Q`. -/ +def affineCongruenceDeterminantLower (n d s : ℕ) (H Q : ℝ) : ℝ := + ((n + 1).factorial : ℝ) ^ (1 / (n + 1 : ℝ)) / (n + 2 : ℝ) * + (s : ℝ) ^ (1 + 1 / (n + 1 : ℝ)) * + (Real.log s - (n + 1 : ℝ) * (4 * Real.log d + + congruenceHeightCharge (n + 3) d H Q) - hypersurfaceDeterminantError n) + + (hypersurfaceDeterminantMain n s - (n + 2 : ℝ) * s) * Q + +/-- A strict numerical gap forces every maximal determinant to vanish. +The arithmetic divisibility and archimedean bounds come from the +equation, its smooth residue classes, and the integer points. -/ +private theorem affine_congruence_evaluation_det_eq_zero {n s M : ℕ} (hs : 0 < s) + (f : MvPolynomial (Fin (n + 2)) ℤ) + (hir : AbsolutelyIrreducible f) + (hd : 0 < f.totalDegree) (H : ℝ) (hH : 1 ≤ H) + (hcoeff : CoefficientHeightLE f H) + (mon : Fin s → (Fin (n + 2) →₀ ℕ)) + (hdegree : ∀ j, Finsupp.degree (mon j) ≤ M) + {B : ℝ} {x : Fin s → IntPoint (n + 2)} (hxB : IsBoxZeroFamily f B x) + (P : Finset ℕ) (a : ℕ → IntPoint (n + 2)) (hres : IsSmoothResidueFamily f P a x) + (hgap : Real.log (s.factorial : ℝ) + + (∑ j, ((Finsupp.degree (mon j) : ℕ) : ℝ)) * Real.log B < + affineCongruenceDeterminantLower n f.totalDegree s H (∑ p ∈ P, Real.log p)) : + (evaluationMatrix mon x).det = 0 := by + by_contra hne + let G : Fin s → MvPolynomial (Fin (n + 2)) ℤ := + fun j => monomial (mon j) 1 + have hG : ∀ j, (G j).totalDegree ≤ M := fun j => + (totalDegree_monomial_le _ _).trans (hdegree j) + have hmat : (Matrix.of fun i j => eval (x i) (G j)) = evaluationMatrix mon x := by + ext i j + simp only [Matrix.of_apply, G, eval_monomial, one_mul] + rfl + have hrows : LinearIndependent ℚ + ((Matrix.of fun i j => eval (x i) (G j)).map ((↑) : ℤ → ℚ)).row := by + rw [hmat] + apply Matrix.linearIndependent_rows_iff_isUnit.mpr + apply (Matrix.isUnit_iff_isUnit_det _).mpr + apply isUnit_iff_ne_zero.mpr + change ((Int.castRingHom ℚ).mapMatrix (evaluationMatrix mon x)).det ≠ 0 + rw [← RingHom.map_det (Int.castRingHom ℚ)] + change ((evaluationMatrix mon x).det : ℚ) ≠ 0 + exact_mod_cast hne + have hlo := affine_congruence_determinant_log_lower_height hs f hir hd H hH hcoeff + G hG x hxB.eval_eq_zero hrows P a hres + rw [hmat, square_minorGcd_eq_abs_det, Int.cast_abs] at hlo + have hhi := weighted_evaluation_det_log_bound mon x hxB.one_le_radius hxB.abs_le hne + exact (not_lt_of_ge (hlo.trans hhi)) hgap + +/-- The determinant gap yields an integer auxiliary, proper over +the rationals, for every given finite family of points in the residue classes. -/ +theorem exists_affine_congruence_auxiliary_of_gap {n N t M : ℕ} (hN : 0 < N) + (f : MvPolynomial (Fin (n + 2)) ℤ) + (hir : AbsolutelyIrreducible f) + (hd : 0 < f.totalDegree) (H : ℝ) (hH : 1 ≤ H) + (hcoeff : CoefficientHeightLE f H) + (mon : Fin N → (Fin (n + 2) →₀ ℕ)) (hmon : Function.Injective mon) + (hstd : ∀ j, mon j ∈ Ideal.standardExponents + (Ideal.span {map (Int.castRingHom ℚ) f}) MonomialOrder.degLex) + (hdegree : ∀ j, Finsupp.degree (mon j) ≤ M) + {B : ℝ} {x : Fin t → IntPoint (n + 2)} (hxB : IsBoxZeroFamily f B x) + (P : Finset ℕ) (a : ℕ → IntPoint (n + 2)) (hres : IsSmoothResidueFamily f P a x) + (hgap : Real.log (N.factorial : ℝ) + + (∑ j, ((Finsupp.degree (mon j) : ℕ) : ℝ)) * Real.log B < + affineCongruenceDeterminantLower n f.totalDegree N H (∑ p ∈ P, Real.log p)) : + ∃ g : MvPolynomial (Fin (n + 2)) ℤ, g ≠ 0 ∧ + ¬ map (Int.castRingHom ℚ) f ∣ map (Int.castRingHom ℚ) g ∧ + g.totalDegree ≤ M ∧ ∀ i, eval (x i) g = 0 := by + apply exists_standard_auxiliary_of_zero_determinants + (map (Int.castRingHom ℚ) f) mon hmon hstd hdegree x + intro b + exact affine_congruence_evaluation_det_eq_zero hN f hir hd H hH hcoeff mon hdegree (x := x ∘ b) + ⟨hxB.one_le_radius, fun i => hxB.eval_eq_zero (b i), fun i j => hxB.abs_le (b i) j⟩ P a + ⟨hres.prime, hres.eval_eq_zero, hres.smooth, fun p hp i j => hres.dvd_sub p hp (b i) j⟩ hgap + +end +end CCDN diff --git a/LeanPool/CCDN/AuxiliaryPolynomial/AffineCongruenceHeight.lean b/LeanPool/CCDN/AuxiliaryPolynomial/AffineCongruenceHeight.lean new file mode 100644 index 0000000000..3e5e0d71fa --- /dev/null +++ b/LeanPool/CCDN/AuxiliaryPolynomial/AffineCongruenceHeight.lean @@ -0,0 +1,82 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.Algebra.PolynomialPredicates +public import LeanPool.CCDN.Determinant.HypersurfaceDeterminantRank +public import LeanPool.CCDN.Height.CountingHypotheses +public import LeanPool.CCDN.Plane.IntegerPlaneLogHeight +import LeanPool.CCDN.AuxiliaryPolynomial.CongruenceBadPrimeCharge +import LeanPool.CCDN.AuxiliaryPolynomial.UnscaledHypersurfaceAuxiliary +import LeanPool.CCDN.AuxiliaryPolynomial.UnscaledHypersurfaceBadPrimes +import LeanPool.CCDN.Determinant.AffineCongruenceDeterminant + +/-! The affine congruence determinant bound with all bad-prime charges +controlled by the equation's coefficient height and congruence modulus. -/ + +@[expose] public section +namespace CCDN +noncomputable section +open MvPolynomial Finset + +/-- The logarithmic bad-prime charge in the affine congruence determinant estimate for a form +of degree `d` in `m` variables with coefficient height at most `H`, where `Q` is the sum of +the logarithms of the congruence primes. -/ +def congruenceHeightCharge (m d : ℕ) (H Q : ℝ) : ℝ := + Real.log (1 + 27 * (d : ℝ) ^ 4 * + (hypersurfaceHeightConstant m * (1 + Real.log H / (d : ℝ) ^ 2)) + Q) + + 2 * mertensError + 1 + +/-- The logarithmic height and modulus are the only equation-dependent +errors in the affine smooth-congruence determinant estimate. -/ +theorem affine_congruence_determinant_log_lower_height {n s N M : ℕ} (hs : 0 < s) + (f : MvPolynomial (Fin (n + 2)) ℤ) + (hir : AbsolutelyIrreducible f) + (hd : 0 < f.totalDegree) (H : ℝ) (hH : 1 ≤ H) + (hcoeff : CoefficientHeightLE f H) + (G : Fin N → MvPolynomial (Fin (n + 2)) ℤ) (hG : ∀ j, (G j).totalDegree ≤ M) + (x : Fin s → IntPoint (n + 2)) (hx : ∀ i, eval (x i) f = 0) + (hrows : LinearIndependent ℚ + ((Matrix.of fun i j => eval (x i) (G j)).map ((↑) : ℤ → ℚ)).row) + (P : Finset ℕ) (a : ℕ → IntPoint (n + 2)) (hres : IsSmoothResidueFamily f P a x) : + hypersurfaceRankLowerBound n s f.totalDegree + (congruenceHeightCharge (n + 3) f.totalDegree H (∑ p ∈ P, Real.log p)) + + (hypersurfaceDeterminantMain n s - (n + 2 : ℝ) * s) * + (∑ p ∈ P, Real.log p) ≤ + Real.log (Int.Matrix.minorGcd (Matrix.of fun i j => eval (x i) (G j))) := by + obtain ⟨bad, hbad, hbound⟩ := exists_affine_congruence_determinant_log_lower hs f hir hd + G hG x hx hrows P a hres + let F := scaledHomogenize f.totalDegree 1 f + have hf0 : f ≠ 0 := ne_zero_of_irreducible_map hir + have hF0 : F ≠ 0 := scaledHomogenize_ne_zero _ one_ne_zero hf0 + have hFdegree : F.totalDegree = f.totalDegree := + (scaledHomogenize_homogeneous 1 f le_rfl).totalDegree hF0 + have hFir : AbsolutelyIrreducible F := + (unscaledHomogenize_absolute_irreducible_iff f hf0).mpr hir + have hheight : (integerCoefficientHeight F : ℝ) ≤ H := by + rw [show F = scaledHomogenize f.totalDegree 1 f from rfl, + integerCoefficientHeight_scaledHomogenize_one] + exact integerCoefficientHeight_le f (by linarith) hcoeff + have hc := hypersurface_and_congruence_prime_charge F hFir (hFdegree ▸ hd) H hH + (fun e => (coefficient_abs_le_integerCoefficientHeight F e).trans hheight) bad P + (fun p hp => by simpa only [hFdegree] using (hbad p).mp hp) hres.prime + change (∑ p ∈ bad ∪ P, Real.log p / p) ≤ + congruenceHeightCharge (n + 3) F.totalDegree H (∑ p ∈ P, Real.log p) at hc + rw [hFdegree] at hc + have hfactor : 0 ≤ ((n + 1).factorial : ℝ) ^ (1 / (n + 1 : ℝ)) / (n + 2 : ℝ) * + (s : ℝ) ^ (1 + 1 / (n + 1 : ℝ)) := by positivity + apply le_trans ?_ hbound + apply add_le_add + · apply mul_le_mul_of_nonneg_left _ hfactor + have hcharge := mul_le_mul_of_nonneg_left hc (show 0 ≤ (n + 1 : ℝ) by positivity) + linarith + · exact le_rfl + +end +end CCDN diff --git a/LeanPool/CCDN/AuxiliaryPolynomial/AffineHeightCutoff.lean b/LeanPool/CCDN/AuxiliaryPolynomial/AffineHeightCutoff.lean new file mode 100644 index 0000000000..a227fff234 --- /dev/null +++ b/LeanPool/CCDN/AuxiliaryPolynomial/AffineHeightCutoff.lean @@ -0,0 +1,129 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import Mathlib.Analysis.SpecialFunctions.Pow.Real +import LeanPool.CCDN.AuxiliaryPolynomial.HypersurfacePositionHeight + +/-! A polynomial box cutoff from the homogeneous coefficient-height bound. +The constants are explicit; only their dependence on dimension is needed in +the affine auxiliary-polynomial theorem. -/ + +@[expose] public section +namespace CCDN + +private lemma box_le_of_logarithmic_height_bound {A B N D : ℝ} + (hA : 1 ≤ A) (hB : 1 ≤ B) (hN : 1 ≤ N) (hD : 0 ≤ D) + (hheight : B / A ≤ N * Real.log N + N * D * Real.log B) : + B ≤ 4 * A ^ 2 * N ^ 4 * (D + 1) ^ 2 := by + have hA0 : 0 < A := by linarith + have hB0 : 0 < B := by linarith + have hN0 : 0 ≤ N := by linarith + have hlogN : Real.log N ≤ N := Real.log_le_self hN0 + have hlogB : Real.log B ≤ 2 * Real.sqrt B := by + have hh := Real.log_le_rpow_div hB0.le (by norm_num : (0 : ℝ) < 1 / 2) + rw [← Real.sqrt_eq_rpow] at hh + linarith + have ht : 1 ≤ Real.sqrt B := by + simpa only [Real.sqrt_one] using Real.sqrt_le_sqrt hB + have ht0 : 0 < Real.sqrt B := by linarith + have hNsq : N ≤ N ^ 2 := by nlinarith + have hfirst : N * Real.log N ≤ N ^ 2 * Real.sqrt B := by + calc + _ ≤ N ^ 2 := by nlinarith [mul_le_mul_of_nonneg_left hlogN hN0] + _ ≤ _ := le_mul_of_one_le_right (sq_nonneg N) ht + have hsecond : N * D * Real.log B ≤ 2 * N ^ 2 * D * Real.sqrt B := by + calc + _ ≤ N * D * (2 * Real.sqrt B) := + mul_le_mul_of_nonneg_left hlogB (mul_nonneg hN0 hD) + _ ≤ N ^ 2 * D * (2 * Real.sqrt B) := by gcongr + _ = _ := by ring + have hmain : B ≤ (A * N ^ 2 * (1 + 2 * D)) * Real.sqrt B := by + have hh := (div_le_iff₀ hA0).mp hheight + have hh' := mul_le_mul_of_nonneg_left (add_le_add hfirst hsecond) hA0.le + nlinarith only [hh, hh'] + have hroot : Real.sqrt B ≤ A * N ^ 2 * (1 + 2 * D) := by + apply (mul_le_mul_iff_right₀ ht0).mp + simpa only [← pow_two, Real.sq_sqrt hB0.le, mul_comm] using hmain + have hroot' : Real.sqrt B ≤ 2 * A * N ^ 2 * (D + 1) := by + have hh : 0 ≤ A * N ^ 2 := by positivity + nlinarith only [hroot, hh] + have hh := pow_le_pow_left₀ ht0.le hroot' 2 + rw [Real.sq_sqrt hB0.le] at hh + nlinarith only [hh] + +/-- If the constant-coefficient prime choice fails while the coefficient +height obeys the determinant bound, the box is polynomially bounded in d. -/ +theorem affine_coefficient_height_cutoff (n d : ℕ) {A B : ℝ} + (hA : 1 ≤ A) (hB : 1 ≤ B) + (hheight : B / A ≤ ((n + d).choose d : ℝ) * Real.log ((n + d).choose d) + + ((n + d).choose d : ℝ) * d * Real.log B) : + B ≤ 4 * A ^ 2 * ((d : ℝ) + 1) ^ (4 * n + 2) := by + have hN : (1 : ℝ) ≤ (n + d).choose d := by + exact_mod_cast Nat.choose_pos (show d ≤ n + d by omega) + have hbound := box_le_of_logarithmic_height_bound hA hB hN (Nat.cast_nonneg d) hheight + have hcount : ((n + d).choose d : ℝ) ≤ ((d : ℝ) + 1) ^ n := by + rw [← Nat.choose_symm_add, Nat.add_comm n d] + exact_mod_cast Nat.choose_add_le_add_one_pow d n + calc + B ≤ 4 * A ^ 2 * ((n + d).choose d : ℝ) ^ 4 * ((d : ℝ) + 1) ^ 2 := hbound + _ ≤ 4 * A ^ 2 * (((d : ℝ) + 1) ^ n) ^ 4 * ((d : ℝ) + 1) ^ 2 := by + gcongr + _ = _ := by rw [pow_add, Nat.mul_comm 4 n, pow_mul]; ring + +/-- The extra projective box exponent is uniformly bounded on every +polynomial range in the degree. -/ +private theorem polynomial_box_root_power_bound {r d k : ℕ} (hr : 0 < r) (hd : 0 < d) + {S B : ℝ} (hS : 1 ≤ S) (hB : 1 ≤ B) + (hbound : B ≤ S * ((d : ℝ) + 1) ^ k) : + B ^ (1 / ((r : ℝ) * (d : ℝ) ^ (1 / (r : ℝ)))) ≤ + Real.exp (Real.log S + (k : ℝ) * ((r : ℝ) + 1)) := by + have hr1 : (1 : ℝ) ≤ r := by exact_mod_cast hr + have hd1 : (1 : ℝ) ≤ d := by exact_mod_cast hd + have hS0 : 0 < S := by linarith + have hB0 : 0 < B := by linarith + have hroot : (1 : ℝ) ≤ (d : ℝ) ^ (1 / (r : ℝ)) := + Real.one_le_rpow hd1 (by positivity) + have hden : (1 : ℝ) ≤ r * (d : ℝ) ^ (1 / (r : ℝ)) := + one_le_mul_of_one_le_of_one_le hr1 hroot + have hlogS := Real.log_nonneg hS + have hlogd := log_nat_add_one_le_root hr hd + have hlog := Real.log_le_log hB0 hbound + rw [Real.log_mul hS0.ne' (by positivity), Real.log_pow] at hlog + have hstep : Real.log B ≤ + (Real.log S + (k : ℝ) * ((r : ℝ) + 1)) * + ((r : ℝ) * (d : ℝ) ^ (1 / (r : ℝ))) := by + have hfirst := mul_le_mul_of_nonneg_left hden hlogS + have hsecond := mul_le_mul_of_nonneg_left hlogd (Nat.cast_nonneg (α := ℝ) k) + have hthird := mul_le_mul_of_nonneg_left hr1 + (show 0 ≤ (k : ℝ) * ((r : ℝ) + 1) * (d : ℝ) ^ (1 / (r : ℝ)) by positivity) + nlinarith only [hlog, hfirst, hsecond, hthird] + rw [Real.rpow_def_of_pos hB0, Real.exp_le_exp, ← div_eq_mul_one_div] + exact (div_le_iff₀ (by linarith)).mpr hstep + +/-- On a polynomial box range, the projective main term has the affine +box exponent up to a constant independent of the degree and the box. -/ +theorem polynomial_box_projective_power_bound {r d k : ℕ} + (hr : 0 < r) (hd : 0 < d) {S B : ℝ} + (hS : 1 ≤ S) (hB : 1 ≤ B) + (hbound : B ≤ S * ((d : ℝ) + 1) ^ k) : + B ^ (((r : ℝ) + 1) / ((r : ℝ) * (d : ℝ) ^ (1 / (r : ℝ)))) ≤ + Real.exp (Real.log S + (k : ℝ) * ((r : ℝ) + 1)) * + B ^ (1 / (d : ℝ) ^ (1 / (r : ℝ))) := by + have hr0 : (0 : ℝ) < r := by exact_mod_cast hr + have hd0 : (0 : ℝ) < d := by exact_mod_cast hd + have hroot : (0 : ℝ) < (d : ℝ) ^ (1 / (r : ℝ)) := by positivity + have he : ((r : ℝ) + 1) / ((r : ℝ) * (d : ℝ) ^ (1 / (r : ℝ))) = + 1 / ((r : ℝ) * (d : ℝ) ^ (1 / (r : ℝ))) + + 1 / (d : ℝ) ^ (1 / (r : ℝ)) := by field_simp; ring + rw [he, Real.rpow_add (by linarith)] + exact mul_le_mul_of_nonneg_right + (polynomial_box_root_power_bound hr hd hS hB hbound) (by positivity) + +end CCDN diff --git a/LeanPool/CCDN/AuxiliaryPolynomial/AffineHypersurfaceAuxiliary.lean b/LeanPool/CCDN/AuxiliaryPolynomial/AffineHypersurfaceAuxiliary.lean new file mode 100644 index 0000000000..83e31d47c4 --- /dev/null +++ b/LeanPool/CCDN/AuxiliaryPolynomial/AffineHypersurfaceAuxiliary.lean @@ -0,0 +1,138 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.AuxiliaryPolynomial.AffineAuxiliaryCases +import LeanPool.CCDN.AuxiliaryPolynomial.AffineAuxiliaryTranslationBound +import LeanPool.CCDN.AuxiliaryPolynomial.AffineBadPrimeTranslation +import LeanPool.CCDN.AuxiliaryPolynomial.AffineLeadingTranslation +import LeanPool.CCDN.AuxiliaryPolynomial.AffineTranslationCount +import LeanPool.CCDN.Height.ScaledHypersurfaceHeight + +/-! The affine auxiliary theorem in every positive dimension and degree, +with the degree-loss-inclusive geometric bad-prime factor. This distinction +from the literal affine wording of CCDN Definition 3.2.1 is intentional. -/ + +@[expose] public section +namespace CCDN +noncomputable section +open MvPolynomial + +/-- The shape of the affine auxiliary degree bound in degree `d` and box radius `B`, with +`L` the coefficient height of the leading form and `b` the bad-prime factor: writing +`r = n + 1`, it is +$d^{2 - 1/r} \min(\log L + d \log B + d^2, d^2 b) B^{1/d^{1/r}} / L^{1/(r d^{1 + 1/r})} ++ d^{1 - 1/r} \log B + d^{4 - 1/r}$. -/ +def affineAuxiliaryDegreeExpression (n d : ℕ) (B L b : ℝ) : ℝ := + (d : ℝ) ^ (2 - 1 / (n + 1 : ℝ)) * + min (Real.log L + d * Real.log B + (d : ℝ) ^ 2) ((d : ℝ) ^ 2 * b) * + B ^ (1 / (d : ℝ) ^ (1 / (n + 1 : ℝ))) / + L ^ (1 / ((n + 1 : ℝ) * d * (d : ℝ) ^ (1 / (n + 1 : ℝ)))) + + (d : ℝ) ^ (1 - 1 / (n + 1 : ℝ)) * Real.log B + + (d : ℝ) ^ (4 - 1 / (n + 1 : ℝ)) + +/-- The dimension constant in the affine auxiliary theorem for equations in `n + 2` +variables, multiplying `affineAuxiliaryDegreeExpression`. -/ +def affineHypersurfaceAuxiliaryConstant (n : ℕ) : ℝ := + (2 * Real.exp ((n : ℝ) + 2)) * hypersurfaceAbsoluteAuxiliaryConstant n * + (max (affineHeightTransferConstant n) (Real.exp 1) * + affineAuxiliaryComparisonConstant n + 1) + +/-- The constant `affineHypersurfaceAuxiliaryConstant n` is positive. -/ +lemma affineHypersurfaceAuxiliaryConstant_pos (n : ℕ) : + 0 < affineHypersurfaceAuxiliaryConstant n := by + have hK : 0 ≤ max (affineHeightTransferConstant n) (Real.exp 1) := + (Real.exp_pos 1).le.trans (le_max_right _ _) + have hA := zero_le_one.trans (affineAuxiliaryComparisonConstant_one_le n) + have hC := hypersurfaceAbsoluteAuxiliaryConstant_pos n + unfold affineHypersurfaceAuxiliaryConstant + positivity + +/-- The affine auxiliary degree bound with comparison constant `A ≥ 0` is at most a constant +depending on `n` and `A` times `affineAuxiliaryDegreeExpression`, for `1 ≤ B`, `1 ≤ L` and +`0 ≤ b`. -/ +lemma affineMinimumAuxiliaryBound_le_expression (n d : ℕ) {A B L b : ℝ} + (hA : 0 ≤ A) (hB : 1 ≤ B) (hL : 1 ≤ L) (hb : 0 ≤ b) : + affineMinimumAuxiliaryBound n d A B L b ≤ + (hypersurfaceAbsoluteAuxiliaryConstant n * + (max (affineHeightTransferConstant n) (Real.exp 1) * A + 1)) * + affineAuxiliaryDegreeExpression n d B L b := by + let K := max (affineHeightTransferConstant n) (Real.exp 1) * A + let M := (d : ℝ) ^ (2 - 1 / (n + 1 : ℝ)) * + min (Real.log L + d * Real.log B + (d : ℝ) ^ 2) ((d : ℝ) ^ 2 * b) * + B ^ (1 / (d : ℝ) ^ (1 / (n + 1 : ℝ))) / + L ^ (1 / ((n + 1 : ℝ) * d * (d : ℝ) ^ (1 / (n + 1 : ℝ)))) + let E := (d : ℝ) ^ (1 - 1 / (n + 1 : ℝ)) * Real.log B + + (d : ℝ) ^ (4 - 1 / (n + 1 : ℝ)) + have hK : 0 ≤ K := mul_nonneg ((Real.exp_pos 1).le.trans (le_max_right _ _)) hA + have hmin : 0 ≤ min (Real.log L + d * Real.log B + (d : ℝ) ^ 2) ((d : ℝ) ^ 2 * b) := + le_min (add_nonneg (add_nonneg (Real.log_nonneg hL) + (mul_nonneg (Nat.cast_nonneg d) (Real.log_nonneg hB))) (sq_nonneg _)) + (mul_nonneg (sq_nonneg _) hb) + have hM : 0 ≤ M := by dsimp only [M]; positivity + have hE : 0 ≤ E := by + have := Real.log_nonneg hB + dsimp only [E] + positivity + have hh : K * M + E ≤ (K + 1) * (M + E) := by nlinarith [mul_nonneg hK hE] + have hh' := mul_le_mul_of_nonneg_left hh (hypersurfaceAbsoluteAuxiliaryConstant_pos n).le + unfold affineMinimumAuxiliaryBound affineAuxiliaryDegreeExpression + dsimp only [K, M, E] at hh' + convert hh' using 1 <;> ring + +/-- Every primitive irreducible integer affine hypersurface admits the +coefficient-sensitive auxiliary in the complete minimum form, using the +defined geometric bad-prime factor (which includes degree-dropping primes). +The constant depends only on the dimension. -/ +theorem affine_hypersurface_auxiliary {n : ℕ} + (f : MvPolynomial (Fin (n + 2)) ℤ) (hprim : PrimitiveIntegerPolynomial f) + (hir : Irreducible f) (hd : 0 < f.totalDegree) {B : ℝ} (hB : 1 ≤ B) : + ∃ g, IsBoxAuxiliary f g B ∧ + (g.totalDegree : ℝ) ≤ affineHypersurfaceAuxiliaryConstant n * + affineAuxiliaryDegreeExpression n f.totalDegree B + (integerCoefficientHeight (homogeneousComponent f.totalDegree f) : ℝ) + (hypersurfaceBadPrimeFactor f) := by + let d := f.totalDegree + let L := (integerCoefficientHeight (homogeneousComponent d f) : ℝ) + let b := hypersurfaceBadPrimeFactor f + let A := affineAuxiliaryComparisonConstant n + have hA : 0 ≤ A := zero_le_one.trans (affineAuxiliaryComparisonConstant_one_le n) + have hL : 1 ≤ L := leading_height_one_le f hir.ne_zero + have hb : 0 ≤ b := hypersurfaceBadPrimeFactor_nonneg f + obtain ⟨a, ha, hc⟩ := exists_bounded_affine_translation_nonzero_constant f hir.ne_zero + let t := affineTranslation ℤ a f + have htprim : PrimitiveIntegerPolynomial t := hprim.translate_affine a + have htir : Irreducible t := (affineTranslation_irreducible_iff a f).mpr hir + have htd : t.totalDegree = d := affineTranslation_totalDegree a f + have htdpos : 0 < t.totalDegree := htd ▸ hd + have hB' : 1 ≤ B + d := by have := Nat.cast_nonneg (α := ℝ) d; linarith + obtain ⟨g, hg, hgdeg⟩ := + affine_auxiliary_minimum_nonzero_constant t htprim htir htdpos hc hB' + have habox (j) : |(a j : ℝ)| ≤ d := by + have h0 : (0 : ℝ) ≤ a j := by exact_mod_cast (ha j).1 + rw [abs_of_nonneg h0] + exact_mod_cast (ha j).2 + obtain ⟨G, hG, hGdeg⟩ := affine_auxiliary_translation_transport f g a hg habox + refine ⟨G, hG, ?_⟩ + have htle : (g.totalDegree : ℝ) ≤ affineMinimumAuxiliaryBound n d A (B + d) L b := by + have htop : homogeneousComponent t.totalDegree t = homogeneousComponent d f := + affineTranslation_leading_homogeneousComponent a f + rw [htop, htd] at hgdeg + simpa only [t, hypersurfaceBadPrimeFactor_affineTranslation] using hgdeg + rw [hGdeg] + apply (htle.trans (affineMinimumAuxiliaryBound_translation hd hA hB hL hb)).trans + have hh := mul_le_mul_of_nonneg_left + (affineMinimumAuxiliaryBound_le_expression n d hA hB hL hb) + (show 0 ≤ 2 * Real.exp ((n : ℝ) + 2) by positivity) + dsimp only [affineHypersurfaceAuxiliaryConstant, A] + convert hh using 1 + ring + +end +end CCDN diff --git a/LeanPool/CCDN/AuxiliaryPolynomial/AffineLeadingTranslation.lean b/LeanPool/CCDN/AuxiliaryPolynomial/AffineLeadingTranslation.lean new file mode 100644 index 0000000000..08d7f761c7 --- /dev/null +++ b/LeanPool/CCDN/AuxiliaryPolynomial/AffineLeadingTranslation.lean @@ -0,0 +1,89 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.Algebra.AffineTranslation +public import LeanPool.CCDN.Algebra.HomogeneousSlice +import LeanPool.CCDN.AuxiliaryPolynomial.AffineTranslationCount +import LeanPool.CCDN.Height.MultivariateGridHeight + +/-! Translation preserves the leading homogeneous equation exactly. Bounded +integer translations make the constant coefficient nonzero in every dimension. -/ + +@[expose] public section +namespace CCDN +noncomputable section +open MvPolynomial + +private lemma scaledHomogenize_one_affineTranslation {R : Type*} [CommRing R] [Nontrivial R] + {n d : ℕ} (a : Fin n → R) (f : MvPolynomial (Fin n) R) (hd : f.totalDegree ≤ d) : + scaledHomogenize d 1 (affineTranslation R a f) = + homogeneousTranslation R a (scaledHomogenize d 1 f) := by + have ht : (affineTranslation R a f).totalDegree ≤ d := by + rwa [affineTranslation_totalDegree] + apply affineChart_injective_on_homogeneous + (scaledHomogenize_homogeneous _ _ ht) + (homogeneousTranslation_homogeneous _ (scaledHomogenize_homogeneous _ _ hd)) + rw [affineChart_scaledHomogenize_one, affineChart_homogeneousTranslation, + affineChart_scaledHomogenize_one] + +private lemma homogeneousSlice_zero_translation {R : Type*} [CommRing R] {n : ℕ} + (a : Fin n → R) (f : MvPolynomial (Fin (n + 1)) R) : + homogeneousSlice n 0 (homogeneousTranslation R a f) = homogeneousSlice n 0 f := by + have he : (homogeneousSlice n (0 : R)).comp (homogeneousTranslation R a).toRingHom = + homogeneousSlice n 0 := by + apply MvPolynomial.ringHom_ext + · intro c + simp [homogeneousSlice, homogeneousTranslation, homogeneousTranslationHom] + · intro j + refine Fin.cases ?_ (fun k => ?_) j <;> + simp [homogeneousSlice, homogeneousTranslation, homogeneousTranslationHom] + exact RingHom.congr_fun he f + +private lemma homogeneousSlice_zero_scaledHomogenize_one {R : Type*} [CommRing R] + {n d : ℕ} (f : MvPolynomial (Fin n) R) (hd : f.totalDegree ≤ d) : + homogeneousSlice n 0 (scaledHomogenize d 1 f) = homogeneousComponent d f := by + classical + rw [scaledHomogenize, map_sum] + conv_rhs => rw [f.as_sum, map_sum] + apply Finset.sum_congr rfl + intro m hm + rw [homogeneousSlice_monomial, chartExponent_homogenizedExponent] + have hmd : m.sum (fun _ e => e) ≤ d := (le_totalDegree hm).trans hd + by_cases he : m.sum (fun _ e => e) = d + · have hhom := isHomogeneous_monomial (f.coeff m) he + rw [homogeneousComponent_eq_self hhom] + simp [homogenizedExponent, he] + · have hlt : m.sum (fun _ e => e) < d := lt_of_le_of_ne hmd he + rw [homogeneousComponent_eq_zero d _ ((totalDegree_monomial_le m (f.coeff m)).trans_lt hlt)] + simp [homogenizedExponent, Nat.sub_ne_zero_of_lt hlt] + +/-- Affine translation of the variables does not change the leading form: the top-degree +homogeneous component of `affineTranslation R a f` equals that of `f`. -/ +theorem affineTranslation_leading_homogeneousComponent {R : Type*} + [CommRing R] [Nontrivial R] {n : ℕ} + (a : Fin n → R) (f : MvPolynomial (Fin n) R) : + homogeneousComponent (affineTranslation R a f).totalDegree (affineTranslation R a f) = + homogeneousComponent f.totalDegree f := by + rw [affineTranslation_totalDegree] + rw [← homogeneousSlice_zero_scaledHomogenize_one _ (affineTranslation_totalDegree_le a f), + scaledHomogenize_one_affineTranslation a f le_rfl, homogeneousSlice_zero_translation, + homogeneousSlice_zero_scaledHomogenize_one f le_rfl] + +/-- A nonzero integral polynomial `f` in `n` variables has an integral translate with nonzero +constant coefficient by a vector whose entries lie in `[0, deg f]`. -/ +theorem exists_bounded_affine_translation_nonzero_constant {n : ℕ} + (f : MvPolynomial (Fin n) ℤ) (hf : f ≠ 0) : + ∃ a : Fin n → ℤ, (∀ j, 0 ≤ a j ∧ a j ≤ f.totalDegree) ∧ + (affineTranslation ℤ a f).coeff 0 ≠ 0 := by + obtain ⟨a, ha, he, _⟩ := exists_integer_multivariate_grid_height_witness f hf le_rfl + exact ⟨a, ha, by rwa [affineTranslation_constantCoeff]⟩ + +end +end CCDN diff --git a/LeanPool/CCDN/AuxiliaryPolynomial/AffineTranslationCount.lean b/LeanPool/CCDN/AuxiliaryPolynomial/AffineTranslationCount.lean new file mode 100644 index 0000000000..7003363c47 --- /dev/null +++ b/LeanPool/CCDN/AuxiliaryPolynomial/AffineTranslationCount.lean @@ -0,0 +1,91 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.Algebra.AffineTranslation +public import LeanPool.CCDN.Determinant.SingularReduction +import LeanPool.CCDN.Plane.GridHeight + +/-! Bounded translations remove a zero constant coefficient and preserve the +finite-field residue count, primitivity and integer point families. -/ + +@[expose] public section +namespace CCDN +noncomputable section +open MvPolynomial + +/-- Evaluating the affine translate of `f` by `a` at `x` gives the value of `f` at `x + a`. -/ +lemma affineTranslation_eval {R : Type*} [CommRing R] {n : ℕ} + (a x : Fin n → R) (f : MvPolynomial (Fin n) R) : + eval x (affineTranslation R a f) = eval (x + a) f := by + change eval x (eval₂ C (fun j => X j + C (a j)) f) = _ + rw [← eval_assoc] + congr 2 + funext j + simp + +/-- The constant coefficient of the affine translate of `f` by `a` is the value of `f` at +`a`. -/ +lemma affineTranslation_constantCoeff {R : Type*} [CommRing R] {n : ℕ} + (a : Fin n → R) (f : MvPolynomial (Fin n) R) : + (affineTranslation R a f).coeff 0 = eval a f := by + have he := affineTranslation_eval a 0 f + simpa only [zero_add, eval_zero, constantCoeff_eq] using he + +/-- An integral affine translate of a primitive integral polynomial is primitive. -/ +lemma PrimitiveIntegerPolynomial.translate_affine {n : ℕ} (a : Fin n → ℤ) + {f : MvPolynomial (Fin n) ℤ} (hf : PrimitiveIntegerPolynomial f) : + PrimitiveIntegerPolynomial (affineTranslation ℤ a f) := by + intro c hc + have hd := (C_dvd_iff_dvd_coeff c _).mpr hc + have he := map_dvd (affineTranslation ℤ a).symm.toRingHom hd + have hC : (affineTranslation ℤ a).symm (C c) = C c := (affineTranslation ℤ a).symm.commutes c + change (affineTranslation ℤ a).symm (C c) ∣ + (affineTranslation ℤ a).symm (affineTranslation ℤ a f) at he + rw [hC, AlgEquiv.symm_apply_apply] at he + exact hf c ((C_dvd_iff_dvd_coeff c f).mp he) + +/-- Translation of the residue zero set, over ZMod p for every p. -/ +private def affineTranslationResidueEquiv (a : Fin 2 → ℤ) (f : MvPolynomial (Fin 2) ℤ) (p : ℕ) : + PlaneResidue (affineTranslation ℤ a f) p ≃ PlaneResidue f p where + toFun x := ⟨x.val + fun j => (a j : ZMod p), by + have h := x.property + rw [← eval_map] at h + rw [affineTranslation_map, affineTranslation_eval] at h + simpa only [eval_map, Function.comp_def, Int.coe_castRingHom] using h⟩ + invFun x := ⟨x.val - fun j => (a j : ZMod p), by + rw [← eval_map] + rw [affineTranslation_map, affineTranslation_eval] + simpa only [Function.comp_def, Int.coe_castRingHom, sub_add_cancel, eval_map] using x.property⟩ + left_inv x := by apply Subtype.ext; exact add_sub_cancel_right _ _ + right_inv x := by apply Subtype.ext; exact sub_add_cancel _ _ + +/-- An integral affine translation of a plane polynomial preserves the number of its zeros +modulo `p` in the affine plane over `ZMod p`. -/ +lemma affineTranslation_residue_card (a : Fin 2 → ℤ) (f : MvPolynomial (Fin 2) ℤ) (p : ℕ) : + Nat.card (PlaneResidue (affineTranslation ℤ a f) p) = Nat.card (PlaneResidue f p) := + Nat.card_congr (affineTranslationResidueEquiv a f p) + +/-- A nonzero integral plane polynomial `f` has an integral translate with nonzero constant +coefficient by a vector whose entries lie in `[0, deg f]`. -/ +theorem exists_bounded_translation_nonzero_constant (f : MvPolynomial (Fin 2) ℤ) (hf : f ≠ 0) : + ∃ a : Fin 2 → ℤ, (∀ j, 0 ≤ a j ∧ a j ≤ f.totalDegree) ∧ + (affineTranslation ℤ a f).coeff 0 ≠ 0 := by + obtain ⟨x, y, hx, hy, hn, _⟩ := exists_integer_plane_grid_height_witness f hf le_rfl + refine ⟨![(x : ℤ), (y : ℤ)], ?_, ?_⟩ + · intro j + fin_cases j + · change 0 ≤ (x : ℤ) ∧ (x : ℤ) ≤ f.totalDegree + exact ⟨by positivity, by exact_mod_cast hx⟩ + · change 0 ≤ (y : ℤ) ∧ (y : ℤ) ≤ f.totalDegree + exact ⟨by positivity, by exact_mod_cast hy⟩ + · rwa [affineTranslation_constantCoeff] + +end +end CCDN diff --git a/LeanPool/CCDN/AuxiliaryPolynomial/BadPrimeCharge.lean b/LeanPool/CCDN/AuxiliaryPolynomial/BadPrimeCharge.lean new file mode 100644 index 0000000000..796987c757 --- /dev/null +++ b/LeanPool/CCDN/AuxiliaryPolynomial/BadPrimeCharge.lean @@ -0,0 +1,98 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.Analysis.PrimeCutoff +public import LeanPool.CCDN.AuxiliaryPolynomial.DegreeBound +import LeanPool.CCDN.Analysis.PrimeProduct + +/-! Analytic control of bad-prime charges from an integer witness. +The construction and coefficient bound for a Noether witness remain separate. -/ + +@[expose] public section +namespace CCDN +noncomputable section +open Finset Real + +/-- Distinct prime divisors of a positive integer have total logarithmic +weight at most the logarithm of that integer. -/ +lemma prime_divisor_log_sum_le (P : Finset ℕ) {D : ℤ} (hD : 0 < D) + (hP : ∀ p ∈ P, p.Prime) (hdiv : ∀ p ∈ P, (p : ℤ) ∣ D) : + (∑ p ∈ P, Real.log p) ≤ Real.log D := by + simpa only [Nat.cast_one, one_mul] using sum_log_prime_powers_le_log P (fun _ => 1) + hD hP (fun p hp => by simpa only [pow_one] using hdiv p hp) + +/-- Above a cutoff T, the reciprocal-weighted charge costs at most log(D)/T. -/ +lemma prime_divisor_charge_le (P : Finset ℕ) {D : ℤ} (hD : 0 < D) + (hP : ∀ p ∈ P, p.Prime) (hdiv : ∀ p ∈ P, (p : ℤ) ∣ D) + {T : ℝ} (hT : 0 < T) (hlo : ∀ p ∈ P, T ≤ (p : ℝ)) : + (∑ p ∈ P, Real.log p / p) ≤ Real.log D / T := by + calc + _ ≤ ∑ p ∈ P, Real.log p / T := by + apply sum_le_sum + intro p hp + exact div_le_div_of_nonneg_left (by positivity) hT (hlo p hp) + _ = (∑ p ∈ P, Real.log p) / T := (sum_div _ _ _).symm + _ ≤ _ := div_le_div_of_nonneg_right (prime_divisor_log_sum_le P hD hP hdiv) hT.le + +/-- Split the bad primes at T. The low part costs log(T/L), while the high +part is bounded using the integer witness. -/ +private theorem bad_prime_charge_split (P : Finset ℕ) {D : ℤ} (hD : 0 < D) + (hP : ∀ p ∈ P, p.Prime) (hdiv : ∀ p ∈ P, (p : ℤ) ∣ D) + {L T : ℝ} (hL : 1 ≤ L) (hLT : L ≤ T) (hlo : ∀ p ∈ P, L < (p : ℝ)) : + (∑ p ∈ P, Real.log p / p) ≤ + Real.log T - Real.log L + 2 * mertensError + Real.log D / T := by + classical + let U := P.filter (fun p : ℕ => (p : ℝ) ≤ T) + let V := P.filter (fun p : ℕ => ¬ (p : ℝ) ≤ T) + have hlow := prime_interval_weight_le hL hLT U + (fun p hp => hP p (mem_filter.mp hp).1) + (fun p hp => hlo p (mem_filter.mp hp).1) + (fun _ hp => (mem_filter.mp hp).2) + have hhigh := prime_divisor_charge_le V hD + (fun p hp => hP p (mem_filter.mp hp).1) + (fun p hp => hdiv p (mem_filter.mp hp).1) + (by linarith : 0 < T) + (fun _ hp => (lt_of_not_ge (mem_filter.mp hp).2).le) + have hsplit := sum_filter_add_sum_filter_not P (fun p : ℕ => (p : ℝ) ≤ T) + (fun p : ℕ => Real.log p / p) + change (∑ p ∈ U, Real.log p / p) + (∑ p ∈ V, Real.log p / p) = _ at hsplit + linarith + +/-- A logarithmic-height bound for an integer witness controls the bad-prime +charge at the scale of the cutoff. Constructing such a witness is algebraic. -/ +theorem bad_prime_charge_of_height (P : Finset ℕ) {D : ℤ} (hD : 0 < D) + (hP : ∀ p ∈ P, p.Prime) (hdiv : ∀ p ∈ P, (p : ℤ) ∣ D) + {L H : ℝ} (hL : 1 ≤ L) (hH : 1 ≤ H) (hlo : ∀ p ∈ P, L < (p : ℝ)) + (hheight : Real.log D ≤ L * H) : + (∑ p ∈ P, Real.log p / p) ≤ Real.log H + 2 * mertensError + 1 := by + have hL0 : 0 < L := by linarith + have hH0 : 0 < H := by linarith + have hLT : L ≤ L * H := le_mul_of_one_le_right hL0.le hH + have h := bad_prime_charge_split P hD hP hdiv hL hLT hlo + have ht : Real.log D / (L * H) ≤ 1 := (div_le_one (mul_pos hL0 hH0)).mpr hheight + rw [Real.log_mul hL0.ne' hH0.ne'] at h + linarith + +/-- The exponential bad-prime factor has linear dependence on the scaled +logarithmic height of a nonzero integer witness. -/ +theorem bad_prime_factor_of_height (P : Finset ℕ) {D : ℤ} (hD : 0 < D) + (hP : ∀ p ∈ P, p.Prime) (hdiv : ∀ p ∈ P, (p : ℤ) ∣ D) + {L H : ℝ} (hL : 1 ≤ L) (hH : 1 ≤ H) (hlo : ∀ p ∈ P, L < (p : ℝ)) + (hheight : Real.log D ≤ L * H) : + primeSetFactor P ≤ Real.exp (2 * mertensError + 1) * H := by + have h := Real.exp_le_exp.mpr (bad_prime_charge_of_height P hD hP hdiv hL hH hlo hheight) + calc + _ ≤ Real.exp (Real.log H + 2 * mertensError + 1) := h + _ = Real.exp (2 * mertensError + 1) * H := by + rw [add_assoc, Real.exp_add, Real.exp_log (by linarith : 0 < H)] + ring + +end +end CCDN diff --git a/LeanPool/CCDN/AuxiliaryPolynomial/CoefficientBound.lean b/LeanPool/CCDN/AuxiliaryPolynomial/CoefficientBound.lean new file mode 100644 index 0000000000..4a723721ba --- /dev/null +++ b/LeanPool/CCDN/AuxiliaryPolynomial/CoefficientBound.lean @@ -0,0 +1,46 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.Algebra.MonomialEvaluationMatrix + +/-! +# Coefficient bounds for interpolation polynomials + +A polynomial assembled by `polynomialOfCoefficients` from an integer coefficient vector +bounded by `R` has every coefficient bounded by `R`. The Bombieri–Vaaler auxiliary +constructions of the determinant method use this to bound the coefficients of the auxiliary +polynomial. Nondivisibility by the defining polynomial is a separate step, proved from the +global determinant lower bound in Section 3.3. +-/ + +@[expose] public section + +namespace CCDN + +open Matrix + +/-- If the monomials `mon` are distinct and the integer coefficient vector `v` lies in the box +of radius `R ≥ 0`, then every coefficient of `polynomialOfCoefficients mon v` has absolute +value at most `R`. -/ +theorem abs_coefficient_polynomialOfCoefficients_le {n N : ℕ} + (mon : Fin N → (Fin n →₀ ℕ)) (hmon : Function.Injective mon) + (v : Fin N → ℤ) {R : ℝ} (hR : 0 ≤ R) (hv : InBox v R) + (e : Fin n →₀ ℕ) : + |((polynomialOfCoefficients mon v).coeff e : ℝ)| ≤ R := by + classical + by_cases he : ∃ j, mon j = e + · obtain ⟨j, rfl⟩ := he + rw [coefficient_polynomialOfCoefficients mon hmon] + exact hv j + · have hz : (polynomialOfCoefficients mon v).coeff e = 0 := by + simp [polynomialOfCoefficients, MvPolynomial.coeff_monomial, not_exists.mp he] + simpa [hz] using hR + +end CCDN diff --git a/LeanPool/CCDN/AuxiliaryPolynomial/CongruenceBadPrimeCharge.lean b/LeanPool/CCDN/AuxiliaryPolynomial/CongruenceBadPrimeCharge.lean new file mode 100644 index 0000000000..8f8ee4ad2d --- /dev/null +++ b/LeanPool/CCDN/AuxiliaryPolynomial/CongruenceBadPrimeCharge.lean @@ -0,0 +1,106 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.Analysis.PrimeCutoff +public import LeanPool.CCDN.AuxiliaryPolynomial.HypersurfaceBadPrimes +public import LeanPool.CCDN.Plane.IntegerPlaneLogHeight +import LeanPool.CCDN.AuxiliaryPolynomial.BadPrimeCharge +import LeanPool.CCDN.AuxiliaryPolynomial.HypersurfaceBadPrimeCoordinates +import LeanPool.CCDN.AuxiliaryPolynomial.HypersurfaceBadPrimeHeight + +/-! A single logarithmic-height witness bounds the combined charge of +geometric bad primes and prescribed congruence primes. -/ + +@[expose] public section +namespace CCDN +noncomputable section +open MvPolynomial Finset + +private theorem exists_fin_hypersurface_bad_prime_height_witness + {L : Type*} [Field L] [CharZero L] [IsAlgClosed L] {n : ℕ} + (f : MvPolynomial (Fin (n + 1)) ℤ) + (hf : Irreducible (map (Int.castRingHom L) f)) (hd : 0 < f.totalDegree) + (H : ℝ) (hH : 1 ≤ H) (hcoeff : CoefficientHeightLE f H) : + ∃ D : ℤ, 0 < D ∧ + Real.log D ≤ (27 * (f.totalDegree : ℝ) ^ 4) * + (hypersurfaceHeightConstant (n + 1) * + (1 + Real.log H / (f.totalDegree : ℝ) ^ 2)) ∧ + ∀ p, f.totalDegree < p → HypersurfaceBadReduction f p → (p : ℤ) ∣ D := by + let e : Fin (n + 1) ≃ Option (Fin n) := finSuccEquiv' 0 + let g := rename e f + have hg : Irreducible (map (Int.castRingHom L) g) := by + rw [show g = rename e f from rfl, map_rename] + exact (MulEquiv.irreducible_iff (f := renameEquiv L e)).mpr hf + have hgd : g.totalDegree = f.totalDegree := totalDegree_renameEquiv e f + obtain ⟨D, hD, hheight, hdiv⟩ := exists_hypersurface_bad_prime_height_witness g hg + (hgd ▸ hd) H hH (polynomial_coefficient_bound_renameEquiv e f H hcoeff) + refine ⟨D, hD, ?_, ?_⟩ + · simpa only [hgd, Fintype.card_option, Fintype.card_fin] using hheight + · intro p hp hbad + apply hdiv p (hgd ▸ hp) + exact (hypersurfaceBadReduction_renameEquiv e f p).mpr hbad + +/-- Multiplying a constructed bad-prime witness by the congruence modulus +keeps the combined prime charge within one logarithm of their log heights. -/ +theorem hypersurface_and_congruence_prime_charge + {L : Type*} [Field L] [CharZero L] [IsAlgClosed L] {n : ℕ} + (f : MvPolynomial (Fin (n + 1)) ℤ) + (hf : Irreducible (map (Int.castRingHom L) f)) (hd : 0 < f.totalDegree) + (H : ℝ) (hH : 1 ≤ H) (hcoeff : CoefficientHeightLE f H) + (bad P : Finset ℕ) + (hbad : ∀ p ∈ bad, stepanovThreshold f.totalDegree < p ∧ HypersurfaceBadReduction f p) + (hP : ∀ p ∈ P, p.Prime) : + (∑ p ∈ bad ∪ P, Real.log p / p) ≤ + Real.log (1 + 27 * (f.totalDegree : ℝ) ^ 4 * + (hypersurfaceHeightConstant (n + 1) * + (1 + Real.log H / (f.totalDegree : ℝ) ^ 2)) + + (∑ p ∈ P, Real.log p)) + 2 * mertensError + 1 := by + classical + obtain ⟨D, hD, hheight, hdiv⟩ := + exists_fin_hypersurface_bad_prime_height_witness f hf hd H hH hcoeff + let q : ℤ := ∏ p ∈ P, (p : ℤ) + have hq : 0 < q := prod_pos fun p hp => by exact_mod_cast (hP p hp).pos + have hlogq : Real.log (q : ℝ) = ∑ p ∈ P, Real.log p := by + dsimp only [q] + rw [Int.cast_prod, Real.log_prod (fun p hp => by exact_mod_cast (hP p hp).ne_zero)] + simp only [Int.cast_natCast] + have hprime (p : ℕ) (hp : p ∈ bad ∪ P) : p.Prime := by + rcases mem_union.mp hp with hb | hp + · exact (hbad p hb).2.choose + · exact hP p hp + have hdvd (p : ℕ) (hp : p ∈ bad ∪ P) : (p : ℤ) ∣ D * q := by + rcases mem_union.mp hp with hb | hp + · apply dvd_mul_of_dvd_left (hdiv p ?_ (hbad p hb).2) + have hcut : 27 * f.totalDegree ^ 4 < p := (hbad p hb).1 + have h₁ := Nat.le_mul_self f.totalDegree + have h₂ := Nat.le_mul_self (f.totalDegree * f.totalDegree) + nlinarith + · exact dvd_mul_of_dvd_right (dvd_prod_of_mem (fun p : ℕ => (p : ℤ)) hp) D + have hcharge := bad_prime_charge_of_height (bad ∪ P) (mul_pos hD hq) hprime hdvd + (L := (1 : ℝ)) (H := 1 + 27 * (f.totalDegree : ℝ) ^ 4 * + (hypersurfaceHeightConstant (n + 1) * + (1 + Real.log H / (f.totalDegree : ℝ) ^ 2)) + ∑ p ∈ P, Real.log p) + (by norm_num) ?_ ?_ ?_ + · exact hcharge + · have hlogH := Real.log_nonneg hH + have hsum := sum_nonneg (s := P) (fun p _ => Real.log_natCast_nonneg p) + have hC := (hypersurfaceHeightConstant_one_le (n + 1)).trans' zero_le_one + have hterm : 0 ≤ 27 * (f.totalDegree : ℝ) ^ 4 * + (hypersurfaceHeightConstant (n + 1) * + (1 + Real.log H / (f.totalDegree : ℝ) ^ 2)) := by positivity + linarith + · intro p hp + exact_mod_cast (hprime p hp).one_lt + · rw [Int.cast_mul, Real.log_mul (by exact_mod_cast hD.ne') (by exact_mod_cast hq.ne'), + hlogq, one_mul] + linarith + +end +end CCDN diff --git a/LeanPool/CCDN/AuxiliaryPolynomial/ConstantLeadingBadReduction.lean b/LeanPool/CCDN/AuxiliaryPolynomial/ConstantLeadingBadReduction.lean new file mode 100644 index 0000000000..1808461765 --- /dev/null +++ b/LeanPool/CCDN/AuxiliaryPolynomial/ConstantLeadingBadReduction.lean @@ -0,0 +1,161 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.Noether.BivariateTotalDegree +public import LeanPool.CCDN.Noether.ClosedFormHeight +public import Mathlib.Algebra.Field.ZMod +public import Mathlib.Analysis.SpecialFunctions.Log.Basic +public import Mathlib.FieldTheory.IsAlgClosed.AlgebraicClosure +import LeanPool.CCDN.Determinant.ClosedFormIntegerMinor +import LeanPool.CCDN.Determinant.ClosedFormMinorCharge +import LeanPool.CCDN.ForMathlib.Matrix +import LeanPool.CCDN.Noether.ClosedFormScaling + +/-! Bad reduction for integer bivariate equations with a nonzero constant +outer leading coefficient. A leading-coefficient factor in the witness also +covers degree-dropping reductions. -/ + +@[expose] public section +namespace CCDN +noncomputable section +open Polynomial Real + +/-- Full outer degree forces the outer leading coefficient to be constant. -/ +lemma bivariate_leadingCoeff_eq_constant {R : Type*} [CommRing R] + (f : R[X][X]) (htotal : bivariateTotalDegree f ≤ f.natDegree) : + f.leadingCoeff = C (f.leadingCoeff.coeff 0) := by + by_cases hc : f.leadingCoeff = 0 + · simp [hc] + have hn : f.leadingCoeff.coeff f.leadingCoeff.natDegree ≠ 0 := + Polynomial.leadingCoeff_ne_zero.mpr hc + have h := bivariateTotalDegree_coeff_le f f.natDegree f.leadingCoeff.natDegree hn + have hz : f.leadingCoeff.natDegree = 0 := by omega + exact eq_C_of_natDegree_eq_zero hz + +private lemma bivariateMap_constant_leading {R S : Type*} [CommRing R] [CommRing S] + (φ : R →+* S) (f : R[X][X]) (a : R) (ha : φ a ≠ 0) + (hl : f.leadingCoeff = C a) : + (bivariateMap φ f).natDegree = f.natDegree ∧ + (bivariateMap φ f).leadingCoeff = C (φ a) := by + have hn : (Polynomial.mapRingHom φ) f.leadingCoeff ≠ 0 := by + simpa [hl] using ha + constructor + · exact natDegree_map_of_leadingCoeff_ne_zero (Polynomial.mapRingHom φ) hn + · exact (leadingCoeff_map_of_leadingCoeff_ne_zero (Polynomial.mapRingHom φ) hn).trans + (by simp [hl]) + +/-- Bad reduction includes both lost total degree and geometric reducibility. -/ +def BivariateBadReduction (f : ℤ[X][X]) (d p : ℕ) : Prop := + ∃ hp : p.Prime, let _ : Fact p.Prime := ⟨hp⟩ + let fₚ := bivariateMap (Int.castRingHom (AlgebraicClosure (ZMod p))) f + bivariateTotalDegree fₚ ≠ d ∨ ¬Irreducible fₚ + +private lemma exists_constant_leading_integer_minor + {F : Type*} [Field F] [CharZero F] [IsAlgClosed F] + (d : ℕ) (f : ℤ[X][X]) (a : ℤ) (ha : a ≠ 0) (hl : f.leadingCoeff = C a) + (hdeg : f.natDegree = d) (hir : Irreducible (bivariateMap (Int.castRingHom F) f)) : + ∃ (r : Fin (2 * d ^ 2 - 1) → ℕ × ℕ) + (c : Fin (2 * d ^ 2 - 1) → ClosedFormColumns d), + Function.Injective r ∧ Function.Injective c ∧ + ((closedFormCoefficientMatrix d f).submatrix r c).det ≠ 0 := by + have haF : (a : F) ≠ 0 := Int.cast_ne_zero.mpr ha + obtain ⟨hdegF, hlF⟩ := bivariateMap_constant_leading (Int.castRingHom F) f a haF hl + have hr := closedForm_rank_lower_of_constant_leading d (a : F) haF _ hlF hir (hdegF.trans hdeg) + obtain ⟨r, c, hr, hc, hdet⟩ := Matrix.exists_det_submatrix_ne_zero_of_le_rank + (closedFormCoefficientMatrix d (bivariateMap (Int.castRingHom F) f)) + (2 * d ^ 2 - 1) (by omega) + refine ⟨r, c, hr, hc, ?_⟩ + intro hz + apply hdet + rw [← closedForm_minor_map, hz, map_zero] + +/-- Every bad prime above the degree divides leading coefficient times minor. +Primes killing the leading coefficient are handled without a rank hypothesis. -/ +private lemma constant_leading_bad_prime_dvd + (d : ℕ) (hd : 0 < d) (f : ℤ[X][X]) (a : ℤ) (hl : f.leadingCoeff = C a) + (hdeg : f.natDegree = d) (htotal : bivariateTotalDegree f ≤ d) + (r : Fin (2 * d ^ 2 - 1) → ℕ × ℕ) + (c : Fin (2 * d ^ 2 - 1) → ClosedFormColumns d) + (p : ℕ) (hp : d < p) (hbad : BivariateBadReduction f d p) : + (p : ℤ) ∣ a * ((closedFormCoefficientMatrix d f).submatrix r c).det := by + by_cases hpa : (p : ℤ) ∣ a + · exact dvd_mul_of_dvd_left hpa _ + obtain ⟨hpp, hbad⟩ := hbad + let : Fact p.Prime := ⟨hpp⟩ + let K := AlgebraicClosure (ZMod p) + let g := bivariateMap (Int.castRingHom K) f + change bivariateTotalDegree g ≠ d ∨ ¬Irreducible g at hbad + have haK : (a : K) ≠ 0 := by + intro hz + exact hpa ((CharP.intCast_eq_zero_iff K p a).mp hz) + obtain ⟨hdegK, hlK⟩ := bivariateMap_constant_leading (Int.castRingHom K) f a haK hl + have ht : bivariateTotalDegree g ≤ d := (bivariateTotalDegree_map_le _ f).trans htotal + have he : bivariateTotalDegree g = d := by + have hn := bivariate_natDegree_le_totalDegree g + change g.natDegree = f.natDegree at hdegK + omega + have hred : ¬Irreducible g := hbad.resolve_left (not_ne_iff.mpr he) + have hr := closedForm_rank_drop_of_constant_leading d hd (.of_charP hp) (a : K) haK g hlK hred + (hdegK.trans hdeg) ht + apply dvd_mul_of_dvd_right + apply characteristic_dvd_closedForm_minor_of_rank_lt (K := K) p d (2 * d ^ 2 - 1) f r c + dsimp only [g] at hr + omega + +/-- A full-outer-degree integer equation has a height-controlled positive +witness for every bad prime, including loss of degree. Integer monicity is +not required. -/ +theorem exists_full_outer_bad_prime_witness + {F : Type*} [Field F] [CharZero F] [IsAlgClosed F] + (d : ℕ) (hd : 1 ≤ d) (f : ℤ[X][X]) (hdeg : f.natDegree = d) + (htotal : bivariateTotalDegree f ≤ d) + (hir : Irreducible (bivariateMap (Int.castRingHom F) f)) + (H : ℝ) (hH : 1 ≤ H) (hcoeff : BivariateCoefficientBound f H) : + ∃ D : ℤ, 0 < D ∧ + Real.log D ≤ (27 * (d : ℝ) ^ 4) * (1 + 2 * Real.log H / (d : ℝ) ^ 2) ∧ + ∀ p : ℕ, d < p → BivariateBadReduction f d p → (p : ℤ) ∣ D := by + have hf : f ≠ 0 := by + intro hz + apply hir.ne_zero + rw [hz, map_zero] + let a := f.leadingCoeff.coeff 0 + have hl : f.leadingCoeff = C a := bivariate_leadingCoeff_eq_constant f (hdeg ▸ htotal) + have ha : a ≠ 0 := by + intro hz + apply (Polynomial.leadingCoeff_ne_zero.mpr hf) + rw [hl, hz, map_zero] + obtain ⟨r, c, _, _, hD⟩ := exists_constant_leading_integer_minor d f a ha hl hdeg hir + let D := ((closedFormCoefficientMatrix d f).submatrix r c).det + have hp : a * D ≠ 0 := mul_ne_zero ha hD + refine ⟨|a * D|, abs_pos.mpr hp, ?_, ?_⟩ + · have hd1 : (1 : ℝ) ≤ d := by exact_mod_cast hd + have hd0 : (0 : ℝ) < d := by linarith + have haH : |(a : ℝ)| ≤ H := hcoeff f.natDegree 0 + have ha0 : (a : ℝ) ≠ 0 := Int.cast_ne_zero.mpr ha + have hD0 : (D : ℝ) ≠ 0 := Int.cast_ne_zero.mpr hD + have hla := Real.log_le_log (abs_pos.mpr ha0) haH + have hminor := closedForm_minor_log_height d (2 * d ^ 2 - 1) hd (by omega) f H hH hcoeff + hdeg.le (fun i => (bivariate_coefficient_natDegree_le_totalDegree f i).trans htotal) r c hD + change Real.log |(D : ℝ)| ≤ _ at hminor + rw [Int.cast_abs, Int.cast_mul, abs_mul, + Real.log_mul (abs_ne_zero.mpr ha0) (abs_ne_zero.mpr hD0)] + have he : (27 * (d : ℝ) ^ 4) * (1 + 2 * Real.log H / (d : ℝ) ^ 2) = + (27 * (d : ℝ) ^ 4) * (1 + Real.log H / (d : ℝ) ^ 2) + + 27 * (d : ℝ) ^ 2 * Real.log H := by field_simp; ring + rw [he] + have hlog : 0 ≤ Real.log H := Real.log_nonneg hH + have hfac : 0 ≤ 27 * (d : ℝ) ^ 2 - 1 := by nlinarith + nlinarith [mul_nonneg hfac hlog] + · intro p hp hbad + exact (dvd_abs _ _).mpr + (constant_leading_bad_prime_dvd d (by omega) f a hl hdeg htotal r c p hp hbad) + +end +end CCDN diff --git a/LeanPool/CCDN/AuxiliaryPolynomial/DegreeBound.lean b/LeanPool/CCDN/AuxiliaryPolynomial/DegreeBound.lean new file mode 100644 index 0000000000..06100115a6 --- /dev/null +++ b/LeanPool/CCDN/AuxiliaryPolynomial/DegreeBound.lean @@ -0,0 +1,60 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import Mathlib.Analysis.SpecialFunctions.Pow.Real + +/-! Named quantities in the determinant and auxiliary-polynomial bounds for a +hypersurface of degree `d` in `ℙ^(n+2)`, and the bracket `auxDegreeBound` of the +coefficient-sensitive auxiliary theorem (CCDN Theorem 3.1.1). -/ + +@[expose] public section +namespace CCDN +noncomputable section + +/-- The root `d ^ (1 / (n + 1))` of the degree. -/ +abbrev degreeRoot (n d : ℕ) : ℝ := (d : ℝ) ^ (1 / (n + 1 : ℝ)) + +/-- The root `((n + 1)!) ^ (1 / (n + 1))`. -/ +abbrev factorialRoot (n : ℕ) : ℝ := ((n + 1).factorial : ℝ) ^ (1 / (n + 1 : ℝ)) + +/-- The coefficient-height exponent `1 / ((n + 1) d d ^ (1 / (n + 1)))`. -/ +abbrev heightExponent (n d : ℕ) : ℝ := 1 / ((n + 1 : ℝ) * d * degreeRoot n d) + +/-- The radius exponent `(n + 2) / ((n + 1) d ^ (1 / (n + 1)))`. -/ +abbrev boxExponent (n d : ℕ) : ℝ := (n + 2 : ℝ) / ((n + 1 : ℝ) * degreeRoot n d) + +/-- The bad-prime factor `exp (∑ p ∈ bad, log p / p)` of a finite set of primes. -/ +abbrev primeSetFactor (bad : Finset ℕ) : ℝ := Real.exp (∑ p ∈ bad, Real.log p / p) + +/-- The main term `d^(4 - 1/(n+1)) b B^boxExponent / H^heightExponent` of the +coefficient-sensitive auxiliary degree bound, for points of height at most `B` on an +equation with coefficient height `H` and bad-prime factor `b`. -/ +def auxMainTerm (n d : ℕ) (B b H : ℝ) : ℝ := + (d : ℝ) ^ (4 - 1 / (n + 1 : ℝ)) * b * B ^ boxExponent n d / H ^ heightExponent n d + +/-- The bracket `auxMainTerm + d^(1 - 1/(n+1)) log B + d^(4 - 1/(n+1))` of the +coefficient-sensitive auxiliary degree bound. -/ +def auxDegreeBound (n d : ℕ) (B b H : ℝ) : ℝ := + auxMainTerm n d B b H + (d : ℝ) ^ (1 - 1 / (n + 1 : ℝ)) * Real.log B + + (d : ℝ) ^ (4 - 1 / (n + 1 : ℝ)) + +private lemma one_le_four_sub_inv (n : ℕ) : (1 : ℝ) ≤ 4 - 1 / (n + 1 : ℝ) := by + have h := (div_le_one (by positivity : (0 : ℝ) < n + 1)).mpr + (show 1 ≤ (n + 1 : ℝ) by have := Nat.cast_nonneg (α := ℝ) n; linarith) + linarith + +/-- `d ≤ d ^ (4 - 1 / (n + 1))` for a positive degree. -/ +lemma le_rpow_four_sub_inv {n d : ℕ} (hd : 0 < d) : + (d : ℝ) ≤ (d : ℝ) ^ (4 - 1 / (n + 1 : ℝ)) := by + have hd1 : (1 : ℝ) ≤ d := by exact_mod_cast hd + simpa only [Real.rpow_one] using Real.rpow_le_rpow_of_exponent_le hd1 (one_le_four_sub_inv n) + +end +end CCDN diff --git a/LeanPool/CCDN/AuxiliaryPolynomial/GeneralBadReduction.lean b/LeanPool/CCDN/AuxiliaryPolynomial/GeneralBadReduction.lean new file mode 100644 index 0000000000..857a456e52 --- /dev/null +++ b/LeanPool/CCDN/AuxiliaryPolynomial/GeneralBadReduction.lean @@ -0,0 +1,116 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.Analysis.PrimeCutoff +public import LeanPool.CCDN.AuxiliaryPolynomial.ConstantLeadingBadReduction +public import LeanPool.CCDN.Plane.FiniteFieldConstants +import LeanPool.CCDN.AuxiliaryPolynomial.BadPrimeCharge +import LeanPool.CCDN.ForMathlib.Log +import LeanPool.CCDN.Plane.ShearHeight + +/-! The geometric bad-prime charge for arbitrary integer plane equations. +The proof combines bounded integral normalization, coefficient growth and an +integer minor witness; reducibility is tested over the residue field's +algebraic closure. -/ + +@[expose] public section +namespace CCDN +noncomputable section +open Polynomial Real + +private lemma normalized_shear_height_one_le (d : ℕ) (H : ℝ) (hH : 1 ≤ H) : + 1 ≤ ((d : ℝ) + 1) ^ (d + 2) * H := by + have hp : 1 ≤ ((d : ℝ) + 1) ^ (d + 2) := + one_le_pow₀ (by have h : (0 : ℝ) ≤ d := Nat.cast_nonneg d; linarith) + nlinarith + +/-- The normalization cost contributes an absolute constant at the d² scale. -/ +private lemma normalized_shear_scaled_log_height (d : ℕ) (hd : 1 ≤ d) + (H : ℝ) (hH : 1 ≤ H) : + 1 + 2 * Real.log (((d : ℝ) + 1) ^ (d + 2) * H) / (d : ℝ) ^ 2 ≤ + 7 * (1 + Real.log H / (d : ℝ) ^ 2) := by + have hd1 : (1 : ℝ) ≤ d := by exact_mod_cast hd + have hd0 : (0 : ℝ) < d := by linarith + have hlog := Real.log_nonneg hH + have hdlog := Real.log_add_one_le_self (Nat.cast_nonneg (α := ℝ) d) + have hcost : ((d : ℝ) + 2) * Real.log ((d : ℝ) + 1) ≤ 3 * (d : ℝ) ^ 2 := by + have hh := mul_le_mul_of_nonneg_left hdlog (show 0 ≤ (d : ℝ) + 2 by positivity) + nlinarith + rw [Real.log_mul (by positivity : ((d : ℝ) + 1) ^ (d + 2) ≠ 0) (by linarith : H ≠ 0), + Real.log_pow] + push_cast + apply (le_of_mul_le_mul_right ?_ (show 0 < (d : ℝ) ^ 2 by positivity)) + field_simp + nlinarith + +/-- Every absolutely irreducible integer plane equation has a coefficient- +controlled positive integer detecting all geometric bad primes above d. -/ +theorem exists_plane_bad_prime_witness + {F : Type*} [Field F] [CharZero F] [IsAlgClosed F] + (d : ℕ) (hd : 1 ≤ d) (f : ℤ[X][X]) (hdeg : bivariateTotalDegree f = d) + (hir : Irreducible (bivariateMap (Int.castRingHom F) f)) + (H : ℝ) (hH : 1 ≤ H) (hcoeff : BivariateCoefficientBound f H) : + ∃ D : ℤ, 0 < D ∧ + Real.log D ≤ (189 * (d : ℝ) ^ 4) * (1 + Real.log H / (d : ℝ) ^ 2) ∧ + ∀ p : ℕ, d < p → BivariateBadReduction f d p → (p : ℤ) ∣ D := by + have hf : f ≠ 0 := by + intro hz + apply hir.ne_zero + rw [hz, map_zero] + obtain ⟨a, ha, hn, ht⟩ := exists_bounded_full_outer_shear f hf + rw [hdeg] at ha hn ht + let g := bivariateShear ℤ (a : ℤ) f + let H' := ((d : ℝ) + 1) ^ (d + 2) * H + have hH' : 1 ≤ H' := normalized_shear_height_one_le d H hH + have hgcoeff : BivariateCoefficientBound g H' := + bivariateShear_bounded_coefficient_bound a d ha f hdeg.le H (by linarith) hcoeff + have hgir : Irreducible (bivariateMap (Int.castRingHom F) g) := by + dsimp only [g] + rw [bivariateShear_map, bivariateShear_irreducible_iff] + exact hir + obtain ⟨D, hD, hheight, hdiv⟩ := + exists_full_outer_bad_prime_witness d hd g hn ht.le hgir H' hH' hgcoeff + refine ⟨D, hD, ?_, ?_⟩ + · have hs := normalized_shear_scaled_log_height d hd H hH + change 1 + 2 * Real.log H' / (d : ℝ) ^ 2 ≤ _ at hs + have hh := mul_le_mul_of_nonneg_left hs (show 0 ≤ 27 * (d : ℝ) ^ 4 by positivity) + nlinarith + · intro p hp hbad + apply hdiv p hp + exact (bivariateBadReduction_shear_iff (a : ℤ) f d p).mpr hbad + +/-- Geometric bad primes above 27*d^4 have logarithmic charge controlled by +log(1+log(H)/d²) plus an absolute constant. -/ +theorem plane_geometric_bad_prime_charge + {F : Type*} [Field F] [CharZero F] [IsAlgClosed F] + (d : ℕ) (hd : 1 ≤ d) (f : ℤ[X][X]) (hdeg : bivariateTotalDegree f = d) + (hir : Irreducible (bivariateMap (Int.castRingHom F) f)) + (H : ℝ) (hH : 1 ≤ H) (hcoeff : BivariateCoefficientBound f H) + (P : Finset ℕ) (hbad : ∀ p ∈ P, BivariateBadReduction f d p) + (hlo : ∀ p ∈ P, stepanovThreshold (d : ℝ) < (p : ℝ)) : + (∑ p ∈ P, Real.log p / p) ≤ + Real.log (1 + Real.log H / (d : ℝ) ^ 2) + Real.log 7 + 2 * mertensError + 1 := by + obtain ⟨D, hD, hheight, hdiv⟩ := exists_plane_bad_prime_witness d hd f hdeg hir H hH hcoeff + have hd1 : (1 : ℝ) ≤ d := by exact_mod_cast hd + have hx : 1 ≤ 1 + Real.log H / (d : ℝ) ^ 2 := + le_add_of_nonneg_right (div_nonneg (Real.log_nonneg hH) (sq_nonneg _)) + have hcut : (d : ℝ) ≤ 27 * (d : ℝ) ^ 4 := by + nlinarith [sq_nonneg ((d : ℝ) ^ 2 - 1)] + have h := bad_prime_charge_of_height (D := D) (L := 27 * (d : ℝ) ^ 4) + (H := 7 * (1 + Real.log H / (d : ℝ) ^ 2)) P hD + (fun p hp => (hbad p hp).choose) + (fun p hp => hdiv p (by exact_mod_cast hcut.trans_lt (hlo p hp)) (hbad p hp)) + (by linarith) (by linarith) hlo (by nlinarith [hheight]) + rw [Real.log_mul (by norm_num : (7 : ℝ) ≠ 0) + (by linarith : 1 + Real.log H / (d : ℝ) ^ 2 ≠ 0)] at h + linarith + +end +end CCDN diff --git a/LeanPool/CCDN/AuxiliaryPolynomial/HypersurfaceAffineCoordinates.lean b/LeanPool/CCDN/AuxiliaryPolynomial/HypersurfaceAffineCoordinates.lean new file mode 100644 index 0000000000..b564ff804e --- /dev/null +++ b/LeanPool/CCDN/AuxiliaryPolynomial/HypersurfaceAffineCoordinates.lean @@ -0,0 +1,146 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.Algebra.OriginShift +public import Mathlib.CategoryTheory.Types.Basic +public import Mathlib.Tactic.LinearCombination +import LeanPool.CCDN.Determinant.PlaneMultiplicity +import LeanPool.CCDN.ForMathlib.MvPolynomialDerivative + +/-! Explicit invertible affine coordinates along a direction with nonzero +first component. The same map carries its base point to the origin and +identifies the first partial derivative with the directional derivative. -/ + +@[expose] public section +namespace CCDN +noncomputable section +open MvPolynomial + +/-- The linear coordinates x_i = b_i*x_0 + z_i, with no z_0. -/ +def directionCoordinatesHom {K σ : Type*} [Field K] (b : Option σ → K) : + MvPolynomial (Option σ) K →ₐ[K] MvPolynomial (Option σ) K := + aeval (fun i => C (b i) * X none + Option.elim i 0 (fun j => X (some j))) + +/-- The inverse of the linear coordinates `directionCoordinatesHom b` when `b none ≠ 0`: the +substitution sending $x_0$ to $b_0^{-1} x_0$ and every other $x_i$ to $x_i - b_i b_0^{-1} x_0$. -/ +def directionCoordinatesInv {K σ : Type*} [Field K] (b : Option σ → K) : + MvPolynomial (Option σ) K →ₐ[K] MvPolynomial (Option σ) K := + aeval (fun i => Option.elim i (C (b none)⁻¹ * X none) + (fun j => X (some j) - C (b (some j)) * C (b none)⁻¹ * X none)) + +/-- The linear coordinates $x_i = b_i x_0 + z_i$ (with no $z_0$) as an algebra automorphism, +for a direction `b` with nonzero first component. -/ +def directionCoordinates {K σ : Type*} [Field K] (b : Option σ → K) (hb : b none ≠ 0) : + MvPolynomial (Option σ) K ≃ₐ[K] MvPolynomial (Option σ) K := by + have hu : (C (b none)⁻¹ : MvPolynomial (Option σ) K) * C (b none) = 1 := by + rw [← map_mul, inv_mul_cancel₀ hb, map_one] + refine AlgEquiv.ofAlgHom (directionCoordinatesHom b) (directionCoordinatesInv b) ?_ ?_ + · apply MvPolynomial.algHom_ext + intro i + cases i with + | none => + simp only [directionCoordinatesHom, directionCoordinatesInv, AlgHom.comp_apply, + aeval_X, Option.elim_none, map_mul, aeval_C, algebraMap_eq, add_zero, AlgHom.id_apply] + linear_combination X (none : Option σ) * hu + | some i => + simp only [directionCoordinatesHom, directionCoordinatesInv, AlgHom.comp_apply, + aeval_X, Option.elim_some, Option.elim_none, map_sub, map_mul, aeval_C, + algebraMap_eq, add_zero, AlgHom.id_apply] + linear_combination -C (b (some i)) * X (none : Option σ) * hu + · apply MvPolynomial.algHom_ext + intro i + cases i with + | none => + simp only [directionCoordinatesHom, directionCoordinatesInv, AlgHom.comp_apply, + aeval_X, Option.elim_none, map_mul, aeval_C, algebraMap_eq, + add_zero, AlgHom.id_apply] + linear_combination X (none : Option σ) * hu + | some i => + simp only [directionCoordinatesHom, directionCoordinatesInv, AlgHom.comp_apply, + aeval_X, Option.elim_some, Option.elim_none, map_add, map_mul, aeval_C, + algebraMap_eq, AlgHom.id_apply] + ring + +/-- Substitute a_i+b_i*x_0+z_i, so the new origin is the old point a. -/ +def hypersurfaceAffineCoordinates {K σ : Type*} [Field K] + (a b : Option σ → K) (hb : b none ≠ 0) : + MvPolynomial (Option σ) K ≃ₐ[K] MvPolynomial (Option σ) K := + (originShiftEquiv a).trans (directionCoordinates b hb) + +/-- The affine coordinates substitute $a_i + b_i x_0 + z_i$ for the variable $x_i$, with no +$z_0$ term for the distinguished variable. -/ +lemma hypersurfaceAffineCoordinates_X {K σ : Type*} [Field K] + (a b : Option σ → K) (hb : b none ≠ 0) (i : Option σ) : + hypersurfaceAffineCoordinates a b hb (X i) = + C (a i) + C (b i) * X none + Option.elim i 0 (fun j => X (some j)) := by + simp [hypersurfaceAffineCoordinates, directionCoordinates, directionCoordinatesHom] + ring + +/-- The affine change of coordinates `hypersurfaceAffineCoordinates a b hb` does not increase +the total degree. -/ +lemma hypersurfaceAffineCoordinates_totalDegree_le {K σ : Type*} [Field K] + (a b : Option σ → K) (hb : b none ≠ 0) (f : MvPolynomial (Option σ) K) : + (hypersurfaceAffineCoordinates a b hb f).totalDegree ≤ f.totalDegree := by + have he : (hypersurfaceAffineCoordinates a b hb).toAlgHom = + aeval (fun i => C (a i) + C (b i) * X none + Option.elim i 0 (fun j => X (some j))) := by + apply MvPolynomial.algHom_ext + intro i + simp only [AlgEquiv.coe_toAlgHom, aeval_X] + exact hypersurfaceAffineCoordinates_X a b hb i + change ((hypersurfaceAffineCoordinates a b hb).toAlgHom f).totalDegree ≤ _ + rw [he] + apply linear_eval₂_totalDegree_le _ _ _ f + intro i + cases i <;> + apply (totalDegree_add _ _).trans <;> + apply max_le + all_goals first | simp | exact (totalDegree_add _ _).trans (max_le (by simp) + ((totalDegree_mul _ _).trans (by simp))) + +/-- The affine coordinates carry the base point `a` to the origin: the transformed polynomial +takes at `0` the value of `f` at `a`. -/ +lemma hypersurfaceAffineCoordinates_eval_zero {K σ : Type*} [Field K] + (a b : Option σ → K) (hb : b none ≠ 0) (f : MvPolynomial (Option σ) K) : + eval (0 : Option σ → K) (hypersurfaceAffineCoordinates a b hb f) = eval a f := by + have he : + (eval (0 : Option σ → K)).comp (hypersurfaceAffineCoordinates a b hb).toRingHom = + eval a := by + apply MvPolynomial.ringHom_ext + · intro r + change eval (0 : Option σ → K) (hypersurfaceAffineCoordinates a b hb (C r)) = eval a (C r) + rw [show hypersurfaceAffineCoordinates a b hb (C r) = C r from + (hypersurfaceAffineCoordinates a b hb).commutes r] + simp + · intro i + change eval (0 : Option σ → K) (hypersurfaceAffineCoordinates a b hb (X i)) = eval a (X i) + rw [hypersurfaceAffineCoordinates_X] + cases i <;> simp + exact RingHom.congr_fun he f + +/-- In the affine coordinates, the first partial derivative at the origin is the derivative of +`f` at `a` in the direction `b`. -/ +lemma hypersurfaceAffineCoordinates_partial_zero {K σ : Type*} [Field K] [Fintype σ] + (a b : Option σ → K) (hb : b none ≠ 0) (f : MvPolynomial (Option σ) K) : + eval (0 : Option σ → K) (pderiv none (hypersurfaceAffineCoordinates a b hb f)) = + ∑ i, eval a (pderiv i f) * b i := by + change eval (0 : Option σ → K) (pderiv none + ((hypersurfaceAffineCoordinates a b hb).toAlgHom f)) = _ + rw [MvPolynomial.pderiv_algHom, map_sum] + apply Finset.sum_congr rfl + intro i _ + rw [map_mul] + simp only [AlgEquiv.coe_toAlgHom] + rw [hypersurfaceAffineCoordinates_eval_zero] + congr 1 + rw [hypersurfaceAffineCoordinates_X] + cases i <;> simp + +end +end CCDN diff --git a/LeanPool/CCDN/AuxiliaryPolynomial/HypersurfaceAuxiliaryTheorem.lean b/LeanPool/CCDN/AuxiliaryPolynomial/HypersurfaceAuxiliaryTheorem.lean new file mode 100644 index 0000000000..87995705ac --- /dev/null +++ b/LeanPool/CCDN/AuxiliaryPolynomial/HypersurfaceAuxiliaryTheorem.lean @@ -0,0 +1,77 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.AuxiliaryPolynomial.AbsoluteAuxiliary +public import LeanPool.CCDN.AuxiliaryPolynomial.HypersurfaceBadPrimeFactor +import LeanPool.CCDN.Algebra.IntegerRationalIrreducibility +import LeanPool.CCDN.AuxiliaryPolynomial.HypersurfaceNonabsoluteAuxiliary +import LeanPool.CCDN.AuxiliaryPolynomial.HypersurfaceProjectiveAuxiliary + +/-! CCDN Theorem 3.1.1 for irreducible integer homogeneous equations. +The constant is fixed before the equation, degree and radius are chosen. -/ + +@[expose] public section +namespace CCDN +noncomputable section +open MvPolynomial + +/-- The constant `hypersurfaceAbsoluteAuxiliaryConstant n` is at least `1`. -/ +lemma hypersurfaceAbsoluteAuxiliaryConstant_one_le (n : ℕ) : + 1 ≤ hypersurfaceAbsoluteAuxiliaryConstant n := by + have hC : + 4 ≤ hypersurfacePositionedAuxiliaryConstant n (hypersurfaceAuxiliaryPositionConstant n) := by + unfold hypersurfacePositionedAuxiliaryConstant + have hE := Real.exp_pos (hypersurfaceLargeHeightError (n + 1) (hypersurfaceNormalizedError n) + (hypersurfaceAuxiliaryPositionConstant n)) + have hS : 0 ≤ Real.exp (hypersurfaceAuxiliaryPositionConstant n / (n + 1 : ℝ)) * + Real.exp (hypersurfaceSmallHeightError (n + 1) + (hypersurfaceBadPrimeNormalizedError n)) := by positivity + have hn : (0 : ℝ) ≤ n := Nat.cast_nonneg n + linarith + unfold hypersurfaceAbsoluteAuxiliaryConstant + apply one_le_mul_of_one_le_of_one_le (by linarith) + have := Real.exp_pos ((n + 2 : ℝ) ^ 2 / (n + 1 : ℝ)) + have := Nat.cast_nonneg (α := ℝ) n + linarith + +/-- CCDN Theorem 3.1.1: the full coefficient-sensitive auxiliary-polynomial +bound, including equations that split over an algebraic closure. -/ +theorem hypersurface_projective_auxiliary + {n d : ℕ} (f : MvPolynomial (Fin (n + 3)) ℤ) + (hf : f.IsHomogeneous d) (hd : 0 < d) (hir : Irreducible f) + {B : ℝ} (hB : 1 ≤ B) : + ∃ M : ℕ, + (M : ℝ) ≤ hypersurfaceAuxiliaryDegreeBound n d B (hypersurfaceBadPrimeFactor f) + (integerCoefficientHeight f) ∧ + ∃ g : MvPolynomial (Fin (n + 3)) ℤ, g ≠ 0 ∧ g.IsHomogeneous M ∧ + (∀ x : ProjectivePoint (n + 2), x.mulHeight ≤ B → + x ∈ projectiveZeroLocus (map (Int.castRingHom ℚ) f) → + x ∈ projectiveZeroLocus (map (Int.castRingHom ℚ) g)) ∧ ¬ f ∣ g := by + classical + by_cases habs : AbsolutelyIrreducible f + · obtain ⟨bad, M, hbad, hdegree, hg⟩ := hypersurface_absolute_projective_auxiliary f hf hd habs hB + refine ⟨M, hdegree.trans_eq ?_, hg⟩ + rw [hypersurfaceBadPrimeFactor_eq_exp f habs (hf.totalDegree hir.ne_zero) bad hbad] + · have hQ := rational_map_irreducible_of_integer f hir + (by rw [hf.totalDegree hir.ne_zero]; exact hd) + have hred : ¬ AbsolutelyIrreducibleRat (map (Int.castRingHom ℚ) f) := + (absolutelyIrreducibleRat_map_iff f).not.mpr habs + obtain ⟨g, hg0, hgh, hgnd, hzeros⟩ := nonabsolute_projective_auxiliary f hf hd hQ hred + refine ⟨d, ?_, g, hg0, hgh, fun x _ hx => hzeros hx, hgnd⟩ + unfold hypersurfaceAuxiliaryDegreeBound auxDegreeBound auxMainTerm + rw [hypersurfaceBadPrimeFactor_eq_zero f habs, mul_zero, zero_mul, zero_div, zero_add] + have hdD : (d : ℝ) ≤ (d : ℝ) ^ (4 - 1 / (n + 1 : ℝ)) := le_rpow_four_sub_inv hd + have hl : 0 ≤ (d : ℝ) ^ (1 - 1 / (n + 1 : ℝ)) * Real.log B := + mul_nonneg (Real.rpow_nonneg (by positivity) _) (Real.log_nonneg hB) + exact (hdD.trans (le_add_of_nonneg_left hl)).trans + (le_mul_of_one_le_left (by positivity) (hypersurfaceAbsoluteAuxiliaryConstant_one_le n)) + +end +end CCDN diff --git a/LeanPool/CCDN/AuxiliaryPolynomial/HypersurfaceBadPrimeComparison.lean b/LeanPool/CCDN/AuxiliaryPolynomial/HypersurfaceBadPrimeComparison.lean new file mode 100644 index 0000000000..46d47f3de1 --- /dev/null +++ b/LeanPool/CCDN/AuxiliaryPolynomial/HypersurfaceBadPrimeComparison.lean @@ -0,0 +1,107 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.AuxiliaryPolynomial.HypersurfaceRankArchimedean +public import LeanPool.CCDN.Determinant.HypersurfaceDeterminantRank +import LeanPool.CCDN.AuxiliaryPolynomial.HypersurfaceBadPrimeCoordinates +import LeanPool.CCDN.AuxiliaryPolynomial.HypersurfaceHeightComparison +import LeanPool.CCDN.AuxiliaryPolynomial.HypersurfaceNormalizedComparison +import LeanPool.CCDN.Determinant.HomogeneousDeterminantComparison + +/-! Preserve the bad-prime factor through the normalized no-auxiliary +comparison, as required by the coefficient-sensitive CCDN Theorem 3.1.1. + +`AuxiliaryRankComparison order f d z bad` is the conclusion of the comparison for a +finite prime set `bad`. It follows from a determinant lower bound with the charge of +`bad` for every subfamily of the points (`auxiliaryRankComparison_of_determinant_lower`); +the hypersurface instance uses the exact geometric bad-prime set. -/ + +@[expose] public section +namespace CCDN +noncomputable section +open MvPolynomial + +/-- The dimension-dependent error term in the normalized no-auxiliary comparison that retains +the bad-prime charge, for forms in `n + 3` variables: the determinant error plus the +archimedean error normalized by $(n + 2) / ((n + 1)!)^{1/(n + 1)}$. -/ +def hypersurfaceBadPrimeNormalizedError (n : ℕ) : ℝ := + hypersurfaceDeterminantError n + + (n + 2 : ℝ) * hypersurfaceArchimedeanError (n + 1) / + ((n + 1).factorial : ℝ) ^ (1 / (n + 1 : ℝ)) + +/-- The normalized no-auxiliary comparison with the charge of a finite prime set `bad`: +for every radius `B ≥ 1` bounding the points `z` and every degree `M ≥ d` in which each +form vanishing on `z` is a multiple of `f`, the rank `s` of the evaluation matrix +satisfies the normalized comparison retaining `log b = ∑ p ∈ bad, log p / p`. -/ +def AuxiliaryRankComparison {n t : ℕ} (order : MonomialOrder (Fin (n + 3))) + (f : MvPolynomial (Fin (n + 3)) ℤ) (d : ℕ) (z : Fin t → IntPoint (n + 3)) + (bad : Finset ℕ) : Prop := + ∀ ⦃B : ℝ⦄, 1 ≤ B → (∀ i, InBox (z i) B) → ∀ ⦃M : ℕ⦄, d ≤ M → + (∀ g : MvPolynomial (Fin (n + 3)) ℤ, g.IsHomogeneous M → (∀ i, eval (z i) g = 0) → f ∣ g) → + ∃ s : ℕ, 0 < s ∧ s + (n + 2 + (M - d)).choose (M - d) = (n + 2 + M).choose M ∧ + factorialRoot n * (s : ℝ) ^ (1 / (n + 1 : ℝ)) / ((n + 2 : ℝ) * M) * + (Real.log s - (n + 1 : ℝ) * (4 * Real.log d + (∑ p ∈ bad, Real.log p / p)) - + hypersurfaceBadPrimeNormalizedError n) ≤ + Real.log B - ((n + 2 + (M - d)).choose (M - d) : ℝ) / ((M : ℝ) * s) * + Real.log |((order.leadingCoeff f : ℤ) : ℝ)| + +/-- The comparison follows from the rank-form determinant lower bound with the charge of +`bad`, for every subfamily of the points and every family of common-degree forms. -/ +theorem auxiliaryRankComparison_of_determinant_lower {n d t : ℕ} + (order : MonomialOrder (Fin (n + 3))) (f : MvPolynomial (Fin (n + 3)) ℤ) + (hf : f.IsHomogeneous d) (hf0 : f ≠ 0) (hd : 0 < d) + (z : Fin t → IntPoint (n + 3)) (hz : ∀ i, eval (z i) f = 0) (bad : Finset ℕ) + (hdet : ∀ ⦃s N M : ℕ⦄ (a : Fin s → Fin t) (F : Fin N → MvPolynomial (Fin (n + 3)) ℤ), + 0 < s → (∀ j, (F j).IsHomogeneous M) → + LinearIndependent ℚ ((Matrix.of fun i j => eval (z (a i)) (F j)).map ((↑) : ℤ → ℚ)).row → + hypersurfaceRankLowerBound n s d (∑ p ∈ bad, Real.log p / p) ≤ + Real.log (Int.Matrix.minorGcd (Matrix.of fun i j => eval (z (a i)) (F j)))) : + AuxiliaryRankComparison order f d z bad := by + intro B hB hbox M hdM hrel + obtain ⟨s, a, _, _, hr, hi, hupper⟩ := + homogeneous_no_auxiliary_box_comparison order f hf hf0 hdM z hz hB hbox hrel + have hs := hypersurface_homogeneous_rank_pos hd hdM hr + let F := fun j => (monomial (homogeneousMonomials (n + 2) M j) 1 : MvPolynomial (Fin (n + 3)) ℤ) + have hF : ∀ j, (F j).IsHomogeneous M := fun j => + isHomogeneous_monomial _ (homogeneousMonomials_degree (n + 2) M j) + have he : (Matrix.of fun i j => eval (z (a i)) (F j)) = + evaluationMatrix (homogeneousMonomials (n + 2) M) (z ∘ a) := by + ext i j + simp [F, evaluationMatrix, eval_monomial] + have hlower := hdet a F hs hF (by rw [he]; exact hi) + rw [he] at hlower + have hc := hlower.trans hupper + refine ⟨s, hs, hr, ?_⟩ + have hnN : n + 1 + 1 = n + 2 := by omega + have hnR : (n : ℝ) + 1 + 1 = n + 2 := by ring + have hh := hypersurface_rank_comparison_normalized (r := n + 1) (by omega) hd hdM hr + B |((order.leadingCoeff f : ℤ) : ℝ)| (primeSetFactor bad) + (hypersurfaceDeterminantError n) + (by simpa only [hypersurfaceRankLowerBound, hnN, Nat.cast_add, Nat.cast_one, hnR, + Real.log_exp] using hc) + simpa only [hypersurfaceBadPrimeNormalizedError, hnN, Nat.cast_add, Nat.cast_one, hnR, + Real.log_exp] using hh + +/-- The comparison retaining `log b(f)` for the exact geometric bad-prime set, rather +than replacing it by the coefficient-height upper bound. -/ +theorem hypersurface_auxiliaryRankComparison {n d t : ℕ} + (order : MonomialOrder (Fin (n + 3))) (f : MvPolynomial (Fin (n + 3)) ℤ) + (hf : f.IsHomogeneous d) (hf0 : f ≠ 0) (hd : 0 < d) + (z : Fin t → IntPoint (n + 3)) (hprim : ∀ i, Finset.univ.gcd (z i) = 1) + (hz : ∀ i, eval (z i) f = 0) (bad : Finset ℕ) + (hbad : ∀ p, p ∈ bad ↔ stepanovThreshold d < p ∧ HypersurfaceBadReduction f p) : + AuxiliaryRankComparison order f d z bad := + auxiliaryRankComparison_of_determinant_lower order f hf hf0 hd z hz bad + fun _ _ _ a F hs hF hi => hypersurface_determinant_log_lower_at_rank hs f hf hd F hF + (z ∘ a) (fun i => hprim (a i)) (fun i => hz (a i)) hi bad + fun p _ hp hcut => hasGoodReduction_of_not_mem hbad p (Finset.mem_sdiff.mp hp).2 hcut + +end +end CCDN diff --git a/LeanPool/CCDN/AuxiliaryPolynomial/HypersurfaceBadPrimeCoordinates.lean b/LeanPool/CCDN/AuxiliaryPolynomial/HypersurfaceBadPrimeCoordinates.lean new file mode 100644 index 0000000000..5bb37faa45 --- /dev/null +++ b/LeanPool/CCDN/AuxiliaryPolynomial/HypersurfaceBadPrimeCoordinates.lean @@ -0,0 +1,153 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.Analysis.PrimeCutoff +public import LeanPool.CCDN.AuxiliaryPolynomial.HypersurfaceBadPrimes +public import LeanPool.CCDN.Plane.IntegerPlaneLogHeight +import LeanPool.CCDN.AuxiliaryPolynomial.HypersurfaceBadPrimeHeight + +/-! Coordinate invariance of geometric bad reduction and coefficient height, +with the exact bad-prime charge in standard Fin-indexed coordinates. -/ + +@[expose] public section +namespace CCDN +noncomputable section +open MvPolynomial + +/-- Geometric bad reduction at `p` is invariant under a bijective renaming of the +variables. -/ +lemma hypersurfaceBadReduction_renameEquiv {σ τ : Type*} (e : σ ≃ τ) + (f : MvPolynomial σ ℤ) (p : ℕ) : + HypersurfaceBadReduction (rename e f) p ↔ HypersurfaceBadReduction f p := by + unfold HypersurfaceBadReduction + apply exists_congr + intro hp + let : Fact p.Prime := ⟨hp⟩ + dsimp only + have hd : (rename e f).totalDegree = f.totalDegree := totalDegree_renameEquiv e f + have hd' : (rename e (map (Int.castRingHom (AlgebraicClosure (ZMod p))) f)).totalDegree = + (map (Int.castRingHom (AlgebraicClosure (ZMod p))) f).totalDegree := + totalDegree_renameEquiv e _ + rw [map_rename, hd, hd'] + apply or_congr Iff.rfl + exact not_congr (MulEquiv.irreducible_iff (f := renameEquiv (AlgebraicClosure (ZMod p)) e)) + +/-- A bound `H` on the coefficients of `f` also bounds the coefficients of `f` after a +bijective renaming of the variables. -/ +lemma polynomial_coefficient_bound_renameEquiv {σ τ : Type*} (e : σ ≃ τ) + (f : MvPolynomial σ ℤ) (H : ℝ) (hf : CoefficientHeightLE f H) : + CoefficientHeightLE (rename e f) H := by + intro m + have he : rename e.symm (rename e f) = f := by simp [rename_rename] + have hc := coeff_rename_mapDomain e.symm e.symm.injective (rename e f) m + rw [he] at hc + rw [← hc] + exact hf _ + +/-- The exact geometric bad-prime set exists, and its charge is bounded. -/ +theorem exists_fin_hypersurface_bad_prime_charge + {L : Type*} [Field L] [CharZero L] [IsAlgClosed L] {n : ℕ} + (f : MvPolynomial (Fin (n + 1)) ℤ) + (hf : Irreducible (map (Int.castRingHom L) f)) + (hpos : 0 < f.totalDegree) (H : ℝ) (hH : 1 ≤ H) + (hcoeff : CoefficientHeightLE f H) : + ∃ bad : Finset ℕ, + (∀ p, p ∈ bad ↔ stepanovThreshold f.totalDegree < p ∧ HypersurfaceBadReduction f p) ∧ + (∑ p ∈ bad, Real.log p / p) ≤ + Real.log (hypersurfaceHeightConstant (n + 1) * + (1 + Real.log H / (f.totalDegree : ℝ) ^ 2)) + 2 * mertensError + 1 := by + classical + let e : Fin (n + 1) ≃ Option (Fin n) := finSuccEquiv' 0 + let g := rename e f + have hg : Irreducible (map (Int.castRingHom L) g) := by + rw [show g = rename e f from rfl, map_rename] + exact (MulEquiv.irreducible_iff (f := renameEquiv L e)).mpr hf + have hd : g.totalDegree = f.totalDegree := totalDegree_renameEquiv e f + have hgc : CoefficientHeightLE g H := polynomial_coefficient_bound_renameEquiv e f H hcoeff + let bad := (finite_hypersurface_bad_primes g hg (hd ▸ hpos)).toFinset + have hmem (p : ℕ) : p ∈ bad ↔ stepanovThreshold f.totalDegree < p ∧ + HypersurfaceBadReduction f p := by + simp only [bad, Set.Finite.mem_toFinset, Set.mem_ofPred_eq, hd, g, + hypersurfaceBadReduction_renameEquiv] + refine ⟨bad, hmem, ?_⟩ + have hc := hypersurface_bad_prime_charge g hg (hd ▸ hpos) H hH hgc bad + (fun p hp => ((hypersurfaceBadReduction_renameEquiv e f p).mpr ((hmem p).mp hp).2)) + (fun p hp => hd ▸ ((hmem p).mp hp).1) + simpa only [hd, Fintype.card_option, Fintype.card_fin] using hc + +/-- Finiteness is invariant under the standard Fin-to-Option reindexing. -/ +theorem finite_fin_hypersurface_bad_primes + {L : Type*} [Field L] [CharZero L] [IsAlgClosed L] {n : ℕ} + (f : MvPolynomial (Fin (n + 1)) ℤ) + (hf : Irreducible (map (Int.castRingHom L) f)) (hpos : 0 < f.totalDegree) : + {p : ℕ | stepanovThreshold f.totalDegree < p ∧ HypersurfaceBadReduction f p}.Finite := by + let e : Fin (n + 1) ≃ Option (Fin n) := finSuccEquiv' 0 + let g := rename e f + have hg : Irreducible (map (Int.castRingHom L) g) := by + rw [show g = rename e f from rfl, map_rename] + exact (MulEquiv.irreducible_iff (f := renameEquiv L e)).mpr hf + have hd : g.totalDegree = f.totalDegree := totalDegree_renameEquiv e f + have hh := finite_hypersurface_bad_primes g hg (hd ▸ hpos) + simpa only [hd, g, hypersurfaceBadReduction_renameEquiv] using hh + +/-- For a nonzero homogeneous equation, degree loss is already included in +failure of geometric irreducibility, so the predicate agrees with the paper. -/ +lemma homogeneous_hypersurfaceBadReduction_iff {σ : Type*} {d : ℕ} + (f : MvPolynomial σ ℤ) (hf : f.IsHomogeneous d) (hf0 : f ≠ 0) + (p : ℕ) [Fact p.Prime] : + HypersurfaceBadReduction f p ↔ + ¬Irreducible (map (Int.castRingHom (AlgebraicClosure (ZMod p))) f) := by + constructor + · rintro ⟨_, hbad⟩ hi + rcases hbad with hdegree | hnot + · apply hdegree + rw [hf.totalDegree hf0] + exact (hf.map _).totalDegree hi.ne_zero + · exact hnot hi + · intro hbad + exact ⟨Fact.out, Or.inr hbad⟩ + +/-- Outside the bad-prime predicate, both full degree and geometric +irreducibility hold for the coefficient reduction. -/ +lemma hypersurface_good_reduction {σ : Type*} (f : MvPolynomial σ ℤ) + (p : ℕ) [Fact p.Prime] (hgood : ¬HypersurfaceBadReduction f p) : + HasGoodReduction f p := by + have hg : Irreducible (map (Int.castRingHom (AlgebraicClosure (ZMod p))) f) := by + by_contra hn + exact hgood ⟨Fact.out, Or.inr hn⟩ + have he : (algebraMap (ZMod p) (AlgebraicClosure (ZMod p))).comp (Int.castRingHom (ZMod p)) = + Int.castRingHom (AlgebraicClosure (ZMod p)) := by ext; simp + have hi : Irreducible (map (algebraMap (ZMod p) (AlgebraicClosure (ZMod p))) + (map (Int.castRingHom (ZMod p)) f)) := by + simpa only [map_map, he] using hg + refine ⟨?_, hi⟩ + intro hz + exact hi.ne_zero (by rw [hz, map_zero]) + +/-- The finite set of primes `p > 27 d ^ 4` of bad reduction of an absolutely +irreducible form of degree `d > 0`. -/ +theorem exists_hypersurface_bad_prime_finset + {L : Type*} [Field L] [CharZero L] [IsAlgClosed L] {n d : ℕ} + (f : MvPolynomial (Fin (n + 1)) ℤ) (hf : f.IsHomogeneous d) (hd : 0 < d) + (hir : Irreducible (map (Int.castRingHom L) f)) : + ∃ bad : Finset ℕ, ∀ p, p ∈ bad ↔ stepanovThreshold d < p ∧ HypersurfaceBadReduction f p := by + have hdeg : f.totalDegree = d := hf.totalDegree (ne_zero_of_irreducible_map hir) + refine ⟨(finite_fin_hypersurface_bad_primes f hir (hdeg ▸ hd)).toFinset, fun p => ?_⟩ + simp only [Set.Finite.mem_toFinset, Set.mem_ofPred_eq, hdeg] + +/-- A prime above the cutoff outside the bad-prime set has good reduction. -/ +lemma hasGoodReduction_of_not_mem {σ : Type*} {f : MvPolynomial σ ℤ} {d : ℕ} + {bad : Finset ℕ} (hbad : ∀ p, p ∈ bad ↔ stepanovThreshold d < p ∧ HypersurfaceBadReduction f p) + (p : ℕ) [Fact p.Prime] (hp : p ∉ bad) (hcut : stepanovThreshold (d : ℝ) < p) : + HasGoodReduction f p := + hypersurface_good_reduction f p fun hb => hp ((hbad p).mpr ⟨by exact_mod_cast hcut, hb⟩) + +end +end CCDN diff --git a/LeanPool/CCDN/AuxiliaryPolynomial/HypersurfaceBadPrimeFactor.lean b/LeanPool/CCDN/AuxiliaryPolynomial/HypersurfaceBadPrimeFactor.lean new file mode 100644 index 0000000000..9640142aa7 --- /dev/null +++ b/LeanPool/CCDN/AuxiliaryPolynomial/HypersurfaceBadPrimeFactor.lean @@ -0,0 +1,176 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.Algebra.PolynomialPredicates +public import LeanPool.CCDN.AuxiliaryPolynomial.DegreeBound +public import LeanPool.CCDN.AuxiliaryPolynomial.HypersurfaceBadPrimeCoordinates +import LeanPool.CCDN.ForMathlib.MvPolynomial + +/-! Bad-prime factors of integer polynomials, and the geometric bad-prime factor used by the +determinant method. A bad-prime factor `badPrimeFactor P` is zero for nonabsolute equations +and the exact finite exponential product over the primes above `stepanovThreshold` satisfying +the bad-reduction predicate `P` otherwise. For nonzero homogeneous equations the geometric +factor agrees with CCDN Definition 3.2.1. For affine equations its bad-prime predicate also +includes degree loss; this stronger convention is not identical to the literal printed +definition. -/ + +@[expose] public section +namespace CCDN +noncomputable section +open MvPolynomial + +/-- An absolutely irreducible integral polynomial has positive total degree. -/ +lemma absolute_integer_polynomial_totalDegree_pos {n : ℕ} + (f : MvPolynomial (Fin (n + 1)) ℤ) + (hf : AbsolutelyIrreducible f) : + 0 < f.totalDegree := by + rw [← totalDegree_map_of_injective (Int.castRingHom (AlgebraicClosure ℚ)).injective_int f] + exact totalDegree_pos_of_ne_zero_of_not_isUnit hf.ne_zero hf.not_isUnit + +/-- The bad-prime factor of an integer polynomial `f` relative to a bad-reduction predicate +`P`: the product `primeSetFactor` of the primes `p > stepanovThreshold (deg f)` with `P f p` +when `f` is absolutely irreducible over `ℚ`, and `0` otherwise. The hypothesis `hP` gives the +finiteness of these primes for absolutely irreducible `f`. -/ +def badPrimeFactor {σ : Type*} (P : MvPolynomial σ ℤ → ℕ → Prop) + (hP : ∀ f : MvPolynomial σ ℤ, AbsolutelyIrreducible f → + {p : ℕ | stepanovThreshold f.totalDegree < p ∧ P f p}.Finite) + (f : MvPolynomial σ ℤ) : ℝ := by + classical + exact if h : AbsolutelyIrreducible f then + primeSetFactor (hP f h).toFinset + else 0 + +section BadPrimeFactor + +variable {σ : Type*} {P Q : MvPolynomial σ ℤ → ℕ → Prop} + {hP : ∀ f : MvPolynomial σ ℤ, AbsolutelyIrreducible f → + {p : ℕ | stepanovThreshold f.totalDegree < p ∧ P f p}.Finite} + {hQ : ∀ f : MvPolynomial σ ℤ, AbsolutelyIrreducible f → + {p : ℕ | stepanovThreshold f.totalDegree < p ∧ Q f p}.Finite} + +/-- The bad-prime factor `badPrimeFactor P hP f` is nonnegative. -/ +lemma badPrimeFactor_nonneg (f : MvPolynomial σ ℤ) : 0 ≤ badPrimeFactor P hP f := by + unfold badPrimeFactor + split <;> positivity + +/-- The bad-prime factor of an integral polynomial that is not absolutely irreducible is +zero. -/ +lemma badPrimeFactor_eq_zero {f : MvPolynomial σ ℤ} + (hf : ¬ AbsolutelyIrreducible f) : + badPrimeFactor P hP f = 0 := by + simp only [badPrimeFactor, dite_eq_right hf] + +/-- For an absolutely irreducible equation of degree `d`, the bad-prime factor is the +`primeSetFactor` of any finite set listing exactly the bad primes above `27 d⁴`. -/ +lemma badPrimeFactor_eq_primeSetFactor {d : ℕ} {f : MvPolynomial σ ℤ} + (hf : AbsolutelyIrreducible f) + (hdegree : f.totalDegree = d) (bad : Finset ℕ) + (hbad : ∀ p, p ∈ bad ↔ stepanovThreshold d < p ∧ P f p) : + badPrimeFactor P hP f = primeSetFactor bad := by + classical + unfold badPrimeFactor + rw [dite_eq_left hf] + congr 1 + ext p + simp only [Set.Finite.mem_toFinset, Set.mem_ofPred_eq, hdegree, hbad] + +/-- If the bad-reduction predicate `P` implies `Q` at `f`, then the bad-prime factor of `f` +for `P` is at most that for `Q`. -/ +lemma badPrimeFactor_mono (f : MvPolynomial σ ℤ) (hPQ : ∀ p, P f p → Q f p) : + badPrimeFactor P hP f ≤ badPrimeFactor Q hQ f := by + classical + by_cases hf : AbsolutelyIrreducible f + · unfold badPrimeFactor + rw [dite_eq_left hf, dite_eq_left hf] + apply Real.exp_le_exp.mpr + apply Finset.sum_le_sum_of_subset_of_nonneg + · intro p hp + simp only [Set.Finite.mem_toFinset, Set.mem_ofPred_eq] at hp ⊢ + exact ⟨hp.1, hPQ p hp.2⟩ + · intro p _ _ + exact div_nonneg (Real.log_natCast_nonneg p) (Nat.cast_nonneg p) + · rw [badPrimeFactor_eq_zero hf, badPrimeFactor_eq_zero hf] + +end BadPrimeFactor + +/-- The geometric bad-prime factor: `badPrimeFactor` for `HypersurfaceBadReduction`, which +counts both failure of absolute irreducibility and degree loss modulo `p`. -/ +def hypersurfaceBadPrimeFactor {n : ℕ} (f : MvPolynomial (Fin (n + 1)) ℤ) : ℝ := + badPrimeFactor HypersurfaceBadReduction + (fun f h => finite_fin_hypersurface_bad_primes f h + (absolute_integer_polynomial_totalDegree_pos f h)) f + +/-- The geometric bad-prime factor `hypersurfaceBadPrimeFactor f` is nonnegative. -/ +lemma hypersurfaceBadPrimeFactor_nonneg {n : ℕ} (f : MvPolynomial (Fin (n + 1)) ℤ) : + 0 ≤ hypersurfaceBadPrimeFactor f := + badPrimeFactor_nonneg f + +/-- The geometric bad-prime factor of an integral polynomial that is not absolutely +irreducible is zero. -/ +lemma hypersurfaceBadPrimeFactor_eq_zero {n : ℕ} (f : MvPolynomial (Fin (n + 1)) ℤ) + (hf : ¬ AbsolutelyIrreducible f) : + hypersurfaceBadPrimeFactor f = 0 := + badPrimeFactor_eq_zero hf + +/-- For an absolutely irreducible integral polynomial of degree `d`, the geometric bad-prime +factor is the `primeSetFactor` of any finite set listing exactly the primes above +`stepanovThreshold d` at which `f` has geometric bad reduction. -/ +lemma hypersurfaceBadPrimeFactor_eq_exp {n d : ℕ} (f : MvPolynomial (Fin (n + 1)) ℤ) + (hf : AbsolutelyIrreducible f) + (hdegree : f.totalDegree = d) (bad : Finset ℕ) + (hbad : ∀ p, p ∈ bad ↔ stepanovThreshold d < p ∧ HypersurfaceBadReduction f p) : + hypersurfaceBadPrimeFactor f = primeSetFactor bad := + badPrimeFactor_eq_primeSetFactor hf hdegree bad hbad + +/-- The bad-prime factor satisfies the coefficient-height bound also +for nonabsolute equations, where the factor is zero by definition. -/ +theorem hypersurfaceBadPrimeFactor_le_height {n : ℕ} + (f : MvPolynomial (Fin (n + 1)) ℤ) (H : ℝ) (hH : 1 ≤ H) + (hcoeff : CoefficientHeightLE f H) : + hypersurfaceBadPrimeFactor f ≤ + (Real.exp (2 * mertensError + 1) * hypersurfaceHeightConstant (n + 1)) * + (1 + Real.log H / (f.totalDegree : ℝ) ^ 2) := by + have hlog : 0 ≤ Real.log H := Real.log_nonneg hH + have hc : 0 < hypersurfaceHeightConstant (n + 1) := + zero_lt_one.trans_le (hypersurfaceHeightConstant_one_le _) + by_cases hf : AbsolutelyIrreducible f + · obtain ⟨bad, hbad, hcharge⟩ := exists_fin_hypersurface_bad_prime_charge f hf + (absolute_integer_polynomial_totalDegree_pos f hf) H hH hcoeff + rw [hypersurfaceBadPrimeFactor_eq_exp f hf rfl bad hbad] + calc + _ ≤ Real.exp (Real.log (hypersurfaceHeightConstant (n + 1) * + (1 + Real.log H / (f.totalDegree : ℝ) ^ 2)) + 2 * mertensError + 1) := + Real.exp_le_exp.mpr hcharge + _ = _ := by + rw [show Real.log (hypersurfaceHeightConstant (n + 1) * + (1 + Real.log H / (f.totalDegree : ℝ) ^ 2)) + 2 * mertensError + 1 = + Real.log (hypersurfaceHeightConstant (n + 1) * + (1 + Real.log H / (f.totalDegree : ℝ) ^ 2)) + (2 * mertensError + 1) by ring, + Real.exp_add, Real.exp_log (by positivity)] + ring + · rw [hypersurfaceBadPrimeFactor_eq_zero f hf] + positivity + +/-- The max-form of CCDN Corollary 3.2.3 for the defined factor itself. -/ +theorem hypersurfaceBadPrimeFactor_le_max {n : ℕ} + (f : MvPolynomial (Fin (n + 1)) ℤ) (H : ℝ) (hH : 1 ≤ H) + (hcoeff : CoefficientHeightLE f H) : + hypersurfaceBadPrimeFactor f ≤ + (2 * Real.exp (2 * mertensError + 1) * hypersurfaceHeightConstant (n + 1)) * + max (Real.log H / (f.totalDegree : ℝ) ^ 2) 1 := by + have hh := hypersurfaceBadPrimeFactor_le_height f H hH hcoeff + have h1 := le_max_left (Real.log H / (f.totalDegree : ℝ) ^ 2) (1 : ℝ) + have h2 := le_max_right (Real.log H / (f.totalDegree : ℝ) ^ 2) (1 : ℝ) + have hc : 0 ≤ Real.exp (2 * mertensError + 1) * hypersurfaceHeightConstant (n + 1) := + mul_nonneg (Real.exp_pos _).le (zero_le_one.trans (hypersurfaceHeightConstant_one_le _)) + nlinarith [mul_le_mul_of_nonneg_left (add_le_add h2 h1) hc] + +end +end CCDN diff --git a/LeanPool/CCDN/AuxiliaryPolynomial/HypersurfaceBadPrimeHeight.lean b/LeanPool/CCDN/AuxiliaryPolynomial/HypersurfaceBadPrimeHeight.lean new file mode 100644 index 0000000000..3ef16f14be --- /dev/null +++ b/LeanPool/CCDN/AuxiliaryPolynomial/HypersurfaceBadPrimeHeight.lean @@ -0,0 +1,247 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.Analysis.PrimeCutoff +public import LeanPool.CCDN.AuxiliaryPolynomial.DegreeBound +public import LeanPool.CCDN.AuxiliaryPolynomial.HypersurfaceBadPrimes +public import LeanPool.CCDN.Plane.IntegerPlaneLogHeight +import LeanPool.CCDN.Algebra.IntegerPolynomialAvoidance +import LeanPool.CCDN.AuxiliaryPolynomial.BadPrimeCharge +import LeanPool.CCDN.AuxiliaryPolynomial.UniversalPlaneBadReduction +import LeanPool.CCDN.Determinant.ClosedFormIntegerMinor +import LeanPool.CCDN.Determinant.ClosedFormMinorCharge + +/-! +The geometric bad-prime charge in arbitrary dimension is controlled by +coefficient height, via an explicit integer witness with bounded height. + +A nonzero bad-prime witness obtained by evaluating the universal +plane polynomial on a bounded integer grid. + +Explicit height estimates for the evaluated universal bad-prime witness. +-/ + +@[expose] public section +namespace CCDN +noncomputable section +open MvPolynomial + +private lemma universalPlaneBadWitness_eval {σ : Type*} + (f : MvPolynomial σ ℤ) + (rows : Fin (2 * f.totalDegree ^ 2 - 1) → ℕ × ℕ) + (cols : Fin (2 * f.totalDegree ^ 2 - 1) → ClosedFormColumns f.totalDegree) + (v : PlaneParameters σ → ℤ) : + eval v (universalPlaneBadWitness f rows cols) = + ((universalPlaneSection f v).coeff f.totalDegree).coeff 0 * + ((closedFormCoefficientMatrix f.totalDegree (universalPlaneSection f v)).submatrix + rows cols).det := by + rw [universalPlaneBadWitness, map_mul, closedForm_minor_map] + congr 1 + exact (bivariateMap_coeff (eval v) (universalPlanePolynomial f) f.totalDegree 0).symm + +/-- The evaluated witness is a leading coefficient times an integer +minor; both are controlled by the height of the same plane section. -/ +private theorem universalPlaneBadWitness_eval_log_bound {σ : Type*} [Finite σ] + (f : MvPolynomial σ ℤ) (hd : 1 ≤ f.totalDegree) + (rows : Fin (2 * f.totalDegree ^ 2 - 1) → ℕ × ℕ) + (cols : Fin (2 * f.totalDegree ^ 2 - 1) → ClosedFormColumns f.totalDegree) + (v : PlaneParameters σ → ℤ) (S : ℝ) (hS : 1 ≤ S) + (hf : BivariateCoefficientBound (universalPlaneSection f v) S) + (hw : eval v (universalPlaneBadWitness f rows cols) ≠ 0) : + Real.log |(eval v (universalPlaneBadWitness f rows cols) : ℝ)| ≤ + Real.log S + (27 * (f.totalDegree : ℝ) ^ 4) * + (1 + Real.log S / (f.totalDegree : ℝ) ^ 2) := by + classical + let := Fintype.ofFinite σ + let g := universalPlaneSection f v + let c := (g.coeff f.totalDegree).coeff 0 + let D := ((closedFormCoefficientMatrix f.totalDegree g).submatrix rows cols).det + have he : eval v (universalPlaneBadWitness f rows cols) = c * D := + universalPlaneBadWitness_eval f rows cols v + have hc : c ≠ 0 := fun h => hw (by rw [he, h, zero_mul]) + have hD : D ≠ 0 := fun h => hw (by rw [he, h, mul_zero]) + have ht : bivariateTotalDegree g ≤ f.totalDegree := + universalPlanePolynomial_specialization_totalDegree f (eval v) + have hb := closedForm_minor_log_height f.totalDegree (2 * f.totalDegree ^ 2 - 1) + hd (Nat.sub_le _ _) g S hS hf + ((bivariate_natDegree_le_totalDegree g).trans ht) + (fun i => (bivariate_coefficient_natDegree_le_totalDegree g i).trans ht) rows cols hD + have hl := Real.log_le_log (abs_pos.mpr (Int.cast_ne_zero.mpr hc)) (hf f.totalDegree 0) + rw [he, Int.cast_mul, abs_mul, Real.log_mul + (abs_ne_zero.mpr (Int.cast_ne_zero.mpr hc)) (abs_ne_zero.mpr (Int.cast_ne_zero.mpr hD))] + exact add_le_add hl hb + +end +end CCDN + +namespace CCDN +noncomputable section +open MvPolynomial + +private lemma bad_prime_dvd_universalPlaneBadWitness_eval {σ : Type*} [Finite σ] + (f : MvPolynomial σ ℤ) (hpos : 0 < f.totalDegree) + (rows : Fin (2 * f.totalDegree ^ 2 - 1) → ℕ × ℕ) + (cols : Fin (2 * f.totalDegree ^ 2 - 1) → ClosedFormColumns f.totalDegree) + (v : PlaneParameters σ → ℤ) (p : ℕ) + (hp : f.totalDegree < p) (hbad : HypersurfaceBadReduction f p) : + (p : ℤ) ∣ eval v (universalPlaneBadWitness f rows cols) := by + classical + let := Fintype.ofFinite σ + obtain ⟨hpprime, hbad⟩ := hbad + let : Fact p.Prime := ⟨hpprime⟩ + rw [eval_eq'] + apply Finset.dvd_sum + intro e _ + exact dvd_mul_of_dvd_left + (bad_prime_dvd_universalPlaneBadWitness_coeff f hpos rows cols p hp hbad e) _ + +/-- The universal plane bad-prime witness of an absolutely irreducible integer polynomial +takes a nonzero value at some integer parameter in the box `[0, 2d³]`, and every prime above +`d` with bad geometric reduction divides that value. -/ +private theorem exists_bounded_integer_plane_witness + {L σ : Type*} [Field L] [CharZero L] [IsAlgClosed L] [Finite σ] + (f : MvPolynomial (Option σ) ℤ) + (hf : Irreducible (MvPolynomial.map (Int.castRingHom L) f)) + (hpos : 0 < f.totalDegree) : + ∃ (rows : Fin (2 * f.totalDegree ^ 2 - 1) → ℕ × ℕ) + (cols : Fin (2 * f.totalDegree ^ 2 - 1) → ClosedFormColumns f.totalDegree) + (v : PlaneParameters (Option σ) → ℤ), + (∀ i, 0 ≤ v i ∧ v i ≤ (2 * f.totalDegree ^ 3 : ℕ)) ∧ + eval v (universalPlaneBadWitness f rows cols) ≠ 0 ∧ + ∀ p : ℕ, f.totalDegree < p → HypersurfaceBadReduction f p → + (p : ℤ) ∣ eval v (universalPlaneBadWitness f rows cols) := by + classical + let := Fintype.ofFinite σ + obtain ⟨rows, cols, hw, hd⟩ := exists_integer_universalPlane_badWitness f hf hpos + obtain ⟨v, hv, he⟩ := exists_integer_grid_eval_ne_zero _ hw _ hd + exact ⟨rows, cols, v, hv, he, + fun p hp hbad => bad_prime_dvd_universalPlaneBadWitness_eval f hpos rows cols v p hp hbad⟩ + +end +end CCDN + +namespace CCDN +noncomputable section +open MvPolynomial + +/-- An effective positive integer detecting every bad prime above the degree. -/ +theorem exists_hypersurface_bad_prime_height_witness + {L σ : Type*} [Field L] [CharZero L] [IsAlgClosed L] [Fintype σ] + (f : MvPolynomial (Option σ) ℤ) + (hf : Irreducible (MvPolynomial.map (Int.castRingHom L) f)) + (hpos : 0 < f.totalDegree) (H : ℝ) (hH : 1 ≤ H) + (hcoeff : CoefficientHeightLE f H) : + ∃ D : ℤ, 0 < D ∧ + Real.log D ≤ (27 * (f.totalDegree : ℝ) ^ 4) * + (hypersurfaceHeightConstant (Fintype.card (Option σ)) * + (1 + Real.log H / (f.totalDegree : ℝ) ^ 2)) ∧ + ∀ p : ℕ, f.totalDegree < p → HypersurfaceBadReduction f p → (p : ℤ) ∣ D := by + obtain ⟨rows, cols, v, hv, hw, hdiv⟩ := exists_bounded_integer_plane_witness f hf hpos + let S := integerPlaneHeight (Fintype.card (Option σ)) f.totalDegree H + (2 * (f.totalDegree : ℝ) ^ 3) + have hd : 1 ≤ f.totalDegree := hpos + have hdR : (1 : ℝ) ≤ f.totalDegree := by exact_mod_cast hd + have hA : 1 ≤ 2 * (f.totalDegree : ℝ) ^ 3 := by nlinarith [one_le_pow₀ hdR (n := 3)] + have hv' (i : PlaneParameters (Option σ)) : |(v i : ℝ)| ≤ 2 * (f.totalDegree : ℝ) ^ 3 := by + rw [abs_of_nonneg (by exact_mod_cast (hv i).1)] + exact_mod_cast (hv i).2 + have hS : 1 ≤ S := integerPlaneHeight_one_le _ _ hd H hH + have hcoeffS : BivariateCoefficientBound (universalPlaneSection f v) S := + integer_plane_section_coefficient_bound f H _ (by linarith) hA hcoeff v hv' + refine ⟨|eval v (universalPlaneBadWitness f rows cols)|, abs_pos.mpr hw, ?_, ?_⟩ + · rw [Int.cast_abs] + exact (universalPlaneBadWitness_eval_log_bound f hd rows cols v S hS hcoeffS hw).trans + (integerPlaneHeight_scaled_log_le _ _ hd H hH) + · intro p hp hbad + exact (dvd_abs _ _).mpr (hdiv p hp hbad) + +/-- The logarithmic charge bound for any finite family of bad primes. +This includes degree-dropping primes as well as geometric reducibility. -/ +theorem hypersurface_bad_prime_charge + {L σ : Type*} [Field L] [CharZero L] [IsAlgClosed L] [Fintype σ] + (f : MvPolynomial (Option σ) ℤ) + (hf : Irreducible (MvPolynomial.map (Int.castRingHom L) f)) + (hpos : 0 < f.totalDegree) (H : ℝ) (hH : 1 ≤ H) + (hcoeff : CoefficientHeightLE f H) + (P : Finset ℕ) (hP : ∀ p ∈ P, HypersurfaceBadReduction f p) + (hcut : ∀ p ∈ P, stepanovThreshold f.totalDegree < p) : + (∑ p ∈ P, Real.log p / p) ≤ + Real.log (hypersurfaceHeightConstant (Fintype.card (Option σ)) * + (1 + Real.log H / (f.totalDegree : ℝ) ^ 2)) + 2 * mertensError + 1 := by + obtain ⟨D, hD, hheight, hdiv⟩ := + exists_hypersurface_bad_prime_height_witness f hf hpos H hH hcoeff + have hd1 : (1 : ℝ) ≤ f.totalDegree := by exact_mod_cast hpos + apply bad_prime_charge_of_height P hD (fun p hp => (hP p hp).choose) _ _ _ _ hheight + · intro p hp + apply hdiv p _ (hP p hp) + have h1 := Nat.le_mul_self f.totalDegree + have h2 := Nat.le_mul_self (f.totalDegree * f.totalDegree) + have h3 : 27 * f.totalDegree ^ 4 < p := hcut p hp + nlinarith [h3] + · nlinarith [one_le_pow₀ hd1 (n := 4)] + · exact one_le_mul_of_one_le_of_one_le (hypersurfaceHeightConstant_one_le _) + (le_add_of_nonneg_right (div_nonneg (Real.log_nonneg hH) (sq_nonneg _))) + · intro p hp + exact_mod_cast hcut p hp + +/-- The exponential bad-prime factor has the coefficient-height dependence +required in CCDN Corollary 3.2.3. -/ +private theorem hypersurface_bad_prime_factor + {L σ : Type*} [Field L] [CharZero L] [IsAlgClosed L] [Fintype σ] + (f : MvPolynomial (Option σ) ℤ) + (hf : Irreducible (MvPolynomial.map (Int.castRingHom L) f)) + (hpos : 0 < f.totalDegree) (H : ℝ) (hH : 1 ≤ H) + (hcoeff : CoefficientHeightLE f H) + (P : Finset ℕ) (hP : ∀ p ∈ P, HypersurfaceBadReduction f p) + (hcut : ∀ p ∈ P, stepanovThreshold f.totalDegree < p) : + primeSetFactor P ≤ + (Real.exp (2 * mertensError + 1) * hypersurfaceHeightConstant (Fintype.card (Option σ))) * + (1 + Real.log H / (f.totalDegree : ℝ) ^ 2) := by + have h := Real.exp_le_exp.mpr (hypersurface_bad_prime_charge f hf hpos H hH hcoeff P hP hcut) + have hc : 0 < hypersurfaceHeightConstant (Fintype.card (Option σ)) := + zero_lt_one.trans_le (hypersurfaceHeightConstant_one_le _) + have hh : 0 < 1 + Real.log H / (f.totalDegree : ℝ) ^ 2 := by + have := div_nonneg (Real.log_nonneg hH) (sq_nonneg (f.totalDegree : ℝ)) + linarith + calc + _ ≤ Real.exp (Real.log (hypersurfaceHeightConstant (Fintype.card (Option σ)) * + (1 + Real.log H / (f.totalDegree : ℝ) ^ 2)) + 2 * mertensError + 1) := h + _ = _ := by + rw [add_assoc, Real.exp_add, Real.exp_log (mul_pos hc hh)] + ring + +/-- The maximum formulation of CCDN Corollary 3.2.3, with an explicit +constant depending only on the number of variables. -/ +theorem hypersurface_bad_prime_factor_max + {L σ : Type*} [Field L] [CharZero L] [IsAlgClosed L] [Fintype σ] + (f : MvPolynomial (Option σ) ℤ) + (hf : Irreducible (MvPolynomial.map (Int.castRingHom L) f)) + (hpos : 0 < f.totalDegree) (H : ℝ) (hH : 1 ≤ H) + (hcoeff : CoefficientHeightLE f H) + (P : Finset ℕ) (hP : ∀ p ∈ P, HypersurfaceBadReduction f p) + (hcut : ∀ p ∈ P, stepanovThreshold f.totalDegree < p) : + primeSetFactor P ≤ + (2 * Real.exp (2 * mertensError + 1) * hypersurfaceHeightConstant (Fintype.card (Option σ))) * + max (Real.log H / (f.totalDegree : ℝ) ^ 2) 1 := by + have h := hypersurface_bad_prime_factor f hf hpos H hH hcoeff P hP hcut + have hc : 0 ≤ Real.exp (2 * mertensError + 1) * + hypersurfaceHeightConstant (Fintype.card (Option σ)) := + mul_nonneg (Real.exp_pos _).le (le_trans zero_le_one (hypersurfaceHeightConstant_one_le _)) + have hm : 1 + Real.log H / (f.totalDegree : ℝ) ^ 2 ≤ + 2 * max (Real.log H / (f.totalDegree : ℝ) ^ 2) 1 := by + have h1 := le_max_left (Real.log H / (f.totalDegree : ℝ) ^ 2) 1 + have h2 := le_max_right (Real.log H / (f.totalDegree : ℝ) ^ 2) 1 + linarith + apply h.trans + convert mul_le_mul_of_nonneg_left hm hc using 1 + ring + +end +end CCDN diff --git a/LeanPool/CCDN/AuxiliaryPolynomial/HypersurfaceBadPrimes.lean b/LeanPool/CCDN/AuxiliaryPolynomial/HypersurfaceBadPrimes.lean new file mode 100644 index 0000000000..56f66456dd --- /dev/null +++ b/LeanPool/CCDN/AuxiliaryPolynomial/HypersurfaceBadPrimes.lean @@ -0,0 +1,86 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.AuxiliaryPolynomial.HypersurfaceBadReduction +public import LeanPool.CCDN.Plane.FiniteFieldConstants +import LeanPool.CCDN.AuxiliaryPolynomial.UniversalPlaneBadReduction + +/-! The finite set of bad primes of an integer hypersurface. -/ + +@[expose] public section +namespace CCDN +noncomputable section +open MvPolynomial + +/-- A polynomial with an irreducible image under a coefficient map is nonzero. -/ +lemma ne_zero_of_irreducible_map {R S σ : Type*} [CommSemiring R] [CommSemiring S] + {φ : R →+* S} {f : MvPolynomial σ R} (h : Irreducible (map φ f)) : f ≠ 0 := by + rintro rfl + exact h.ne_zero (map_zero _) + +/-- Good reduction at a prime `p`: the reduction of `f` modulo `p` is nonzero and +absolutely irreducible. -/ +abbrev HasGoodReduction {σ : Type*} (f : MvPolynomial σ ℤ) (p : ℕ) [Fact p.Prime] : Prop := + map (Int.castRingHom (ZMod p)) f ≠ 0 ∧ + Irreducible (map (algebraMap (ZMod p) (AlgebraicClosure (ZMod p))) + (map (Int.castRingHom (ZMod p)) f)) + +/-- A positive integer and the exact finite bad-prime set above the +degree. -/ +private theorem exists_hypersurface_bad_prime_set + {L σ : Type*} [Field L] [CharZero L] [IsAlgClosed L] [Finite σ] + (f : MvPolynomial (Option σ) ℤ) + (hf : Irreducible (MvPolynomial.map (Int.castRingHom L) f)) + (hpos : 0 < f.totalDegree) : + ∃ (D : ℤ) (B : Finset ℕ), 0 < D ∧ + (∀ p, p ∈ B ↔ f.totalDegree < p ∧ HypersurfaceBadReduction f p) ∧ + (∀ p ∈ B, (p : ℤ) ∣ D) := by + classical + let := Fintype.ofFinite σ + obtain ⟨rows, cols, e, he, hdiv⟩ := exists_integer_hypersurface_bad_prime_witness f hf hpos + let D := |(universalPlaneBadWitness f rows cols).coeff e| + have hD : 0 < D := abs_pos.mpr he + have hdiv' (p : ℕ) (hp : f.totalDegree < p) (hbad : HypersurfaceBadReduction f p) : + (p : ℤ) ∣ D := by + obtain ⟨hpp, hbad⟩ := hbad + let : Fact p.Prime := ⟨hpp⟩ + exact (dvd_abs _ _).mpr (hdiv p hp hbad) + let B := D.natAbs.primeFactors.filter (fun p => f.totalDegree < p ∧ HypersurfaceBadReduction f p) + have hmem (p : ℕ) : p ∈ B ↔ f.totalDegree < p ∧ HypersurfaceBadReduction f p := by + constructor + · exact fun h => (Finset.mem_filter.mp h).2 + · intro hp + apply Finset.mem_filter.mpr + refine ⟨Nat.mem_primeFactors.mpr ⟨hp.2.choose, ?_, Int.natAbs_ne_zero.mpr hD.ne'⟩, hp⟩ + simpa only [Int.natAbs_natCast] using Int.natAbs_dvd_natAbs.mpr (hdiv' p hp.1 hp.2) + exact ⟨D, B, hD, hmem, fun p hp => hdiv' p ((hmem p).mp hp).1 ((hmem p).mp hp).2⟩ + +/-- Finiteness of the paper's bad-prime set above 27*d^4. A stronger set above +d is finite; filtering it gives the exact prescribed cutoff. -/ +theorem finite_hypersurface_bad_primes + {L σ : Type*} [Field L] [CharZero L] [IsAlgClosed L] [Finite σ] + (f : MvPolynomial (Option σ) ℤ) + (hf : Irreducible (MvPolynomial.map (Int.castRingHom L) f)) + (hpos : 0 < f.totalDegree) : + {p : ℕ | stepanovThreshold f.totalDegree < p ∧ HypersurfaceBadReduction f p}.Finite := by + classical + let := Fintype.ofFinite σ + obtain ⟨_, B, _, hmem, _⟩ := exists_hypersurface_bad_prime_set f hf hpos + apply B.finite_toSet.subset + intro p hp + apply (hmem p).mpr + refine ⟨?_, hp.2⟩ + have h1 := Nat.le_mul_self f.totalDegree + have h2 := Nat.le_mul_self (f.totalDegree * f.totalDegree) + have h3 : 27 * f.totalDegree ^ 4 < p := hp.1 + nlinarith [h3] + +end +end CCDN diff --git a/LeanPool/CCDN/AuxiliaryPolynomial/HypersurfaceBadReduction.lean b/LeanPool/CCDN/AuxiliaryPolynomial/HypersurfaceBadReduction.lean new file mode 100644 index 0000000000..25e754835d --- /dev/null +++ b/LeanPool/CCDN/AuxiliaryPolynomial/HypersurfaceBadReduction.lean @@ -0,0 +1,47 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import Mathlib.Algebra.Field.ZMod +public import Mathlib.FieldTheory.IsAlgClosed.AlgebraicClosure + +/-! Bad reduction of an integer polynomial at a prime: degree loss or failure of absolute +irreducibility modulo `p`. -/ + +@[expose] public section +namespace CCDN +noncomputable section +open MvPolynomial + +/-- Bad reduction at `p`: `p` is prime and the reduction of `f` over the algebraic closure of +`𝔽_p` either has smaller total degree than `f` or is not irreducible. -/ +def HypersurfaceBadReduction {σ : Type*} (f : MvPolynomial σ ℤ) (p : ℕ) : Prop := + ∃ hp : p.Prime, let _ : Fact p.Prime := ⟨hp⟩ + (MvPolynomial.map (Int.castRingHom (AlgebraicClosure (ZMod p))) f).totalDegree ≠ f.totalDegree ∨ + ¬Irreducible (MvPolynomial.map (Int.castRingHom (AlgebraicClosure (ZMod p))) f) + +/-- At a prime `p`, good reduction means that the reduction over the algebraic closure of +`𝔽_p` keeps the total degree and is irreducible. -/ +lemma not_hypersurfaceBadReduction_iff {σ : Type*} (f : MvPolynomial σ ℤ) (p : ℕ) + [Fact p.Prime] : + ¬HypersurfaceBadReduction f p ↔ + (map (Int.castRingHom (AlgebraicClosure (ZMod p))) f).totalDegree = f.totalDegree ∧ + Irreducible (map (Int.castRingHom (AlgebraicClosure (ZMod p))) f) := by + constructor + · intro h + refine ⟨?_, ?_⟩ + · by_contra hn + exact h ⟨Fact.out, Or.inl hn⟩ + · by_contra hn + exact h ⟨Fact.out, Or.inr hn⟩ + · rintro ⟨hdeg, hir⟩ ⟨_, hbad⟩ + exact hbad.elim (· hdeg) (· hir) + +end +end CCDN diff --git a/LeanPool/CCDN/AuxiliaryPolynomial/HypersurfaceCoefficientDichotomy.lean b/LeanPool/CCDN/AuxiliaryPolynomial/HypersurfaceCoefficientDichotomy.lean new file mode 100644 index 0000000000..141e03dbeb --- /dev/null +++ b/LeanPool/CCDN/AuxiliaryPolynomial/HypersurfaceCoefficientDichotomy.lean @@ -0,0 +1,150 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.ArithmeticHeights.BombieriVaaler +public import LeanPool.CCDN.Algebra.HomogeneousMonomials +public import LeanPool.CCDN.AuxiliaryPolynomial.Nonmultiple +public import LeanPool.CCDN.Height.IntegerCoefficientHeight +import LeanPool.CCDN.Algebra.HomogeneousEvaluation +import LeanPool.CCDN.Algebra.HomogeneousRank +import LeanPool.CCDN.Algebra.PrimitiveNormalization +import LeanPool.CCDN.Algebra.RowSelection +import LeanPool.CCDN.AuxiliaryPolynomial.CoefficientBound +import LeanPool.CCDN.Determinant.HomogeneousDeterminantComparison + +/-! +Bounded rational zeros either admit a degree-d auxiliary nonmultiple, +or control the defining equation's coefficient height by an explicit +finite-dimensional interpolation bound. + +Bombieri--Vaaler for complete homogeneous evaluation matrices. The defining +equation supplies rank deficiency, and selected rows span the whole point set. +-/ + +@[expose] public section +namespace CCDN +noncomputable section +open MvPolynomial Matrix + + +end +end CCDN + +namespace CCDN +noncomputable section +open MvPolynomial Matrix + +/-- If an integer form `f` of degree `d` divides a nonzero integer form `g` of the same degree, +then every coefficient of `f` is bounded in absolute value by the corresponding coefficient +of `g`, since `g` is a nonzero integer multiple of `f`. -/ +lemma homogeneous_same_degree_coefficient_le_of_dvd {n d : ℕ} + {f g : MvPolynomial (Fin n) ℤ} (hf : f.IsHomogeneous d) (hg : g.IsHomogeneous d) + (hg0 : g ≠ 0) (hdiv : f ∣ g) (m : Fin n →₀ ℕ) : + |(f.coeff m : ℝ)| ≤ |(g.coeff m : ℝ)| := by + obtain ⟨q, he⟩ := hdiv + have hf0 : f ≠ 0 := fun h => hg0 (by simp [he, h]) + have hq0 : q ≠ 0 := fun h => hg0 (by simp [he, h]) + have hqdegree : q.totalDegree = 0 := by + have hh := totalDegree_mul_of_isDomain hf0 hq0 + rw [← he, hf.totalDegree hf0, hg.totalDegree hg0] at hh + omega + have hq := totalDegree_eq_zero_iff_eq_C.mp hqdegree + have hc : q.coeff 0 ≠ 0 := by intro h; apply hq0; simpa [h] using hq + have he' : g = C (q.coeff 0) * f := calc + g = f * q := he + _ = f * C (q.coeff 0) := congrArg (f * ·) hq + _ = C (q.coeff 0) * f := mul_comm _ _ + exact integer_scalar_factor_coefficient_bound hc he' m + +/-- Explicit coefficient control when every degree-d relation is a multiple +of f. N is the exact dimension of the space of homogeneous degree-d forms. -/ +private theorem homogeneous_no_auxiliary_coefficient_height {n d t : ℕ} + (f : MvPolynomial (Fin (n + 1)) ℤ) (hf : f.IsHomogeneous d) (hf0 : f ≠ 0) + (z : Fin t → IntPoint (n + 1)) (hz : ∀ i, eval (z i) f = 0) + {B : ℝ} (hB : 1 ≤ B) (hbox : ∀ i, InBox (z i) B) + (hrel : ∀ g : MvPolynomial (Fin (n + 1)) ℤ, g.IsHomogeneous d → + (∀ i, eval (z i) g = 0) → f ∣ g) : + Real.log (integerCoefficientHeight f : ℝ) ≤ + ((n + d).choose d : ℝ) * Real.log ((n + d).choose d) + + ((n + d).choose d : ℝ) * d * Real.log B := by + obtain ⟨s, a, _, hs, _, hi, _, g, hg0, hgh, hgz, hc⟩ := + exists_small_homogeneous_vanishing_polynomial f hf hf0 le_rfl z hz + let N := (n + d).choose d + let A := evaluationMatrix (homogeneousMonomials n d) (z ∘ a) + let H := (integerCoefficientHeight f : ℝ) + let R := (Real.sqrt (((A * Aᵀ).det : ℤ) : ℝ) / (Int.Matrix.minorGcd A : ℝ)) ^ (((N : ℝ) - s)⁻¹) + have hD1 : (1 : ℝ) ≤ (Int.Matrix.minorGcd A : ℝ) := by + exact_mod_cast Int.Matrix.one_le_minorGcd hi + have hD0 : (0 : ℝ) < (Int.Matrix.minorGcd A : ℝ) := by linarith + have hR : 0 ≤ R := Real.rpow_nonneg (div_nonneg (Real.sqrt_nonneg _) hD0.le) _ + have hH1 : 1 ≤ H := by dsimp only [H]; exact_mod_cast integerCoefficientHeight_pos f hf0 + have hHb : H ≤ R := integerCoefficientHeight_le f hR (fun m => + (homogeneous_same_degree_coefficient_le_of_dvd hf hgh hg0 (hrel g hgh hgz) m).trans (hc m)) + have hNs : (1 : ℝ) ≤ (N : ℝ) - s := by + have hh : s + 1 ≤ N := hs + have hhR : (s : ℝ) + 1 ≤ N := by exact_mod_cast hh + linarith + obtain ⟨hGram, hcomp⟩ := log_gcd_le_of_coefficient_bound (by linarith : 0 < H) hD0 + (by linarith : (0 : ℝ) < (N : ℝ) - s) hHb + have hN : 0 < N := lt_of_le_of_lt (Nat.zero_le s) hs + have hN1 : (1 : ℝ) ≤ N := by exact_mod_cast hN + have hlogH : 0 ≤ Real.log H := Real.log_nonneg hH1 + have hlogN : 0 ≤ Real.log (N : ℝ) := Real.log_nonneg hN1 + have hlogB : 0 ≤ Real.log B := Real.log_nonneg hB + have hlogD : 0 ≤ Real.log (Int.Matrix.minorGcd A : ℝ) := Real.log_nonneg hD1 + have hmulH := mul_le_mul_of_nonneg_right hNs hlogH + have harch := evaluation_log_gram_le hN (homogeneousMonomials n d) (z ∘ a) + hB (fun i j => hbox (a i) j) (fun j => (homogeneousMonomials_degree n d j).le) hGram + have hfact : (s.factorial : ℝ) ≤ (N : ℝ) ^ s := by + exact_mod_cast (Nat.factorial_le_pow s).trans (Nat.pow_le_pow_left hs.le s) + have hlogfact : Real.log (s.factorial : ℝ) ≤ (s : ℝ) * Real.log (N : ℝ) := by + have hh := Real.log_le_log (by exact_mod_cast Nat.factorial_pos s) hfact + rwa [Real.log_pow] at hh + have hsN : (s : ℝ) ≤ N := by exact_mod_cast hs.le + have hlast1 := mul_le_mul_of_nonneg_right hsN hlogN + have hlast2 := mul_le_mul_of_nonneg_right hsN + (mul_nonneg (Nat.cast_nonneg (α := ℝ) d) hlogB) + change Real.log H ≤ (N : ℝ) * Real.log N + N * d * Real.log B + nlinarith + +/-- A quantitative coefficient-height dichotomy on the complete bounded +projective zero set. The height branch has log(H)<=N log(N)+d N log(B). -/ +theorem projective_coefficient_height_dichotomy {n d : ℕ} + (f : MvPolynomial (Fin (n + 1)) ℤ) (hf : f.IsHomogeneous d) (hf0 : f ≠ 0) + {B : ℝ} (hB : 1 ≤ B) : + Real.log (integerCoefficientHeight f : ℝ) ≤ + ((n + d).choose d : ℝ) * Real.log ((n + d).choose d) + + ((n + d).choose d : ℝ) * d * Real.log B ∨ + ∃ g, IsProjectiveAuxiliary f g d B := by + classical + let S := projectivePoints (projectiveZeroLocus (map (Int.castRingHom ℚ) f)) B + let e := (Fintype.equivFin S).symm + let z : Fin (Fintype.card S) → IntPoint (n + 1) := fun i => primitiveProjectiveRep (e i).val + have hz (i) : eval (z i) f = 0 := + (mem_integer_projectiveZeroLocus hf _).mp (mem_projectivePoints.mp (e i).property).2 + have hbox (i j) : |(z i j : ℝ)| ≤ B := + (primitiveProjectiveRep_bound _ j).trans (mem_projectivePoints.mp (e i).property).1 + by_cases hrel : ∀ g : MvPolynomial (Fin (n + 1)) ℤ, g.IsHomogeneous d → + (∀ i, eval (z i) g = 0) → f ∣ g + · exact Or.inl (homogeneous_no_auxiliary_coefficient_height f hf hf0 z hz hB hbox hrel) + · right + push Not at hrel + obtain ⟨g, hgh, hgz, hgnd⟩ := hrel + refine ⟨g, fun h => hgnd (by rw [h]; exact dvd_zero _), hgh, ?_, hgnd⟩ + intro x hxB hxf + let xS : S := ⟨x, mem_projectivePoints.mpr ⟨hxB, hxf⟩⟩ + obtain ⟨i, hi⟩ := e.surjective xS + have hh := hgz i + dsimp only [z] at hh + rw [hi] at hh + exact (mem_integer_projectiveZeroLocus hgh x).mpr hh + +end +end CCDN diff --git a/LeanPool/CCDN/AuxiliaryPolynomial/HypersurfaceFullOuterDegree.lean b/LeanPool/CCDN/AuxiliaryPolynomial/HypersurfaceFullOuterDegree.lean new file mode 100644 index 0000000000..9fe9127a13 --- /dev/null +++ b/LeanPool/CCDN/AuxiliaryPolynomial/HypersurfaceFullOuterDegree.lean @@ -0,0 +1,88 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.Algebra.AffineLineLeading +public import LeanPool.CCDN.AuxiliaryPolynomial.HypersurfaceAffineCoordinates + +/-! A direction where the highest homogeneous component is nonzero gives full +outer degree after the explicit affine coordinate change. Its leading +coefficient is the scalar obtained by evaluating that component. -/ + +@[expose] public section +namespace CCDN +noncomputable section +open MvPolynomial + +private lemma optionEquivLeft_top_coefficient_constant {K σ : Type*} [Field K] + (f : MvPolynomial (Option σ) K) (d : ℕ) (hd : f.totalDegree ≤ d) : + (optionEquivLeft K σ f).coeff d = C (eval (0 : σ → K) ((optionEquivLeft K σ f).coeff d)) := by + by_cases h : d ≤ f.totalDegree + · have ht := totalDegree_coeff_optionEquivLeft_add_le K σ f d h + have hz : ((optionEquivLeft K σ f).coeff d).totalDegree = 0 := by omega + simpa only [eval_zero, constantCoeff_eq] using totalDegree_eq_zero_iff_eq_C.mp hz + · have hn : (optionEquivLeft K σ f).natDegree < d := by + rw [natDegree_optionEquivLeft] + exact (degreeOf_le_totalDegree f none).trans_lt (by omega) + rw [Polynomial.coeff_eq_zero_of_natDegree_lt hn, map_zero, map_zero] + +private lemma hypersurfaceAffineCoordinates_axis {K σ : Type*} [Field K] + (a b : Option σ → K) (hb : b none ≠ 0) (f : MvPolynomial (Option σ) K) : + Polynomial.map (eval (0 : σ → K)) + (optionEquivLeft K σ (hypersurfaceAffineCoordinates a b hb f)) = + affineLinePolynomial a b f := by + let φ := (Polynomial.mapRingHom (eval (0 : σ → K))).comp + ((optionEquivLeft K σ).toRingHom.comp (hypersurfaceAffineCoordinates a b hb).toRingHom) + have he : φ = affineLinePolynomial a b := by + apply MvPolynomial.ringHom_ext + · intro r + change Polynomial.map (eval (0 : σ → K)) + (optionEquivLeft K σ (hypersurfaceAffineCoordinates a b hb (C r))) = _ + rw [show hypersurfaceAffineCoordinates a b hb (C r) = C r from + (hypersurfaceAffineCoordinates a b hb).commutes r] + simp [affineLinePolynomial] + · intro i + change Polynomial.map (eval (0 : σ → K)) + (optionEquivLeft K σ (hypersurfaceAffineCoordinates a b hb (X i))) = _ + rw [hypersurfaceAffineCoordinates_X] + cases i <;> simp [affineLinePolynomial] + exact RingHom.congr_fun he f + +/-- The top outer coefficient is a constant polynomial, and its scalar is +exactly the leading homogeneous form evaluated on the chosen direction. -/ +theorem hypersurfaceAffineCoordinates_top_coefficient {K σ : Type*} [Field K] + (a b : Option σ → K) (hb : b none ≠ 0) (f : MvPolynomial (Option σ) K) : + (optionEquivLeft K σ (hypersurfaceAffineCoordinates a b hb f)).coeff f.totalDegree = + C (eval b (homogeneousComponent f.totalDegree f)) := by + rw [optionEquivLeft_top_coefficient_constant _ _ + (hypersurfaceAffineCoordinates_totalDegree_le a b hb f)] + congr 1 + have h := congrArg (fun q : Polynomial K => q.coeff f.totalDegree) + (hypersurfaceAffineCoordinates_axis a b hb f) + rw [Polynomial.coeff_map, affineLinePolynomial_top_coefficient a b f _ le_rfl] at h + exact h + +/-- Nonvanishing in the direction forces the outer degree to be the original +total degree, even after moving an arbitrary base point to the origin. -/ +theorem hypersurfaceAffineCoordinates_full_outer_degree {K σ : Type*} [Field K] + (a b : Option σ → K) (hb : b none ≠ 0) (f : MvPolynomial (Option σ) K) + (hdir : eval b (homogeneousComponent f.totalDegree f) ≠ 0) : + (optionEquivLeft K σ (hypersurfaceAffineCoordinates a b hb f)).natDegree = f.totalDegree := by + have hc : + (optionEquivLeft K σ (hypersurfaceAffineCoordinates a b hb f)).coeff f.totalDegree ≠ 0 := by + rw [hypersurfaceAffineCoordinates_top_coefficient] + exact (map_ne_zero_iff _ (C_injective σ K)).mpr hdir + apply le_antisymm + · rw [natDegree_optionEquivLeft] + exact (degreeOf_le_totalDegree _ none).trans + (hypersurfaceAffineCoordinates_totalDegree_le a b hb f) + · exact Polynomial.le_natDegree_of_ne_zero hc + +end +end CCDN diff --git a/LeanPool/CCDN/AuxiliaryPolynomial/HypersurfaceGoodDirection.lean b/LeanPool/CCDN/AuxiliaryPolynomial/HypersurfaceGoodDirection.lean new file mode 100644 index 0000000000..d6f529c506 --- /dev/null +++ b/LeanPool/CCDN/AuxiliaryPolynomial/HypersurfaceGoodDirection.lean @@ -0,0 +1,76 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import Mathlib.Algebra.MvPolynomial.PDeriv +public import Mathlib.RingTheory.MvPolynomial.Homogeneous +import LeanPool.CCDN.ForMathlib.MvPolynomial + +/-! Simultaneous avoidance of the top homogeneous form, a tangent hyperplane, +and a chosen coordinate hyperplane, by a direction over the base field. -/ + +@[expose] public section +namespace CCDN +noncomputable section +open MvPolynomial + +/-- The linear form evaluating a differential on a direction. -/ +private def tangentDirectionPolynomial {K σ : Type*} [Field K] [Fintype σ] + (f : MvPolynomial σ K) (z : σ → K) : MvPolynomial σ K := + ∑ i, C (eval z (pderiv i f)) * X i + +private lemma tangentDirectionPolynomial_coeff {K σ : Type*} [Field K] [Fintype σ] + (f : MvPolynomial σ K) (z : σ → K) (j : σ) : + (tangentDirectionPolynomial f z).coeff (Finsupp.single j 1) = eval z (pderiv j f) := by + classical + simp [tangentDirectionPolynomial, coeff_C_mul, coeff_X, Finsupp.single_eq_single_iff] + +private lemma tangentDirectionPolynomial_eval {K σ : Type*} [Field K] [Fintype σ] + (f : MvPolynomial σ K) (z b : σ → K) : + eval b (tangentDirectionPolynomial f z) = ∑ i, eval z (pderiv i f) * b i := by + simp [tangentDirectionPolynomial] + +private lemma tangentDirectionPolynomial_isHomogeneous {K σ : Type*} [Field K] [Fintype σ] + (f : MvPolynomial σ K) (z : σ → K) : + (tangentDirectionPolynomial f z).IsHomogeneous 1 := by + exact IsHomogeneous.sum _ _ _ (fun i _ => isHomogeneous_C_mul_X _ i) + +/-- Over an infinite field, a smooth point admits a direction with nonzero +leading form, nonzero directional derivative, and nonzero selected coordinate. -/ +theorem exists_good_hypersurface_direction_with_coordinate + {K σ : Type*} [Field K] [Infinite K] [Fintype σ] + (f : MvPolynomial σ K) (hf : f ≠ 0) (z : σ → K) + (j : σ) (hj : eval z (pderiv j f) ≠ 0) (k : σ) : + ∃ b : σ → K, b k ≠ 0 ∧ + eval b (homogeneousComponent f.totalDegree f) ≠ 0 ∧ + (∑ i, eval z (pderiv i f) * b i) ≠ 0 := by + classical + let H := homogeneousComponent f.totalDegree f + let T := tangentDirectionPolynomial f z + have hH : H ≠ 0 := homogeneousComponent_totalDegree_ne_zero hf + have hT : T ≠ 0 := by + intro h + have hc := congrArg (fun g : MvPolynomial σ K => g.coeff (Finsupp.single j 1)) h + rw [tangentDirectionPolynomial_coeff] at hc + exact hj hc + have hprod : H * T * X k ≠ 0 := mul_ne_zero (mul_ne_zero hH hT) (X_ne_zero k) + have hhom : (H * T * X k).IsHomogeneous (f.totalDegree + 1 + 1) := + ((homogeneousComponent_isHomogeneous _ _).mul + (tangentDirectionPolynomial_isHomogeneous f z)).mul (isHomogeneous_X K k) + have hex : ∃ b : σ → K, eval b (H * T * X k) ≠ 0 := by + by_contra! hn + exact hprod (hhom.eq_zero_of_forall_eval_eq_zero hn) + obtain ⟨b, hb⟩ := hex + rw [map_mul, map_mul, eval_X] at hb + refine ⟨b, right_ne_zero_of_mul hb, left_ne_zero_of_mul (left_ne_zero_of_mul hb), ?_⟩ + rw [← tangentDirectionPolynomial_eval] + exact right_ne_zero_of_mul (left_ne_zero_of_mul hb) + +end +end CCDN diff --git a/LeanPool/CCDN/AuxiliaryPolynomial/HypersurfaceHeightComparison.lean b/LeanPool/CCDN/AuxiliaryPolynomial/HypersurfaceHeightComparison.lean new file mode 100644 index 0000000000..3546413521 --- /dev/null +++ b/LeanPool/CCDN/AuxiliaryPolynomial/HypersurfaceHeightComparison.lean @@ -0,0 +1,81 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.Determinant.HypersurfaceHeightDeterminant +import LeanPool.CCDN.Determinant.HomogeneousDeterminantComparison + +/-! Exact-rank Bombieri--Vaaler comparison for higher-dimensional hypersurfaces. +Absence of an auxiliary polynomial forces the explicit numerical inequality. -/ + +@[expose] public section +namespace CCDN +noncomputable section +open MvPolynomial + +/-- The Hilbert function `s` of a degree-`d` hypersurface in `n + 3` variables, defined by +`s + C(n + 2 + (M - d), M - d) = C(n + 2 + M, M)`, is positive in every degree `M ≥ d`. -/ +lemma hypersurface_homogeneous_rank_pos {n d M s : ℕ} (hd : 0 < d) (hdM : d ≤ M) + (hr : s + (n + 2 + (M - d)).choose (M - d) = (n + 2 + M).choose M) : 0 < s := by + rw [← Nat.choose_symm_add (a := n + 2) (b := M - d), + ← Nat.choose_symm_add (a := n + 2) (b := M)] at hr + have hM : 0 < M := lt_of_lt_of_le hd hdM + have hmon := Nat.choose_le_choose (n + 2) + (show n + 2 + (M - d) ≤ n + 1 + M by omega) + have hpascal : (n + 2 + M).choose (n + 2) = + (n + 1 + M).choose (n + 1) + (n + 1 + M).choose (n + 2) := by + convert Nat.choose_succ_succ' (n + 1 + M) (n + 1) using 1 + congr 1 + omega + have hp : 0 < (n + 1 + M).choose (n + 1) := Nat.choose_pos (by omega) + omega + +/-- With no auxiliary nonmultiple of degree M, the exact homogeneous Hilbert +rank satisfies the archimedean/nonarchimedean determinant comparison. -/ +theorem hypersurface_no_auxiliary_height_comparison + {K : Type*} [Field K] [CharZero K] [IsAlgClosed K] {n d M t : ℕ} + (order : MonomialOrder (Fin (n + 3))) (f : MvPolynomial (Fin (n + 3)) ℤ) + (hf : f.IsHomogeneous d) (hd : 0 < d) (hdM : d ≤ M) + (hir : Irreducible (map (Int.castRingHom K) f)) + (H : ℝ) (hH : 1 ≤ H) (hcoeff : CoefficientHeightLE f H) + {B : ℝ} {z : Fin t → IntPoint (n + 3)} (hzB : IsPrimitiveBoxZeroFamily f B z) + (hrel : ∀ g : MvPolynomial (Fin (n + 3)) ℤ, g.IsHomogeneous M → + (∀ i, eval (z i) g = 0) → f ∣ g) : + ∃ s : ℕ, 0 < s ∧ s + (n + 2 + (M - d)).choose (M - d) = (n + 2 + M).choose M ∧ + ((n + 1).factorial : ℝ) ^ (1 / (n + 1 : ℝ)) / (n + 2 : ℝ) * + (s : ℝ) ^ (1 + 1 / (n + 1 : ℝ)) * + (Real.log s - (n + 1 : ℝ) * (4 * Real.log d + Real.log (1 + Real.log H / (d : ℝ) ^ 2)) - + hypersurfaceHeightDeterminantError n) ≤ + Real.log (s.factorial : ℝ) / 2 + (s : ℝ) / 2 * Real.log ((n + 2 + M).choose M) + + s * M * Real.log B - (((n + 2 + M).choose M : ℝ) - s) * + Real.log |((order.leadingCoeff f : ℤ) : ℝ)| := by + have hf0 : f ≠ 0 := by intro h; exact hir.ne_zero (by rw [h, map_zero]) + obtain ⟨s, a, _, _, hr, hi, hupper⟩ := + homogeneous_no_auxiliary_box_comparison order f hf hf0 hdM z hzB.eval_eq_zero + hzB.one_le_radius hzB.abs_le hrel + have hs := hypersurface_homogeneous_rank_pos hd hdM hr + refine ⟨s, hs, hr, ?_⟩ + let F := fun j => (monomial (homogeneousMonomials (n + 2) M j) 1 : MvPolynomial (Fin (n + 3)) ℤ) + have hF : ∀ j, (F j).IsHomogeneous M := fun j => + isHomogeneous_monomial _ (homogeneousMonomials_degree (n + 2) M j) + have he : (Matrix.of fun i j => eval ((z ∘ a) i) (F j)) = + evaluationMatrix (homogeneousMonomials (n + 2) M) (z ∘ a) := by + ext i j + simp [F, evaluationMatrix, eval_monomial] + have hi' : LinearIndependent ℚ + ((Matrix.of fun i j => eval ((z ∘ a) i) (F j)).map ((↑) : ℤ → ℚ)).row := by + rw [he] + exact hi + have hlower := hypersurface_determinant_log_lower_height (z := z ∘ a) hs f hf hd hir H hH + hcoeff ⟨hF, fun i => hzB.primitive (a i), fun i => hzB.eval_eq_zero (a i), hi'⟩ + rw [he] at hlower + exact hlower.trans hupper + +end +end CCDN diff --git a/LeanPool/CCDN/AuxiliaryPolynomial/HypersurfaceHeightTransfer.lean b/LeanPool/CCDN/AuxiliaryPolynomial/HypersurfaceHeightTransfer.lean new file mode 100644 index 0000000000..c0219e92fc --- /dev/null +++ b/LeanPool/CCDN/AuxiliaryPolynomial/HypersurfaceHeightTransfer.lean @@ -0,0 +1,135 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.AuxiliaryPolynomial.HypersurfaceBadPrimeFactor + +/-! +CCDN Lemma 4.2.3: replace the full coefficient height by any lower height +in the bad-prime contribution, uniformly in the degree. + +Uniform comparison of logarithmic coefficient-height ratios. +-/ + +@[expose] public section +namespace CCDN + +private lemma weighted_exp_ratio_le {u v a r : ℝ} (hu : 0 ≤ u) (huv : u ≤ v) + (ha : 0 < a) (hr : 1 ≤ r) (hra : 1 ≤ r * a) : + (v + a) / Real.exp v ≤ r * ((u + a) / Real.exp u) := by + have ht : 0 ≤ v - u := sub_nonneg.mpr huv + have hru : 1 ≤ r * (u + a) := by nlinarith + have he : 1 + (v - u) ≤ Real.exp (v - u) := by + simpa only [add_comm] using Real.add_one_le_exp (v - u) + have hh : v + a ≤ r * (u + a) * Real.exp (v - u) := by + have h1 := mul_nonneg (sub_nonneg.mpr hr) (show 0 ≤ u + a by linarith) + have h2 := mul_nonneg (sub_nonneg.mpr hru) ht + have h3 := mul_le_mul_of_nonneg_left he (show 0 ≤ r * (u + a) by positivity) + nlinarith + rw [← mul_div_assoc] + apply (div_le_div_iff₀ (Real.exp_pos v) (Real.exp_pos u)).mpr + have hh' := mul_le_mul_of_nonneg_right hh (Real.exp_pos u).le + rw [mul_assoc, ← Real.exp_add, sub_add_cancel] at hh' + exact hh' + +/-- Replace an upper height by any lower height with a dimension-only loss. -/ +private theorem logarithmic_height_ratio_le {H Y q c r : ℝ} + (hY : 1 ≤ Y) (hYH : Y ≤ H) (hq : 0 < q) (hc : 0 < c) + (hr : 1 ≤ r) (hrqc : 1 ≤ r * (q * c)) : + (Real.log H + c) / H ^ q ≤ r * ((Real.log Y + c) / Y ^ q) := by + have hY0 : 0 < Y := by linarith + have hH0 : 0 < H := by linarith + have hu : 0 ≤ q * Real.log Y := mul_nonneg hq.le (Real.log_nonneg hY) + have huv : q * Real.log Y ≤ q * Real.log H := + mul_le_mul_of_nonneg_left (Real.log_le_log hY0 hYH) hq.le + have hh := weighted_exp_ratio_le hu huv (mul_pos hq hc) hr hrqc + rw [Real.rpow_def_of_pos hH0, Real.rpow_def_of_pos hY0] + apply (mul_le_mul_iff_right₀ hq).mp + rw [mul_comm (Real.log H) q, mul_comm (Real.log Y) q] + convert hh using 1 <;> ring + +end CCDN + +namespace CCDN +noncomputable section + +private lemma dimension_root_le_degree {r d : ℕ} (hr : 0 < r) (hd : 0 < d) : + (d : ℝ) ^ (1 / (r : ℝ)) ≤ d := by + have hd1 : (1 : ℝ) ≤ d := by exact_mod_cast hd + have hr1 : (1 : ℝ) ≤ r := by exact_mod_cast hr + have he : 1 / (r : ℝ) ≤ 1 := (div_le_one (by linarith)).mpr hr1 + simpa only [Real.rpow_one] using Real.rpow_le_rpow_of_exponent_le hd1 he + +/-- The coefficient-weighted ratio has loss at most r in dimension r. -/ +theorem hypersurface_logarithmic_height_transfer {r d : ℕ} (hr : 0 < r) (hd : 0 < d) + {H Y : ℝ} (hY : 1 ≤ Y) (hYH : Y ≤ H) : + (Real.log H + (d : ℝ) ^ 2) / H ^ (1 / ((r : ℝ) * d * (d : ℝ) ^ (1 / (r : ℝ)))) ≤ + r * ((Real.log Y + (d : ℝ) ^ 2) / Y ^ (1 / ((r : ℝ) * d * (d : ℝ) ^ (1 / (r : ℝ))))) := by + have hr0 : (0 : ℝ) < r := by exact_mod_cast hr + have hr1 : (1 : ℝ) ≤ r := by exact_mod_cast hr + have hd0 : (0 : ℝ) < d := by exact_mod_cast hd + have hroot : (0 : ℝ) < (d : ℝ) ^ (1 / (r : ℝ)) := by positivity + apply logarithmic_height_ratio_le hY hYH (by positivity) (by positivity) hr1 + have he : (r : ℝ) * (1 / ((r : ℝ) * d * (d : ℝ) ^ (1 / (r : ℝ))) * (d : ℝ) ^ 2) = + (d : ℝ) / (d : ℝ) ^ (1 / (r : ℝ)) := by field_simp + rw [he] + exact (one_le_div hroot).mpr (dimension_root_le_degree hr hd) + +/-- The exponent bookkeeping `d ^ (4 - 1 / (n + 1)) = d ^ (2 - 1 / (n + 1)) * d ^ 2` used by the +affine minimum and height-transfer estimates. -/ +lemma rpow_four_sub_eq_mul_sq {d : ℝ} (hd : 0 < d) (n : ℕ) : + d ^ (4 - 1 / (n + 1 : ℝ)) = d ^ (2 - 1 / (n + 1 : ℝ)) * d ^ 2 := by + rw [← Real.rpow_natCast d 2, ← Real.rpow_add hd] + congr 1 + norm_num + ring + +/-- CCDN Lemma 4.2.3 with an explicit dimension constant. The coefficient +height is the maximum, and the bad-prime factor is the defined factor. -/ +theorem hypersurface_badPrime_height_transfer {n d : ℕ} + (f : MvPolynomial (Fin (n + 3)) ℤ) (hf : f ≠ 0) + (hdegree : f.totalDegree = d) (hd : 0 < d) + {Y : ℝ} (hY : 1 ≤ Y) (hYH : Y ≤ (integerCoefficientHeight f : ℝ)) : + (d : ℝ) ^ (4 - 1 / (n + 1 : ℝ)) * hypersurfaceBadPrimeFactor f / + (integerCoefficientHeight f : ℝ) ^ (1 / ((n + 1 : ℝ) * d * (d : ℝ) ^ (1 / (n + 1 : ℝ)))) ≤ + ((n + 1 : ℝ) * Real.exp (2 * mertensError + 1) * hypersurfaceHeightConstant (n + 3)) * + (d : ℝ) ^ (2 - 1 / (n + 1 : ℝ)) * (Real.log Y + (d : ℝ) ^ 2) / + Y ^ (1 / ((n + 1 : ℝ) * d * (d : ℝ) ^ (1 / (n + 1 : ℝ)))) := by + have hd0 : (0 : ℝ) < d := by exact_mod_cast hd + let H := (integerCoefficientHeight f : ℝ) + have hH : 1 ≤ H := by dsimp only [H]; exact_mod_cast integerCoefficientHeight_pos f hf + let q := 1 / ((n + 1 : ℝ) * d * (d : ℝ) ^ (1 / (n + 1 : ℝ))) + let C := Real.exp (2 * mertensError + 1) * hypersurfaceHeightConstant (n + 3) + have hC : 0 ≤ C := mul_nonneg (Real.exp_pos _).le + (zero_le_one.trans (hypersurfaceHeightConstant_one_le _)) + have hbad : hypersurfaceBadPrimeFactor f ≤ C * (1 + Real.log H / (d : ℝ) ^ 2) := by + simpa only [hdegree] using hypersurfaceBadPrimeFactor_le_height f H hH + (coefficient_abs_le_integerCoefficientHeight f) + have hratio : (Real.log H + (d : ℝ) ^ 2) / H ^ q ≤ + (n + 1 : ℝ) * ((Real.log Y + (d : ℝ) ^ 2) / Y ^ q) := by + simpa only [Nat.cast_add, Nat.cast_one] using + hypersurface_logarithmic_height_transfer (r := n + 1) (by omega) hd hY hYH + have hpowers : (d : ℝ) ^ (4 - 1 / (n + 1 : ℝ)) = + (d : ℝ) ^ (2 - 1 / (n + 1 : ℝ)) * (d : ℝ) ^ 2 := + rpow_four_sub_eq_mul_sq hd0 n + have hstep : (d : ℝ) ^ (4 - 1 / (n + 1 : ℝ)) * hypersurfaceBadPrimeFactor f / H ^ q ≤ + C * (d : ℝ) ^ (2 - 1 / (n + 1 : ℝ)) * ((Real.log H + (d : ℝ) ^ 2) / H ^ q) := by + calc + _ ≤ (d : ℝ) ^ (4 - 1 / (n + 1 : ℝ)) * (C * (1 + Real.log H / (d : ℝ) ^ 2)) / H ^ q := + div_le_div_of_nonneg_right (mul_le_mul_of_nonneg_left hbad (by positivity)) (by positivity) + _ = _ := by rw [hpowers]; field_simp; ring + have hlast := mul_le_mul_of_nonneg_left hratio + (show 0 ≤ C * (d : ℝ) ^ (2 - 1 / (n + 1 : ℝ)) by positivity) + apply hstep.trans + convert hlast using 1 + dsimp only [C] + ring + +end +end CCDN diff --git a/LeanPool/CCDN/AuxiliaryPolynomial/HypersurfaceKernelRatio.lean b/LeanPool/CCDN/AuxiliaryPolynomial/HypersurfaceKernelRatio.lean new file mode 100644 index 0000000000..50c3e7d27e --- /dev/null +++ b/LeanPool/CCDN/AuxiliaryPolynomial/HypersurfaceKernelRatio.lean @@ -0,0 +1,88 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import Mathlib.Basic.Real.Basic +public import Mathlib.Data.Nat.Choose.Basic +import LeanPool.CCDN.AuxiliaryPolynomial.HypersurfaceRankProfile +import Mathlib.Tactic.FieldSimp +import Mathlib.Tactic.Linarith +import Mathlib.Tactic.Positivity + +/-! The homogeneous kernel-to-rank ratio differs from its leading term by +at most 1/M. Exact binomial identities avoid a formal asymptotic expansion. -/ + +@[expose] public section +namespace CCDN +noncomputable section + +private lemma shifted_choose_successor_identity (r k : ℕ) : + (r + k + 1) * (r + k).choose r = (r + 1 + k).choose (r + 1) * (r + 1) := by + rw [show r + 1 + k = r + k + 1 by omega] + exact Nat.add_one_mul_choose_eq (r + k) r + +/-- Sharp leading term for the fraction of polynomial coefficients in the +homogeneous kernel, normalized by degree and rank. -/ +theorem hypersurface_kernel_ratio_bounds {r d M s : ℕ} (hd : 0 < d) (hdM : d ≤ M) + (hr : s + (r + 1 + (M - d)).choose (M - d) = (r + 1 + M).choose M) : + 1 / ((d : ℝ) * (r + 1 : ℝ)) - 1 / (M : ℝ) ≤ + ((r + 1 + (M - d)).choose (M - d) : ℝ) / ((M : ℝ) * s) ∧ + ((r + 1 + (M - d)).choose (M - d) : ℝ) / ((M : ℝ) * s) ≤ + 1 / ((d : ℝ) * (r + 1 : ℝ)) + 1 / (M : ℝ) := by + obtain ⟨hlo, hhi⟩ := hypersurface_rank_binomial_bounds hdM hr + have hs : 0 < s := lt_of_lt_of_le + (Nat.mul_pos hd (Nat.choose_pos (by omega : r ≤ r + (M - d)))) hlo + have hd0 : (0 : ℝ) < d := by exact_mod_cast hd + have hs0 : (0 : ℝ) < s := by exact_mod_cast hs + have hM0 : (0 : ℝ) < M := by exact_mod_cast lt_of_lt_of_le hd hdM + have hd1 : (1 : ℝ) ≤ d := by exact_mod_cast hd + have hr0 : (0 : ℝ) < r + 1 := by positivity + have hmd : (d : ℝ) ≤ M := by exact_mod_cast hdM + let K : ℝ := (r + 1 + (M - d)).choose (M - d) + let Qlo : ℝ := (r + (M - d)).choose r + let Qhi : ℝ := (r + M).choose r + have hlow : (d : ℝ) * Qlo ≤ s := by + dsimp only [Qlo] + exact_mod_cast hlo + have hhigh : (s : ℝ) ≤ (d : ℝ) * Qhi := by + dsimp only [Qhi] + exact_mod_cast hhi + have hK : ((r : ℝ) + (M - d) + 1) * Qlo = K * (r + 1) := by + have hh := shifted_choose_successor_identity r (M - d) + rw [Nat.choose_symm_add (a := r + 1) (b := M - d)] at hh + have hhR := congrArg (fun x : ℕ => (x : ℝ)) hh + push_cast [Nat.cast_sub hdM] at hhR + exact hhR + have hR : ((r : ℝ) + M + 1) * Qhi = ((s : ℝ) + K) * (r + 1) := by + have hh := shifted_choose_successor_identity r M + rw [Nat.choose_symm_add (a := r + 1) (b := M), ← hr] at hh + have hhR := congrArg (fun x : ℕ => (x : ℝ)) hh + push_cast [Nat.cast_sub hdM] at hhR + exact hhR + have hKmul : (d : ℝ) * (r + 1) * K ≤ ((M : ℝ) + r + 1) * s := by + have hh := mul_le_mul_of_nonneg_left hlow + (show 0 ≤ (r : ℝ) + (M - d) + 1 by have := Nat.cast_nonneg (α := ℝ) r; linarith) + nlinarith [mul_nonneg hd0.le hs0.le] + have hRmul : ((M : ℝ) + r + 1) * s ≤ (d : ℝ) * (r + 1) * ((s : ℝ) + K) := by + have hh := mul_le_mul_of_nonneg_left hhigh (show 0 ≤ (r : ℝ) + M + 1 by positivity) + nlinarith + change 1 / ((d : ℝ) * (r + 1 : ℝ)) - 1 / (M : ℝ) ≤ K / ((M : ℝ) * s) ∧ + K / ((M : ℝ) * s) ≤ 1 / ((d : ℝ) * (r + 1 : ℝ)) + 1 / (M : ℝ) + constructor + · apply (le_div_iff₀ (mul_pos hM0 hs0)).mpr + field_simp + nlinarith [mul_nonneg hr0.le hs0.le] + · apply (div_le_iff₀ (mul_pos hM0 hs0)).mpr + field_simp + have hh : ((r : ℝ) + 1) * s ≤ (d : ℝ) * (r + 1) * s := by + nlinarith [mul_le_mul_of_nonneg_right hd1 (mul_nonneg hr0.le hs0.le)] + nlinarith + +end +end CCDN diff --git a/LeanPool/CCDN/AuxiliaryPolynomial/HypersurfaceLargeHeightAuxiliary.lean b/LeanPool/CCDN/AuxiliaryPolynomial/HypersurfaceLargeHeightAuxiliary.lean new file mode 100644 index 0000000000..fc7a4a35ca --- /dev/null +++ b/LeanPool/CCDN/AuxiliaryPolynomial/HypersurfaceLargeHeightAuxiliary.lean @@ -0,0 +1,100 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.AuxiliaryPolynomial.HypersurfaceLargeHeightDegree +public import LeanPool.CCDN.AuxiliaryPolynomial.HypersurfaceNormalizedComparison +public import LeanPool.CCDN.AuxiliaryPolynomial.Nonmultiple +import LeanPool.CCDN.AuxiliaryPolynomial.PolynomialMapAuxiliary + +/-! Auxiliary polynomials in the large-height range. The degree is +O_{n,T}(d^(4-1/(n+1))); the leading-coefficient positioning cost is explicit. -/ + +@[expose] public section +namespace CCDN +noncomputable section +open MvPolynomial + +private def hypersurfaceLargeThreshold (n d : ℕ) (T : ℝ) : ℝ := + Real.exp (hypersurfaceLargeHeightError (n + 1) (hypersurfaceNormalizedError n) T) * + (d : ℝ) ^ (4 - 1 / (n + 1 : ℝ)) + +/-- In the large-height range, an integer degree above the explicit threshold +gives a homogeneous auxiliary nonmultiple. -/ +private theorem hypersurface_exists_auxiliary_large_height + {K : Type*} [Field K] [CharZero K] [IsAlgClosed K] {n d M t : ℕ} + (order : MonomialOrder (Fin (n + 3))) (f : MvPolynomial (Fin (n + 3)) ℤ) + (hf : f.IsHomogeneous d) (hd : 0 < d) (hM : 8 * d * (n + 2) ≤ M) + (hir : Irreducible (map (Int.castRingHom K) f)) + (H : ℝ) (hH : 1 ≤ H) (hcoeff : CoefficientHeightLE f H) + {B : ℝ} {z : Fin t → IntPoint (n + 3)} (hzB : IsPrimitiveBoxZeroFamily f B z) + (T : ℝ) (hT : 0 ≤ T) + (hheight : 2 * d * (n + 2 : ℝ) * Real.log B ≤ Real.log H) + (hposition : Real.log H ≤ Real.log |((order.leadingCoeff f : ℤ) : ℝ)| + + T * d * degreeRoot n d) + (hthreshold : hypersurfaceLargeThreshold n d T < M) : + ∃ g, IsPointAuxiliary f g M z := by + classical + have hf0 : f ≠ 0 := ne_zero_of_irreducible_map hir + have hdM : d ≤ M := by nlinarith + by_contra hnone + obtain ⟨s, _, hr, hc⟩ := hypersurface_no_auxiliary_normalized_comparison order f hf hd hdM hir + H hH hcoeff hzB (dvd_of_not_exists_isPointAuxiliary hnone) + have hB := hzB.one_le_radius + have hL : 1 ≤ |((order.leadingCoeff f : ℤ) : ℝ)| := by + exact_mod_cast Int.one_le_abs (order.leadingCoeff_ne_zero_iff.mpr hf0) + have hnR : (n : ℝ) + 1 + 1 = n + 2 := by ring + have hlog := hypersurface_large_height_log_degree_bound (r := n + 1) (by omega) hd hM hr + B |((order.leadingCoeff f : ℤ) : ℝ)| H (hypersurfaceNormalizedError n) T hB hL hH hT + (by simpa only [Nat.cast_add, Nat.cast_one, hnR] using hheight) + (by simpa only [Nat.cast_add, Nat.cast_one] using hposition) + (by simpa only [Nat.cast_add, Nat.cast_one, hnR] using hc) + have hb := hypersurface_large_degree_bound_of_log_bound hd (lt_of_lt_of_le hd hdM) hlog + have hb' : (M : ℝ) ≤ hypersurfaceLargeThreshold n d T := by + simpa only [hypersurfaceLargeThreshold, Nat.cast_add, Nat.cast_one] using hb + exact (not_lt_of_ge hb') hthreshold + +/-- Select a bounded integer degree in the large-height range. The bound is +independent of the box radius and the equation's coefficient height. -/ +theorem hypersurface_auxiliary_large_height_degree + {K : Type*} [Field K] [CharZero K] [IsAlgClosed K] {n d t : ℕ} + (order : MonomialOrder (Fin (n + 3))) (f : MvPolynomial (Fin (n + 3)) ℤ) + (hf : f.IsHomogeneous d) (hd : 0 < d) + (hir : Irreducible (map (Int.castRingHom K) f)) + (H : ℝ) (hH : 1 ≤ H) (hcoeff : CoefficientHeightLE f H) + (z : Fin t → IntPoint (n + 3)) (hprim : ∀ i, Finset.univ.gcd (z i) = 1) + (hz : ∀ i, eval (z i) f = 0) + {B : ℝ} (hB : 1 ≤ B) (hbox : ∀ i, InBox (z i) B) + (T : ℝ) (hT : 0 ≤ T) + (hheight : 2 * d * (n + 2 : ℝ) * Real.log B ≤ Real.log H) + (hposition : Real.log H ≤ Real.log |((order.leadingCoeff f : ℤ) : ℝ)| + + T * d * degreeRoot n d) : + ∃ M : ℕ, (M : ℝ) ≤ + (8 * (n + 2 : ℝ) + + Real.exp (hypersurfaceLargeHeightError (n + 1) (hypersurfaceNormalizedError n) T) + 2) * + (d : ℝ) ^ (4 - 1 / (n + 1 : ℝ)) ∧ + ∃ g : MvPolynomial (Fin (n + 3)) ℤ, g ≠ 0 ∧ g.IsHomogeneous M ∧ + (∀ i, eval (z i) g = 0) ∧ ¬ f ∣ g := by + have hdR : (0 : ℝ) < d := by exact_mod_cast hd + have hd1 : (1 : ℝ) ≤ d := by exact_mod_cast hd + have hR : 0 ≤ hypersurfaceLargeThreshold n d T := by unfold hypersurfaceLargeThreshold; positivity + obtain ⟨M, hMlarge, _, hthreshold, hbound⟩ := auxiliary_integer_degree_selection (8 * d * (n + 2)) + (show (0 : ℝ) ≤ 0 from le_rfl) hR + have hdPow : (d : ℝ) ≤ (d : ℝ) ^ (4 - 1 / (n + 1 : ℝ)) := le_rpow_four_sub_inv hd + have hPow1 := hd1.trans hdPow + obtain ⟨g, hg⟩ := hypersurface_exists_auxiliary_large_height order f hf hd hMlarge hir H hH + hcoeff ⟨⟨hB, hz, hbox⟩, hprim⟩ T hT hheight hposition hthreshold + refine ⟨M, ?_, g, isPointAuxiliary_iff.mp hg⟩ + unfold hypersurfaceLargeThreshold at hbound + push_cast at hbound + have hh := mul_le_mul_of_nonneg_left hdPow (show 0 ≤ 8 * (n + 2 : ℝ) by positivity) + nlinarith + +end +end CCDN diff --git a/LeanPool/CCDN/AuxiliaryPolynomial/HypersurfaceLargeHeightDegree.lean b/LeanPool/CCDN/AuxiliaryPolynomial/HypersurfaceLargeHeightDegree.lean new file mode 100644 index 0000000000..5b1108c8e1 --- /dev/null +++ b/LeanPool/CCDN/AuxiliaryPolynomial/HypersurfaceLargeHeightDegree.lean @@ -0,0 +1,103 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.AuxiliaryPolynomial.HypersurfaceRankLogarithm +public import Mathlib.Analysis.SpecialFunctions.Pow.Real +import LeanPool.CCDN.AuxiliaryPolynomial.HypersurfaceKernelRatio +import LeanPool.CCDN.AuxiliaryPolynomial.HypersurfaceRankRoots +import LeanPool.CCDN.AuxiliaryPolynomial.LargeHeightRatios +import LeanPool.CCDN.ForMathlib.Log + +/-! The large-height auxiliary-degree calculation eliminates both the radius +and coefficient height, leaving O_r(d^(4-1/r)). -/ + +@[expose] public section +namespace CCDN +noncomputable section + +/-- The additive error term, depending only on the dimension `r` and the constants `E` and `T`, +in the large-height bound `log M ≤ hypersurfaceLargeHeightError r E T + (4 - 1/r) log d`. -/ +def hypersurfaceLargeHeightError (r : ℕ) (E T : ℝ) : ℝ := + Real.log (8 * (r : ℝ)) + (E + hypersurfaceRankLogError r + T) / r + +/-- The large-height normalized determinant inequality gives a degree bound +independent of B and H; positioning is stated with its precise height cost. -/ +theorem hypersurface_large_height_log_degree_bound {r d M s : ℕ} + (hrpos : 0 < r) (hd : 0 < d) (hM : 8 * d * (r + 1) ≤ M) + (hr : s + (r + 1 + (M - d)).choose (M - d) = (r + 1 + M).choose M) + (B L H E T : ℝ) (hB : 1 ≤ B) (hL : 1 ≤ L) (hH : 1 ≤ H) (hT : 0 ≤ T) + (hheight : 2 * d * (r + 1 : ℝ) * Real.log B ≤ Real.log H) + (hposition : Real.log H ≤ Real.log L + T * d * (d : ℝ) ^ (1 / (r : ℝ))) + (hcomparison : (r.factorial : ℝ) ^ (1 / (r : ℝ)) * (s : ℝ) ^ (1 / (r : ℝ)) / + ((r + 1 : ℝ) * M) * + (Real.log s - (r : ℝ) * (4 * Real.log d + Real.log (1 + Real.log H / (d : ℝ) ^ 2)) - E) ≤ + Real.log B - ((r + 1 + (M - d)).choose (M - d) : ℝ) / ((M : ℝ) * s) * Real.log L) : + Real.log M ≤ hypersurfaceLargeHeightError r E T + (4 - 1 / (r : ℝ)) * Real.log d := by + have hM2 : 2 * d ≤ M := by nlinarith only [hM, Nat.zero_le (d * r)] + have hdM : d ≤ M := by omega + have hd0 : (0 : ℝ) < d := by exact_mod_cast hd + have hd1 : (1 : ℝ) ≤ d := by exact_mod_cast hd + have hr0 : (0 : ℝ) < r := by exact_mod_cast hrpos + have hr1 : (1 : ℝ) ≤ r := by exact_mod_cast hrpos + have hM0 : (0 : ℝ) < M := by exact_mod_cast lt_of_lt_of_le hd hdM + have hX : (0 : ℝ) < (d : ℝ) ^ (1 / (r : ℝ)) := by positivity + obtain ⟨hl, hu⟩ := hypersurface_rank_root_bounds hrpos hdM hr + have halow : (d : ℝ) ^ (1 / (r : ℝ)) * ((M : ℝ) - d) / ((r + 1 : ℝ) * M) ≤ + (r.factorial : ℝ) ^ (1 / (r : ℝ)) * (s : ℝ) ^ (1 / (r : ℝ)) / ((r + 1 : ℝ) * M) := by + apply div_le_div_of_nonneg_right _ (by positivity) + nlinarith only [hl, hX] + have haup := div_le_div_of_nonneg_right hu (show 0 ≤ (r + 1 : ℝ) * M by positivity) + have hk := (hypersurface_kernel_ratio_bounds hd hdM hr).1 + have hh := auxiliary_large_height_bracket_bound (r := (r : ℝ)) (Nat.cast_nonneg r) + hd1 (by exact_mod_cast hM) hX halow haup (by positivity) hk + (Real.log_nonneg hB) (Real.log_nonneg hL) hT hheight hposition hcomparison + have hlog := (abs_le.mp (hypersurface_rank_log_error hrpos hd hM2 hr)).1 + have hXle : (d : ℝ) ^ (1 / (r : ℝ)) ≤ d := by + calc + _ ≤ (d : ℝ) ^ (1 : ℝ) := Real.rpow_le_rpow_of_exponent_le hd1 ((div_le_one hr0).mpr hr1) + _ = _ := Real.rpow_one _ + have hHlog := Real.log_nonneg hH + have hcost := log_one_add_le_linear_add_log + (div_nonneg hHlog (sq_nonneg (d : ℝ))) (show 1 ≤ 8 * (r : ℝ) by linarith) + have hcost' := mul_le_mul_of_nonneg_left hcost hr0.le + have he : (r : ℝ) * (Real.log H / (d : ℝ) ^ 2 / (8 * (r : ℝ))) = + Real.log H / (8 * (d : ℝ) ^ 2) := by field_simp + have hcancel : (r : ℝ) * Real.log (1 + Real.log H / (d : ℝ) ^ 2) - + Real.log H / (8 * d * (d : ℝ) ^ (1 / (r : ℝ))) ≤ r * Real.log (8 * (r : ℝ)) := by + have hden : 8 * (d : ℝ) * (d : ℝ) ^ (1 / (r : ℝ)) ≤ 8 * (d : ℝ) ^ 2 := by + simpa only [pow_two, mul_assoc] using + mul_le_mul_of_nonneg_left hXle (show 0 ≤ 8 * (d : ℝ) by positivity) + have hdiv := div_le_div_of_nonneg_left hHlog + (show 0 < 8 * d * (d : ℝ) ^ (1 / (r : ℝ)) by positivity) hden + rw [mul_add, he] at hcost' + linarith only [hcost', hdiv] + have hraw : (r : ℝ) * Real.log M ≤ (4 * (r : ℝ) - 1) * Real.log d + + r * Real.log (8 * (r : ℝ)) + E + hypersurfaceRankLogError r + T := by + linarith only [hh, hlog, hcancel] + have hdiv := div_le_div_of_nonneg_right hraw hr0.le + rw [mul_div_cancel_left₀ (Real.log M) hr0.ne'] at hdiv + convert hdiv using 1 + unfold hypersurfaceLargeHeightError + field_simp + ring + +/-- Exponentiating a logarithmic degree bound: if `log M ≤ C + (4 - 1/r) log d` with `d` and `M` +positive, then `M ≤ exp C * d ^ (4 - 1/r)`. -/ +lemma hypersurface_large_degree_bound_of_log_bound {r d M : ℕ} {C : ℝ} + (hd : 0 < d) (hM : 0 < M) + (hlog : Real.log M ≤ C + (4 - 1 / (r : ℝ)) * Real.log d) : + (M : ℝ) ≤ Real.exp C * (d : ℝ) ^ (4 - 1 / (r : ℝ)) := by + have hdR : (0 : ℝ) < d := by exact_mod_cast hd + refine Real.natCast_le_of_log_le_log hM (by positivity) ?_ + rw [Real.log_mul (Real.exp_pos _).ne' (by positivity), Real.log_exp, Real.log_rpow hdR] + exact hlog + +end +end CCDN diff --git a/LeanPool/CCDN/AuxiliaryPolynomial/HypersurfaceNonabsoluteAuxiliary.lean b/LeanPool/CCDN/AuxiliaryPolynomial/HypersurfaceNonabsoluteAuxiliary.lean new file mode 100644 index 0000000000..3c7e4396a0 --- /dev/null +++ b/LeanPool/CCDN/AuxiliaryPolynomial/HypersurfaceNonabsoluteAuxiliary.lean @@ -0,0 +1,110 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.Height.ProjectiveZeroLocus +import LeanPool.CCDN.Algebra.HomogeneousLinearDescent +import LeanPool.CCDN.Algebra.PrimitiveNormalization +import LeanPool.CCDN.ForMathlib.MvPolynomial +import Mathlib.Algebra.MvPolynomial.Nilpotent +import Mathlib.Algebra.MvPolynomial.NoZeroDivisors + +/-! A degree-d auxiliary equation for nonabsolute irreducible hypersurfaces. +Linear descent of the two homogeneous factors suffices over any extension. -/ + +@[expose] public section +namespace CCDN +noncomputable section +open MvPolynomial + +/-- Over an arbitrary field extension, descent of a proper factorization +produces a degree-d nonmultiple vanishing at all base-field zeros. -/ +private theorem nonabsolute_homogeneous_auxiliary {k K : Type*} + [Field k] [Field K] [Algebra k K] {n d : ℕ} + (f : MvPolynomial (Fin n) k) (hf : f.IsHomogeneous d) (hd : 0 < d) + (hir : Irreducible f) (hred : ¬ Irreducible (map (algebraMap k K) f)) : + ∃ q : MvPolynomial (Fin n) k, q ≠ 0 ∧ q.IsHomogeneous d ∧ ¬ f ∣ q ∧ + ∀ x : Fin n → k, eval x f = 0 → eval x q = 0 := by + have hmap0 : map (algebraMap k K) f ≠ 0 := by + exact fun he => hir.ne_zero ((map_injective (algebraMap k K) (algebraMap k K).injective) + (by simpa using he)) + have hmapu : ¬ IsUnit (map (algebraMap k K) f) := by + intro hu + have he := (MvPolynomial.isUnit_iff_totalDegree_of_isReduced.mp hu).2 + rw [(hf.map _).totalDegree hmap0] at he + omega + obtain ⟨g, h, hgu, hhu, hfac⟩ := (irreducible_or_factor hmapu).resolve_left hred + have hg0 : g ≠ 0 := fun he => hmap0 (by simp [hfac, he]) + have hh0 : h ≠ 0 := fun he => hmap0 (by simp [hfac, he]) + have hgd := totalDegree_pos_of_ne_zero_of_not_isUnit hg0 hgu + have hhd := totalDegree_pos_of_ne_zero_of_not_isUnit hh0 hhu + obtain ⟨hdegrees, htop⟩ := homogeneous_factorization_top (hf.map _) hmap0 hg0 hh0 hfac + obtain ⟨g₀, hg₀, hg₀hom, hgz⟩ := exists_nonzero_homogeneous_linear_descent (k := k) + (homogeneousComponent g.totalDegree g) (homogeneousComponent_totalDegree_ne_zero hg0) + (homogeneousComponent_isHomogeneous _ _) + obtain ⟨h₀, hh₀, hh₀hom, hhz⟩ := exists_nonzero_homogeneous_linear_descent (k := k) + (homogeneousComponent h.totalDegree h) (homogeneousComponent_totalDegree_ne_zero hh0) + (homogeneousComponent_isHomogeneous _ _) + refine ⟨g₀ * h₀, mul_ne_zero hg₀ hh₀, hdegrees ▸ hg₀hom.mul hh₀hom, ?_, ?_⟩ + · intro hdiv + rcases hir.prime.dvd_or_dvd hdiv with hgdiv | hhdiv + · have he := totalDegree_le_of_dvd_of_isDomain hgdiv hg₀ + rw [hf.totalDegree hir.ne_zero, hg₀hom.totalDegree hg₀] at he + omega + · have he := totalDegree_le_of_dvd_of_isDomain hhdiv hh₀ + rw [hf.totalDegree hir.ne_zero, hh₀hom.totalDegree hh₀] at he + omega + · intro x hx + have he := congrArg (algebraMap k K) hx + rw [map_eval, htop, eval_mul, map_zero] at he + rw [eval_mul] + rcases mul_eq_zero.mp he with hgzero | hhzero + · rw [hgz x hgzero, zero_mul] + · rw [hhz x hhzero, mul_zero] + +/-- Integer denominator clearing preserves the exact degree and all rational +zeros. Irreducibility is stated over Q, and splitting may occur over any extension. -/ +theorem integer_nonabsolute_homogeneous_auxiliary {K : Type*} + [Field K] [Algebra ℚ K] {n d : ℕ} + (f : MvPolynomial (Fin n) ℤ) (hf : f.IsHomogeneous d) (hd : 0 < d) + (hir : Irreducible (map (Int.castRingHom ℚ) f)) + (hred : ¬ Irreducible (map (algebraMap ℚ K) (map (Int.castRingHom ℚ) f))) : + ∃ g : MvPolynomial (Fin n) ℤ, g ≠ 0 ∧ g.IsHomogeneous d ∧ ¬ f ∣ g ∧ + ∀ x : Fin n → ℚ, eval x (map (Int.castRingHom ℚ) f) = 0 → + eval x (map (Int.castRingHom ℚ) g) = 0 := by + obtain ⟨q, hq0, hqh, hqnd, hqzero⟩ := nonabsolute_homogeneous_auxiliary + (map (Int.castRingHom ℚ) f) (hf.map _) hd hir hred + obtain ⟨g, a, _, ha, he⟩ := exists_primitive_integer_model q hq0 + have hg0 : g ≠ 0 := by + intro hz + have hh := mul_ne_zero (C_eq_zero.not.mpr ha) hq0 + apply hh + simpa only [hz, map_zero] using he.symm + refine ⟨g, hg0, rational_scalar_model_homogeneous he hqh, ?_, ?_⟩ + · intro hdiv + apply hqnd + have hh := dvd_mul_of_dvd_right (map_dvd (map (Int.castRingHom ℚ)) hdiv) (C a⁻¹) + simpa only [he, ← mul_assoc, ← C_mul, inv_mul_cancel₀ ha, C_1, one_mul] using hh + · intro x hx + exact (rational_scalar_model_zero_iff ha he x).mpr (hqzero x hx) + +/-- CCDN Lemma 3.3.1 for all rational projective zeros, independently of height. -/ +theorem nonabsolute_projective_auxiliary {K : Type*} + [Field K] [Algebra ℚ K] {n d : ℕ} + (f : MvPolynomial (Fin (n + 1)) ℤ) (hf : f.IsHomogeneous d) (hd : 0 < d) + (hir : Irreducible (map (Int.castRingHom ℚ) f)) + (hred : ¬ Irreducible (map (algebraMap ℚ K) (map (Int.castRingHom ℚ) f))) : + ∃ g : MvPolynomial (Fin (n + 1)) ℤ, g ≠ 0 ∧ g.IsHomogeneous d ∧ ¬ f ∣ g ∧ + projectiveZeroLocus (map (Int.castRingHom ℚ) f) ⊆ + projectiveZeroLocus (map (Int.castRingHom ℚ) g) := by + obtain ⟨g, hg0, hgh, hgnd, hz⟩ := integer_nonabsolute_homogeneous_auxiliary f hf hd hir hred + exact ⟨g, hg0, hgh, hgnd, fun x hx => hz x.rep hx⟩ + +end +end CCDN diff --git a/LeanPool/CCDN/AuxiliaryPolynomial/HypersurfaceNormalizedComparison.lean b/LeanPool/CCDN/AuxiliaryPolynomial/HypersurfaceNormalizedComparison.lean new file mode 100644 index 0000000000..865cb012d7 --- /dev/null +++ b/LeanPool/CCDN/AuxiliaryPolynomial/HypersurfaceNormalizedComparison.lean @@ -0,0 +1,109 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.AuxiliaryPolynomial.HypersurfaceRankArchimedean +public import LeanPool.CCDN.Determinant.HypersurfaceHeightDeterminant +import LeanPool.CCDN.AuxiliaryPolynomial.HypersurfaceHeightComparison + +/-! The normalized higher-dimensional determinant inequality, retaining the +exact root-rank and kernel-rank coefficients needed for uniform degree selection. -/ + +@[expose] public section +namespace CCDN +noncomputable section +open MvPolynomial + +/-- Absorb the archimedean rank terms and divide by Ms. No fixed-factor +approximation of the leading rank or coefficient-height term is made. -/ +theorem hypersurface_rank_comparison_normalized {r d M s : ℕ} + (hrpos : 0 < r) (hd : 0 < d) (hdM : d ≤ M) + (hr : s + (r + 1 + (M - d)).choose (M - d) = (r + 1 + M).choose M) + (B L A E : ℝ) + (hcomparison : (r.factorial : ℝ) ^ (1 / (r : ℝ)) / (r + 1 : ℝ) * + (s : ℝ) ^ (1 + 1 / (r : ℝ)) * + (Real.log s - (r : ℝ) * (4 * Real.log d + Real.log A) - E) ≤ + Real.log (s.factorial : ℝ) / 2 + (s : ℝ) / 2 * Real.log ((r + 1 + M).choose M : ℝ) + + s * M * Real.log B - (((r + 1 + M).choose M : ℝ) - s) * Real.log L) : + (r.factorial : ℝ) ^ (1 / (r : ℝ)) * (s : ℝ) ^ (1 / (r : ℝ)) / ((r + 1 : ℝ) * M) * + (Real.log s - (r : ℝ) * (4 * Real.log d + Real.log A) - + (E + (r + 1 : ℝ) * hypersurfaceArchimedeanError r / (r.factorial : ℝ) ^ (1 / (r : ℝ)))) ≤ + Real.log B - ((r + 1 + (M - d)).choose (M - d) : ℝ) / ((M : ℝ) * s) * Real.log L := by + have hMs := hypersurface_rank_ge_degree_parameter hrpos hd hdM hr + have hs0 : (0 : ℝ) < s := by exact_mod_cast lt_of_lt_of_le (lt_of_lt_of_le hd hdM) hMs + have hM0 : (0 : ℝ) < M := by exact_mod_cast lt_of_lt_of_le hd hdM + have hr1 : (r + 1 : ℝ) ≠ 0 := by positivity + have hfac : (r.factorial : ℝ) ^ (1 / (r : ℝ)) ≠ 0 := by positivity + have harch := hypersurface_rank_archimedean_error hrpos hd hdM hr + have hkernel : ((r + 1 + M).choose M : ℝ) - s = (r + 1 + (M - d)).choose (M - d) := by + have hh : (s : ℝ) + ((r + 1 + (M - d)).choose (M - d) : ℝ) = (r + 1 + M).choose M := by + exact_mod_cast hr + linarith + rw [hkernel] at hcomparison + let T := Real.log s - (r : ℝ) * (4 * Real.log d + Real.log A) - E + let U := (r + 1 : ℝ) * hypersurfaceArchimedeanError r / (r.factorial : ℝ) ^ (1 / (r : ℝ)) + have hsplit : (r.factorial : ℝ) ^ (1 / (r : ℝ)) / (r + 1 : ℝ) * + (s : ℝ) ^ (1 + 1 / (r : ℝ)) * (T - U) = + (r.factorial : ℝ) ^ (1 / (r : ℝ)) / (r + 1 : ℝ) * + (s : ℝ) ^ (1 + 1 / (r : ℝ)) * T - + hypersurfaceArchimedeanError r * (s : ℝ) ^ (1 + 1 / (r : ℝ)) := by + dsimp only [U] + field_simp + have hraw : (r.factorial : ℝ) ^ (1 / (r : ℝ)) / (r + 1 : ℝ) * + (s : ℝ) ^ (1 + 1 / (r : ℝ)) * (T - U) ≤ + s * M * Real.log B - ((r + 1 + (M - d)).choose (M - d) : ℝ) * Real.log L := by + rw [hsplit] + dsimp only [T] + linarith + have hquot := div_le_div_of_nonneg_right hraw (mul_nonneg hM0.le hs0.le) + convert hquot using 1 + · rw [Real.rpow_add hs0, Real.rpow_one] + dsimp only [T, U] + field_simp + ring + · field_simp + +/-- A dimension-only error for the normalized, height-controlled inequality. -/ +def hypersurfaceNormalizedError (n : ℕ) : ℝ := + hypersurfaceHeightDeterminantError n + + (n + 2 : ℝ) * hypersurfaceArchimedeanError (n + 1) / + ((n + 1).factorial : ℝ) ^ (1 / (n + 1 : ℝ)) + +/-- Absence of an auxiliary polynomial implies the normalized +inequality with constructed geometry and exact Hilbert rank. -/ +theorem hypersurface_no_auxiliary_normalized_comparison + {K : Type*} [Field K] [CharZero K] [IsAlgClosed K] {n d M t : ℕ} + (order : MonomialOrder (Fin (n + 3))) (f : MvPolynomial (Fin (n + 3)) ℤ) + (hf : f.IsHomogeneous d) (hd : 0 < d) (hdM : d ≤ M) + (hir : Irreducible (map (Int.castRingHom K) f)) + (H : ℝ) (hH : 1 ≤ H) (hcoeff : CoefficientHeightLE f H) + {B : ℝ} {z : Fin t → IntPoint (n + 3)} (hzB : IsPrimitiveBoxZeroFamily f B z) + (hrel : ∀ g : MvPolynomial (Fin (n + 3)) ℤ, g.IsHomogeneous M → + (∀ i, eval (z i) g = 0) → f ∣ g) : + ∃ s : ℕ, 0 < s ∧ s + (n + 2 + (M - d)).choose (M - d) = (n + 2 + M).choose M ∧ + ((n + 1).factorial : ℝ) ^ (1 / (n + 1 : ℝ)) * (s : ℝ) ^ (1 / (n + 1 : ℝ)) / + ((n + 2 : ℝ) * M) * + (Real.log s - (n + 1 : ℝ) * (4 * Real.log d + Real.log (1 + Real.log H / (d : ℝ) ^ 2)) - + hypersurfaceNormalizedError n) ≤ + Real.log B - ((n + 2 + (M - d)).choose (M - d) : ℝ) / ((M : ℝ) * s) * + Real.log |((order.leadingCoeff f : ℤ) : ℝ)| := by + obtain ⟨s, hs, hr, hc⟩ := hypersurface_no_auxiliary_height_comparison order f hf hd hdM hir + H hH hcoeff hzB hrel + refine ⟨s, hs, hr, ?_⟩ + have hnN : n + 1 + 1 = n + 2 := by omega + have hnR : (n : ℝ) + 1 + 1 = n + 2 := by ring + have hh := hypersurface_rank_comparison_normalized (r := n + 1) (by omega) hd hdM hr + B |((order.leadingCoeff f : ℤ) : ℝ)| (1 + Real.log H / (d : ℝ) ^ 2) + (hypersurfaceHeightDeterminantError n) (by simpa only [hnN, Nat.cast_add, Nat.cast_one, + hnR] using hc) + simpa only [hypersurfaceNormalizedError, hnN, + Nat.cast_add, Nat.cast_one, hnR] using hh + +end +end CCDN diff --git a/LeanPool/CCDN/AuxiliaryPolynomial/HypersurfacePositionHeight.lean b/LeanPool/CCDN/AuxiliaryPolynomial/HypersurfacePositionHeight.lean new file mode 100644 index 0000000000..4785f6f3c5 --- /dev/null +++ b/LeanPool/CCDN/AuxiliaryPolynomial/HypersurfacePositionHeight.lean @@ -0,0 +1,188 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.Algebra.HomogeneousTranslation +public import LeanPool.CCDN.Height.IntegerCoefficientHeight +public import Mathlib.Analysis.SpecialFunctions.Pow.Real +public import Mathlib.RingTheory.MvPolynomial.MonomialOrder +import LeanPool.CCDN.Algebra.HomogeneousChart +import LeanPool.CCDN.Height.MultivariateGridHeight +import LeanPool.CCDN.Height.PolynomialBoxHeight +import LeanPool.CCDN.Plane.HomogeneousLeadingCoefficient + +/-! +The bounded shear has a dimension-uniform logarithmic positioning cost. + +Bounded integer shears placing a large coefficient first, in all dimensions. +-/ + +@[expose] public section +namespace CCDN +noncomputable section +open MvPolynomial + +/-- The large leading-coefficient witness in arbitrary projective dimension. +Every original coefficient is controlled by one bounded grid evaluation. -/ +private theorem exists_hypersurface_leading_coefficient_translation {n d : ℕ} + (f : MvPolynomial (Fin (n + 1)) ℤ) (hf : f.IsHomogeneous d) (hf0 : f ≠ 0) : + ∃ a : Fin n → ℤ, (∀ j, 0 ≤ a j ∧ a j ≤ d) ∧ + MonomialOrder.lex.leadingCoeff (homogeneousTranslation ℤ a f) ≠ 0 ∧ + CoefficientHeightLE f (((d : ℝ) + 1) ^ (n * (d + 1)) * + |((MonomialOrder.lex.leadingCoeff (homogeneousTranslation ℤ a f) : ℤ) : ℝ)|) := by + have hc0 : affineChart n f ≠ 0 := by + intro hz + exact hf0 (affineChart_injective_on_homogeneous hf (isHomogeneous_zero _ _ d) + (by simpa only [map_zero] using hz)) + obtain ⟨a, ha, hval, hcoeff⟩ := exists_integer_multivariate_grid_height_witness (affineChart n f) + hc0 ((affineChart_totalDegree_le f).trans hf.totalDegree_le) + have he : (homogeneousTranslation ℤ a f).coeff (Finsupp.single 0 d) = + eval a (affineChart n f) := by + rw [homogeneousTranslation_pure_first a f hf, affineChart_eval] + have hlc := lex_leadingCoeff_eq_pure_first (homogeneousTranslation ℤ a f) + (homogeneousTranslation_homogeneous a hf).totalDegree_le (he ▸ hval) + refine ⟨a, ha, by rw [hlc, he]; exact hval, ?_⟩ + intro m + by_cases hm : m.degree = d + · obtain ⟨j, rfl⟩ := homogeneousMonomials_surjective n d m hm + rw [← affineChart_homogeneous_coefficient f hf j, hlc, he] + exact hcoeff _ + · rw [hf.coeff_eq_zero hm, Int.cast_zero, abs_zero] + positivity + +end +end CCDN + +namespace CCDN +noncomputable section +open MvPolynomial + +private def hypersurfacePositionHeightFactor (n d : ℕ) : ℝ := + ((d : ℝ) + 1) ^ ((n + 1) * (d + 1) + (n + 1) + n * (d + 1) + 2 * d) + +private lemma hypersurfacePositionHeightFactor_one_le (n d : ℕ) : + 1 ≤ hypersurfacePositionHeightFactor n d := by + apply one_le_pow₀ + have := Nat.cast_nonneg (α := ℝ) d + linarith + +private lemma hypersurfacePositionHeightFactor_ge_grid (n d : ℕ) : + ((d : ℝ) + 1) ^ (n * (d + 1)) ≤ hypersurfacePositionHeightFactor n d := by + apply pow_le_pow_right₀ (by have := Nat.cast_nonneg (α := ℝ) d; linarith) + omega + +/-- Full homogeneous coefficient control after a bounded shear. -/ +private theorem hypersurfaceTranslation_coefficient_bound {n d : ℕ} + (f : MvPolynomial (Fin (n + 1)) ℤ) (hf : f.IsHomogeneous d) (hf0 : f ≠ 0) + (a : Fin n → ℤ) (ha : ∀ j, 0 ≤ a j ∧ a j ≤ d) + {L : ℝ} (hL : 0 ≤ L) + (hcoeff : CoefficientHeightLE f (((d : ℝ) + 1) ^ (n * (d + 1)) * L)) + (m : Fin (n + 1) →₀ ℕ) : + |((homogeneousTranslation ℤ a f).coeff m : ℝ)| ≤ hypersurfacePositionHeightFactor n d * L := by + let K := ((d : ℝ) + 1) ^ (n * (d + 1)) + let V := ((d : ℝ) + 1) ^ (n + 1) * (K * L) * (((d : ℝ) + 1) ^ 2) ^ d + have heval (z : Fin (n + 1) → ℕ) (hz : ∀ j, z j ≤ d) : + |(eval (fun i => (z i : ℤ)) (homogeneousTranslation ℤ a f) : ℝ)| ≤ V := by + let w : Fin (n + 1) → ℤ := Fin.cons (z 0 : ℤ) + (fun j => (z j.succ : ℤ) + a j * (z 0 : ℤ)) + have hbox : InBox w (((d : ℝ) + 1) ^ 2) := by + intro j + have hd0 : (0 : ℝ) ≤ d := Nat.cast_nonneg d + refine Fin.cases ?_ (fun k => ?_) j + · have hz0 : (z 0 : ℝ) ≤ d := by exact_mod_cast hz 0 + simp only [w, Fin.cons_zero, Int.cast_natCast, + abs_of_nonneg (Nat.cast_nonneg (α := ℝ) (z 0))] + nlinarith + · have hz1 : (z k.succ : ℝ) ≤ d := by exact_mod_cast hz k.succ + have hz0 : (z 0 : ℝ) ≤ d := by exact_mod_cast hz 0 + have ha0 : (0 : ℝ) ≤ a k := by exact_mod_cast (ha k).1 + have had : (a k : ℝ) ≤ d := by exact_mod_cast (ha k).2 + simp only [w, Fin.cons_succ, Int.cast_add, Int.cast_mul, Int.cast_natCast] + rw [abs_of_nonneg (by positivity)] + nlinarith [mul_le_mul had hz0 (Nat.cast_nonneg (z 0)) hd0] + have hh := polynomial_eval₂_box_bound f (K * L) (((d : ℝ) + 1) ^ 2) + (by dsimp [K]; positivity) (one_le_pow₀ (by have := Nat.cast_nonneg (α := ℝ) d; linarith)) + hcoeff (fun j => (w j : ℝ)) hbox + rw [hf.totalDegree hf0] at hh + rw [homogeneousTranslation_eval] + change |(eval w f : ℝ)| ≤ V + have he : eval₂ (Int.castRingHom ℝ) (fun j => (w j : ℝ)) f = (eval w f : ℝ) := + (eval₂_comp (Int.castRingHom ℝ) w f).symm + simpa only [he, Fintype.card_fin, V] using hh + have hh := integer_multivariate_coefficient_bound_of_grid (homogeneousTranslation ℤ a f) + (homogeneousTranslation_homogeneous a hf).totalDegree_le + (show 0 ≤ V by dsimp [V, K]; positivity) + heval m + convert hh using 1 + dsimp only [hypersurfacePositionHeightFactor, V, K] + simp only [pow_add, pow_mul] + ring + +/-- For positive integers `r` and `d`, `log (d + 1) ≤ (r + 1) * d ^ (1/r)`. -/ +lemma log_nat_add_one_le_root {r d : ℕ} (hr : 0 < r) (hd : 0 < d) : + Real.log ((d : ℝ) + 1) ≤ ((r : ℝ) + 1) * (d : ℝ) ^ (1 / (r : ℝ)) := by + have hd1 : (1 : ℝ) ≤ d := by exact_mod_cast hd + have hr0 : (0 : ℝ) < r := by exact_mod_cast hr + have hroot : (1 : ℝ) ≤ (d : ℝ) ^ (1 / (r : ℝ)) := + Real.one_le_rpow hd1 (by positivity) + have hlog := Real.log_le_rpow_div (by linarith : (0 : ℝ) ≤ d) + (show (0 : ℝ) < 1 / (r : ℝ) by positivity) + have h2 : Real.log (2 : ℝ) ≤ 1 := by + have hh := Real.log_le_sub_one_of_pos (by norm_num : (0 : ℝ) < 2) + norm_num at hh ⊢ + exact hh + have hh := Real.log_le_log (by positivity : (0 : ℝ) < d + 1) + (show (d : ℝ) + 1 ≤ 2 * d by linarith) + rw [Real.log_mul (by norm_num) (by positivity)] at hh + rw [div_div_eq_mul_div, div_one] at hlog + nlinarith + +/-- The loss is O_{n,r}(d^(1+1/r)), as required by the auxiliary theorem. -/ +private lemma hypersurfacePositionHeightFactor_log_le {r d : ℕ} (n : ℕ) (hr : 0 < r) (hd : 0 < d) : + Real.log (hypersurfacePositionHeightFactor n d) ≤ + (5 * (n : ℝ) + 5) * ((r : ℝ) + 1) * d * (d : ℝ) ^ (1 / (r : ℝ)) := by + have hd1 : (1 : ℝ) ≤ d := by exact_mod_cast hd + have hn : (0 : ℝ) ≤ n := Nat.cast_nonneg n + have hlog := log_nat_add_one_le_root hr hd + have he : (((n + 1) * (d + 1) + (n + 1) + n * (d + 1) + 2 * d : ℕ) : ℝ) ≤ + (5 * (n : ℝ) + 5) * d := by push_cast; nlinarith + unfold hypersurfacePositionHeightFactor + rw [Real.log_pow] + have hmul := mul_le_mul he hlog (Real.log_nonneg (by linarith : (1 : ℝ) ≤ d + 1)) + (show 0 ≤ (5 * (n : ℝ) + 5) * d by positivity) + nlinarith + +/-- Integer coordinates supply the logarithmic positioning condition, +and the chosen height bounds both the original and translated coefficients. -/ +theorem exists_hypersurface_coefficient_position {n r d : ℕ} (hr : 0 < r) (hd : 0 < d) + (f : MvPolynomial (Fin (n + 1)) ℤ) (hf : f.IsHomogeneous d) (hf0 : f ≠ 0) : + ∃ (a : Fin n → ℤ) (H : ℝ), (∀ j, 0 ≤ a j ∧ a j ≤ d) ∧ 1 ≤ H ∧ + CoefficientHeightLE f H ∧ + CoefficientHeightLE (homogeneousTranslation ℤ a f) H ∧ + Real.log H ≤ + Real.log |((MonomialOrder.lex.leadingCoeff (homogeneousTranslation ℤ a f) : ℤ) : ℝ)| + + ((5 * (n : ℝ) + 5) * ((r : ℝ) + 1)) * d * (d : ℝ) ^ (1 / (r : ℝ)) := by + obtain ⟨a, ha, hne, hc⟩ := exists_hypersurface_leading_coefficient_translation f hf hf0 + let L := |((MonomialOrder.lex.leadingCoeff (homogeneousTranslation ℤ a f) : ℤ) : ℝ)| + have hL : 1 ≤ L := by dsimp only [L]; exact_mod_cast Int.one_le_abs hne + have hL0 : 0 < L := by linarith + have hP := hypersurfacePositionHeightFactor_one_le n d + refine ⟨a, hypersurfacePositionHeightFactor n d * L, ha, + one_le_mul_of_one_le_of_one_le hP hL, ?_, + hypersurfaceTranslation_coefficient_bound f hf hf0 a ha hL0.le hc, ?_⟩ + · intro m + exact (hc m).trans + (mul_le_mul_of_nonneg_right (hypersurfacePositionHeightFactor_ge_grid n d) hL0.le) + · rw [Real.log_mul (by linarith) hL0.ne'] + have hh := hypersurfacePositionHeightFactor_log_le n hr hd + dsimp only [L] + linarith + +end +end CCDN diff --git a/LeanPool/CCDN/AuxiliaryPolynomial/HypersurfacePositioning.lean b/LeanPool/CCDN/AuxiliaryPolynomial/HypersurfacePositioning.lean new file mode 100644 index 0000000000..6349b00d29 --- /dev/null +++ b/LeanPool/CCDN/AuxiliaryPolynomial/HypersurfacePositioning.lean @@ -0,0 +1,146 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.Algebra.CharZeroOrGT +public import LeanPool.CCDN.AuxiliaryPolynomial.HypersurfaceAffineCoordinates +public import LeanPool.CCDN.ForMathlib.Matrix +public import LeanPool.CCDN.Noether.GenericPlaneIrreducibility +public import Mathlib.RingTheory.MvPolynomial.Homogeneous +import LeanPool.CCDN.AuxiliaryPolynomial.HypersurfaceFullOuterDegree +import LeanPool.CCDN.AuxiliaryPolynomial.HypersurfaceGoodDirection +import LeanPool.CCDN.Determinant.GenericPlaneMinor +import LeanPool.CCDN.Determinant.HypersurfaceSmoothPoint + +/-! Full-degree monic positioning of an irreducible hypersurface at a +smooth point, using an invertible affine change and a nonzero scalar. -/ + +@[expose] public section +namespace CCDN +noncomputable section +open MvPolynomial + +/-- Normalize the leading scalar of the explicit affine coordinate change. -/ +def normalizedHypersurfaceCoordinates {K σ : Type*} [Field K] + (a b : Option σ → K) (hb : b none ≠ 0) (f : MvPolynomial (Option σ) K) : + MvPolynomial (Option σ) K := + C (eval b (homogeneousComponent f.totalDegree f))⁻¹ * hypersurfaceAffineCoordinates a b hb f + +/-- If the leading form of `f` does not vanish at the direction `b`, then the normalized affine +coordinate change of `f` is monic as a polynomial in the distinguished variable `none`. -/ +lemma normalizedHypersurfaceCoordinates_monic {K σ : Type*} [Field K] + (a b : Option σ → K) (hb : b none ≠ 0) (f : MvPolynomial (Option σ) K) + (hdir : eval b (homogeneousComponent f.totalDegree f) ≠ 0) : + (optionEquivLeft K σ (normalizedHypersurfaceCoordinates a b hb f)).Monic := by + unfold normalizedHypersurfaceCoordinates + rw [map_mul, optionEquivLeft_C] + apply Polynomial.monic_C_mul_of_mul_leadingCoeff_eq_one + rw [Polynomial.leadingCoeff, hypersurfaceAffineCoordinates_full_outer_degree a b hb f hdir, + hypersurfaceAffineCoordinates_top_coefficient, ← map_mul, inv_mul_cancel₀ hdir, map_one] + +private lemma normalizedHypersurfaceCoordinates_natDegree {K σ : Type*} [Field K] + (a b : Option σ → K) (hb : b none ≠ 0) (f : MvPolynomial (Option σ) K) + (hdir : eval b (homogeneousComponent f.totalDegree f) ≠ 0) : + (optionEquivLeft K σ (normalizedHypersurfaceCoordinates a b hb f)).natDegree = + f.totalDegree := by + unfold normalizedHypersurfaceCoordinates + rw [map_mul, optionEquivLeft_C, + Polynomial.natDegree_C_mul_of_isUnit ((isUnit_iff_ne_zero.mpr (inv_ne_zero hdir)).map C)] + exact hypersurfaceAffineCoordinates_full_outer_degree a b hb f hdir + +private lemma normalizedHypersurfaceCoordinates_totalDegree_le {K σ : Type*} [Field K] + (a b : Option σ → K) (hb : b none ≠ 0) (f : MvPolynomial (Option σ) K) : + (normalizedHypersurfaceCoordinates a b hb f).totalDegree ≤ f.totalDegree := by + exact (totalDegree_mul _ _).trans (by + rw [totalDegree_C, zero_add] + exact hypersurfaceAffineCoordinates_totalDegree_le a b hb f) + +private lemma normalizedHypersurfaceCoordinates_irreducible {K σ : Type*} [Field K] + (a b : Option σ → K) (hb : b none ≠ 0) (f : MvPolynomial (Option σ) K) + (hf : Irreducible f) (hdir : eval b (homogeneousComponent f.totalDegree f) ≠ 0) : + Irreducible (normalizedHypersurfaceCoordinates a b hb f) := by + apply (irreducible_isUnit_mul ((isUnit_iff_ne_zero.mpr (inv_ne_zero hdir)).map C)).mpr + exact (MulEquiv.irreducible_iff (f := hypersurfaceAffineCoordinates a b hb)).mpr hf + +private lemma normalizedHypersurfaceCoordinates_eval_zero {K σ : Type*} [Field K] + (a b : Option σ → K) (hb : b none ≠ 0) (f : MvPolynomial (Option σ) K) + (hz : eval a f = 0) : + eval (0 : Option σ → K) (normalizedHypersurfaceCoordinates a b hb f) = 0 := by + rw [normalizedHypersurfaceCoordinates, map_mul, hypersurfaceAffineCoordinates_eval_zero, hz, + mul_zero] + +private lemma normalizedHypersurfaceCoordinates_partial_ne_zero {K σ : Type*} [Field K] [Fintype σ] + (a b : Option σ → K) (hb : b none ≠ 0) (f : MvPolynomial (Option σ) K) + (hdir : eval b (homogeneousComponent f.totalDegree f) ≠ 0) + (ht : (∑ i, eval a (pderiv i f) * b i) ≠ 0) : + eval (0 : Option σ → K) (pderiv none (normalizedHypersurfaceCoordinates a b hb f)) ≠ 0 := by + rw [normalizedHypersurfaceCoordinates, pderiv_C_mul, map_mul, eval_C, + hypersurfaceAffineCoordinates_partial_zero] + exact mul_ne_zero (inv_ne_zero hdir) ht + +/-- The equation `g` is in position for the generic-plane minor: it is irreducible, monic of +degree `d` in the distinguished variable `none`, of total degree at most `d`, and passes through +the origin, where its derivative in the distinguished variable does not vanish. -/ +structure IsMonicSmoothAtOrigin {K σ : Type*} [Field K] (g : MvPolynomial (Option σ) K) + (d : ℕ) : Prop where + /-- The equation is irreducible. -/ + irreducible : Irreducible g + /-- The equation is monic in the distinguished variable `none`. -/ + monic : (optionEquivLeft K σ g).Monic + /-- The equation has degree `d` in the distinguished variable `none`. -/ + natDegree_eq : (optionEquivLeft K σ g).natDegree = d + /-- The equation has total degree at most `d`. -/ + totalDegree_le : g.totalDegree ≤ d + /-- The equation vanishes at the origin. -/ + eval_zero : eval (0 : Option σ → K) g = 0 + /-- The partial derivative in the distinguished variable `none` does not vanish at the + origin. -/ + pderiv_ne_zero : eval (0 : Option σ → K) (pderiv none g) ≠ 0 + +/-- Smooth-point existence and direction avoidance discharge all positioning +hypotheses: the coordinates and the nonzero scalar are actually constructed. -/ +theorem exists_monic_smooth_hypersurface_coordinates + {K σ : Type*} [Field K] [IsAlgClosed K] [Finite σ] + (f : MvPolynomial (Option σ) K) (hf : Irreducible f) + (hpos : 0 < f.totalDegree) (hchar : CharZeroOrGT K f.totalDegree) : + ∃ (a b : Option σ → K) (hb : b none ≠ 0), + eval b (homogeneousComponent f.totalDegree f) ≠ 0 ∧ + IsMonicSmoothAtOrigin (normalizedHypersurfaceCoordinates a b hb f) f.totalDegree := by + classical + let := Fintype.ofFinite σ + obtain ⟨a, j, ha, hj⟩ := exists_smooth_hypersurface_point_of_degree_lt_char f hf hpos hchar + obtain ⟨b, hb, hdir, ht⟩ := + exists_good_hypersurface_direction_with_coordinate f hf.ne_zero a j hj none + exact ⟨a, b, hb, hdir, normalizedHypersurfaceCoordinates_irreducible a b hb f hf hdir, + normalizedHypersurfaceCoordinates_monic a b hb f hdir, + normalizedHypersurfaceCoordinates_natDegree a b hb f hdir, + normalizedHypersurfaceCoordinates_totalDegree_le a b hb f, + normalizedHypersurfaceCoordinates_eval_zero a b hb f ha, + normalizedHypersurfaceCoordinates_partial_ne_zero a b hb f hdir ht⟩ + +/-- Every irreducible positive-degree hypersurface in characteristic zero or larger +than 2*d^2 has an explicitly positioned generic-plane family with a nonzero slope minor. -/ +theorem exists_positioned_genericPlane_minor + {K σ : Type*} [Field K] [IsAlgClosed K] [Finite σ] + (f : MvPolynomial (Option σ) K) (hf : Irreducible f) + (hpos : 0 < f.totalDegree) (hchar : CharZeroOrGT K (2 * f.totalDegree ^ 2)) : + ∃ (a b : Option σ → K) (hb : b none ≠ 0), + eval b (homogeneousComponent f.totalDegree f) ≠ 0 ∧ + (closedFormCoefficientMatrix f.totalDegree + (genericPlanePolynomial (normalizedHypersurfaceCoordinates a b hb f))).HasNonzeroMinor + (2 * f.totalDegree ^ 2 - 1) := by + classical + let := Fintype.ofFinite σ + obtain ⟨a, b, hb, hdir, hg⟩ := exists_monic_smooth_hypersurface_coordinates f hf hpos + (hchar.mono (by nlinarith [Nat.le_mul_self f.totalDegree])) + exact ⟨a, b, hb, hdir, exists_genericPlane_minor f.totalDegree hpos _ hg.irreducible hg.monic + hg.natDegree_eq hg.totalDegree_le hchar hg.eval_zero hg.pderiv_ne_zero⟩ + +end +end CCDN diff --git a/LeanPool/CCDN/AuxiliaryPolynomial/HypersurfaceProjectiveAuxiliary.lean b/LeanPool/CCDN/AuxiliaryPolynomial/HypersurfaceProjectiveAuxiliary.lean new file mode 100644 index 0000000000..ddb24b4e85 --- /dev/null +++ b/LeanPool/CCDN/AuxiliaryPolynomial/HypersurfaceProjectiveAuxiliary.lean @@ -0,0 +1,67 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.AuxiliaryPolynomial.AbsoluteAuxiliary + +/-! A single auxiliary polynomial for all rational points of bounded projective +height, with no finite-family hypothesis. -/ + +@[expose] public section +namespace CCDN +noncomputable section +open MvPolynomial + +/-- The rational points of height at most `B` on the zero locus of `f`, as a finite family +of bounded primitive integer zeros. A form vanishing on the family vanishes at every such +point. -/ +lemma exists_bounded_projective_zero_family {n d : ℕ} (f : MvPolynomial (Fin (n + 1)) ℤ) + (hf : f.IsHomogeneous d) (B : ℝ) : + ∃ (t : ℕ) (z : Fin t → IntPoint (n + 1)), (∀ i, Finset.univ.gcd (z i) = 1) ∧ + (∀ i, eval (z i) f = 0) ∧ (∀ i, InBox (z i) B) ∧ + ∀ {M : ℕ} (g : MvPolynomial (Fin (n + 1)) ℤ), g.IsHomogeneous M → + (∀ i, eval (z i) g = 0) → ∀ x : ProjectivePoint n, x.mulHeight ≤ B → + x ∈ projectiveZeroLocus (map (Int.castRingHom ℚ) f) → + x ∈ projectiveZeroLocus (map (Int.castRingHom ℚ) g) := by + classical + let S := projectivePoints (projectiveZeroLocus (map (Int.castRingHom ℚ) f)) B + let e := (Fintype.equivFin S).symm + refine ⟨Fintype.card S, fun i => primitiveProjectiveRep (e i).val, + fun i => primitiveProjectiveRep_gcd _, + fun i => (mem_integer_projectiveZeroLocus hf _).mp (mem_projectivePoints.mp (e i).property).2, + fun i j => (primitiveProjectiveRep_bound _ j).trans + (mem_projectivePoints.mp (e i).property).1, ?_⟩ + intro M g hgh hgz x hxB hxf + obtain ⟨i, hi⟩ := e.surjective ⟨x, mem_projectivePoints.mpr ⟨hxB, hxf⟩⟩ + have hh : eval (primitiveProjectiveRep (e i).val) g = 0 := hgz i + rw [hi] at hh + exact (mem_integer_projectiveZeroLocus hgh x).mpr hh + +/-- The full projective-height formulation of the absolutely irreducible +case of CCDN Theorem 3.1.1. -/ +theorem hypersurface_absolute_projective_auxiliary + {K : Type*} [Field K] [CharZero K] [IsAlgClosed K] {n d : ℕ} + (f : MvPolynomial (Fin (n + 3)) ℤ) (hf : f.IsHomogeneous d) (hd : 0 < d) + (hir : Irreducible (map (Int.castRingHom K) f)) + {B : ℝ} (hB : 1 ≤ B) : + ∃ (bad : Finset ℕ) (M : ℕ), + (∀ p, p ∈ bad ↔ stepanovThreshold d < p ∧ HypersurfaceBadReduction f p) ∧ + (M : ℝ) ≤ hypersurfaceAuxiliaryDegreeBound n d B (primeSetFactor bad) + (integerCoefficientHeight f) ∧ + ∃ g : MvPolynomial (Fin (n + 3)) ℤ, g ≠ 0 ∧ g.IsHomogeneous M ∧ + (∀ x : ProjectivePoint (n + 2), x.mulHeight ≤ B → + x ∈ projectiveZeroLocus (map (Int.castRingHom ℚ) f) → + x ∈ projectiveZeroLocus (map (Int.castRingHom ℚ) g)) ∧ ¬ f ∣ g := by + obtain ⟨t, z, hprim, hzero, hbox, hlift⟩ := exists_bounded_projective_zero_family f hf B + obtain ⟨bad, M, hbad, hdegree, g, hg0, hgh, hgz, hgnd⟩ := + hypersurface_absolute_auxiliary_degree f hf hd hir z hprim hzero hB hbox + exact ⟨bad, M, hbad, hdegree, g, hg0, hgh, hlift g hgh hgz, hgnd⟩ + +end +end CCDN diff --git a/LeanPool/CCDN/AuxiliaryPolynomial/HypersurfaceRankArchimedean.lean b/LeanPool/CCDN/AuxiliaryPolynomial/HypersurfaceRankArchimedean.lean new file mode 100644 index 0000000000..5454d3c9f3 --- /dev/null +++ b/LeanPool/CCDN/AuxiliaryPolynomial/HypersurfaceRankArchimedean.lean @@ -0,0 +1,103 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import Mathlib.Analysis.SpecialFunctions.Pow.Real +import LeanPool.CCDN.AuxiliaryPolynomial.HypersurfaceRankProfile + +/-! The factorial and coefficient-count logarithms in the determinant +comparison cost only a dimension constant times s^(1+1/r). -/ + +@[expose] public section +namespace CCDN +noncomputable section + +/-- The Hilbert function `s` of a degree-`d` hypersurface in `r + 2` homogeneous variables, given by +`s + C(r + 1 + (M - d), M - d) = C(r + 1 + M, M)`, is at least `M` when `0 < r` and +`0 < d ≤ M`. -/ +lemma hypersurface_rank_ge_degree_parameter {r d M s : ℕ} + (hrpos : 0 < r) (hd : 0 < d) (hdM : d ≤ M) + (hr : s + (r + 1 + (M - d)).choose (M - d) = (r + 1 + M).choose M) : M ≤ s := by + have hc : M - d + 1 ≤ (r + (M - d)).choose r := by + calc + _ = (M - d + 1).choose (M - d) := (Nat.choose_succ_self_right _).symm + _ ≤ (r + (M - d)).choose (M - d) := Nat.choose_le_choose _ (by omega) + _ = _ := Nat.choose_symm_add.symm + have hl := (Nat.mul_le_mul_left d hc).trans (hypersurface_rank_binomial_bounds hdM hr).1 + have hk := Nat.mul_le_mul_right (M - d) (show 1 ≤ d from hd) + have he := Nat.sub_add_cancel hdM + nlinarith + +/-- For `0 < r` and positive real `s`, `log s ≤ r * s ^ (1/r)`. -/ +lemma log_le_dimension_root {r : ℕ} (hr : 0 < r) {s : ℝ} (hs : 0 < s) : + Real.log s ≤ (r : ℝ) * s ^ (1 / (r : ℝ)) := by + have hr0 : (0 : ℝ) < r := by exact_mod_cast hr + have hh := Real.log_le_self (Real.rpow_nonneg hs.le (1 / (r : ℝ))) + rw [Real.log_rpow hs] at hh + have hm := mul_le_mul_of_nonneg_left hh hr0.le + have he : (r : ℝ) * ((1 / (r : ℝ)) * Real.log s) = Real.log s := by field_simp + rwa [he] at hm + +/-- The coefficient count is polynomial in rank, with explicit dimension. -/ +private lemma hypersurface_rank_log_dimension_bound {r d M s : ℕ} + (hrpos : 0 < r) (hd : 0 < d) (hdM : d ≤ M) + (hr : s + (r + 1 + (M - d)).choose (M - d) = (r + 1 + M).choose M) : + Real.log ((r + 1 + M).choose M : ℝ) ≤ (r + 1 : ℝ) * (Real.log 2 + Real.log s) := by + have hMs := hypersurface_rank_ge_degree_parameter hrpos hd hdM hr + have hs : (1 : ℝ) ≤ s := by exact_mod_cast lt_of_lt_of_le (lt_of_lt_of_le hd hdM) hMs + have hdim : (r + 1 + M).choose M ≤ (M + 1) ^ (r + 1) := by + rw [← Nat.choose_symm_add (a := r + 1) (b := M), Nat.add_comm (r + 1) M] + exact Nat.choose_add_le_add_one_pow M (r + 1) + have hdimR : ((r + 1 + M).choose M : ℝ) ≤ ((M : ℝ) + 1) ^ (r + 1) := by exact_mod_cast hdim + have hMsR : (M : ℝ) ≤ s := by exact_mod_cast hMs + have hbound : ((r + 1 + M).choose M : ℝ) ≤ (2 * (s : ℝ)) ^ (r + 1) := + hdimR.trans (pow_le_pow_left₀ (by positivity) (by linarith) _) + have hpos : (0 : ℝ) < (r + 1 + M).choose M := by + exact_mod_cast Nat.choose_pos (by omega : M ≤ r + 1 + M) + have hh := Real.log_le_log hpos hbound + rw [Real.log_pow, Real.log_mul (by norm_num) (by linarith : (s : ℝ) ≠ 0)] at hh + simpa only [Nat.cast_add, Nat.cast_one] using hh + +/-- The dimension constant `((r + 2) r + (r + 1)) / 2` bounding the archimedean logarithmic +terms of the determinant comparison by a multiple of `s ^ (1 + 1/r)`. -/ +def hypersurfaceArchimedeanError (r : ℕ) : ℝ := ((r + 2 : ℝ) * r + (r + 1 : ℝ)) / 2 + +/-- The two archimedean logarithmic terms have the exact scale of the main +rank term and cost only an explicit dimension constant. -/ +theorem hypersurface_rank_archimedean_error {r d M s : ℕ} + (hrpos : 0 < r) (hd : 0 < d) (hdM : d ≤ M) + (hr : s + (r + 1 + (M - d)).choose (M - d) = (r + 1 + M).choose M) : + Real.log (s.factorial : ℝ) / 2 + (s : ℝ) / 2 * Real.log ((r + 1 + M).choose M : ℝ) ≤ + hypersurfaceArchimedeanError r * (s : ℝ) ^ (1 + 1 / (r : ℝ)) := by + have hMs := hypersurface_rank_ge_degree_parameter hrpos hd hdM hr + have hs : 0 < s := lt_of_lt_of_le (lt_of_lt_of_le hd hdM) hMs + have hs0 : (0 : ℝ) < s := by exact_mod_cast hs + have hs1 : (1 : ℝ) ≤ s := by exact_mod_cast hs + have hf : Real.log (s.factorial : ℝ) ≤ (s : ℝ) * Real.log s := by + have hh : (s.factorial : ℝ) ≤ (s : ℝ) ^ s := by exact_mod_cast Nat.factorial_le_pow s + simpa only [Real.log_pow] using Real.log_le_log (by positivity) hh + have hlog := hypersurface_rank_log_dimension_bound hrpos hd hdM hr + have hlroot := log_le_dimension_root hrpos hs0 + have hroot1 : 1 ≤ (s : ℝ) ^ (1 / (r : ℝ)) := Real.one_le_rpow hs1 (by positivity) + have hl2 : Real.log 2 ≤ 1 := by + have hh := Real.log_le_sub_one_of_pos (by norm_num : (0 : ℝ) < 2) + linarith + have hdim : Real.log ((r + 1 + M).choose M : ℝ) ≤ + (r + 1 : ℝ) * ((r + 1 : ℝ) * (s : ℝ) ^ (1 / (r : ℝ))) := by + have hh := mul_le_mul_of_nonneg_left hlroot (show 0 ≤ (r + 1 : ℝ) by positivity) + have h2 := mul_le_mul_of_nonneg_left (hl2.trans hroot1) (show 0 ≤ (r + 1 : ℝ) by positivity) + nlinarith + have hfac := mul_le_mul_of_nonneg_left hlroot hs0.le + have hdim' := mul_le_mul_of_nonneg_left hdim (show 0 ≤ (s : ℝ) / 2 by positivity) + rw [Real.rpow_add hs0, Real.rpow_one] + unfold hypersurfaceArchimedeanError + nlinarith + +end +end CCDN diff --git a/LeanPool/CCDN/AuxiliaryPolynomial/HypersurfaceRankLogarithm.lean b/LeanPool/CCDN/AuxiliaryPolynomial/HypersurfaceRankLogarithm.lean new file mode 100644 index 0000000000..efa95bb80b --- /dev/null +++ b/LeanPool/CCDN/AuxiliaryPolynomial/HypersurfaceRankLogarithm.lean @@ -0,0 +1,71 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import Mathlib.Analysis.SpecialFunctions.Log.Basic +import LeanPool.CCDN.AuxiliaryPolynomial.HypersurfaceRankArchimedean +import LeanPool.CCDN.AuxiliaryPolynomial.HypersurfaceRankProfile + +/-! The logarithm of the exact hypersurface rank has the uniform leading +term log(d)+r*log(M), with an explicit error depending only on dimension. -/ + +@[expose] public section +namespace CCDN +noncomputable section + +/-- The dimension constant `r log 2 + log r! + r log (r + 1)` bounding the deviation of the +logarithm of the hypersurface Hilbert function from `log d + r log M`. -/ +def hypersurfaceRankLogError (r : ℕ) : ℝ := + (r : ℝ) * Real.log 2 + Real.log (r.factorial : ℝ) + r * Real.log (r + 1 : ℝ) + +/-- The rank logarithm loses only a dimension constant once M>=2d. -/ +theorem hypersurface_rank_log_error {r d M s : ℕ} (hrpos : 0 < r) (hd : 0 < d) + (hM : 2 * d ≤ M) + (hr : s + (r + 1 + (M - d)).choose (M - d) = (r + 1 + M).choose M) : + |Real.log s - (Real.log d + (r : ℝ) * Real.log M)| ≤ hypersurfaceRankLogError r := by + have hdM : d ≤ M := by omega + have hMs := hypersurface_rank_ge_degree_parameter hrpos hd hdM hr + have hs0 : (0 : ℝ) < s := by exact_mod_cast lt_of_lt_of_le (lt_of_lt_of_le hd hdM) hMs + have hd0 : (0 : ℝ) < d := by exact_mod_cast hd + have hM0 : (0 : ℝ) < M := by exact_mod_cast lt_of_lt_of_le hd hdM + have hM1 : (1 : ℝ) ≤ M := by exact_mod_cast lt_of_lt_of_le hd hdM + have hdMR : 2 * (d : ℝ) ≤ M := by exact_mod_cast hM + have hfac : (0 : ℝ) < r.factorial := by positivity + obtain ⟨hl, hu⟩ := hypersurface_rank_power_bounds hdM hr + have hl' : (d : ℝ) * ((M : ℝ) / 2) ^ r / (r.factorial : ℝ) ≤ s := by + apply le_trans _ hl + apply div_le_div_of_nonneg_right _ hfac.le + apply mul_le_mul_of_nonneg_left _ hd0.le + exact pow_le_pow_left₀ (by positivity) (by linarith) r + have hu' : (s : ℝ) ≤ (d : ℝ) * (((r : ℝ) + 1) * M) ^ r / (r.factorial : ℝ) := by + apply hu.trans + apply div_le_div_of_nonneg_right _ hfac.le + apply mul_le_mul_of_nonneg_left _ hd0.le + apply pow_le_pow_left₀ (by positivity) _ r + have hh := mul_le_mul_of_nonneg_left hM1 (Nat.cast_nonneg r) + nlinarith + have hlow := + Real.log_le_log (by positivity : (0 : ℝ) < (d : ℝ) * ((M : ℝ) / 2) ^ r / r.factorial) hl' + rw [Real.log_div (by positivity) hfac.ne', Real.log_mul hd0.ne' (by positivity), + Real.log_pow, Real.log_div hM0.ne' (by norm_num)] at hlow + have hhigh := Real.log_le_log hs0 hu' + rw [Real.log_div (by positivity) hfac.ne', Real.log_mul hd0.ne' (by positivity), + Real.log_pow, Real.log_mul (by positivity) hM0.ne'] at hhigh + have hlogfac : 0 ≤ Real.log (r.factorial : ℝ) := + Real.log_nonneg (by exact_mod_cast Nat.factorial_pos r) + have hlog2 : 0 ≤ Real.log 2 := Real.log_nonneg (by norm_num) + have hlogr : 0 ≤ Real.log (r + 1 : ℝ) := + Real.log_nonneg (by have := Nat.cast_nonneg (α := ℝ) r; linarith) + unfold hypersurfaceRankLogError + apply abs_le.mpr + constructor <;> + nlinarith [mul_nonneg (Nat.cast_nonneg r) hlog2, mul_nonneg (Nat.cast_nonneg r) hlogr] + +end +end CCDN diff --git a/LeanPool/CCDN/AuxiliaryPolynomial/HypersurfaceRankProfile.lean b/LeanPool/CCDN/AuxiliaryPolynomial/HypersurfaceRankProfile.lean new file mode 100644 index 0000000000..af4b58fd62 --- /dev/null +++ b/LeanPool/CCDN/AuxiliaryPolynomial/HypersurfaceRankProfile.lean @@ -0,0 +1,86 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import Mathlib.Algebra.BigOperators.Group.Finset.Defs +public import Mathlib.Basic.Real.Basic +public import Mathlib.Data.Nat.Choose.Basic +import Mathlib.Algebra.BigOperators.Intervals +import Mathlib.Algebra.Order.BigOperators.Group.Finset +import Mathlib.Data.Nat.Choose.Bounds + +/-! Uniform bounds for the exact homogeneous hypersurface Hilbert rank. +The factorial leading coefficient is retained in both directions. -/ + +@[expose] public section +namespace CCDN +noncomputable section +open Finset + +private lemma sum_shifted_choose_add (r k d : ℕ) : + (∑ i ∈ range d, (r + 1 + k + i).choose r) + (r + 1 + k).choose (r + 1) = + (r + 1 + k + d).choose (r + 1) := by + induction d with + | zero => simp + | succ d ih => + rw [sum_range_succ] + have hp := Nat.choose_succ_succ' (r + 1 + k + d) r + rw [show r + 1 + k + (d + 1) = (r + 1 + k + d) + 1 by omega] + omega + +/-- The exact rank is a sum of d adjacent binomial layers. -/ +private theorem hypersurface_rank_eq_sum {r d M s : ℕ} (hdM : d ≤ M) + (hr : s + (r + 1 + (M - d)).choose (M - d) = (r + 1 + M).choose M) : + s = ∑ i ∈ range d, (r + 1 + (M - d) + i).choose r := by + have hh := sum_shifted_choose_add r (M - d) d + rw [show r + 1 + (M - d) + d = r + 1 + M by omega] at hh + rw [← Nat.choose_symm_add (a := r + 1) (b := M - d), + ← Nat.choose_symm_add (a := r + 1) (b := M)] at hr + omega + +/-- The Hilbert function `s` of a degree-`d` hypersurface in `r + 2` homogeneous variables, +given by `s + C(r + 1 + (M - d), M - d) = C(r + 1 + M, M)`, satisfies +`d * C(r + (M - d), r) ≤ s ≤ d * C(r + M, r)`. -/ +lemma hypersurface_rank_binomial_bounds {r d M s : ℕ} (hdM : d ≤ M) + (hr : s + (r + 1 + (M - d)).choose (M - d) = (r + 1 + M).choose M) : + d * (r + (M - d)).choose r ≤ s ∧ s ≤ d * (r + M).choose r := by + rw [hypersurface_rank_eq_sum hdM hr] + constructor + · calc + _ = ∑ _i ∈ range d, (r + (M - d)).choose r := by simp + _ ≤ _ := sum_le_sum fun i _ => Nat.choose_le_choose r (by omega) + · calc + _ ≤ ∑ _i ∈ range d, (r + M).choose r := + sum_le_sum fun i hi => Nat.choose_le_choose r (by have := mem_range.mp hi; omega) + _ = _ := by simp + +/-- Uniform power bounds with exact leading coefficient d/r!. +The error is controlled by shifts of at most d and r in the degree M. -/ +theorem hypersurface_rank_power_bounds {r d M s : ℕ} (hdM : d ≤ M) + (hr : s + (r + 1 + (M - d)).choose (M - d) = (r + 1 + M).choose M) : + (d : ℝ) * ((M : ℝ) - d + 1) ^ r / (r.factorial : ℝ) ≤ s ∧ + (s : ℝ) ≤ (d : ℝ) * ((M : ℝ) + r) ^ r / (r.factorial : ℝ) := by + obtain ⟨hlo, hhi⟩ := hypersurface_rank_binomial_bounds hdM hr + have hloR : (d : ℝ) * ((r + (M - d)).choose r : ℝ) ≤ s := by exact_mod_cast hlo + have hhiR : (s : ℝ) ≤ (d : ℝ) * ((r + M).choose r : ℝ) := by exact_mod_cast hhi + have hl : ((M : ℝ) - d + 1) ^ r / (r.factorial : ℝ) ≤ (r + (M - d)).choose r := by + have h := Nat.pow_le_choose (α := ℝ) r (r + (M - d)) + rw [show r + (M - d) + 1 - r = M - d + 1 by omega] at h + simpa only [Nat.cast_add, Nat.cast_one, Nat.cast_sub hdM] using h + have hu : ((r + M).choose r : ℝ) ≤ ((M : ℝ) + r) ^ r / (r.factorial : ℝ) := by + simpa only [Nat.cast_add, add_comm] using Nat.choose_le_pow_div (α := ℝ) r (r + M) + constructor + · have h := mul_le_mul_of_nonneg_left hl (Nat.cast_nonneg d) + rw [← mul_div_assoc] at h + exact h.trans hloR + · exact hhiR.trans + (by simpa only [mul_div_assoc] using mul_le_mul_of_nonneg_left hu (Nat.cast_nonneg d)) + +end +end CCDN diff --git a/LeanPool/CCDN/AuxiliaryPolynomial/HypersurfaceRankRoots.lean b/LeanPool/CCDN/AuxiliaryPolynomial/HypersurfaceRankRoots.lean new file mode 100644 index 0000000000..6b65935f10 --- /dev/null +++ b/LeanPool/CCDN/AuxiliaryPolynomial/HypersurfaceRankRoots.lean @@ -0,0 +1,49 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import Mathlib.Analysis.SpecialFunctions.Pow.Real +import LeanPool.CCDN.AuxiliaryPolynomial.HypersurfaceRankProfile + +/-! Exact leading term for the dimension root of the homogeneous Hilbert rank. -/ + +@[expose] public section +namespace CCDN +noncomputable section + +/-- Taking the dimension root preserves the factorial leading coefficient. -/ +theorem hypersurface_rank_root_bounds {r d M s : ℕ} (hrpos : 0 < r) (hdM : d ≤ M) + (hr : s + (r + 1 + (M - d)).choose (M - d) = (r + 1 + M).choose M) : + (d : ℝ) ^ (1 / (r : ℝ)) * ((M : ℝ) - d + 1) ≤ + (r.factorial : ℝ) ^ (1 / (r : ℝ)) * (s : ℝ) ^ (1 / (r : ℝ)) ∧ + (r.factorial : ℝ) ^ (1 / (r : ℝ)) * (s : ℝ) ^ (1 / (r : ℝ)) ≤ + (d : ℝ) ^ (1 / (r : ℝ)) * ((M : ℝ) + r) := by + obtain ⟨hl, hu⟩ := hypersurface_rank_power_bounds hdM hr + have hfac : (0 : ℝ) < r.factorial := by positivity + have hmd : (0 : ℝ) ≤ (M : ℝ) - d + 1 := by + have : (d : ℝ) ≤ M := by exact_mod_cast hdM + linarith + have hl' := (div_le_iff₀ hfac).mp hl + have hu' := (le_div_iff₀ hfac).mp hu + have hlroot := Real.rpow_le_rpow (mul_nonneg (Nat.cast_nonneg d) (pow_nonneg hmd r)) hl' + (show 0 ≤ 1 / (r : ℝ) by positivity) + have huroot := Real.rpow_le_rpow + (mul_nonneg (Nat.cast_nonneg s) hfac.le) hu' (show 0 ≤ 1 / (r : ℝ) by positivity) + rw [Real.mul_rpow (Nat.cast_nonneg d) (pow_nonneg hmd r), + Real.mul_rpow (Nat.cast_nonneg s) hfac.le, one_div, + Real.pow_rpow_inv_natCast hmd hrpos.ne'] at hlroot + rw [Real.mul_rpow (Nat.cast_nonneg s) hfac.le, + Real.mul_rpow (Nat.cast_nonneg d) (by positivity), one_div, + Real.pow_rpow_inv_natCast (by positivity) hrpos.ne'] at huroot + constructor + · simpa only [one_div, mul_comm] using hlroot + · simpa only [one_div, mul_comm] using huroot + +end +end CCDN diff --git a/LeanPool/CCDN/AuxiliaryPolynomial/HypersurfaceSmallHeightAuxiliary.lean b/LeanPool/CCDN/AuxiliaryPolynomial/HypersurfaceSmallHeightAuxiliary.lean new file mode 100644 index 0000000000..9f2a0403b9 --- /dev/null +++ b/LeanPool/CCDN/AuxiliaryPolynomial/HypersurfaceSmallHeightAuxiliary.lean @@ -0,0 +1,125 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.AuxiliaryPolynomial.HypersurfaceBadPrimeComparison +public import LeanPool.CCDN.AuxiliaryPolynomial.HypersurfaceSmallHeightDegree +public import LeanPool.CCDN.AuxiliaryPolynomial.Nonmultiple +import LeanPool.CCDN.AuxiliaryPolynomial.HypersurfaceBadPrimeCoordinates +import LeanPool.CCDN.AuxiliaryPolynomial.PolynomialMapAuxiliary + +/-! Higher-dimensional auxiliary polynomials in the small-height range, with exact +exponents, for any finite prime set whose normalized comparison holds. The +hypersurface instance retains the original geometric bad-prime factor. -/ + +@[expose] public section +namespace CCDN +noncomputable section +open MvPolynomial + +/-- The coefficient-sensitive threshold, with a dimension-only constant. -/ +def hypersurfaceSmallThreshold (n d : ℕ) (B L A : ℝ) : ℝ := + Real.exp (hypersurfaceSmallHeightError (n + 1) (hypersurfaceBadPrimeNormalizedError n)) * + (d : ℝ) ^ (4 - 1 / (n + 1 : ℝ)) * A * + B ^ ((n + 2 : ℝ) / ((n + 1 : ℝ) * (d : ℝ) ^ (1 / (n + 1 : ℝ)))) / + L ^ (1 / ((n + 1 : ℝ) * d * (d : ℝ) ^ (1 / (n + 1 : ℝ)))) + +/-- A degree above the proved threshold yields an integer homogeneous +nonmultiple vanishing at all given bounded points, for any finite prime set `bad` +for which the normalized comparison holds. -/ +private theorem exists_auxiliary_small_height_of_comparison {n d M t : ℕ} + (order : MonomialOrder (Fin (n + 3))) (f : MvPolynomial (Fin (n + 3)) ℤ) + (hf0 : f ≠ 0) (hd : 0 < d) (hM : 2 * d ≤ M) (z : Fin t → IntPoint (n + 3)) + {B : ℝ} (hB : 1 ≤ B) (hbox : ∀ i, InBox (z i) B) + (bad : Finset ℕ) (hcomp : AuxiliaryRankComparison order f d z bad) + (hheight : Real.log |((order.leadingCoeff f : ℤ) : ℝ)| ≤ 2 * d * (n + 2 : ℝ) * Real.log B) + (hscale : (d : ℝ) * Real.log B ≤ degreeRoot n d * M) + (hthreshold : hypersurfaceSmallThreshold n d B |((order.leadingCoeff f : ℤ) : ℝ)| + (primeSetFactor bad) < M) : + ∃ g, IsPointAuxiliary f g M z := by + classical + have hdM : d ≤ M := by omega + by_contra hnone + obtain ⟨s, _, hr, hc⟩ := hcomp hB hbox hdM (dvd_of_not_exists_isPointAuxiliary hnone) + have hL : 1 ≤ |((order.leadingCoeff f : ℤ) : ℝ)| := by + exact_mod_cast Int.one_le_abs (order.leadingCoeff_ne_zero_iff.mpr hf0) + have hnR : (n : ℝ) + 1 + 1 = n + 2 := by ring + have hlog := hypersurface_small_height_log_degree_bound (r := n + 1) (by omega) hd hM hr + B |((order.leadingCoeff f : ℤ) : ℝ)| (primeSetFactor bad) + (hypersurfaceBadPrimeNormalizedError n) hB hL + (by simpa only [Nat.cast_add, Nat.cast_one, hnR] using hheight) + (by simpa only [Nat.cast_add, Nat.cast_one] using hscale) + (by simpa only [Nat.cast_add, Nat.cast_one, hnR, Real.log_exp] using hc) + have hb := hypersurface_degree_bound_of_log_bound hd (lt_of_lt_of_le hd hdM) + (by linarith : 0 < B) (by linarith : 0 < |((order.leadingCoeff f : ℤ) : ℝ)|) + (Real.exp_pos _) hlog + have hb' : (M : ℝ) ≤ hypersurfaceSmallThreshold n d B |((order.leadingCoeff f : ℤ) : ℝ)| + (primeSetFactor bad) := by + simpa only [hypersurfaceSmallThreshold, Nat.cast_add, Nat.cast_one, hnR] using hb + exact (not_lt_of_ge hb') hthreshold + +/-- An integer auxiliary degree for any finite prime set `bad` for which the +normalized comparison holds. The bound keeps the leading-coefficient saving and +every radius exponent. -/ +theorem auxiliary_small_height_degree_of_comparison {n d t : ℕ} + (order : MonomialOrder (Fin (n + 3))) (f : MvPolynomial (Fin (n + 3)) ℤ) + (hf0 : f ≠ 0) (hd : 0 < d) (z : Fin t → IntPoint (n + 3)) + {B : ℝ} (hB : 1 ≤ B) (hbox : ∀ i, InBox (z i) B) + (bad : Finset ℕ) (hcomp : AuxiliaryRankComparison order f d z bad) + (hheight : Real.log |((order.leadingCoeff f : ℤ) : ℝ)| ≤ 2 * d * (n + 2 : ℝ) * Real.log B) : + ∃ M : ℕ, + (M : ℝ) ≤ 2 * d + (d : ℝ) ^ (1 - 1 / (n + 1 : ℝ)) * Real.log B + + hypersurfaceSmallThreshold n d B |((order.leadingCoeff f : ℤ) : ℝ)| + (primeSetFactor bad) + 2 ∧ + ∃ g, IsPointAuxiliary f g M z := by + have hdR : (0 : ℝ) < d := by exact_mod_cast hd + let R := hypersurfaceSmallThreshold n d B |((order.leadingCoeff f : ℤ) : ℝ)| + (primeSetFactor bad) + have hR : 0 ≤ R := by dsimp only [R, hypersurfaceSmallThreshold]; positivity + let l := (d : ℝ) ^ (1 - 1 / (n + 1 : ℝ)) * Real.log B + have hl : 0 ≤ l := mul_nonneg (Real.rpow_nonneg hdR.le _) (Real.log_nonneg hB) + obtain ⟨M, hM, hscale, hthreshold, hbound⟩ := auxiliary_integer_degree_selection (2 * d) hl hR + refine ⟨M, by simpa only [Nat.cast_mul, Nat.cast_ofNat] using hbound, ?_⟩ + apply exists_auxiliary_small_height_of_comparison order f hf0 hd hM z hB hbox bad hcomp + hheight _ hthreshold + have hX : 0 < (d : ℝ) ^ (1 / (n + 1 : ℝ)) := by positivity + dsimp only [l] at hscale + rw [Real.rpow_sub hdR, Real.rpow_one] at hscale + have hh := mul_le_mul_of_nonneg_right hscale hX.le + have he : (d : ℝ) / (d : ℝ) ^ (1 / (n + 1 : ℝ)) * Real.log B * + (d : ℝ) ^ (1 / (n + 1 : ℝ)) = d * Real.log B := by field_simp + rw [he] at hh + nlinarith + +/-- The exact geometric bad-prime set together with an integer auxiliary +degree. The bound keeps the leading-coefficient saving and every radius exponent. -/ +theorem hypersurface_auxiliary_small_height_degree + {K : Type*} [Field K] [CharZero K] [IsAlgClosed K] {n d t : ℕ} + (order : MonomialOrder (Fin (n + 3))) (f : MvPolynomial (Fin (n + 3)) ℤ) + (hf : f.IsHomogeneous d) (hd : 0 < d) + (hir : Irreducible (map (Int.castRingHom K) f)) + (z : Fin t → IntPoint (n + 3)) (hprim : ∀ i, Finset.univ.gcd (z i) = 1) + (hz : ∀ i, eval (z i) f = 0) + {B : ℝ} (hB : 1 ≤ B) (hbox : ∀ i, InBox (z i) B) + (hheight : Real.log |((order.leadingCoeff f : ℤ) : ℝ)| ≤ 2 * d * (n + 2 : ℝ) * Real.log B) : + ∃ (bad : Finset ℕ) (M : ℕ), + (∀ p, p ∈ bad ↔ stepanovThreshold d < p ∧ HypersurfaceBadReduction f p) ∧ + (M : ℝ) ≤ 2 * d + (d : ℝ) ^ (1 - 1 / (n + 1 : ℝ)) * Real.log B + + hypersurfaceSmallThreshold n d B |((order.leadingCoeff f : ℤ) : ℝ)| + (primeSetFactor bad) + 2 ∧ + ∃ g : MvPolynomial (Fin (n + 3)) ℤ, g ≠ 0 ∧ g.IsHomogeneous M ∧ + (∀ i, eval (z i) g = 0) ∧ ¬ f ∣ g := by + have hf0 := ne_zero_of_irreducible_map hir + obtain ⟨bad, hbad⟩ := exists_hypersurface_bad_prime_finset f hf hd hir + obtain ⟨M, hM, g, hg⟩ := auxiliary_small_height_degree_of_comparison order f hf0 hd z hB hbox + bad (hypersurface_auxiliaryRankComparison order f hf hf0 hd z hprim hz bad hbad) hheight + exact ⟨bad, M, hbad, hM, g, isPointAuxiliary_iff.mp hg⟩ + +end +end CCDN diff --git a/LeanPool/CCDN/AuxiliaryPolynomial/HypersurfaceSmallHeightDegree.lean b/LeanPool/CCDN/AuxiliaryPolynomial/HypersurfaceSmallHeightDegree.lean new file mode 100644 index 0000000000..fb1c0b1a70 --- /dev/null +++ b/LeanPool/CCDN/AuxiliaryPolynomial/HypersurfaceSmallHeightDegree.lean @@ -0,0 +1,92 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.AuxiliaryPolynomial.HypersurfaceRankLogarithm +public import Mathlib.Analysis.SpecialFunctions.Pow.Real +import LeanPool.CCDN.AuxiliaryPolynomial.HypersurfaceKernelRatio +import LeanPool.CCDN.AuxiliaryPolynomial.HypersurfaceRankRoots +import LeanPool.CCDN.AuxiliaryPolynomial.RatioEstimates +import LeanPool.CCDN.ForMathlib.Log + +/-! The small-height numerical auxiliary-degree bound, with exact box and +coefficient exponents and a constant depending only on dimension. -/ + +@[expose] public section +namespace CCDN +noncomputable section + +/-- The additive error term, depending only on the dimension `r` and the constant `E`, in the +small-height logarithmic bound on the auxiliary degree `M`. -/ +def hypersurfaceSmallHeightError (r : ℕ) (E : ℝ) : ℝ := + (E + hypersurfaceRankLogError r + 4 * (r + 1 : ℝ) ^ 2) / r + +/-- Solve the normalized exact-rank inequality in the small-height range. -/ +theorem hypersurface_small_height_log_degree_bound {r d M s : ℕ} + (hrpos : 0 < r) (hd : 0 < d) (hM : 2 * d ≤ M) + (hr : s + (r + 1 + (M - d)).choose (M - d) = (r + 1 + M).choose M) + (B L A E : ℝ) (hB : 1 ≤ B) (hL : 1 ≤ L) + (hheight : Real.log L ≤ 2 * d * (r + 1 : ℝ) * Real.log B) + (hscale : (d : ℝ) * Real.log B ≤ (d : ℝ) ^ (1 / (r : ℝ)) * M) + (hcomparison : (r.factorial : ℝ) ^ (1 / (r : ℝ)) * (s : ℝ) ^ (1 / (r : ℝ)) / + ((r + 1 : ℝ) * M) * + (Real.log s - (r : ℝ) * (4 * Real.log d + Real.log A) - E) ≤ + Real.log B - ((r + 1 + (M - d)).choose (M - d) : ℝ) / ((M : ℝ) * s) * Real.log L) : + Real.log M ≤ hypersurfaceSmallHeightError r E + (4 - 1 / (r : ℝ)) * Real.log d + Real.log A + + (r + 1 : ℝ) / ((r : ℝ) * (d : ℝ) ^ (1 / (r : ℝ))) * Real.log B - + Real.log L / ((r : ℝ) * d * (d : ℝ) ^ (1 / (r : ℝ))) := by + have hdM : d ≤ M := by omega + have hd0 : (0 : ℝ) < d := by exact_mod_cast hd + have hr0 : (0 : ℝ) < r := by exact_mod_cast hrpos + have hM0 : (0 : ℝ) < M := by exact_mod_cast lt_of_lt_of_le hd hdM + have hX : (0 : ℝ) < (d : ℝ) ^ (1 / (r : ℝ)) := by positivity + obtain ⟨hl, hu⟩ := hypersurface_rank_root_bounds hrpos hdM hr + have halow : (d : ℝ) ^ (1 / (r : ℝ)) * ((M : ℝ) - d) / ((r + 1 : ℝ) * M) ≤ + (r.factorial : ℝ) ^ (1 / (r : ℝ)) * (s : ℝ) ^ (1 / (r : ℝ)) / ((r + 1 : ℝ) * M) := by + apply div_le_div_of_nonneg_right _ (by positivity) + nlinarith + have haup := div_le_div_of_nonneg_right hu (show 0 ≤ (r + 1 : ℝ) * M by positivity) + have hk := (hypersurface_kernel_ratio_bounds hd hdM hr).1 + have hh := auxiliary_small_height_bracket_bound (r := (r : ℝ)) (Nat.cast_nonneg r) + (by exact_mod_cast hd) (by exact_mod_cast hM) hX halow haup (by positivity) hk + (Real.log_nonneg hB) (Real.log_nonneg hL) hheight hscale hcomparison + have hlog := (abs_le.mp (hypersurface_rank_log_error hrpos hd hM hr)).1 + have hraw : (r : ℝ) * Real.log M ≤ (r + 1 : ℝ) / (d : ℝ) ^ (1 / (r : ℝ)) * Real.log B - + Real.log L / ((d : ℝ) * (d : ℝ) ^ (1 / (r : ℝ))) + + 4 * (r + 1 : ℝ) ^ 2 + E + hypersurfaceRankLogError r + + (4 * (r : ℝ) - 1) * Real.log d + r * Real.log A := by + nlinarith + have hdiv := div_le_div_of_nonneg_right hraw hr0.le + rw [mul_div_cancel_left₀ (Real.log M) hr0.ne'] at hdiv + convert hdiv using 1 + unfold hypersurfaceSmallHeightError + field_simp + ring + +/-- Exponentiating retains all exponents in CCDN's coefficient-sensitive +auxiliary bound. -/ +theorem hypersurface_degree_bound_of_log_bound {r d M : ℕ} {B L A C : ℝ} + (hd : 0 < d) (hM : 0 < M) (hB : 0 < B) (hL : 0 < L) (hA : 0 < A) + (hlog : Real.log M ≤ C + (4 - 1 / (r : ℝ)) * Real.log d + Real.log A + + (r + 1 : ℝ) / ((r : ℝ) * (d : ℝ) ^ (1 / (r : ℝ))) * Real.log B - + Real.log L / ((r : ℝ) * d * (d : ℝ) ^ (1 / (r : ℝ)))) : + (M : ℝ) ≤ Real.exp C * (d : ℝ) ^ (4 - 1 / (r : ℝ)) * A * + B ^ ((r + 1 : ℝ) / ((r : ℝ) * (d : ℝ) ^ (1 / (r : ℝ)))) / + L ^ (1 / ((r : ℝ) * d * (d : ℝ) ^ (1 / (r : ℝ)))) := by + have hdR : (0 : ℝ) < d := by exact_mod_cast hd + refine Real.natCast_le_of_log_le_log hM (by positivity) ?_ + rw [Real.log_div (by positivity) (by positivity), + Real.log_mul (by positivity) (by positivity), Real.log_mul (by positivity) hA.ne', + Real.log_mul (Real.exp_pos _).ne' (by positivity), Real.log_exp, + Real.log_rpow hdR, Real.log_rpow hB, Real.log_rpow hL] + convert hlog using 1 + ring + +end +end CCDN diff --git a/LeanPool/CCDN/AuxiliaryPolynomial/LargeHeightRatios.lean b/LeanPool/CCDN/AuxiliaryPolynomial/LargeHeightRatios.lean new file mode 100644 index 0000000000..a9dc6c4b16 --- /dev/null +++ b/LeanPool/CCDN/AuxiliaryPolynomial/LargeHeightRatios.lean @@ -0,0 +1,81 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import Mathlib.Analysis.SpecialFunctions.Log.Basic +import LeanPool.CCDN.AuxiliaryPolynomial.RatioEstimates + +/-! In the large-height range, the coefficient saving absorbs the radius +and leaves a negative logarithmic-height term to control the bad-prime factor. -/ + +@[expose] public section +namespace CCDN +noncomputable section + +/-- Abstract real-variable form of the large-height step: if `a` is the normalized rank ratio, +`k` the kernel ratio and the comparison `a * u ≤ b - k * l` holds, with `M ≥ 8 d (r + 1)`, +the height `h` at least `2 d (r + 1) b` and at most `l + T d X`, then +`u ≤ T - h / (8 d X)`. -/ +lemma auxiliary_large_height_bracket_bound {r d M X a k u b l h T : ℝ} + (hr : 0 ≤ r) (hd : 1 ≤ d) (hM : 8 * d * (r + 1) ≤ M) (hX : 0 < X) + (halow : X * (M - d) / ((r + 1) * M) ≤ a) + (haup : a ≤ X * (M + r) / ((r + 1) * M)) + (hk : 0 ≤ k) (hklow : 1 / (d * (r + 1)) - 1 / M ≤ k) + (hb : 0 ≤ b) (hl : 0 ≤ l) (hT : 0 ≤ T) + (hheight : 2 * d * (r + 1) * b ≤ h) (hposition : h ≤ l + T * d * X) + (hcomparison : a * u ≤ b - k * l) : + u ≤ T - h / (8 * d * X) := by + have hd0 : 0 < d := by linarith + have hM2 : 2 * d ≤ M := by nlinarith only [hM, hd0, mul_nonneg hd0.le hr] + have hM0 : 0 < M := by linarith + obtain ⟨ha, hrecip⟩ := auxiliary_reciprocal_bound hr hd hM2 hX halow + have hquot := auxiliary_kernel_quotient_bound hr hd hM0 hX ha hk haup hklow + have hrecip' : 1 / a ≤ 5 * (r + 1) / (4 * X) := by + apply hrecip.trans + field_simp + have hMd : 8 * d ≤ M := by nlinarith only [hM, mul_nonneg hd0.le hr] + linarith only [hMd] + have hquot' : 3 / (4 * d * X) ≤ k / a := by + apply le_trans _ hquot + field_simp + nlinarith only [hM, hd0] + have hu : u ≤ b / a - (k / a) * l := by + have hh := (le_div_iff₀ ha).mpr (by simpa only [mul_comm] using hcomparison) + convert hh using 1 + ring + have hb' : b / a ≤ (5 * (r + 1) / (4 * X)) * b := by + simpa only [one_div, div_eq_mul_inv, mul_comm, mul_one] using + mul_le_mul_of_nonneg_right hrecip' hb + have hl' := mul_le_mul_of_nonneg_right hquot' hl + have hu' : u ≤ (5 * (r + 1) / (4 * X)) * b - (3 / (4 * d * X)) * l := by + linarith only [hu, hb', hl'] + have he : (5 * (r + 1) / (4 * X)) * b - (3 / (4 * d * X)) * l = + (10 * d * (r + 1) * b - 6 * l) / (8 * d * X) := by field_simp; ring + rw [he] at hu' + have hden : 0 < 8 * d * X := by positivity + have hmul := (le_div_iff₀ hden).mp hu' + apply (le_of_mul_le_mul_right ?_ hden) + have he' : (T - h / (8 * d * X)) * (8 * d * X) = T * (8 * d * X) - h := by field_simp + rw [he'] + nlinarith only [hmul, hheight, hposition, mul_nonneg (mul_nonneg hT hd0.le) hX.le] + +/-- A logarithmic bad-prime loss is absorbed by a linear height saving. -/ +lemma log_one_add_le_linear_add_log {x c : ℝ} (hx : 0 ≤ x) (hc : 1 ≤ c) : + Real.log (1 + x) ≤ x / c + Real.log c := by + have hc0 : 0 < c := by linarith + have hp : 0 < (1 + x) / c := by positivity + have hh := Real.log_le_sub_one_of_pos hp + rw [Real.log_div (by positivity) hc0.ne'] at hh + have hdiv : 1 / c ≤ 1 := (div_le_one hc0).mpr hc + have he : (1 + x) / c = 1 / c + x / c := by ring + rw [he] at hh + linarith + +end +end CCDN diff --git a/LeanPool/CCDN/AuxiliaryPolynomial/Nonmultiple.lean b/LeanPool/CCDN/AuxiliaryPolynomial/Nonmultiple.lean new file mode 100644 index 0000000000..e59f70746e --- /dev/null +++ b/LeanPool/CCDN/AuxiliaryPolynomial/Nonmultiple.lean @@ -0,0 +1,105 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.Height.ProjectiveZeroLocus + +/-! +# Auxiliary nonmultiples + +The determinant method produces an *auxiliary polynomial* `g` for an equation `f`: a nonzero +polynomial that is not a multiple of `f` but vanishes on the points to be counted, so that these +points lie on the intersection of `f = 0` and `g = 0`. Three forms occur, according to how the +points are given. + +* `CCDN.IsBoxAuxiliary f g B`: `g` vanishes at every integral zero of `f` in the box of + radius `B`. +* `CCDN.IsPointAuxiliary f g M z`: `g` is a form of degree `M` vanishing at the finitely many + points `z i`. +* `CCDN.IsProjectiveAuxiliary f g M B`: `g` is a form of degree `M` vanishing at every rational + projective zero of `f` of multiplicative height at most `B`. + +The degree bound on `g`, which differs from statement to statement, is kept separate. +-/ + +@[expose] public section + +namespace CCDN + +open MvPolynomial + +/-- `g` is an auxiliary polynomial for the integral affine equation `f` on the box of +radius `B`: it is nonzero, it is not a multiple of `f`, and it vanishes at every integral zero +of `f` all of whose coordinates are at most `B` in absolute value. -/ +structure IsBoxAuxiliary {n : ℕ} (f g : MvPolynomial (Fin n) ℤ) (B : ℝ) : Prop where + /-- The auxiliary polynomial is nonzero. -/ + ne_zero : g ≠ 0 + /-- The auxiliary polynomial is not a multiple of `f`. -/ + not_dvd : ¬ f ∣ g + /-- The auxiliary polynomial vanishes at every integral zero of `f` in the box of radius + `B`. -/ + vanishes : ∀ x : IntPoint n, InBox x B → eval x f = 0 → eval x g = 0 + +/-- Unbundled form of an auxiliary polynomial on a box satisfying a further condition `P`, +typically a degree bound, in the order in which the auxiliary theorems are stated. -/ +theorem exists_isBoxAuxiliary_and_iff {n : ℕ} {f : MvPolynomial (Fin n) ℤ} {B : ℝ} + {P : MvPolynomial (Fin n) ℤ → Prop} : + (∃ g, IsBoxAuxiliary f g B ∧ P g) ↔ ∃ g, g ≠ 0 ∧ ¬ f ∣ g ∧ P g ∧ + ∀ x : IntPoint n, InBox x B → eval x f = 0 → eval x g = 0 := + ⟨fun ⟨g, hg, hP⟩ => ⟨g, hg.1, hg.2, hP, hg.3⟩, + fun ⟨g, h0, hnd, hP, hz⟩ => ⟨g, ⟨h0, hnd, hz⟩, hP⟩⟩ + +/-- `g` is a homogeneous auxiliary polynomial of degree `M` for `f` through the points `z`: a +nonzero form of degree `M` that vanishes at every point `z i` but is not a multiple of `f`. -/ +structure IsPointAuxiliary {σ R ι : Type*} [CommRing R] (f g : MvPolynomial σ R) (M : ℕ) + (z : ι → σ → R) : Prop where + /-- The auxiliary form is nonzero. -/ + ne_zero : g ≠ 0 + /-- The auxiliary form is homogeneous of degree `M`. -/ + isHomogeneous : g.IsHomogeneous M + /-- The auxiliary form vanishes at every point `z i`. -/ + eval_eq_zero : ∀ i, eval (z i) g = 0 + /-- The auxiliary form is not a multiple of `f`. -/ + not_dvd : ¬ f ∣ g + +/-- Unbundled form of `CCDN.IsPointAuxiliary`. -/ +theorem isPointAuxiliary_iff {σ R ι : Type*} [CommRing R] {f g : MvPolynomial σ R} {M : ℕ} + {z : ι → σ → R} : IsPointAuxiliary f g M z ↔ + g ≠ 0 ∧ g.IsHomogeneous M ∧ (∀ i, eval (z i) g = 0) ∧ ¬ f ∣ g := + ⟨fun h => ⟨h.1, h.2, h.3, h.4⟩, fun h => ⟨h.1, h.2.1, h.2.2.1, h.2.2.2⟩⟩ + +/-- If there is no auxiliary form of degree `M` through the points `z`, then every form of +degree `M` vanishing at all of them is a multiple of `f`. This is the hypothesis under which the +determinant method bounds the number of points. -/ +theorem dvd_of_not_exists_isPointAuxiliary {σ R ι : Type*} [CommRing R] + {f : MvPolynomial σ R} {M : ℕ} {z : ι → σ → R} (h : ¬ ∃ g, IsPointAuxiliary f g M z) + (g : MvPolynomial σ R) (hg : g.IsHomogeneous M) (hz : ∀ i, eval (z i) g = 0) : f ∣ g := by + by_cases hg0 : g = 0 + · simp [hg0] + · by_contra hnd + exact h ⟨g, hg0, hg, hz, hnd⟩ + +/-- `g` is a projective auxiliary form of degree `M` for the integral form `f` at height `B`: +a nonzero form of degree `M`, not a multiple of `f`, whose rational projective zero locus +contains every rational projective zero of `f` of multiplicative height at most `B`. -/ +structure IsProjectiveAuxiliary {n : ℕ} (f g : MvPolynomial (Fin (n + 1)) ℤ) (M : ℕ) (B : ℝ) : + Prop where + /-- The auxiliary form is nonzero. -/ + ne_zero : g ≠ 0 + /-- The auxiliary form is homogeneous of degree `M`. -/ + isHomogeneous : g.IsHomogeneous M + /-- The auxiliary form vanishes at every rational projective zero of `f` of multiplicative + height at most `B`. -/ + vanishes : ∀ x : ProjectivePoint n, x.mulHeight ≤ B → + x ∈ projectiveZeroLocus (map (Int.castRingHom ℚ) f) → + x ∈ projectiveZeroLocus (map (Int.castRingHom ℚ) g) + /-- The auxiliary form is not a multiple of `f`. -/ + not_dvd : ¬ f ∣ g + +end CCDN diff --git a/LeanPool/CCDN/AuxiliaryPolynomial/PlaneAuxiliaryCount.lean b/LeanPool/CCDN/AuxiliaryPolynomial/PlaneAuxiliaryCount.lean new file mode 100644 index 0000000000..b96d89f402 --- /dev/null +++ b/LeanPool/CCDN/AuxiliaryPolynomial/PlaneAuxiliaryCount.lean @@ -0,0 +1,100 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.Algebra.HomogeneousChart +public import Mathlib.Analysis.SpecialFunctions.Pow.Real +import LeanPool.CCDN.AuxiliaryPolynomial.PlaneBadPrimes +import LeanPool.CCDN.ForMathlib.MvPolynomial +import LeanPool.CCDN.Plane.Bezout + +/-! Distinct-point bounds obtained from the auxiliary polynomials and +Bézout. The sharp finite-field estimate remains an explicit hypothesis. -/ + +@[expose] public section +namespace CCDN +noncomputable section +open MvPolynomial + +/-- Mapping an integer polynomial to `ℚ` preserves its total degree. -/ +lemma rational_map_totalDegree {σ : Type*} (f : MvPolynomial σ ℤ) : + (MvPolynomial.map (Int.castRingHom ℚ) f).totalDegree = f.totalDegree := + totalDegree_map_of_injective Int.cast_injective _ + +/-- If the image of an integer polynomial in a field `F` of characteristic zero is irreducible, +then its image over `ℚ` is irreducible. -/ +lemma rational_irreducible_of_charZero_map {F σ : Type*} [Field F] [CharZero F] + (f : MvPolynomial σ ℤ) (hf : Irreducible (MvPolynomial.map (Int.castRingHom F) f)) : + Irreducible (MvPolynomial.map (Int.castRingHom ℚ) f) := by + apply mvPolynomial_irreducible_of_field_map (algebraMap ℚ F) + rw [map_map] + have he : (algebraMap ℚ F).comp (Int.castRingHom ℚ) = Int.castRingHom F := by ext; simp + rw [he] + exact hf + +/-- The precise Bézout step for a homogeneous integer auxiliary +polynomial and a primitive equation whose chart has full degree. -/ +theorem plane_chart_points_card_le_auxiliary_degree {d M t : ℕ} + (f g : MvPolynomial (Fin 3) ℤ) (hf : f.IsHomogeneous d) (hf0 : f ≠ 0) + (hg : g.IsHomogeneous M) (hprim : PrimitiveIntegerPolynomial f) + (hdchart : (affineChart 2 f).totalDegree = d) + (hir : Irreducible (MvPolynomial.map (Int.castRingHom ℚ) (affineChart 2 f))) + (hnd : ¬ f ∣ g) (z : Fin t → Fin 2 → ℤ) (hinj : Function.Injective z) + (hzf : ∀ i, eval (z i) (affineChart 2 f) = 0) + (hzg : ∀ i, eval (Fin.cons 1 (z i)) g = 0) : t ≤ d * M := by + let zQ : Fin t → Fin 2 → ℚ := fun i j => (z i j : ℚ) + have hiQ : Function.Injective zQ := by + intro i j he + apply hinj + funext k + exact Int.cast_injective (congrFun he k) + have hfQ : ∀ i, eval (zQ i) (MvPolynomial.map (Int.castRingHom ℚ) (affineChart 2 f)) = 0 := by + intro i + have h := congrArg (Int.castRingHom ℚ) (hzf i) + rw [map_eval, map_zero] at h + exact h + have hgQ : ∀ i, eval (zQ i) (MvPolynomial.map (Int.castRingHom ℚ) (affineChart 2 g)) = 0 := by + intro i + have h := congrArg (Int.castRingHom ℚ) (hzg i) + rw [← affineChart_eval] at h + rw [map_eval, map_zero] at h + exact h + have hndQ : ¬ MvPolynomial.map (Int.castRingHom ℚ) (affineChart 2 f) ∣ + MvPolynomial.map (Int.castRingHom ℚ) (affineChart 2 g) := + rational_affineChart_not_dvd hf hf0 hg hprim hdchart hnd + have h := plane_common_zeros_card_le _ _ hir hndQ zQ hiQ hfQ hgQ + rw [Fintype.card_fin, rational_map_totalDegree, rational_map_totalDegree, hdchart] at h + exact h.trans (Nat.mul_le_mul_left d ((affineChart_totalDegree_le g).trans hg.totalDegree_le)) + +/-- Enumerating the integer zeros transfers finite-family bounds to +the counting interface, including empty point sets. -/ +lemma integer_zero_count_le_of_family_bound {n : ℕ} (f : MvPolynomial (Fin n) ℤ) + (B R : ℝ) + (hbound : ∀ (t : ℕ) (z : Fin t → Fin n → ℤ), Function.Injective z → + (∀ i, eval (z i) f = 0) → (∀ i, InBox (z i) B) → (t : ℝ) ≤ R) : + (count (zeroLocus (MvPolynomial.map (Int.castRingHom ℚ) f)) B : ℝ) ≤ R := by + classical + let S := integralPoints (zeroLocus (MvPolynomial.map (Int.castRingHom ℚ) f)) B + let e := (Fintype.equivFin S).symm + let z : Fin (Fintype.card S) → Fin n → ℤ := fun i => (e i).val + have hinj : Function.Injective z := Subtype.val_injective.comp e.injective + have hzero : ∀ i, eval (z i) f = 0 := by + intro i + apply Int.cast_injective (α := ℚ) + have h := (mem_integralPoints.mp (e i).property).2 + change eval (fun j => (z i j : ℚ)) (MvPolynomial.map (Int.castRingHom ℚ) f) = 0 at h + change (Int.castRingHom ℚ) (eval (z i) f) = 0 + rw [map_eval] + exact h + have hbox : ∀ i, InBox (z i) B := fun i => (mem_integralPoints.mp (e i).property).1 + have h := hbound _ z hinj hzero hbox + simpa only [Fintype.card_coe, S, count] using h + +end +end CCDN diff --git a/LeanPool/CCDN/AuxiliaryPolynomial/PlaneAuxiliaryDegree.lean b/LeanPool/CCDN/AuxiliaryPolynomial/PlaneAuxiliaryDegree.lean new file mode 100644 index 0000000000..d007a1fdf1 --- /dev/null +++ b/LeanPool/CCDN/AuxiliaryPolynomial/PlaneAuxiliaryDegree.lean @@ -0,0 +1,169 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import Mathlib.Analysis.SpecialFunctions.Pow.Real +import Mathlib.Data.Nat.Choose.Cast +import LeanPool.CCDN.ForMathlib.Log + +/-! Quantitative degree control from the plane determinant comparison. -/ + +@[expose] public section +namespace CCDN +noncomputable section +open Real + +/-- The Hilbert function `s` of a plane curve of degree `d` in degree `M`, given by +`2 s + d² = d (2 M + 3)`, is at least `M` when `0 < d ≤ M`. -/ +lemma plane_rank_ge_degree_parameter {s d M : ℕ} (hd : 0 < d) (hdM : d ≤ M) + (hr : 2 * s + d ^ 2 = d * (2 * M + 3)) : M ≤ s := by + have h := Nat.mul_le_mul_left (d - 1) hdM + have he : d - 1 + 1 = d := Nat.sub_add_cancel hd + nlinarith + +/-- For the Hilbert function `s` of a plane curve of degree `d` in degree `M ≥ d`, the archimedean +terms `log s! / 2 + (s / 2) log C(M + 2, M)` of the determinant comparison are at most +`5 s² / 2`. -/ +lemma plane_rank_archimedean_error {s d M : ℕ} (hd : 0 < d) (hdM : d ≤ M) + (hr : 2 * s + d ^ 2 = d * (2 * M + 3)) : + Real.log (s.factorial : ℝ) / 2 + (s : ℝ) / 2 * Real.log ((2 + M).choose M) ≤ + 5 * (s : ℝ) ^ 2 / 2 := by + have hMs := plane_rank_ge_degree_parameter hd hdM hr + have hs : 0 < s := lt_of_lt_of_le (lt_of_lt_of_le hd hdM) hMs + have hsR : (0 : ℝ) < s := by exact_mod_cast hs + have hMR : (1 : ℝ) ≤ M := by exact_mod_cast (show 1 ≤ M by omega) + have hMsR : (M : ℝ) ≤ s := by exact_mod_cast hMs + have hl := (Real.log_le_sub_one_of_pos hsR).trans (by linarith : (s : ℝ) - 1 ≤ s) + have hf : Real.log (s.factorial : ℝ) ≤ (s : ℝ) * Real.log s := by + have hp : (s.factorial : ℝ) ≤ (s : ℝ) ^ s := by exact_mod_cast Nat.factorial_le_pow s + simpa only [Real.log_pow] using Real.log_le_log (by positivity) hp + have hn : (0 : ℝ) < (2 + M).choose M := by + exact_mod_cast Nat.choose_pos (show M ≤ 2 + M by omega) + have hdim : 2 * ((2 + M).choose M : ℝ) = ((M : ℝ) + 2) * (M + 1) := by + rw [← Nat.choose_symm_add, Nat.cast_choose_two] + push_cast + ring + have hbound : ((2 + M).choose M : ℝ) ≤ ((M : ℝ) + 2) ^ 2 := by + nlinarith only [hdim, hMR] + have hlog : Real.log ((2 + M).choose M) ≤ 4 * s := by + have h := Real.log_le_log hn hbound + rw [Real.log_pow] at h + norm_num only [Nat.cast_ofNat] at h + have h' := Real.log_le_sub_one_of_pos (show (0 : ℝ) < M + 2 by positivity) + linarith only [h, h', hMsR, hMR] + nlinarith only [hf, mul_le_mul_of_nonneg_left hl hsR.le, + mul_le_mul_of_nonneg_left hlog hsR.le] + +/-- The small-height case of the plane auxiliary-degree calculation. The +coefficient obstruction is retained with its exact exponent 1/d². -/ +theorem plane_auxiliary_log_degree_bound {s d M : ℕ} {B L A K : ℝ} + (hd : 0 < d) (hdM : d ≤ M) + (hr : 2 * s + d ^ 2 = d * (2 * M + 3)) + (hB : 1 ≤ B) (hL : 1 ≤ L) (hML : Real.log B ≤ M) + (hheight : Real.log L ≤ 4 * d * Real.log B) + (hcomparison : (s : ℝ) ^ 2 / 2 * + (Real.log s - 4 * Real.log d - Real.log A - K) ≤ + Real.log (s.factorial : ℝ) / 2 + (s : ℝ) / 2 * Real.log ((2 + M).choose M) + + s * M * Real.log B - (((2 + M).choose M : ℝ) - s) * Real.log L) : + Real.log M ≤ K + 23 + Real.log 2 + 3 * Real.log d + Real.log A + + 2 / d * Real.log B - Real.log L / (d : ℝ) ^ 2 := by + have hdR : (0 : ℝ) < d := by exact_mod_cast hd + have hMR : (0 : ℝ) < M := by exact_mod_cast lt_of_lt_of_le hd hdM + have hdMR : (d : ℝ) ≤ M := by exact_mod_cast hdM + have hd1 : (1 : ℝ) ≤ d := by exact_mod_cast hd + have hMs := plane_rank_ge_degree_parameter hd hdM hr + have hsR : (0 : ℝ) < s := by exact_mod_cast lt_of_lt_of_le (lt_of_lt_of_le hd hdM) hMs + have he : 2 * (s : ℝ) + (d : ℝ) ^ 2 = d * (2 * M + 3) := by exact_mod_cast hr + have hslo : (d : ℝ) * M ≤ 2 * s := by + nlinarith only [he, hdR, mul_nonneg hdR.le (sub_nonneg.mpr hdMR)] + have hshi : (s : ℝ) ≤ d * (M + 1) := by + nlinarith only [he, mul_nonneg hdR.le (sub_nonneg.mpr hd1)] + have hdim : 2 * ((2 + M).choose M : ℝ) = ((M : ℝ) + 2) * (M + 1) := by + rw [← Nat.choose_symm_add, Nat.cast_choose_two] + push_cast + ring + have hsquare : (s : ℝ) ^ 2 ≤ 2 * (d : ℝ) ^ 2 * ((2 + M).choose M) := by + have hsq := pow_le_pow_left₀ hsR.le hshi 2 + have hscaled := congrArg (fun x : ℝ => (d : ℝ) ^ 2 * x) hdim + nlinarith only [hsq, hscaled, + mul_nonneg (sq_nonneg (d : ℝ)) (show 0 ≤ (M : ℝ) + 1 by positivity)] + have hlogB := Real.log_nonneg hB + have hlogL := Real.log_nonneg hL + have harch := plane_rank_archimedean_error hd hdM hr + have hbasic : Real.log s - 4 * Real.log d - Real.log A - K ≤ + 5 + 2 * M / s * Real.log B - + 2 * (((2 + M).choose M : ℝ) - s) / (s : ℝ) ^ 2 * Real.log L := by + have hc : Real.log s - 4 * Real.log d - Real.log A - K ≤ + (Real.log (s.factorial : ℝ) / 2 + (s : ℝ) / 2 * Real.log ((2 + M).choose M) + + s * M * Real.log B - (((2 + M).choose M : ℝ) - s) * Real.log L) / + ((s : ℝ) ^ 2 / 2) := (le_div_iff₀ (show (0 : ℝ) < (s : ℝ) ^ 2 / 2 by positivity)).mpr + (by simpa only [mul_comm] using hcomparison) + calc + _ ≤ _ := hc + _ ≤ (5 * (s : ℝ) ^ 2 / 2 + s * M * Real.log B - + (((2 + M).choose M : ℝ) - s) * Real.log L) / ((s : ℝ) ^ 2 / 2) := by + gcongr + _ = _ := by field_simp + have hbox : 2 * (M : ℝ) / s * Real.log B ≤ 2 / d * Real.log B + 2 := by + have hrat : 2 * (M : ℝ) / s ≤ 2 / d + 2 / M := by + apply (div_le_iff₀ hsR).mpr + field_simp + have hscaled := congrArg (fun x : ℝ => x * ((M : ℝ) + d)) he + nlinarith only [hscaled, mul_nonneg (sq_nonneg (d : ℝ)) (sub_nonneg.mpr hdMR), + mul_nonneg hdR.le hMR.le, sq_nonneg (d : ℝ)] + have hh := mul_le_mul_of_nonneg_right hrat hlogB + have hrem : 2 / (M : ℝ) * Real.log B ≤ 2 := by + rw [div_mul_eq_mul_div] + exact (div_le_iff₀ hMR).mpr (by linarith only [hML]) + rw [add_mul] at hh + linarith only [hh, hrem] + have hcoef : Real.log L / (d : ℝ) ^ 2 - 16 ≤ + 2 * (((2 + M).choose M : ℝ) - s) / (s : ℝ) ^ 2 * Real.log L := by + have hrat : 1 / (d : ℝ) ^ 2 - 2 / s ≤ + 2 * (((2 + M).choose M : ℝ) - s) / (s : ℝ) ^ 2 := by + apply (le_div_iff₀ (show (0 : ℝ) < (s : ℝ) ^ 2 by positivity)).mpr + field_simp + nlinarith only [hsquare] + have hh := mul_le_mul_of_nonneg_right hrat hlogL + have hrem : 2 / (s : ℝ) * Real.log L ≤ 16 := by + rw [div_mul_eq_mul_div] + apply (div_le_iff₀ hsR).mpr + have hh := mul_le_mul_of_nonneg_left hML hdR.le + linarith only [hheight, hh, hslo] + simp only [sub_mul, one_div, inv_mul_eq_div] at hh + linarith only [hh, hrem] + have hlogs : Real.log d + Real.log M - Real.log 2 ≤ Real.log s := by + have h := Real.log_le_log (show (0 : ℝ) < d * M / 2 by positivity) + (show (d : ℝ) * M / 2 ≤ s by linarith only [hslo]) + rwa [Real.log_div (by positivity) (by norm_num), Real.log_mul hdR.ne' hMR.ne'] at h + linarith only [hbasic, hbox, hcoef, hlogs] + +/-- Exponentiation of the logarithmic degree bound, keeping the coefficient +saving and the leading box exponent unchanged. -/ +lemma plane_degree_bound_of_log_bound {d M : ℕ} {B L A K : ℝ} + (hd : 0 < d) (hM : 0 < M) (hB : 0 < B) (hL : 0 < L) (hA : 0 < A) + (hlog : Real.log M ≤ K + 23 + Real.log 2 + 3 * Real.log d + Real.log A + + 2 / d * Real.log B - Real.log L / (d : ℝ) ^ 2) : + (M : ℝ) ≤ 2 * Real.exp (K + 23) * (d : ℝ) ^ 3 * A * B ^ (2 / (d : ℝ)) / + L ^ (1 / (d : ℝ) ^ 2) := by + have hdR : (0 : ℝ) < d := by exact_mod_cast hd + refine Real.natCast_le_of_log_le_log hM (by positivity) ?_ + rw [Real.log_div (by positivity) (by positivity)] + simp only [Real.log_mul (by positivity : (2 : ℝ) ≠ 0) (Real.exp_pos _).ne', + Real.log_mul (by positivity : 2 * Real.exp (K + 23) ≠ 0) (by positivity : (d : ℝ) ^ 3 ≠ 0), + Real.log_mul (by positivity : 2 * Real.exp (K + 23) * (d : ℝ) ^ 3 ≠ 0) hA.ne', + Real.log_mul (by positivity : 2 * Real.exp (K + 23) * (d : ℝ) ^ 3 * A ≠ 0) + (Real.rpow_pos_of_pos hB _).ne', Real.log_exp, Real.log_pow, Real.log_rpow hB, + Real.log_rpow hL] + convert hlog using 1 + push_cast + ring + +end +end CCDN diff --git a/LeanPool/CCDN/AuxiliaryPolynomial/PlaneAuxiliaryLargeHeight.lean b/LeanPool/CCDN/AuxiliaryPolynomial/PlaneAuxiliaryLargeHeight.lean new file mode 100644 index 0000000000..23ce336bd7 --- /dev/null +++ b/LeanPool/CCDN/AuxiliaryPolynomial/PlaneAuxiliaryLargeHeight.lean @@ -0,0 +1,174 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import Mathlib.Analysis.SpecialFunctions.Log.Basic +import LeanPool.CCDN.Algebra.HomogeneousRankDichotomy +import LeanPool.CCDN.AuxiliaryPolynomial.PlaneAuxiliaryDegree +import LeanPool.CCDN.ForMathlib.Log + +/-! The complementary large-coefficient-height auxiliary-degree estimate. -/ + +@[expose] public section +namespace CCDN +noncomputable section +open Real + +/-- Real bounds for the plane rank `s` with `2s + d² = d(2M + 3)` in the large-degree range +`32 d ≤ M`. -/ +private lemma plane_rank_large_degree_bounds {s d M : ℕ} (hd : 0 < d) (hMlarge : 32 * d ≤ M) + (hr : 2 * s + d ^ 2 = d * (2 * M + 3)) : + (0 : ℝ) < s ∧ (d : ℝ) * M ≤ 2 * s ∧ (s : ℝ) ≤ d * (M + 1) ∧ 16 * (d : ℝ) ^ 2 ≤ s := by + have hdM : d ≤ M := by omega + have hdR : (0 : ℝ) < d := by exact_mod_cast hd + have hMs := plane_rank_ge_degree_parameter hd hdM hr + have hsR : (0 : ℝ) < s := by exact_mod_cast lt_of_lt_of_le (lt_of_lt_of_le hd hdM) hMs + have he : 2 * (s : ℝ) + (d : ℝ) ^ 2 = d * (2 * M + 3) := by exact_mod_cast hr + have hdMR : (d : ℝ) ≤ M := by exact_mod_cast hdM + have hd1 : (1 : ℝ) ≤ d := by exact_mod_cast hd + have hslo : (d : ℝ) * M ≤ 2 * s := by nlinarith + have hshi : (s : ℝ) ≤ d * (M + 1) := by nlinarith [sq_nonneg ((d : ℝ) - 1)] + have hMlargeR : 32 * (d : ℝ) ≤ M := by exact_mod_cast hMlarge + exact ⟨hsR, hslo, hshi, by nlinarith⟩ + +/-- Dividing the no-auxiliary comparison by `s² / 2` and bounding its archimedean error. -/ +private lemma plane_comparison_div_le {s d M : ℕ} {B L K X : ℝ} (hd : 0 < d) (hdM : d ≤ M) + (hr : 2 * s + d ^ 2 = d * (2 * M + 3)) + (hcomparison : (s : ℝ) ^ 2 / 2 * (Real.log s - 4 * Real.log d - X - K) ≤ + Real.log (s.factorial : ℝ) / 2 + (s : ℝ) / 2 * Real.log ((2 + M).choose M) + + s * M * Real.log B - (((2 + M).choose M : ℝ) - s) * Real.log L) : + Real.log s - 4 * Real.log d - X - K ≤ + 5 + 2 * M / s * Real.log B - + 2 * (((2 + M).choose M : ℝ) - s) / (s : ℝ) ^ 2 * Real.log L := by + have hMs := plane_rank_ge_degree_parameter hd hdM hr + have hsR : (0 : ℝ) < s := by exact_mod_cast lt_of_lt_of_le (lt_of_lt_of_le hd hdM) hMs + have harch := plane_rank_archimedean_error hd hdM hr + have hc : Real.log s - 4 * Real.log d - X - K ≤ + (Real.log (s.factorial : ℝ) / 2 + (s : ℝ) / 2 * Real.log ((2 + M).choose M) + + s * M * Real.log B - (((2 + M).choose M : ℝ) - s) * Real.log L) / + ((s : ℝ) ^ 2 / 2) := (le_div_iff₀ (show (0 : ℝ) < (s : ℝ) ^ 2 / 2 by positivity)).mpr + (by simpa only [mul_comm] using hcomparison) + calc + _ ≤ _ := hc + _ ≤ (5 * (s : ℝ) ^ 2 / 2 + s * M * Real.log B - + (((2 + M).choose M : ℝ) - s) * Real.log L) / ((s : ℝ) ^ 2 / 2) := by + gcongr + _ = _ := by field_simp + +/-- In the large-degree range, the box term is controlled by the coefficient height: +`(2M / s) log B ≤ (5/8) log H / d²` whenever `4 d log B ≤ log H`. -/ +private lemma plane_box_term_le {s d M : ℕ} {B H : ℝ} (hd : 0 < d) (hMlarge : 32 * d ≤ M) + (hr : 2 * s + d ^ 2 = d * (2 * M + 3)) (hB : 1 ≤ B) + (hheight : 4 * d * Real.log B ≤ Real.log H) : + 2 * (M : ℝ) / s * Real.log B ≤ 5 / 8 * (Real.log H / (d : ℝ) ^ 2) := by + obtain ⟨hsR, hslo, -, -⟩ := plane_rank_large_degree_bounds hd hMlarge hr + have hdR : (0 : ℝ) < d := by exact_mod_cast hd + have he : 2 * (s : ℝ) + (d : ℝ) ^ 2 = d * (2 * M + 3) := by exact_mod_cast hr + have hMlargeR : 32 * (d : ℝ) ≤ M := by exact_mod_cast hMlarge + have hlogB := Real.log_nonneg hB + have hboxrat : 2 * (M : ℝ) / s ≤ (5 / 2 : ℝ) / d := by + apply (div_le_iff₀ hsR).mpr + field_simp + nlinarith + have hh := mul_le_mul_of_nonneg_right hboxrat hlogB + have hh' : (5 / 2 : ℝ) / d * Real.log B ≤ 5 / 8 * (Real.log H / (d : ℝ) ^ 2) := by + field_simp + nlinarith + exact hh.trans hh' + +/-- In the large-degree range, the coefficient term dominates the positioned height: +`(7/8)(log H / d² - T) ≤ 2 (binom(M + 2, 2) - s) / s² · log L` whenever +`log H ≤ log L + T d²`. -/ +private lemma plane_coefficient_term_ge {s d M : ℕ} {L H T : ℝ} (hd : 0 < d) (hMlarge : 32 * d ≤ M) + (hr : 2 * s + d ^ 2 = d * (2 * M + 3)) (hL : 1 ≤ L) + (hposition : Real.log H ≤ Real.log L + T * (d : ℝ) ^ 2) : + 7 / 8 * (Real.log H / (d : ℝ) ^ 2) - 7 / 8 * T ≤ + 2 * (((2 + M).choose M : ℝ) - s) / (s : ℝ) ^ 2 * Real.log L := by + obtain ⟨hsR, -, hshi, hslarge⟩ := plane_rank_large_degree_bounds hd hMlarge hr + have hdR : (0 : ℝ) < d := by exact_mod_cast hd + have hdim : 2 * ((2 + M).choose M : ℝ) = ((M : ℝ) + 2) * (M + 1) := by + exact_mod_cast ternary_homogeneous_dimension M + have hsquare : (s : ℝ) ^ 2 ≤ 2 * (d : ℝ) ^ 2 * ((2 + M).choose M) := by + have hsq := pow_le_pow_left₀ hsR.le hshi 2 + nlinarith [mul_nonneg (sq_nonneg (d : ℝ)) (show 0 ≤ (M : ℝ) + 1 by positivity)] + have hlogL := Real.log_nonneg hL + have hcoefrat : (7 / 8 : ℝ) / (d : ℝ) ^ 2 ≤ + 2 * (((2 + M).choose M : ℝ) - s) / (s : ℝ) ^ 2 := by + have hrat : 1 / (d : ℝ) ^ 2 - 2 / s ≤ + 2 * (((2 + M).choose M : ℝ) - s) / (s : ℝ) ^ 2 := by + apply (le_div_iff₀ (show (0 : ℝ) < (s : ℝ) ^ 2 by positivity)).mpr + field_simp + nlinarith + have hrem : 2 / (s : ℝ) ≤ (1 / 8 : ℝ) / (d : ℝ) ^ 2 := by + apply (div_le_iff₀ hsR).mpr + field_simp + nlinarith + have hid : (7 / 8 : ℝ) / (d : ℝ) ^ 2 = + 1 / (d : ℝ) ^ 2 - (1 / 8 : ℝ) / (d : ℝ) ^ 2 := by ring + linarith only [hrat, hrem, hid] + have hh := mul_le_mul_of_nonneg_right hcoefrat hlogL + have hh' : 7 / 8 * (Real.log H / (d : ℝ) ^ 2) - 7 / 8 * T ≤ + (7 / 8 : ℝ) / (d : ℝ) ^ 2 * Real.log L := by + field_simp + nlinarith + exact hh'.trans hh + +/-- The plane rank satisfies `log d + log M - log 2 ≤ log s`, since `d M ≤ 2 s`. -/ +private lemma plane_rank_log_ge {s d M : ℕ} (hd : 0 < d) (hMlarge : 32 * d ≤ M) + (hr : 2 * s + d ^ 2 = d * (2 * M + 3)) : + Real.log d + Real.log M - Real.log 2 ≤ Real.log s := by + obtain ⟨-, hslo, -, -⟩ := plane_rank_large_degree_bounds hd hMlarge hr + have hdR : (0 : ℝ) < d := by exact_mod_cast hd + have hMR : (0 : ℝ) < M := by exact_mod_cast (show 0 < M by omega) + have h := Real.log_le_log (show (0 : ℝ) < d * M / 2 by positivity) + (show (d : ℝ) * M / 2 ≤ s by linarith) + rwa [Real.log_div (by positivity) (by norm_num), Real.log_mul hdR.ne' hMR.ne'] at h + +/-- When the coefficient height exceeds B^(4d), a degree at least 32d +satisfying the no-auxiliary comparison is bounded by a constant times d³. +The constant includes the loss in positioning the leading coefficient. -/ +theorem plane_auxiliary_large_height_log_bound {s d M : ℕ} {B L H T K : ℝ} + (hd : 0 < d) (hMlarge : 32 * d ≤ M) + (hr : 2 * s + d ^ 2 = d * (2 * M + 3)) + (hB : 1 ≤ B) (hL : 1 ≤ L) (hH : 1 ≤ H) (hT : 0 ≤ T) + (hheight : 4 * d * Real.log B ≤ Real.log H) + (hposition : Real.log H ≤ Real.log L + T * (d : ℝ) ^ 2) + (hcomparison : (s : ℝ) ^ 2 / 2 * + (Real.log s - 4 * Real.log d - Real.log (1 + Real.log H / (d : ℝ) ^ 2) - K) ≤ + Real.log (s.factorial : ℝ) / 2 + (s : ℝ) / 2 * Real.log ((2 + M).choose M) + + s * M * Real.log B - (((2 + M).choose M : ℝ) - s) * Real.log L) : + Real.log M ≤ K + 6 + Real.log 2 + Real.log 4 + T + 3 * Real.log d := by + have hbasic := plane_comparison_div_le hd (by omega) hr hcomparison + have hbox := plane_box_term_le hd hMlarge hr hB hheight + have hcoef := plane_coefficient_term_ge (T := T) hd hMlarge hr hL hposition + have hlogs := plane_rank_log_ge hd hMlarge hr + have hlogA := Real.log_one_add_sub_div_four_le + (show 0 ≤ Real.log H / (d : ℝ) ^ 2 by have := Real.log_nonneg hH; positivity) + linarith + +/-- Exponentiating the large-height logarithmic degree bound for plane curves: if +`log M ≤ K + 6 + log 2 + log 4 + T + 3 log d`, then `M ≤ 8 exp (K + 6 + T) d³`. -/ +lemma plane_large_degree_bound_of_log_bound {d M : ℕ} {T K : ℝ} + (hd : 0 < d) (hM : 0 < M) + (hlog : Real.log M ≤ K + 6 + Real.log 2 + Real.log 4 + T + 3 * Real.log d) : + (M : ℝ) ≤ 8 * Real.exp (K + 6 + T) * (d : ℝ) ^ 3 := by + have hdR : (0 : ℝ) < d := by exact_mod_cast hd + refine Real.natCast_le_of_log_le_log hM (by positivity) ?_ + rw [Real.log_mul (by positivity) (by positivity), + Real.log_mul (by norm_num) (Real.exp_pos _).ne', Real.log_exp, Real.log_pow] + have h8 : Real.log 8 = Real.log 2 + Real.log 4 := by + rw [← Real.log_mul (by norm_num : (2 : ℝ) ≠ 0) (by norm_num : (4 : ℝ) ≠ 0)] + norm_num + rw [h8] + convert hlog using 1 + push_cast + ring + +end +end CCDN diff --git a/LeanPool/CCDN/AuxiliaryPolynomial/PlaneBadPrimes.lean b/LeanPool/CCDN/AuxiliaryPolynomial/PlaneBadPrimes.lean new file mode 100644 index 0000000000..a59c4a2856 --- /dev/null +++ b/LeanPool/CCDN/AuxiliaryPolynomial/PlaneBadPrimes.lean @@ -0,0 +1,135 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.Analysis.PrimeCutoff +public import LeanPool.CCDN.AuxiliaryPolynomial.ConstantLeadingBadReduction +public import LeanPool.CCDN.AuxiliaryPolynomial.HypersurfaceBadReduction +public import LeanPool.CCDN.Height.IntegerCoefficientHeight +public import LeanPool.CCDN.Plane.FiniteFieldConstants +import LeanPool.CCDN.AuxiliaryPolynomial.GeneralBadReduction +import LeanPool.CCDN.ForMathlib.MvPolynomial +import LeanPool.CCDN.Noether.ShearNormalization +import Mathlib.Algebra.MvPolynomial.Nilpotent + +/-! Finite bad-prime sets for plane equations, with coefficient-height +charge bounds and irreducible good reductions. -/ + +@[expose] public section +namespace CCDN +noncomputable section +open MvPolynomial Real + +/-- A multivariate polynomial over a field whose image under a field homomorphism is irreducible +is itself irreducible. -/ +lemma mvPolynomial_irreducible_of_field_map + {F K σ : Type*} [Field F] [Field K] (φ : F →+* K) + (f : MvPolynomial σ F) (hf : Irreducible (MvPolynomial.map φ f)) : Irreducible f := by + have hunit (q : MvPolynomial σ F) (hq : IsUnit (MvPolynomial.map φ q)) : IsUnit q := by + obtain ⟨hc, hd⟩ := MvPolynomial.isUnit_iff_totalDegree_of_isReduced.mp hq + have he : (MvPolynomial.map φ q).totalDegree = q.totalDegree := + MvPolynomial.totalDegree_map_of_injective φ.injective q + apply MvPolynomial.isUnit_iff_totalDegree_of_isReduced.mpr + refine ⟨isUnit_iff_ne_zero.mpr ?_, he ▸ hd⟩ + intro hz + apply hc.ne_zero + rw [MvPolynomial.coeff_map, hz, map_zero] + refine ⟨fun h => hf.not_isUnit (h.map (MvPolynomial.map φ)), ?_⟩ + intro g h he + have hh := hf.isUnit_or_isUnit (by rw [he, map_mul]) + exact hh.imp (hunit g) (hunit h) + +/-- The total degree of the bivariate model `bivariatePolynomialEquiv R f` of a polynomial `f` in +two variables equals the total degree of `f`. -/ +lemma bivariateTotalDegree_equiv {R : Type*} [CommRing R] + (f : MvPolynomial (Fin 2) R) : + bivariateTotalDegree (bivariatePolynomialEquiv R f) = f.totalDegree := by + simp only [bivariateTotalDegree, AlgEquiv.symm_apply_apply] + +/-- Bad reduction of the bivariate model of a plane equation is bad reduction of the +equation itself. -/ +private lemma bivariateBadReduction_iff_hypersurfaceBadReduction (f : MvPolynomial (Fin 2) ℤ) + (p : ℕ) : + BivariateBadReduction (bivariatePolynomialEquiv ℤ f) f.totalDegree p ↔ + HypersurfaceBadReduction f p := by + apply exists_congr + intro hp + let : Fact p.Prime := ⟨hp⟩ + let K := AlgebraicClosure (ZMod p) + change bivariateTotalDegree (bivariateMap (Int.castRingHom K) (bivariatePolynomialEquiv ℤ f)) ≠ + f.totalDegree ∨ + ¬Irreducible (bivariateMap (Int.castRingHom K) (bivariatePolynomialEquiv ℤ f)) ↔ + (MvPolynomial.map (Int.castRingHom K) f).totalDegree ≠ f.totalDegree ∨ + ¬Irreducible (MvPolynomial.map (Int.castRingHom K) f) + rw [bivariatePolynomialEquiv_map, bivariateTotalDegree_equiv, + MulEquiv.irreducible_iff (f := bivariatePolynomialEquiv K)] + +/-- Good reduction implies the irreducibility over the prime field needed by the singular +determinant estimate. -/ +lemma irreducible_reduction_of_not_hypersurfaceBadReduction {σ : Type*} + (f : MvPolynomial σ ℤ) (p : ℕ) [Fact p.Prime] + (hf : ¬HypersurfaceBadReduction f p) : + Irreducible (MvPolynomial.map (Int.castRingHom (ZMod p)) f) := by + have h := ((not_hypersurfaceBadReduction_iff f p).mp hf).2 + apply mvPolynomial_irreducible_of_field_map (algebraMap (ZMod p) (AlgebraicClosure (ZMod p))) + have he : (algebraMap (ZMod p) (AlgebraicClosure (ZMod p))).comp (Int.castRingHom (ZMod p)) = + Int.castRingHom (AlgebraicClosure (ZMod p)) := by ext; simp + simpa only [MvPolynomial.map_map, he] using h + +/-- The complete set of bad primes above `stepanovThreshold d` is finite and has the +required charge bound. Good reductions outside this set are +irreducible reductions, not an additional input. -/ +theorem exists_plane_bad_prime_set + {F : Type*} [Field F] [CharZero F] [IsAlgClosed F] + (f : MvPolynomial (Fin 2) ℤ) (hd : 0 < f.totalDegree) + (hir : Irreducible (MvPolynomial.map (Int.castRingHom F) f)) + (H : ℝ) (hH : 1 ≤ H) (hcoeff : CoefficientHeightLE f H) : + ∃ B : Finset ℕ, + (∀ p, p ∈ B ↔ + stepanovThreshold (f.totalDegree : ℝ) < (p : ℝ) ∧ HypersurfaceBadReduction f p) ∧ + (∑ p ∈ B, Real.log p / p) ≤ + Real.log (1 + Real.log H / (f.totalDegree : ℝ) ^ 2) + + Real.log 7 + 2 * mertensError + 1 := by + classical + let q := bivariatePolynomialEquiv ℤ f + have hqdeg : bivariateTotalDegree q = f.totalDegree := bivariateTotalDegree_equiv f + have hqir : Irreducible (bivariateMap (Int.castRingHom F) q) := by + rw [bivariatePolynomialEquiv_map, MulEquiv.irreducible_iff (f := bivariatePolynomialEquiv F)] + exact hir + have hqcoeff : BivariateCoefficientBound q H := by + intro i j + rw [bivariatePolynomialEquiv_coeff] + exact hcoeff _ + obtain ⟨D, hD, _, hdiv⟩ := + exists_plane_bad_prime_witness f.totalDegree hd q hqdeg hqir H hH hqcoeff + let B := D.natAbs.primeFactors.filter + (fun p : ℕ => stepanovThreshold (f.totalDegree : ℝ) < (p : ℝ) ∧ HypersurfaceBadReduction f p) + have hd1 : (1 : ℝ) ≤ f.totalDegree := by exact_mod_cast hd + have hcut : (f.totalDegree : ℝ) ≤ stepanovThreshold (f.totalDegree : ℝ) := by + unfold stepanovThreshold + nlinarith [sq_nonneg ((f.totalDegree : ℝ) ^ 2 - 1)] + have hmem (p : ℕ) : p ∈ B ↔ + stepanovThreshold (f.totalDegree : ℝ) < (p : ℝ) ∧ HypersurfaceBadReduction f p := by + constructor + · exact fun h => (Finset.mem_filter.mp h).2 + · intro hp + apply Finset.mem_filter.mpr + refine ⟨?_, hp⟩ + apply Nat.mem_primeFactors.mpr + refine ⟨hp.2.choose, ?_, Int.natAbs_ne_zero.mpr hD.ne'⟩ + have hz := hdiv p (by exact_mod_cast hcut.trans_lt hp.1) + ((bivariateBadReduction_iff_hypersurfaceBadReduction f p).mpr hp.2) + simpa only [Int.natAbs_natCast] using Int.natAbs_dvd_natAbs.mpr hz + refine ⟨B, hmem, ?_⟩ + exact plane_geometric_bad_prime_charge f.totalDegree hd q hqdeg hqir H hH hqcoeff B + (fun p hp => (bivariateBadReduction_iff_hypersurfaceBadReduction f p).mpr ((hmem p).mp hp).2) + (fun p hp => ((hmem p).mp hp).1) + +end +end CCDN diff --git a/LeanPool/CCDN/AuxiliaryPolynomial/PlaneFrobeniusAuxiliary.lean b/LeanPool/CCDN/AuxiliaryPolynomial/PlaneFrobeniusAuxiliary.lean new file mode 100644 index 0000000000..0cc6c2e3ff --- /dev/null +++ b/LeanPool/CCDN/AuxiliaryPolynomial/PlaneFrobeniusAuxiliary.lean @@ -0,0 +1,179 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.Noether.FormalPlaneDifferentiation +public import Mathlib.Algebra.Field.ZMod +public import Mathlib.RingTheory.Ideal.Quotient.Operations +import LeanPool.CCDN.Determinant.FormalBranchMultiplicity +import LeanPool.CCDN.ForMathlib.MvPolynomialDerivative +import LeanPool.CCDN.Noether.FormalBranchFaithful +import LeanPool.CCDN.Noether.FormalBranchOrder +import Mathlib.FieldTheory.Finite.Basic + +/-! +Frobenius auxiliary polynomials: noncancellation, tangent-derivative +constraints, and their resulting count of smooth rational points. + +A polynomial criterion for the multiplicity form of plane Bézout. +All formal branches are constructed from the nonsingularity assumptions. +-/ + +@[expose] public section +namespace CCDN +noncomputable section +open MvPolynomial + +/-- Successive tangent derivatives vanishing at distinct smooth points force +N times their cardinality to be at most the section-degree product. -/ +private theorem plane_tangent_multiplicity_bound {K ι : Type*} [Field K] + [Fintype ι] + (f : MvPolynomial (Fin 2) K) (hf : Irreducible f) + (z : ι → Fin 2 → K) (hz : Function.Injective z) + (hzero : ∀ i, eval (z i) f = 0) (j : ι → Fin 2) + (hsmooth : ∀ i, eval (z i) (pderiv (j i).rev f) ≠ 0) + (g : MvPolynomial (Fin 2) K) (hnd : ¬ f ∣ g) (N : ℕ) + (hchar : ∀ n, 0 < n → n < N → (n : K) ≠ 0) + (hvan : ∀ i r, r < N → eval (z i) ((planeTangent f (j i))^[r] g) = 0) : + Fintype.card ι * N ≤ f.totalDegree * g.totalDegree := by + classical + have hb (i : ι) := exists_faithful_formal_plane_branch f hf (z i) (j i).rev + (hzero i) (hsmooth i) + choose ψ hconst hparam hfaith using hb + apply formal_plane_multiplicity_bound f hf z hz ψ + (fun i => (hfaith i f).mpr dvd_rfl) hconst j + (by simpa only [Fin.rev_rev] using hparam) g hnd N + intro i + exact formal_plane_multiplicity_of_tangent f (ψ i) ((hfaith i f).mpr dvd_rfl) + (z i) (hconst i) (j i) (by simpa only [Fin.rev_rev] using hparam i) + (hsmooth i) g N hchar (hvan i) + +/-- Characteristic p allows all requested multiplicities up to p. -/ +private theorem plane_tangent_multiplicity_bound_charP {K ι : Type*} [Field K] + [Fintype ι] (p : ℕ) [CharP K p] + (f : MvPolynomial (Fin 2) K) (hf : Irreducible f) + (z : ι → Fin 2 → K) (hz : Function.Injective z) + (hzero : ∀ i, eval (z i) f = 0) (j : ι → Fin 2) + (hsmooth : ∀ i, eval (z i) (pderiv (j i).rev f) ≠ 0) + (g : MvPolynomial (Fin 2) K) (hnd : ¬ f ∣ g) (N : ℕ) (hN : N ≤ p) + (hvan : ∀ i r, r < N → eval (z i) ((planeTangent f (j i))^[r] g) = 0) : + Fintype.card ι * N ≤ f.totalDegree * g.totalDegree := by + classical + exact plane_tangent_multiplicity_bound f hf z hz hzero j hsmooth g hnd N + (fun _ hn hnN ↦ CharP.natCast_ne_zero_of_lt K hn.ne' (hnN.trans_le hN)) hvan + +end +end CCDN + +namespace CCDN +noncomputable section +open MvPolynomial + +/-- Polynomial used in the Stepanov construction. -/ +private def planeFrobeniusAuxiliary {K ι : Type*} [Field K] [Fintype ι] + (p : ℕ) (h a : ι → MvPolynomial (Fin 2) K) : MvPolynomial (Fin 2) K := + ∑ i, h i * (a i) ^ p + +private lemma planeFrobeniusAuxiliary_degree {K ι : Type*} [Field K] [Fintype ι] + (p : ℕ) (h a : ι → MvPolynomial (Fin 2) K) {D A : ℕ} + (hd : ∀ i, (h i).totalDegree ≤ D) (ha : ∀ i, (a i).totalDegree ≤ A) : + (planeFrobeniusAuxiliary p h a).totalDegree ≤ D + p * A := by + apply totalDegree_finsetSum_le + intro i _ + exact (totalDegree_mul _ _).trans (Nat.add_le_add (hd i) + ((totalDegree_pow _ _).trans (Nat.mul_le_mul_left p (ha i)))) + +/-- A nontrivial coefficient class gives a proper section, with the precise +low-degree restriction d*D
(Ideal.Quotient.mkₐ K (Ideal.span {f})) (h i))) + (g : ι → PowerSeries K) (hrel : ∑ i, ψ (h i) * (g i) ^ (p ^ r) = 0) : ∀ i, g i = 0 := by + classical + exact powerSeries_frobenius_relation_trivial p r (fun i => ψ (h i)) + (formal_plane_independent_jets f hf ψ hψ j a hparam hD h hd hi) g hrel + +end +end CCDN diff --git a/LeanPool/CCDN/Noether/FormalImplicitRoot.lean b/LeanPool/CCDN/Noether/FormalImplicitRoot.lean new file mode 100644 index 0000000000..845ca03330 --- /dev/null +++ b/LeanPool/CCDN/Noether/FormalImplicitRoot.lean @@ -0,0 +1,145 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import Mathlib.Algebra.Polynomial.Derivative +public import Mathlib.RingTheory.PowerSeries.Basic +import LeanPool.CCDN.ForMathlib.Polynomial +import Mathlib.RingTheory.PowerSeries.NoZeroDivisors +import Mathlib.RingTheory.SimpleRing.Principal + +/-! A simple root over the constant field lifts to a formal power +series root. Newton iteration and coefficient stabilization prove existence. -/ + +@[expose] public section +namespace CCDN +noncomputable section + +private def formalNewtonSequence {K : Type*} [Field K] + (f : Polynomial (PowerSeries K)) (b u : K) : ℕ → PowerSeries K + | 0 => PowerSeries.C b + | n + 1 => formalNewtonSequence f b u n - PowerSeries.C u * f.eval (formalNewtonSequence f b u n) + +private lemma formalNewton_step {K : Type*} [Field K] + (f : Polynomial (PowerSeries K)) (b u : K) + (hu : u * (f.derivative.map PowerSeries.constantCoeff).eval b = 1) + (g : PowerSeries K) (hg : PowerSeries.constantCoeff g = b) + {k : ℕ} (hk : 1 ≤ k) (hfg : PowerSeries.X ^ k ∣ f.eval g) : + PowerSeries.X ^ (k + 1) ∣ f.eval (g - PowerSeries.C u * f.eval g) := by + let g' := g - PowerSeries.C u * f.eval g + have hXf : (PowerSeries.X : PowerSeries K) ∣ f.eval g := by + simpa only [pow_one] using (pow_dvd_pow PowerSeries.X hk).trans hfg + have hXdiff : (PowerSeries.X : PowerSeries K) ∣ g' - g := by + have hh := dvd_mul_of_dvd_right hXf (PowerSeries.C u) + convert dvd_neg.mpr hh using 1 + dsimp [g'] + ring + obtain ⟨q, he, hq⟩ := Polynomial.exists_divided_difference f g' g + have hqder : (PowerSeries.X : PowerSeries K) ∣ q - f.derivative.eval g := hXdiff.trans hq + have hbase : (PowerSeries.X : PowerSeries K) ∣ 1 - PowerSeries.C u * f.derivative.eval g := by + apply PowerSeries.X_dvd_iff.mpr + simp only [map_sub, map_one, map_mul, PowerSeries.constantCoeff_C, + ← Polynomial.eval_map_apply, hg, hu, sub_self] + have hbracket : (PowerSeries.X : PowerSeries K) ∣ 1 - PowerSeries.C u * q := by + have hh := dvd_mul_of_dvd_left hqder (PowerSeries.C u) + convert dvd_sub hbase hh using 1 + ring + have heq : f.eval g' = f.eval g * (1 - PowerSeries.C u * q) := by + dsimp only [g'] at he + linear_combination he + rw [heq, pow_succ] + exact mul_dvd_mul hfg hbracket + +private lemma formalNewton_precision {K : Type*} [Field K] + (f : Polynomial (PowerSeries K)) (b u : K) + (hroot : (f.map PowerSeries.constantCoeff).eval b = 0) + (hu : u * (f.derivative.map PowerSeries.constantCoeff).eval b = 1) (n : ℕ) : + PowerSeries.constantCoeff (formalNewtonSequence f b u n) = b ∧ + PowerSeries.X ^ (n + 1) ∣ f.eval (formalNewtonSequence f b u n) := by + induction n with + | zero => + refine ⟨by simp [formalNewtonSequence], ?_⟩ + rw [show (0 : ℕ) + 1 = 1 by omega, pow_one] + apply PowerSeries.X_dvd_iff.mpr + simp only [formalNewtonSequence, ← Polynomial.eval_map_apply, PowerSeries.constantCoeff_C, + hroot] + | succ n ih => + refine ⟨?_, formalNewton_step f b u hu _ ih.1 (by omega) ih.2⟩ + simp only [formalNewtonSequence, map_sub, map_mul, PowerSeries.constantCoeff_C, + ← Polynomial.eval_map_apply, ih.1, hroot, mul_zero, sub_zero] + +private lemma formalNewton_stabilizes {K : Type*} [Field K] + (f : Polynomial (PowerSeries K)) (b u : K) + (hroot : (f.map PowerSeries.constantCoeff).eval b = 0) + (hu : u * (f.derivative.map PowerSeries.constantCoeff).eval b = 1) + {n m : ℕ} (hnm : n ≤ m) : + PowerSeries.X ^ (n + 1) ∣ formalNewtonSequence f b u m - formalNewtonSequence f b u n := by + induction m, hnm using Nat.le_induction with + | base => simp + | succ m hnm ih => + have hprec := (formalNewton_precision f b u hroot hu m).2 + have hd := (pow_dvd_pow (PowerSeries.X : PowerSeries K) (Nat.add_le_add_right hnm 1)).trans + hprec + have hh := dvd_mul_of_dvd_right hd (PowerSeries.C u) + convert dvd_sub ih hh using 1 + simp only [formalNewtonSequence] + ring + +/-- Every simple root of the constant polynomial lifts to a formal root. -/ +theorem exists_formal_simple_root {K : Type*} [Field K] + (f : Polynomial (PowerSeries K)) (b : K) + (hroot : (f.map PowerSeries.constantCoeff).eval b = 0) + (hder : (f.derivative.map PowerSeries.constantCoeff).eval b ≠ 0) : + ∃ g : PowerSeries K, PowerSeries.constantCoeff g = b ∧ f.eval g = 0 := by + let u := ((f.derivative.map PowerSeries.constantCoeff).eval b)⁻¹ + have hu : u * (f.derivative.map PowerSeries.constantCoeff).eval b = 1 := inv_mul_cancel₀ hder + let g := PowerSeries.mk (fun n => PowerSeries.coeff n (formalNewtonSequence f b u n)) + have happrox (n : ℕ) : PowerSeries.X ^ (n + 1) ∣ g - formalNewtonSequence f b u n := by + apply PowerSeries.X_pow_dvd_iff.mpr + intro j hj + have hh := formalNewton_stabilizes f b u hroot hu (show j ≤ n by omega) + have hc := PowerSeries.X_pow_dvd_iff.mp hh j (by omega) + simp only [map_sub] at hc ⊢ + dsimp only [g] + rw [PowerSeries.coeff_mk] + exact sub_eq_zero.mpr (sub_eq_zero.mp hc).symm + refine ⟨g, ?_, ?_⟩ + · have hh := happrox 0 + simp only [zero_add, pow_one] at hh + have hc := PowerSeries.X_dvd_iff.mp hh + simpa only [map_sub, formalNewtonSequence, PowerSeries.constantCoeff_C, sub_eq_zero] using hc + · apply PowerSeries.ext + intro n + have hdiff := (happrox n).trans (Polynomial.sub_dvd_eval_sub g (formalNewtonSequence f b u n) f) + have hprec := (formalNewton_precision f b u hroot hu n).2 + have hh : PowerSeries.X ^ (n + 1) ∣ f.eval g := by + convert dvd_add hdiff hprec using 1 + ring + have hc := PowerSeries.X_pow_dvd_iff.mp hh n (by omega) + simpa only [map_zero] using hc + +/-- The simple formal branch is unique among roots with the chosen constant. -/ +theorem formal_simple_root_unique {K : Type*} [Field K] + (f : Polynomial (PowerSeries K)) (b : K) + (hder : (f.derivative.map PowerSeries.constantCoeff).eval b ≠ 0) + {g h : PowerSeries K} (hg : PowerSeries.constantCoeff g = b) + (hh : PowerSeries.constantCoeff h = b) (hfg : f.eval g = 0) (hfh : f.eval h = 0) : g = h := by + obtain ⟨q, he, hq⟩ := Polynomial.exists_divided_difference f g h + have hX : (PowerSeries.X : PowerSeries K) ∣ g - h := PowerSeries.X_dvd_iff.mpr (by simp [hg, hh]) + have hc := PowerSeries.X_dvd_iff.mp (hX.trans hq) + simp only [map_sub, ← Polynomial.eval_map_apply, hh, sub_eq_zero] at hc + have hq0 : q ≠ 0 := by + intro hz + apply hder + simpa only [hz, map_zero] using hc.symm + rw [hfg, hfh, sub_self] at he + exact sub_eq_zero.mp ((mul_eq_zero.mp he.symm).resolve_right hq0) + +end +end CCDN diff --git a/LeanPool/CCDN/Noether/FormalPlaneBranch.lean b/LeanPool/CCDN/Noether/FormalPlaneBranch.lean new file mode 100644 index 0000000000..3b591c7a9a --- /dev/null +++ b/LeanPool/CCDN/Noether/FormalPlaneBranch.lean @@ -0,0 +1,106 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import Mathlib.Algebra.MvPolynomial.PDeriv +public import Mathlib.RingTheory.PowerSeries.Basic +import LeanPool.CCDN.Noether.FormalImplicitRoot + +/-! Formal branches of plane equations at nonsingular points, in either +coordinate chart. The other coordinate is the parameter X plus its base value. -/ + +@[expose] public section +namespace CCDN +noncomputable section + +/-- Turn coordinate j into a polynomial variable and the other coordinate +into the formal parameter X+a. -/ +def formalPlanePolynomial {K : Type*} [CommRing K] (j : Fin 2) (a : K) : + MvPolynomial (Fin 2) K →+* Polynomial (PowerSeries K) := + MvPolynomial.eval₂Hom (Polynomial.C.comp PowerSeries.C) + (fun k => if k = j then Polynomial.X else Polynomial.C (PowerSeries.X + PowerSeries.C a)) + +/-- `formalPlanePolynomial j a` sends variable `j` to the polynomial variable and the other +variable to the constant polynomial with value the power series `X + a`. -/ +@[simp] lemma formalPlanePolynomial_X {K : Type*} [CommRing K] + (j k : Fin 2) (a : K) : + formalPlanePolynomial j a (MvPolynomial.X k) = + if k = j then Polynomial.X else Polynomial.C (PowerSeries.X + PowerSeries.C a) := by + simp [formalPlanePolynomial] + +private lemma formalPlanePolynomial_derivative {K : Type*} [CommRing K] + (j : Fin 2) (a : K) (f : MvPolynomial (Fin 2) K) : + (formalPlanePolynomial j a f).derivative = + formalPlanePolynomial j a (MvPolynomial.pderiv j f) := by + induction f using MvPolynomial.induction_on with + | C c => simp [formalPlanePolynomial] + | add f g hf hg => simp [map_add, hf, hg] + | mul_X f k hf => + by_cases h : k = j + · subst k + simp [map_mul, Polynomial.derivative_mul, formalPlanePolynomial_X, hf] + ring + · simp [map_mul, Polynomial.derivative_mul, formalPlanePolynomial_X, hf, h] + ring + +private lemma formalPlanePolynomial_eval {K : Type*} [CommRing K] + (j : Fin 2) (a : K) (f : MvPolynomial (Fin 2) K) (g : PowerSeries K) : + (formalPlanePolynomial j a f).eval g = + MvPolynomial.eval₂ PowerSeries.C + (fun k => if k = j then g else PowerSeries.X + PowerSeries.C a) f := by + induction f using MvPolynomial.induction_on with + | C c => simp [formalPlanePolynomial] + | add f h hf hh => simp [map_add, hf, hh] + | mul_X f k hf => + by_cases h : k = j <;> simp [map_mul, formalPlanePolynomial_X, hf, h] + +private lemma formalPlanePolynomial_constant_eval {K : Type*} [CommRing K] + (j : Fin 2) (a b : K) (f : MvPolynomial (Fin 2) K) : + ((formalPlanePolynomial j a f).map PowerSeries.constantCoeff).eval b = + MvPolynomial.eval (fun k => if k = j then b else a) f := by + induction f using MvPolynomial.induction_on with + | C c => simp [formalPlanePolynomial] + | add f h hf hh => simp [map_add, hf, hh] + | mul_X f k hf => + by_cases h : k = j <;> simp [map_mul, formalPlanePolynomial_X, hf, h] + +/-- A point of the plane is recovered from its coordinate `j` and its other coordinate `j.rev`. -/ +lemma plane_coordinate_cases {K : Type*} (z : Fin 2 → K) (j : Fin 2) : + (fun k => if k = j then z j else z j.rev) = z := by + funext k + fin_cases j <;> fin_cases k <;> simp + +/-- A nonzero chosen partial derivative supplies a unique formal branch +through the given point. This works in arbitrary characteristic. -/ +theorem exists_unique_formal_plane_branch {K : Type*} [Field K] + (f : MvPolynomial (Fin 2) K) (z : Fin 2 → K) (j : Fin 2) + (hz : MvPolynomial.eval z f = 0) (hj : MvPolynomial.eval z (MvPolynomial.pderiv j f) ≠ 0) : + ∃! g : PowerSeries K, PowerSeries.constantCoeff g = z j ∧ + MvPolynomial.eval₂ PowerSeries.C + (fun k => if k = j then g else PowerSeries.X + PowerSeries.C (z j.rev)) f = 0 := by + let F := formalPlanePolynomial j (z j.rev) f + have hroot : (F.map PowerSeries.constantCoeff).eval (z j) = 0 := by + rw [formalPlanePolynomial_constant_eval, plane_coordinate_cases] + exact hz + have hder : (F.derivative.map PowerSeries.constantCoeff).eval (z j) ≠ 0 := by + dsimp only [F] + rw [formalPlanePolynomial_derivative, formalPlanePolynomial_constant_eval, + plane_coordinate_cases] + exact hj + obtain ⟨g, hg, hfg⟩ := exists_formal_simple_root F (z j) hroot hder + refine ⟨g, ⟨hg, ?_⟩, ?_⟩ + · rwa [formalPlanePolynomial_eval] at hfg + · intro h hh + apply formal_simple_root_unique F (z j) hder hh.1 hg + · rw [formalPlanePolynomial_eval] + exact hh.2 + · exact hfg + +end +end CCDN diff --git a/LeanPool/CCDN/Noether/FormalPlaneDifferentiation.lean b/LeanPool/CCDN/Noether/FormalPlaneDifferentiation.lean new file mode 100644 index 0000000000..6a4beef202 --- /dev/null +++ b/LeanPool/CCDN/Noether/FormalPlaneDifferentiation.lean @@ -0,0 +1,123 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import Mathlib.RingTheory.MvPolynomial.IrreducibleQuadratic +public import Mathlib.RingTheory.PowerSeries.Derivative +import Mathlib.RingTheory.PowerSeries.Inverse + +/-! Tangent differentiation on formal plane branches. Vanishing of successive +tangent derivatives implies local multiplicity below the characteristic. -/ + +@[expose] public section +namespace CCDN +noncomputable section +open MvPolynomial + +/-- The polynomial tangent vector field, with coordinate j as local parameter. -/ +def planeTangent {K : Type*} [CommRing K] (f : MvPolynomial (Fin 2) K) (j : Fin 2) : + Derivation K (MvPolynomial (Fin 2) K) (MvPolynomial (Fin 2) K) := + (pderiv j.rev f) • pderiv j - (pderiv j f) • pderiv j.rev + +/-- The tangent vector field of `f` with local parameter coordinate `j` applied to `h` is +`∂_{j.rev} f * ∂_j h - ∂_j f * ∂_{j.rev} h`. -/ +lemma planeTangent_apply {K : Type*} [CommRing K] + (f h : MvPolynomial (Fin 2) K) (j : Fin 2) : + planeTangent f j h = pderiv j.rev f * pderiv j h - pderiv j f * pderiv j.rev h := rfl + +private lemma powerSeries_plane_chain {K : Type*} [Field K] + (ψ : MvPolynomial (Fin 2) K →ₐ[K] PowerSeries K) + (h : MvPolynomial (Fin 2) K) (j : Fin 2) : + PowerSeries.derivative (ψ h) = + ψ (pderiv j h) * PowerSeries.derivative (ψ (X j)) + + ψ (pderiv j.rev h) * PowerSeries.derivative (ψ (X j.rev)) := by + have hC (c : K) : ψ (C c) = PowerSeries.C c := ψ.commutes c + induction h using MvPolynomial.induction_on with + | C c => simp [hC] + | add h l hh hl => simp only [map_add, hh, hl]; ring + | mul_X h k hh => + fin_cases j <;> fin_cases k <;> + simp [map_mul, Derivation.leibniz, smul_eq_mul, hh] <;> ring + +/-- Tangent differentiation becomes a unit times formal differentiation at a +smooth point whose chosen coordinate is the parameter. -/ +private theorem formal_plane_tangent {K : Type*} [Field K] + (f : MvPolynomial (Fin 2) K) + (ψ : MvPolynomial (Fin 2) K →ₐ[K] PowerSeries K) (hψ : ψ f = 0) + (j : Fin 2) (a : K) (hparam : ψ (X j - C a) = PowerSeries.X) + (h : MvPolynomial (Fin 2) K) : + ψ (planeTangent f j h) = ψ (pderiv j.rev f) * PowerSeries.derivative (ψ h) := by + have hdX : PowerSeries.derivative (ψ (X j)) = 1 := by + have he := congrArg PowerSeries.derivative hparam + have hC : ψ (C a) = PowerSeries.C a := ψ.commutes a + simpa only [map_sub, hC, PowerSeries.derivative_C, sub_zero, PowerSeries.derivative_X] using he + have hf := powerSeries_plane_chain ψ f j + rw [hψ, map_zero, hdX, mul_one] at hf + rw [powerSeries_plane_chain ψ h j, hdX, mul_one, planeTangent_apply, map_sub, map_mul, map_mul] + linear_combination ψ (pderiv j.rev h) * hf + +/-- A unit multiple of d/dX detects multiplicity from its successive constant +terms, as long as the positive integers below the requested order are invertible. -/ +private lemma powerSeries_weighted_derivatives_vanishing {K : Type*} [Field K] + (u : PowerSeries K) (hu : IsUnit u) (N : ℕ) + (hchar : ∀ n, 0 < n → n < N → (n : K) ≠ 0) + (g : PowerSeries K) + (hzero : ∀ r < N, + PowerSeries.constantCoeff ((fun h => u * PowerSeries.derivative h)^[r] g) = 0) : + PowerSeries.X ^ N ∣ g := by + induction N generalizing g with + | zero => simp + | succ N ih => + have ht : PowerSeries.X ^ N ∣ u * PowerSeries.derivative g := by + apply ih (fun n hn hnN => hchar n hn (by omega)) + intro r hr + have hh := hzero (r + 1) (by omega) + simpa only [Function.iterate_succ_apply] using hh + have hd := hu.dvd_mul_left.mp ht + apply PowerSeries.X_pow_dvd_iff.mpr + intro n hn + cases n with + | zero => + simpa only [Function.iterate_zero, id_eq, PowerSeries.coeff_zero_eq_constantCoeff] using + hzero 0 (by omega) + | succ n => + have he := PowerSeries.X_pow_dvd_iff.mp hd n (by omega) + rw [PowerSeries.coeff_derivative] at he + exact (mul_eq_zero.mp he).resolve_right (by + simpa only [Nat.cast_add, Nat.cast_one] using hchar (n + 1) (by omega) hn) + +/-- Polynomial tangent derivatives give local divisibility. -/ +theorem formal_plane_multiplicity_of_tangent {K : Type*} [Field K] + (f : MvPolynomial (Fin 2) K) + (ψ : MvPolynomial (Fin 2) K →ₐ[K] PowerSeries K) (hψ : ψ f = 0) + (z : Fin 2 → K) (hconst : ∀ h, PowerSeries.constantCoeff (ψ h) = eval z h) + (j : Fin 2) (hparam : ψ (X j - C (z j)) = PowerSeries.X) + (hsmooth : eval z (pderiv j.rev f) ≠ 0) + (g : MvPolynomial (Fin 2) K) (N : ℕ) + (hchar : ∀ n, 0 < n → n < N → (n : K) ≠ 0) + (hzero : ∀ r < N, eval z ((planeTangent f j)^[r] g) = 0) : + PowerSeries.X ^ N ∣ ψ g := by + let u := ψ (pderiv j.rev f) + have hu : IsUnit u := by + rw [PowerSeries.isUnit_iff_constantCoeff, hconst] + exact isUnit_iff_ne_zero.mpr hsmooth + apply powerSeries_weighted_derivatives_vanishing u hu N hchar + have hiter (r : ℕ) : + ((fun h => u * PowerSeries.derivative h)^[r] (ψ g)) = ψ ((planeTangent f j)^[r] g) := by + induction r with + | zero => rfl + | succ r hr => + rw [Function.iterate_succ_apply', Function.iterate_succ_apply', hr] + exact (formal_plane_tangent f ψ hψ j (z j) hparam _).symm + intro r hr + rw [hiter, hconst] + exact hzero r hr + +end +end CCDN diff --git a/LeanPool/CCDN/Noether/GenericPlaneIrreducibility.lean b/LeanPool/CCDN/Noether/GenericPlaneIrreducibility.lean new file mode 100644 index 0000000000..ef9ff07e68 --- /dev/null +++ b/LeanPool/CCDN/Noether/GenericPlaneIrreducibility.lean @@ -0,0 +1,100 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.Noether.ClosedFormBaseChange +public import LeanPool.CCDN.Noether.GenericPlaneStretch +import LeanPool.CCDN.Algebra.PolynomialPrimeLocalization +import LeanPool.CCDN.Noether.ShearNormalization + +/-! Ordinary irreducibility of f(x,y*z_1,...,y*z_n) over the rational function +field in the slopes z_i. The original equation is irreducible and monic in x; +all parameter extensions and changes of polynomial presentation are proved. -/ + +@[expose] public section +namespace CCDN +noncomputable section +open MvPolynomial + +/-- Exchange the fresh y parameter with the transverse slope variables. -/ +private def genericPlaneCoefficientEquiv (K σ : Type*) [Field K] : + MvPolynomial σ (MvPolynomial Unit K) ≃ₐ[K] Polynomial (MvPolynomial σ K) := + (commAlgEquiv K σ Unit).trans ((uniqueAlgEquiv (MvPolynomial σ K) Unit).restrictScalars K) + +/-- Replace each transverse variable z_i with the polynomial z_i*y. -/ +def genericPlaneCoefficients (K σ : Type*) [Field K] : + MvPolynomial σ K →ₐ[K] Polynomial (MvPolynomial σ K) := + aeval (fun i => Polynomial.C (X i) * Polynomial.X) + +/-- The generic plane equation, with x outermost and y the inner variable. -/ +def genericPlanePolynomial {K σ : Type*} [Field K] + (f : MvPolynomial (Option σ) K) : Polynomial (Polynomial (MvPolynomial σ K)) := + Polynomial.map (genericPlaneCoefficients K σ).toRingHom (optionEquivLeft K σ f) + +private lemma genericPlaneCoefficientEquiv_stretch {K σ : Type*} [Field K] + (f : MvPolynomial (Option σ) K) : + Polynomial.map (genericPlaneCoefficientEquiv K σ).toRingHom (genericPlaneStretch f) = + genericPlanePolynomial f := by + have he : (genericPlaneCoefficientEquiv K σ).toRingHom.comp + (genericStretchCoefficients K σ).toRingHom = (genericPlaneCoefficients K σ).toRingHom := by + apply MvPolynomial.ringHom_ext + · intro a + simp [genericPlaneCoefficientEquiv, genericStretchCoefficients, genericPlaneCoefficients] + · intro i + simp [genericPlaneCoefficientEquiv, genericStretchCoefficients, genericPlaneCoefficients] + simp only [genericPlaneStretch, genericPlanePolynomial, Polynomial.map_map, he] + +/-- If `f` is monic in its distinguished variable, then so is the generic plane equation of `f`. -/ +lemma genericPlanePolynomial_monic {K σ : Type*} [Field K] + (f : MvPolynomial (Option σ) K) (hf : (optionEquivLeft K σ f).Monic) : + (genericPlanePolynomial f).Monic := hf.map _ + +/-- If `f` is monic in its distinguished variable, then the generic plane equation of `f` has the +same degree in that variable. -/ +lemma genericPlanePolynomial_natDegree {K σ : Type*} [Field K] + (f : MvPolynomial (Option σ) K) (hf : (optionEquivLeft K σ f).Monic) : + (genericPlanePolynomial f).natDegree = (optionEquivLeft K σ f).natDegree := + hf.natDegree_map _ + +/-- The joint polynomial in x, y and all slope variables is irreducible. -/ +private theorem genericPlanePolynomial_irreducible {K σ : Type*} [Field K] + (f : MvPolynomial (Option σ) K) (hf : Irreducible f) + (hd : 0 < f.totalDegree) (hm : (optionEquivLeft K σ f).Monic) : + Irreducible (genericPlanePolynomial f) := by + rw [← genericPlaneCoefficientEquiv_stretch] + exact (MulEquiv.irreducible_iff + (f := Polynomial.mapAlgEquiv (genericPlaneCoefficientEquiv K σ))).mpr + (genericPlaneStretch_irreducible f hf hd hm) + +/-- Localizing the slope parameters preserves the ordinary irreducibility of +the generic plane equation. -/ +theorem genericPlanePolynomial_irreducible_fraction_slopes + {K F σ : Type*} [Field K] [Field F] + [Algebra (MvPolynomial σ K) F] [IsFractionRing (MvPolynomial σ K) F] + (f : MvPolynomial (Option σ) K) (hf : Irreducible f) + (hd : 0 < f.totalDegree) (hm : (optionEquivLeft K σ f).Monic) + (hx : 0 < (optionEquivLeft K σ f).natDegree) : + Irreducible (bivariateMap (algebraMap (MvPolynomial σ K) F) (genericPlanePolynomial f)) := by + let R := MvPolynomial σ K + let g := (bivariatePolynomialEquiv R).symm (genericPlanePolynomial f) + have hg : Irreducible g := + (MulEquiv.irreducible_iff (f := (bivariatePolynomialEquiv R).symm)).mpr + (genericPlanePolynomial_irreducible f hf hd hm) + have hgdeg : 0 < g.totalDegree := by + have hh := bivariate_natDegree_le_totalDegree (genericPlanePolynomial f) + rw [genericPlanePolynomial_natDegree f hm] at hh + exact hx.trans_le hh + have hp := prime_mvPolynomial_map_localization (S := F) (nonZeroDivisors R) + le_rfl g hg.prime hgdeg + have he := (MulEquiv.irreducible_iff (f := bivariatePolynomialEquiv F)).mpr hp.irreducible + rw [← bivariatePolynomialEquiv_map] at he + simpa only [g, AlgEquiv.apply_symm_apply] using he + +end +end CCDN diff --git a/LeanPool/CCDN/Noether/GenericPlaneStretch.lean b/LeanPool/CCDN/Noether/GenericPlaneStretch.lean new file mode 100644 index 0000000000..47f13b075c --- /dev/null +++ b/LeanPool/CCDN/Noether/GenericPlaneStretch.lean @@ -0,0 +1,116 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import Mathlib.Algebra.MvPolynomial.Equiv +import LeanPool.CCDN.Algebra.PolynomialPrimeLocalization +import Mathlib.Algebra.MvPolynomial.Division +import Mathlib.Algebra.Polynomial.Eval.Irreducible +import Mathlib.Algebra.Polynomial.Monic + +/-! Ordinary irreducibility of the generic stretching substitution +f(x,z) -> f(x,y*z). The fresh variable y is first made invertible; scaling +then becomes a polynomial automorphism. Monicity in x descends irreducibility +back to the polynomial coefficient ring. -/ + +@[expose] public section +namespace CCDN +noncomputable section +open MvPolynomial + +private lemma optionEquivLeft_map_coefficients {R S σ : Type*} [CommRing R] [CommRing S] + (φ : R →+* S) (f : MvPolynomial (Option σ) R) : + optionEquivLeft S σ (MvPolynomial.map φ f) = + Polynomial.map (MvPolynomial.map φ) (optionEquivLeft R σ f) := by + have he : (optionEquivLeft S σ).toRingHom.comp (MvPolynomial.map φ) = + (Polynomial.mapRingHom (MvPolynomial.map φ)).comp (optionEquivLeft R σ).toRingHom := by + apply MvPolynomial.ringHom_ext + · intro a + simp + · intro i + cases i <;> simp [optionEquivLeft_apply] + exact RingHom.congr_fun he f + +/-- Simultaneously multiply all polynomial variables by a nonzero scalar. -/ +def variableScaleEquiv (K σ : Type*) [Field K] (a : K) (ha : a ≠ 0) : + MvPolynomial σ K ≃ₐ[K] MvPolynomial σ K := + AlgEquiv.ofAlgHom (aeval (fun i => C a * X i)) + (aeval (fun i => C a⁻¹ * X i)) + (by + apply MvPolynomial.algHom_ext + intro i + simp only [AlgHom.comp_apply, aeval_X, AlgHom.id_apply] + simp only [map_mul, aeval_C, aeval_X, MvPolynomial.algebraMap_eq] + rw [← mul_assoc, ← map_mul, inv_mul_cancel₀ ha, map_one, one_mul]) + (by + apply MvPolynomial.algHom_ext + intro i + simp only [AlgHom.comp_apply, aeval_X, AlgHom.id_apply] + simp only [map_mul, aeval_C, aeval_X, MvPolynomial.algebraMap_eq] + rw [← mul_assoc, ← map_mul, mul_inv_cancel₀ ha, map_one, one_mul]) + +/-- The scaling automorphism by `a` sends each variable `X i` to `a * X i`. -/ +@[simp] lemma variableScaleEquiv_X {K σ : Type*} [Field K] + (a : K) (ha : a ≠ 0) (i : σ) : + variableScaleEquiv K σ a ha (X i) = C a * X i := by + simp [variableScaleEquiv] + +/-- Introduce a fresh polynomial parameter and scale each transverse variable +by that parameter, keeping the distinguished outer variable fixed. -/ +def genericStretchCoefficients (K σ : Type*) [Field K] : + MvPolynomial σ K →ₐ[K] MvPolynomial σ (MvPolynomial Unit K) := + aeval (fun i => C (X ()) * X i) + +/-- The generic stretch `f(x, z) ↦ f(x, y * z)` of `f`, viewed as a polynomial in the distinguished +outer variable `x` whose coefficients involve the transverse variables `z` and the fresh +parameter `y`. -/ +def genericPlaneStretch {K σ : Type*} [Field K] + (f : MvPolynomial (Option σ) K) : + Polynomial (MvPolynomial σ (MvPolynomial Unit K)) := + Polynomial.map (genericStretchCoefficients K σ).toRingHom (optionEquivLeft K σ f) + +private lemma genericPlaneStretch_monic {K σ : Type*} [Field K] + (f : MvPolynomial (Option σ) K) (hf : (optionEquivLeft K σ f).Monic) : + (genericPlaneStretch f).Monic := hf.map _ + +/-- The generic stretch is ordinarily irreducible before any slope parameters +are specialized or localized. -/ +theorem genericPlaneStretch_irreducible {K σ : Type*} [Field K] + (f : MvPolynomial (Option σ) K) (hf : Irreducible f) + (hd : 0 < f.totalDegree) (hm : (optionEquivLeft K σ f).Monic) : + Irreducible (genericPlaneStretch f) := by + let R := MvPolynomial Unit K + let F := FractionRing R + let ι : R →+* F := algebraMap R F + let κ : K →+* F := ι.comp C + let y : F := ι (X ()) + have hy : y ≠ 0 := by + exact (map_ne_zero_iff _ (IsFractionRing.injective R F)).mpr (X_ne_zero ()) + let e := Polynomial.mapAlgEquiv (variableScaleEquiv F σ y hy) + have hbase : Irreducible (optionEquivLeft F σ (MvPolynomial.map κ f)) := + (MulEquiv.irreducible_iff (f := optionEquivLeft F σ)).mpr + (irreducible_mvPolynomial_map_fraction_parameters f hf hd) + have hi : Irreducible (e (optionEquivLeft F σ (MvPolynomial.map κ f))) := + (MulEquiv.irreducible_iff (f := e)).mpr hbase + apply Polynomial.Monic.irreducible_of_irreducible_map + (MvPolynomial.map ι) (genericPlaneStretch f) (genericPlaneStretch_monic f hm) + have he : (MvPolynomial.map ι).comp (genericStretchCoefficients K σ).toRingHom = + (variableScaleEquiv F σ y hy).toRingHom.comp (MvPolynomial.map κ) := by + apply MvPolynomial.ringHom_ext + · intro a + simp [genericStretchCoefficients, κ, ι, variableScaleEquiv] + · intro i + simp [genericStretchCoefficients, y, variableScaleEquiv] + change Irreducible (Polynomial.map (variableScaleEquiv F σ y hy).toRingHom + (optionEquivLeft F σ (MvPolynomial.map κ f))) at hi + rw [optionEquivLeft_map_coefficients, Polynomial.map_map] at hi + simpa only [genericPlaneStretch, Polynomial.map_map, he] using hi + +end +end CCDN diff --git a/LeanPool/CCDN/Noether/LeadingDirection.lean b/LeanPool/CCDN/Noether/LeadingDirection.lean new file mode 100644 index 0000000000..3fcd7da54e --- /dev/null +++ b/LeanPool/CCDN/Noether/LeadingDirection.lean @@ -0,0 +1,89 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.Noether.BivariateTotalDegree + +/-! A bounded integer direction where the highest homogeneous part does not +vanish. The direction polynomial packages the top-degree coefficients. -/ + +@[expose] public section +namespace CCDN +noncomputable section +open Polynomial + +/-- Highest homogeneous part, evaluated at outer coordinate one and inner +coordinate the polynomial variable. -/ +def leadingDirectionPolynomial {R : Type*} [CommRing R] (f : R[X][X]) (d : ℕ) : R[X] := + ∑ j ∈ Finset.range (d + 1), monomial j ((f.coeff (d - j)).coeff j) + +/-- The `j`-th coefficient of the leading direction polynomial of degree `d` is the coefficient of +`f` with outer exponent `d - j` and inner exponent `j` when `j ≤ d`, and zero otherwise. -/ +lemma leadingDirectionPolynomial_coeff {R : Type*} [CommRing R] + (f : R[X][X]) (d j : ℕ) : + (leadingDirectionPolynomial f d).coeff j = + if j ≤ d then (f.coeff (d - j)).coeff j else 0 := by + classical + simp [leadingDirectionPolynomial, coeff_monomial] + +private lemma leadingDirectionPolynomial_natDegree_le {R : Type*} [CommRing R] + (f : R[X][X]) (d : ℕ) : (leadingDirectionPolynomial f d).natDegree ≤ d := by + apply natDegree_sum_le_of_forall_le + intro j hj + exact (natDegree_monomial_le _).trans (by simpa only [Finset.mem_range, Nat.lt_succ_iff] using hj) + +private lemma exists_bivariate_top_coefficient {R : Type*} [CommRing R] + (f : R[X][X]) (hf : f ≠ 0) : + ∃ i j : ℕ, i + j = bivariateTotalDegree f ∧ (f.coeff i).coeff j ≠ 0 := by + let g := (bivariatePolynomialEquiv R).symm f + have hg : g ≠ 0 := (map_ne_zero_iff _ (bivariatePolynomialEquiv R).symm.injective).mpr hf + have hs : g.support.Nonempty := by + exact Finset.nonempty_iff_ne_empty.mpr (fun h => hg (MvPolynomial.support_eq_empty.mp h)) + obtain ⟨m, hm, hmax⟩ := Finset.exists_mem_eq_sup g.support hs (fun s => s.sum (fun _ e => e)) + refine ⟨m 0, m 1, ?_, ?_⟩ + · change _ = g.support.sup (fun s => s.sum (fun _ e => e)) + rw [hmax] + conv_rhs => rw [bivariate_exponent_eq m] + exact (bivariate_exponent_sum _ _).symm + · have hc := bivariatePolynomialEquiv_coeff g (m 0) (m 1) + change ((bivariatePolynomialEquiv R ((bivariatePolynomialEquiv R).symm f)).coeff (m 0)).coeff + (m 1) = _ at hc + rw [AlgEquiv.apply_symm_apply, ← bivariate_exponent_eq m] at hc + exact hc.symm ▸ MvPolynomial.mem_support_iff.mp hm + +private lemma leadingDirectionPolynomial_ne_zero {R : Type*} [CommRing R] + (f : R[X][X]) (hf : f ≠ 0) : + leadingDirectionPolynomial f (bivariateTotalDegree f) ≠ 0 := by + obtain ⟨i, j, hdeg, hc⟩ := exists_bivariate_top_coefficient f hf + intro hz + have hh := congrArg (fun q : R[X] => q.coeff j) hz + rw [leadingDirectionPolynomial_coeff, ite_eq_left (by omega), coeff_zero] at hh + have he : bivariateTotalDegree f - j = i := by omega + exact hc (he ▸ hh) + +/-- Among 0,...,d, at least one integer parameter gives a nonvanishing leading +direction. The proof uses only the univariate root bound. -/ +theorem exists_bounded_leading_direction (f : ℤ[X][X]) (hf : f ≠ 0) : + ∃ a : ℕ, a ≤ bivariateTotalDegree f ∧ + (leadingDirectionPolynomial f (bivariateTotalDegree f)).eval (a : ℤ) ≠ 0 := by + let d := bivariateTotalDegree f + let q := leadingDirectionPolynomial f d + by_contra! hn + apply leadingDirectionPolynomial_ne_zero f hf + apply eq_zero_of_natDegree_lt_card_of_eval_eq_zero (ι := Fin (d + 1)) + (f := fun i => (i.val : ℤ)) q + · intro i j h + exact Fin.ext (Int.ofNat_inj.mp h) + · intro i + exact hn i.val (by omega) + · have h := leadingDirectionPolynomial_natDegree_le f d + simpa only [Fintype.card_fin] using Nat.lt_succ_of_le h + +end +end CCDN diff --git a/LeanPool/CCDN/Noether/PlaneShear.lean b/LeanPool/CCDN/Noether/PlaneShear.lean new file mode 100644 index 0000000000..2a83847b9e --- /dev/null +++ b/LeanPool/CCDN/Noether/PlaneShear.lean @@ -0,0 +1,120 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import Mathlib.Algebra.MvPolynomial.Degrees +public import Mathlib.Data.Fin.VecNotation +import LeanPool.CCDN.Noether.BivariateTotalDegree + +/-! Integral plane shears are polynomial-ring automorphisms in every +characteristic. They will place the highest-degree part in the outer variable. -/ + +@[expose] public section +namespace CCDN +noncomputable section +open MvPolynomial + +/-- Fix variable zero and replace variable one by x₁+a*x₀. -/ +def planeShearHom (R : Type*) [CommRing R] (a : R) : + MvPolynomial (Fin 2) R →ₐ[R] MvPolynomial (Fin 2) R := + aeval ![X 0, X 1 + C a * X 0] + +/-- Composing the plane shears with parameters `a` and `b` gives the shear with parameter +`a + b`. -/ +lemma planeShearHom_comp {R : Type*} [CommRing R] (a b : R) : + (planeShearHom R a).comp (planeShearHom R b) = planeShearHom R (a + b) := by + apply MvPolynomial.algHom_ext + intro i + fin_cases i <;> simp [planeShearHom] + ring + +/-- The plane shear with parameter zero is the identity. -/ +@[simp] +lemma planeShearHom_zero (R : Type*) [CommRing R] : + planeShearHom R 0 = AlgHom.id R _ := by + apply MvPolynomial.algHom_ext + intro i + fin_cases i <;> simp [planeShearHom] + +/-- The plane shear `x₁ ↦ x₁ + a * x₀` as an algebra automorphism. The inverse shear uses the +negative parameter, so this works over Z and all its residue fields without determinant +denominators. -/ +def planeShear (R : Type*) [CommRing R] (a : R) : + MvPolynomial (Fin 2) R ≃ₐ[R] MvPolynomial (Fin 2) R := + AlgEquiv.ofAlgHom (planeShearHom R a) (planeShearHom R (-a)) + (by rw [planeShearHom_comp, add_neg_cancel, planeShearHom_zero]) + (by rw [planeShearHom_comp, neg_add_cancel, planeShearHom_zero]) + +private lemma planeShear_monomial {R : Type*} [CommRing R] (a : R) + (m : Fin 2 →₀ ℕ) (c : R) : + planeShear R a (monomial m c) = + C c * (X 0) ^ m 0 * (X 1 + C a * X 0) ^ m 1 := by + change aeval ![X 0, X 1 + C a * X 0] (monomial m c) = _ + rw [aeval_monomial, Finsupp.prod_fintype _ _ (fun _ => pow_zero _)] + simp only [algebraMap_eq, Fin.prod_univ_two, Matrix.cons_val_zero, Matrix.cons_val_one, + Matrix.cons_val_fin_one] + ring + +private lemma planeShear_monomial_totalDegree_le {R : Type*} [CommRing R] [Nontrivial R] + (a : R) (m : Fin 2 →₀ ℕ) (c : R) : + (planeShear R a (monomial m c)).totalDegree ≤ m.sum (fun _ e => e) := by + have hx : (X 1 + C a * X 0 : MvPolynomial (Fin 2) R).totalDegree ≤ 1 := by + apply (totalDegree_add _ _).trans + apply max_le + · simp + · simpa using totalDegree_mul (C a : MvPolynomial (Fin 2) R) (X 0) + rw [planeShear_monomial] + have h₁ := totalDegree_mul (C c : MvPolynomial (Fin 2) R) ((X 0) ^ m 0) + have h₂ := totalDegree_mul (C c * (X 0) ^ m 0 : MvPolynomial (Fin 2) R) ((X 1 + C a * X 0) ^ m 1) + have h₃ := totalDegree_pow (X 1 + C a * X 0 : MvPolynomial (Fin 2) R) (m 1) + simp only [totalDegree_C, totalDegree_X_pow, zero_add] at h₁ + have hs : m.sum (fun _ e => e) = m 0 + m 1 := by + conv_lhs => rw [bivariate_exponent_eq m] + exact bivariate_exponent_sum _ _ + rw [hs] + have h₄ : ((X 1 + C a * X 0 : MvPolynomial (Fin 2) R) ^ m 1).totalDegree ≤ m 1 := + h₃.trans (by simpa using Nat.mul_le_mul_left (m 1) hx) + omega + +private lemma planeShear_totalDegree_le {R : Type*} [CommRing R] [Nontrivial R] + (a : R) (f : MvPolynomial (Fin 2) R) : + (planeShear R a f).totalDegree ≤ f.totalDegree := by + classical + conv_lhs => rw [f.as_sum, map_sum] + apply (totalDegree_finsetSum _ _).trans + apply Finset.sup_le + intro m hm + exact (planeShear_monomial_totalDegree_le a m (f.coeff m)).trans (le_totalDegree hm) + +/-- A plane shear preserves the total degree; the inverse shear gives the reverse inequality. -/ +lemma planeShear_totalDegree {R : Type*} [CommRing R] [Nontrivial R] + (a : R) (f : MvPolynomial (Fin 2) R) : + (planeShear R a f).totalDegree = f.totalDegree := by + apply le_antisymm (planeShear_totalDegree_le a f) + have h := planeShear_totalDegree_le (-a) (planeShear R a f) + have he : planeShear R (-a) (planeShear R a f) = f := (planeShear R a).symm_apply_apply f + rw [he] at h + exact h + +/-- `planeShear` commutes with every coefficient map. -/ +lemma planeShear_map {R S : Type*} [CommRing R] [CommRing S] + (φ : R →+* S) (a : R) (f : MvPolynomial (Fin 2) R) : + MvPolynomial.map φ (planeShear R a f) = + planeShear S (φ a) (MvPolynomial.map φ f) := by + suffices hh : (MvPolynomial.map φ).comp (planeShear R a).toRingHom = + (planeShear S (φ a)).toRingHom.comp (MvPolynomial.map φ) from + congrArg (fun h : MvPolynomial (Fin 2) R →+* MvPolynomial (Fin 2) S => h f) hh + apply MvPolynomial.ringHom_ext + · intro c + simp [planeShear, planeShearHom] + · intro i + fin_cases i <;> simp [planeShear, planeShearHom] + +end +end CCDN diff --git a/LeanPool/CCDN/Noether/PowerSeriesBaseChange.lean b/LeanPool/CCDN/Noether/PowerSeriesBaseChange.lean new file mode 100644 index 0000000000..78ea86db3f --- /dev/null +++ b/LeanPool/CCDN/Noether/PowerSeriesBaseChange.lean @@ -0,0 +1,84 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import Mathlib.LinearAlgebra.LinearIndependent.Defs +public import Mathlib.RingTheory.PowerSeries.Basic +import LeanPool.CCDN.ForMathlib.Matrix + +/-! Finite linear independence of formal power series survives arbitrary +field extension. A finite nonzero coefficient minor witnesses independence. -/ + +@[expose] public section +namespace CCDN +noncomputable section +open Matrix + +private theorem powerSeries_linearIndependent_map + {K L ι : Type*} [Field K] [Field L] [Algebra K L] [Finite ι] + (v : ι → PowerSeries K) (hv : LinearIndependent K v) : + LinearIndependent L (fun i => PowerSeries.map (algebraMap K L) (v i)) := by + classical + let := Fintype.ofFinite ι + let A : Matrix ℕ ι K := fun n i => PowerSeries.coeff n (v i) + have hcols : LinearIndependent K A.col := by + rw [Fintype.linearIndependent_iff] + intro c hc i + apply (Fintype.linearIndependent_iff.mp hv) c _ i + apply PowerSeries.ext + intro n + have h := congrFun hc n + simp only [Finset.sum_apply, Pi.smul_apply, Pi.zero_apply] at h + change (∑ x, c x • PowerSeries.coeff n (v x)) = 0 at h + simpa only [map_sum, PowerSeries.coeff_smul, map_zero] using h + have hrank : A.rank = Fintype.card ι := by + rw [Matrix.rank_eq_finrank_span_cols] + exact (linearIndependent_iff_card_eq_finrank_span.mp hcols).symm + obtain ⟨a, c, _, _, hdet⟩ := + Matrix.exists_det_submatrix_ne_zero_of_le_rank A (Fintype.card ι) hrank.ge + let B : Matrix ℕ ι L := A.map (algebraMap K L) + have hdetB : (B.submatrix a c).det ≠ 0 := by + have hm : (B.submatrix a c).det = algebraMap K L ((A.submatrix a c).det) := by + rw [RingHom.map_det] + rfl + rw [hm] + exact (map_ne_zero (algebraMap K L)).mpr hdet + have hrankB : Fintype.card ι ≤ B.rank := B.le_rank_of_det_submatrix_ne_zero a c hdetB + have hker : B.mulVecLin.ker = ⊥ := by + apply Submodule.finrank_eq_zero.mp + have h := B.mulVecLin.finrank_range_add_finrank_ker + rw [Module.finrank_pi] at h + change B.rank + Module.finrank L B.mulVecLin.ker = Fintype.card ι at h + omega + rw [Fintype.linearIndependent_iff] + intro w hw i + have hz : B.mulVecLin w = 0 := by + ext n + have h := congrArg (PowerSeries.coeff n) hw + simpa only [B, A, Matrix.mulVecLin_apply, Matrix.mulVec, dotProduct, Matrix.map, + Matrix.of_apply, Pi.zero_apply, map_sum, PowerSeries.coeff_smul, PowerSeries.coeff_map, + map_zero, smul_eq_mul, mul_comm] using h + have hmem : w ∈ B.mulVecLin.ker := hz + rw [hker, Submodule.mem_bot] at hmem + exact congrFun hmem i + +/-- Independence survives coefficient extension even for an infinite family: +each relation involves only a finite subfamily. -/ +theorem powerSeries_linearIndependent_map_of_linearIndependent + {K L ι : Type*} [Field K] [Field L] [Algebra K L] + (v : ι → PowerSeries K) (hv : LinearIndependent K v) : + LinearIndependent L (fun i => PowerSeries.map (algebraMap K L) (v i)) := by + classical + rw [linearIndependent_iff_finset_linearIndependent] + intro s + exact powerSeries_linearIndependent_map (v ∘ (Subtype.val : s → ι)) + (hv.comp _ Subtype.val_injective) + +end +end CCDN diff --git a/LeanPool/CCDN/Noether/PowerSeriesFrobenius.lean b/LeanPool/CCDN/Noether/PowerSeriesFrobenius.lean new file mode 100644 index 0000000000..dfca7c9ecf --- /dev/null +++ b/LeanPool/CCDN/Noether/PowerSeriesFrobenius.lean @@ -0,0 +1,108 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import Mathlib.RingTheory.PowerSeries.Basic +import Mathlib.Algebra.CharP.Algebra +import Mathlib.Algebra.CharP.Lemmas +import Mathlib.RingTheory.PowerSeries.NoZeroDivisors + +/-! Frobenius noncancellation from injectivity of finite coefficient jets. +These are statements about power series in prime-power characteristic. -/ + +@[expose] public section +namespace CCDN +noncomputable section +open PowerSeries + +private lemma powerSeries_frobenius_constant_congruence {K : Type*} [Field K] + (p r : ℕ) [Fact p.Prime] [CharP K p] (g : PowerSeries K) : + PowerSeries.X ^ (p ^ r) ∣ g ^ (p ^ r) - PowerSeries.C (g.constantCoeff ^ (p ^ r)) := by + let : CharP (PowerSeries K) p := + charP_of_injective_algebraMap (algebraMap K (PowerSeries K)).injective p + have hh : (PowerSeries.X : PowerSeries K) ∣ g - PowerSeries.C g.constantCoeff := + PowerSeries.X_dvd_iff.mpr (by simp) + have ht := pow_dvd_pow_of_dvd hh (p ^ r) + rw [sub_pow_char_pow, ← map_pow] at ht + exact ht + +/-- A relation with Frobenius-powered series coefficients forces all their +constant terms to vanish if the low coefficient jets of f are independent. -/ +private lemma powerSeries_frobenius_relation_constants {K ι : Type*} [Field K] + [Fintype ι] (p r : ℕ) [Fact p.Prime] [CharP K p] + (f g : ι → PowerSeries K) + (hjet : ∀ a : ι → K, (∀ n < p ^ r, PowerSeries.coeff n (∑ i, a i • f i) = 0) → ∀ i, a i = 0) + (hrel : ∑ i, f i * (g i) ^ (p ^ r) = 0) : ∀ i, (g i).constantCoeff = 0 := by + classical + have hdiff : PowerSeries.X ^ (p ^ r) ∣ + (∑ i, f i * (g i) ^ (p ^ r)) - ∑ i, ((g i).constantCoeff ^ (p ^ r)) • f i := by + rw [← Finset.sum_sub_distrib] + apply Finset.dvd_sum + intro i _ + have hh := dvd_mul_of_dvd_right (powerSeries_frobenius_constant_congruence p r (g i)) (f i) + convert hh using 1 + rw [Algebra.smul_def, PowerSeries.algebraMap_eq] + ring + rw [hrel, zero_sub] at hdiff + have hzero := hjet (fun i => (g i).constantCoeff ^ (p ^ r)) + (PowerSeries.X_pow_dvd_iff.mp (dvd_neg.mp hdiff)) + intro i + exact (eq_zero_of_pow_eq_zero (hzero i)) + +/-- A finite family of series has a common lowest power of X; after dividing +it out, at least one remaining series has nonzero constant coefficient. -/ +private lemma powerSeries_common_order_factor {K ι : Type*} [Field K] [Finite ι] + (g : ι → PowerSeries K) (hg : ∃ i, g i ≠ 0) : + ∃ (m : ℕ) (h : ι → PowerSeries K), (∀ i, g i = PowerSeries.X ^ m * h i) ∧ + ∃ i, (h i).constantCoeff ≠ 0 := by + classical + let := Fintype.ofFinite ι + let S := Finset.univ.filter (fun i => g i ≠ 0) + have hS : S.Nonempty := by obtain ⟨i, hi⟩ := hg; exact ⟨i, by simp [S, hi]⟩ + obtain ⟨j, hj, hmin⟩ := S.exists_min_image (fun i => (g i).order.toNat) hS + have hj0 : g j ≠ 0 := (Finset.mem_filter.mp hj).2 + let m := (g j).order.toNat + have hdiv (i : ι) : PowerSeries.X ^ m ∣ g i := by + by_cases hi : g i = 0 + · simp [hi] + · have hle := hmin i (by simp [S, hi]) + exact (pow_dvd_pow PowerSeries.X hle).trans (PowerSeries.X_pow_order_dvd (φ := g i)) + choose h hh using hdiv + refine ⟨m, h, hh, j, ?_⟩ + have hc := PowerSeries.coeff_order hj0 + change PowerSeries.coeff m (g j) ≠ 0 at hc + rw [hh j] at hc + simpa only [PowerSeries.coeff_X_pow_mul', + le_refl, ite_true, Nat.sub_self, PowerSeries.coeff_zero_eq_constantCoeff_apply] using hc + +/-- Independent jets of length p^r prevent any nontrivial relation with +p^r-th-power power-series coefficients. -/ +theorem powerSeries_frobenius_relation_trivial {K ι : Type*} [Field K] + [Fintype ι] (p r : ℕ) [Fact p.Prime] [CharP K p] + (f : ι → PowerSeries K) + (hjet : ∀ a : ι → K, (∀ n < p ^ r, PowerSeries.coeff n (∑ i, a i • f i) = 0) → ∀ i, a i = 0) + (g : ι → PowerSeries K) (hrel : ∑ i, f i * (g i) ^ (p ^ r) = 0) : ∀ i, g i = 0 := by + classical + by_contra! hn + obtain ⟨m, h, hh, j, hj⟩ := powerSeries_common_order_factor g hn + have hfactor : (∑ i, f i * (g i) ^ (p ^ r)) = + (PowerSeries.X : PowerSeries K) ^ (m * (p ^ r)) * ∑ i, f i * (h i) ^ (p ^ r) := by + rw [Finset.mul_sum] + apply Finset.sum_congr rfl + intro i _ + rw [hh i, mul_pow, ← pow_mul] + ring + have hX : (PowerSeries.X : PowerSeries K) ^ (m * (p ^ r)) ≠ 0 := + pow_ne_zero _ PowerSeries.X_ne_zero + rw [hfactor] at hrel + have hz := (mul_eq_zero.mp hrel).resolve_left hX + exact hj (powerSeries_frobenius_relation_constants p r f h hjet hz j) + +end +end CCDN diff --git a/LeanPool/CCDN/Noether/RationalDifferential.lean b/LeanPool/CCDN/Noether/RationalDifferential.lean new file mode 100644 index 0000000000..2a499dfaa2 --- /dev/null +++ b/LeanPool/CCDN/Noether/RationalDifferential.lean @@ -0,0 +1,103 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import Mathlib.FieldTheory.IsAlgClosed.Basic +public import Mathlib.FieldTheory.RatFunc.Basic +public import Mathlib.RingTheory.Derivation.DifferentialRing +public import Mathlib.RingTheory.SimpleRing.Principal +import Mathlib.FieldTheory.RatFunc.AsPolynomial + +/-! The formal derivative on a rational function field and its constant field. -/ + +@[expose] public section +namespace CCDN +noncomputable section +open Polynomial + +variable {K : Type*} [Field K] + +/-- The quotient-rule derivative `(p' * q - p * q') / q ^ 2` of the fraction `p / q`. -/ +def rationalDerivativeFraction (p q : K[X]) : RatFunc K := + (algebraMap K[X] (RatFunc K) p.derivative * algebraMap K[X] (RatFunc K) q - + algebraMap K[X] (RatFunc K) p * algebraMap K[X] (RatFunc K) q.derivative) / + (algebraMap K[X] (RatFunc K) q) ^ 2 + +/-- The quotient-rule derivative of a fraction is unchanged by multiplying its numerator and +denominator by the same nonzero polynomial. -/ +lemma rationalDerivativeFraction_mul {p q a : K[X]} (hq : q ≠ 0) (ha : a ≠ 0) : + rationalDerivativeFraction (a * p) (a * q) = rationalDerivativeFraction p q := by + simp only [rationalDerivativeFraction, derivative_mul, map_add, map_mul] + field_simp [RatFunc.algebraMap_ne_zero hq, RatFunc.algebraMap_ne_zero ha] + ring + +/-- Formal rational differentiation, well-defined under changing fractions. -/ +def rationalDerivative (x : RatFunc K) : RatFunc K := + x.liftOn' rationalDerivativeFraction (fun _hq _ha => rationalDerivativeFraction_mul _hq _ha) + +private lemma rationalDerivative_div (p q : K[X]) : + rationalDerivative (algebraMap K[X] (RatFunc K) p / algebraMap K[X] (RatFunc K) q) = + rationalDerivativeFraction p q := by + rw [rationalDerivative, RatFunc.liftOn'_div] + intro p + simp [rationalDerivativeFraction] + +/-- The formal rational derivative of a polynomial is its ordinary derivative. -/ +lemma rationalDerivative_polynomial (p : K[X]) : + rationalDerivative (algebraMap K[X] (RatFunc K) p) = + algebraMap K[X] (RatFunc K) p.derivative := by + simpa [rationalDerivativeFraction] using rationalDerivative_div p 1 + +/-- The formal rational derivative is additive. -/ +lemma rationalDerivative_add (x y : RatFunc K) : + rationalDerivative (x + y) = rationalDerivative x + rationalDerivative y := by + induction x using RatFunc.induction_on with | _ p q hq + induction y using RatFunc.induction_on with | _ r s hs + have hq' := RatFunc.algebraMap_ne_zero hq + have hs' := RatFunc.algebraMap_ne_zero hs + rw [div_add_div _ _ hq' hs', ← map_mul, ← map_mul, ← map_add, ← map_mul, + rationalDerivative_div, rationalDerivative_div, rationalDerivative_div] + simp only [rationalDerivativeFraction, derivative_mul, map_add, map_mul] + field_simp + ring + +/-- The formal rational derivative satisfies the Leibniz rule. -/ +lemma rationalDerivative_mul (x y : RatFunc K) : + rationalDerivative (x * y) = x * rationalDerivative y + y * rationalDerivative x := by + induction x using RatFunc.induction_on with | _ p q hq + induction y using RatFunc.induction_on with | _ r s hs + rw [div_mul_div_comm, ← map_mul, ← map_mul, + rationalDerivative_div, rationalDerivative_div, rationalDerivative_div] + simp only [rationalDerivativeFraction, derivative_mul, map_add, map_mul] + field_simp + ring + +/-- Rational formal differentiation as a derivation over the coefficient field. -/ +def rationalDerivation (K : Type*) [Field K] : Derivation K (RatFunc K) (RatFunc K) where + toFun := rationalDerivative + map_add' := rationalDerivative_add + map_smul' c x := by + rw [Algebra.smul_def, rationalDerivative_mul, Algebra.smul_def] + have hc : rationalDerivative (algebraMap K (RatFunc K) c) = 0 := by + rw [IsScalarTower.algebraMap_apply K K[X] (RatFunc K), rationalDerivative_polynomial] + simp + simp only [hc, mul_zero, add_zero, RingHom.id_apply] + map_one_eq_zero' := by + simpa using rationalDerivative_polynomial (1 : K[X]) + leibniz' x y := rationalDerivative_mul x y + +/-- The differential field structure on `RatFunc K` given by formal rational differentiation; it +is kept explicit to avoid introducing a competing global instance on rational functions. -/ +@[reducible] +def rationalDifferential (K : Type*) [Field K] : Differential (RatFunc K) := by + let : Algebra ℤ (RatFunc K) := Ring.toIntAlgebra _ + exact ⟨(rationalDerivation K).restrictScalars ℤ⟩ + +end +end CCDN diff --git a/LeanPool/CCDN/Noether/ShearNormalization.lean b/LeanPool/CCDN/Noether/ShearNormalization.lean new file mode 100644 index 0000000000..0d5f898c54 --- /dev/null +++ b/LeanPool/CCDN/Noether/ShearNormalization.lean @@ -0,0 +1,225 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.AuxiliaryPolynomial.ConstantLeadingBadReduction +public import LeanPool.CCDN.Noether.LeadingDirection +public import LeanPool.CCDN.Noether.PlaneShear + +/-! Bounded integral shears give full outer degree. -/ + +@[expose] public section +namespace CCDN +noncomputable section +open Polynomial + +/-- Variable zero corresponds to the outer variable of the nested polynomial ring. -/ +lemma bivariatePolynomialEquiv_X_zero {R : Type*} [CommRing R] : + bivariatePolynomialEquiv R (MvPolynomial.X 0) = (X : R[X][X]) := by + simp [bivariatePolynomialEquiv, MvPolynomial.finSuccEquiv_X_zero] + +/-- Variable one corresponds to the inner variable of the nested polynomial ring. -/ +lemma bivariatePolynomialEquiv_X_one {R : Type*} [CommRing R] : + bivariatePolynomialEquiv R (MvPolynomial.X 1) = (C X : R[X][X]) := by + change Polynomial.map (MvPolynomial.uniqueAlgEquiv R (Fin 1)).toRingHom + (MvPolynomial.finSuccEquiv R 1 (MvPolynomial.X (Fin.succ (0 : Fin 1)))) = _ + rw [MvPolynomial.finSuccEquiv_X_succ, Polynomial.map_C] + simp + +/-- Constants correspond to constants of the nested polynomial ring. -/ +lemma bivariatePolynomialEquiv_C {R : Type*} [CommRing R] (a : R) : + bivariatePolynomialEquiv R (MvPolynomial.C a) = C (C a) := by + exact (bivariatePolynomialEquiv R).commutes a + +/-- The integral shear in nested polynomial coordinates. -/ +def bivariateShear (R : Type*) [CommRing R] (a : R) : R[X][X] ≃ₐ[R] R[X][X] := + (bivariatePolynomialEquiv R).symm.trans ((planeShear R a).trans (bivariatePolynomialEquiv R)) + +/-- Restrict the sheared polynomial to the outer coordinate axis. -/ +private def directionAxisHom (R : Type*) [CommRing R] (a : R) : + MvPolynomial (Fin 2) R →+* R[X] := + MvPolynomial.eval₂Hom C ![X, C a * X] + +private lemma directionAxisHom_eq_shear_axis {R : Type*} [CommRing R] (a : R) + (f : MvPolynomial (Fin 2) R) : + directionAxisHom R a f = + Polynomial.map (Polynomial.evalRingHom 0) + (bivariatePolynomialEquiv R (planeShear R a f)) := by + suffices hh : directionAxisHom R a = + (Polynomial.mapRingHom (Polynomial.evalRingHom 0)).comp + ((bivariatePolynomialEquiv R).toRingHom.comp (planeShear R a).toRingHom) from + congrArg (fun h : MvPolynomial (Fin 2) R →+* R[X] => h f) hh + apply MvPolynomial.ringHom_ext + · intro c + simp [directionAxisHom, planeShear, planeShearHom, bivariatePolynomialEquiv_C] + · intro i + fin_cases i <;> + simp [directionAxisHom, planeShear, planeShearHom, + bivariatePolynomialEquiv_X_zero, bivariatePolynomialEquiv_X_one, bivariatePolynomialEquiv_C] + +private lemma directionAxisHom_monomial {R : Type*} [CommRing R] + (a : R) (m : Fin 2 →₀ ℕ) (c : R) : + directionAxisHom R a (MvPolynomial.monomial m c) = monomial (m 0 + m 1) (c * a ^ m 1) := by + change MvPolynomial.eval₂Hom C ![X, C a * X] (MvPolynomial.monomial m c) = _ + rw [MvPolynomial.eval₂Hom_monomial, Finsupp.prod_fintype _ _ (fun _ => pow_zero _)] + simp only [Fin.prod_univ_two, Matrix.cons_val_zero, Matrix.cons_val_one, Matrix.cons_val_fin_one] + rw [← C_mul_X_pow_eq_monomial, mul_pow, ← map_pow, map_mul, pow_add] + ring + +/-- A two-variable exponent vector `m` equals `Finsupp.cons i (Finsupp.single 0 j)` if and only if +its components are `i` and `j`. -/ +lemma bivariate_exponent_eq_iff (m : Fin 2 →₀ ℕ) (i j : ℕ) : + Finsupp.cons i (Finsupp.single 0 j) = m ↔ m 0 = i ∧ m 1 = j := by + constructor + · intro h + have h₀ := congrArg (fun s : Fin 2 →₀ ℕ => s 0) h + have h₁ := congrArg (fun s : Fin 2 →₀ ℕ => s (Fin.succ (0 : Fin 1))) h + simp only [Finsupp.cons_zero] at h₀ + simp only [Finsupp.cons_succ, Finsupp.single_eq_same] at h₁ + exact ⟨h₀.symm, h₁.symm⟩ + · rintro ⟨h₀,h₁⟩ + rw [← h₀, ← h₁, ← bivariate_exponent_eq m] + +private lemma leadingDirectionPolynomial_monomial {R : Type*} [CommRing R] + (m : Fin 2 →₀ ℕ) (c : R) (d : ℕ) : + leadingDirectionPolynomial (bivariatePolynomialEquiv R (MvPolynomial.monomial m c)) d = + if m 0 + m 1 = d then monomial (m 1) c else 0 := by + classical + by_cases hsum : m 0 + m 1 = d + · rw [ite_eq_left hsum] + ext j + rw [leadingDirectionPolynomial_coeff, bivariatePolynomialEquiv_coeff, coeff_monomial] + have he : m = Finsupp.cons (d - j) (Finsupp.single 0 j) ↔ m 1 = j := by + rw [eq_comm, bivariate_exponent_eq_iff] + omega + simp only [MvPolynomial.coeff_monomial, he] + by_cases hj : m 1 = j + · have hjd : j ≤ d := by omega + simp [hj, hjd] + · simp [hj] + · rw [ite_eq_right hsum] + ext j + rw [leadingDirectionPolynomial_coeff, bivariatePolynomialEquiv_coeff, coeff_zero] + by_cases hj : j ≤ d + · have he : m ≠ Finsupp.cons (d - j) (Finsupp.single 0 j) := by + intro h + have hh := (bivariate_exponent_eq_iff m (d - j) j).mp h.symm + omega + simp [MvPolynomial.coeff_monomial, hj, he] + · simp [hj] + +private lemma leadingDirectionPolynomial_add {R : Type*} [CommRing R] + (f g : R[X][X]) (d : ℕ) : + leadingDirectionPolynomial (f + g) d = + leadingDirectionPolynomial f d + leadingDirectionPolynomial g d := by + simp [leadingDirectionPolynomial, Finset.sum_add_distrib] + +/-- The direction polynomial computes the leading axis coefficient. -/ +private theorem directionAxisHom_coeff {R : Type*} [CommRing R] + (a : R) (f : MvPolynomial (Fin 2) R) (d : ℕ) : + (directionAxisHom R a f).coeff d = + (leadingDirectionPolynomial (bivariatePolynomialEquiv R f) d).eval a := by + induction f using MvPolynomial.induction_on' with + | add f g hf hg => + simp only [map_add, coeff_add, leadingDirectionPolynomial_add, eval_add, hf, hg] + | monomial m c => + rw [directionAxisHom_monomial, leadingDirectionPolynomial_monomial, coeff_monomial] + split_ifs <;> simp [eval_monomial] + +private lemma bivariateShear_totalDegree {R : Type*} [CommRing R] [Nontrivial R] + (a : R) (f : R[X][X]) : + bivariateTotalDegree (bivariateShear R a f) = bivariateTotalDegree f := by + unfold bivariateTotalDegree bivariateShear + simp only [AlgEquiv.trans_apply, AlgEquiv.symm_apply_apply] + exact planeShear_totalDegree _ _ + +/-- A plane shear preserves irreducibility of bivariate polynomials. -/ +lemma bivariateShear_irreducible_iff {R : Type*} [CommRing R] + (a : R) (f : R[X][X]) : Irreducible (bivariateShear R a f) ↔ Irreducible f := + MulEquiv.irreducible_iff (f := bivariateShear R a) + +private lemma bivariateShear_axis_coeff {R : Type*} [CommRing R] + (a : R) (f : R[X][X]) (d : ℕ) : + ((bivariateShear R a f).coeff d).coeff 0 = (leadingDirectionPolynomial f d).eval a := by + have h := congrArg (fun q : R[X] => q.coeff d) + (directionAxisHom_eq_shear_axis a ((bivariatePolynomialEquiv R).symm f)) + rw [directionAxisHom_coeff, AlgEquiv.apply_symm_apply, coeff_map] at h + change (leadingDirectionPolynomial f d).eval a = + Polynomial.eval 0 ((bivariateShear R a f).coeff d) at h + rw [← Polynomial.coeff_zero_eq_eval_zero] at h + exact h.symm + +/-- Every nonzero integer plane equation admits a shear with parameter in +0,...,d whose outer degree is exactly its original total degree. -/ +theorem exists_bounded_full_outer_shear (f : ℤ[X][X]) (hf : f ≠ 0) : + ∃ a : ℕ, a ≤ bivariateTotalDegree f ∧ + (bivariateShear ℤ (a : ℤ) f).natDegree = bivariateTotalDegree f ∧ + bivariateTotalDegree (bivariateShear ℤ (a : ℤ) f) = bivariateTotalDegree f := by + obtain ⟨a, ha, he⟩ := exists_bounded_leading_direction f hf + refine ⟨a, ha, ?_, bivariateShear_totalDegree _ _⟩ + have hc : ((bivariateShear ℤ (a : ℤ) f).coeff (bivariateTotalDegree f)).coeff 0 ≠ 0 := by + rw [bivariateShear_axis_coeff] + exact he + have hp : (bivariateShear ℤ (a : ℤ) f).coeff (bivariateTotalDegree f) ≠ 0 := by + intro hz + exact hc (by rw [hz, coeff_zero]) + have hlo := le_natDegree_of_ne_zero hp + have hhi := bivariate_natDegree_le_totalDegree (bivariateShear ℤ (a : ℤ) f) + rw [bivariateShear_totalDegree] at hhi + omega + +/-- The identification of two-variable polynomials with nested polynomials commutes with applying a +coefficient ring homomorphism. -/ +lemma bivariatePolynomialEquiv_map {R S : Type*} [CommRing R] [CommRing S] + (φ : R →+* S) (f : MvPolynomial (Fin 2) R) : + bivariateMap φ (bivariatePolynomialEquiv R f) = + bivariatePolynomialEquiv S (MvPolynomial.map φ f) := by + ext i j + simp only [bivariateMap_coeff, bivariatePolynomialEquiv_coeff, MvPolynomial.coeff_map] + +/-- The nested plane shear corresponds to the plane shear of two-variable polynomials. -/ +lemma bivariateShear_equiv {R : Type*} [CommRing R] + (a : R) (f : MvPolynomial (Fin 2) R) : + bivariateShear R a (bivariatePolynomialEquiv R f) = + bivariatePolynomialEquiv R (planeShear R a f) := by + simp only [bivariateShear, AlgEquiv.trans_apply, AlgEquiv.symm_apply_apply] + +/-- Applying a coefficient ring homomorphism `φ` commutes with shearing, the shear parameter `a` +becoming `φ a`. -/ +lemma bivariateShear_map {R S : Type*} [CommRing R] [CommRing S] + (φ : R →+* S) (a : R) (f : R[X][X]) : + bivariateMap φ (bivariateShear R a f) = + bivariateShear S (φ a) (bivariateMap φ f) := by + obtain ⟨g, rfl⟩ := (bivariatePolynomialEquiv R).surjective f + simp only [bivariateShear_equiv, bivariatePolynomialEquiv_map, planeShear_map] + +/-- Shearing does not change the set of bad primes, including reductions +where the degree drops. -/ +theorem bivariateBadReduction_shear_iff (a : ℤ) (f : ℤ[X][X]) (d p : ℕ) : + BivariateBadReduction (bivariateShear ℤ a f) d p ↔ BivariateBadReduction f d p := by + constructor + · rintro ⟨hp, hbad⟩ + refine ⟨hp, ?_⟩ + let : Fact p.Prime := ⟨hp⟩ + change bivariateTotalDegree (bivariateMap (Int.castRingHom (AlgebraicClosure (ZMod p))) + (bivariateShear ℤ a f)) ≠ d ∨ ¬Irreducible + (bivariateMap (Int.castRingHom (AlgebraicClosure (ZMod p))) (bivariateShear ℤ a f)) at hbad + rw [bivariateShear_map, bivariateShear_totalDegree, bivariateShear_irreducible_iff] at hbad + exact hbad + · rintro ⟨hp, hbad⟩ + refine ⟨hp, ?_⟩ + let : Fact p.Prime := ⟨hp⟩ + change bivariateTotalDegree (bivariateMap (Int.castRingHom (AlgebraicClosure (ZMod p))) + (bivariateShear ℤ a f)) ≠ d ∨ ¬Irreducible + (bivariateMap (Int.castRingHom (AlgebraicClosure (ZMod p))) (bivariateShear ℤ a f)) + rw [bivariateShear_map, bivariateShear_totalDegree, bivariateShear_irreducible_iff] + exact hbad + +end +end CCDN diff --git a/LeanPool/CCDN/Noether/UniversalHomogeneousForm.lean b/LeanPool/CCDN/Noether/UniversalHomogeneousForm.lean new file mode 100644 index 0000000000..a1e5ead09e --- /dev/null +++ b/LeanPool/CCDN/Noether/UniversalHomogeneousForm.lean @@ -0,0 +1,114 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.Algebra.HomogeneousMonomials +public import LeanPool.CCDN.Noether.UniversalPlane +public import Mathlib.CategoryTheory.Category.Basic +public import Mathlib.RingTheory.MvPolynomial + +/-! A homogeneous form with independent integer coefficient variables, +and its exact specialization to every homogeneous equation. -/ + +@[expose] public section +namespace CCDN +noncomputable section +open MvPolynomial + +/-- The exponent vector of degree `d` in the variables `σ` corresponding to a multiset `i` of size +`d`. -/ +def homogeneousCoefficientExponent (σ : Type*) [Fintype σ] [DecidableEq σ] + (d : ℕ) (i : Sym σ d) : σ →₀ ℕ := + ((degreeEqEquivSym σ d).symm i).val + +/-- The exponent vector `homogeneousCoefficientExponent σ d i` has degree `d`. -/ +lemma homogeneousCoefficientExponent_degree (σ : Type*) [Fintype σ] [DecidableEq σ] + (d : ℕ) (i : Sym σ d) : (homogeneousCoefficientExponent σ d i).degree = d := + ((degreeEqEquivSym σ d).symm i).property + +private lemma homogeneousCoefficientExponent_injective (σ : Type*) [Fintype σ] [DecidableEq σ] + (d : ℕ) : Function.Injective (homogeneousCoefficientExponent σ d) := + Subtype.val_injective.comp (degreeEqEquivSym σ d).symm.injective + +/-- The universal homogeneous form of degree `d` in the variables `σ`: the sum of all monomials of +degree `d`, each with its own independent integer coefficient variable. -/ +def universalHomogeneousForm (σ : Type*) [Fintype σ] [DecidableEq σ] (d : ℕ) : + MvPolynomial σ (MvPolynomial (Sym σ d) ℤ) := + ∑ i, monomial (homogeneousCoefficientExponent σ d i) (X i) + +/-- The coefficient of `f` at the degree-`d` monomial indexed by `i`. -/ +def homogeneousCoefficientData {R σ : Type*} [CommRing R] [Fintype σ] [DecidableEq σ] + (d : ℕ) (f : MvPolynomial σ R) (i : Sym σ d) : R := + f.coeff (homogeneousCoefficientExponent σ d i) + +/-- A homogeneous polynomial `f` of degree `d` is the sum of its degree-`d` monomials weighted by +`homogeneousCoefficientData d f`. -/ +theorem homogeneousCoefficientData_reconstruct {R σ : Type*} [CommRing R] + [Fintype σ] [DecidableEq σ] (d : ℕ) + (f : MvPolynomial σ R) (hf : f.IsHomogeneous d) : + ∑ i : Sym σ d, monomial (homogeneousCoefficientExponent σ d i) + (homogeneousCoefficientData d f i) = f := by + classical + ext m + rw [coeff_sum] + by_cases hm : m.degree = d + · let i : Sym σ d := degreeEqEquivSym σ d ⟨m, hm⟩ + have hi : homogeneousCoefficientExponent σ d i = m := by + exact congrArg Subtype.val ((degreeEqEquivSym σ d).symm_apply_apply ⟨m, hm⟩) + rw [Finset.sum_eq_single i] + · rw [coeff_monomial, hi, ite_eq_left rfl] + exact congrArg f.coeff hi + · intro j _ hji + rw [coeff_monomial, ite_eq_right] + intro hj + exact hji (homogeneousCoefficientExponent_injective σ d (hj.trans hi.symm)) + · simp + · rw [hf.coeff_eq_zero hm] + apply Finset.sum_eq_zero + intro i _ + rw [coeff_monomial, ite_eq_right] + intro hi + exact hm (hi ▸ homogeneousCoefficientExponent_degree σ d i) + +/-- The ring homomorphism from the integer polynomials in the coefficient variables to `R` that +sends each coefficient variable to the corresponding coefficient of `f`. -/ +def homogeneousCoefficientSpecialization {R σ : Type*} [CommRing R] [Fintype σ] [DecidableEq σ] + (d : ℕ) (f : MvPolynomial σ R) : MvPolynomial (Sym σ d) ℤ →+* R := + eval₂Hom (Int.castRingHom R) (homogeneousCoefficientData d f) + +/-- The coefficient variables specialize to the homogeneous equation, +over every commutative coefficient ring. -/ +theorem universalHomogeneousForm_specialize {R σ : Type*} [CommRing R] + [Fintype σ] [DecidableEq σ] (d : ℕ) + (f : MvPolynomial σ R) (hf : f.IsHomogeneous d) : + MvPolynomial.map (homogeneousCoefficientSpecialization d f) (universalHomogeneousForm σ d) = + f := by + rw [universalHomogeneousForm, map_sum] + have hX (i : Sym σ d) : homogeneousCoefficientSpecialization d f (X i) = + homogeneousCoefficientData d f i := eval₂_X _ _ _ + simpa only [map_monomial, hX] using homogeneousCoefficientData_reconstruct d f hf + +/-- The universal homogeneous form is the sum over all degree-`d` monomials of the coefficient +variable times that monomial, an expansion linear in the coefficient variables. -/ +lemma universalHomogeneousForm_linear_expansion (σ : Type*) [Fintype σ] [DecidableEq σ] + (d : ℕ) : + universalHomogeneousForm σ d = + ∑ i : Sym σ d, (X i : MvPolynomial (Sym σ d) ℤ) • + MvPolynomial.map (C : ℤ →+* MvPolynomial (Sym σ d) ℤ) + (monomial (homogeneousCoefficientExponent σ d i) (1 : ℤ)) := by + simp only [universalHomogeneousForm, map_monomial, map_one, smul_monomial, smul_eq_mul, mul_one] + +/-- The universal plane restriction `universalPlanePolynomial` as an `R`-linear map. -/ +def universalPlanePolynomialLinear (R σ : Type*) [CommRing R] : + MvPolynomial σ R →ₗ[R] Polynomial (Polynomial (MvPolynomial (PlaneParameters σ) R)) := + ((bivariatePolynomialEquiv (MvPolynomial (PlaneParameters σ) R)).toLinearMap.restrictScalars + R).comp (universalPlaneMap R σ).toLinearMap + +end +end CCDN diff --git a/LeanPool/CCDN/Noether/UniversalNoetherForms.lean b/LeanPool/CCDN/Noether/UniversalNoetherForms.lean new file mode 100644 index 0000000000..946f2ede64 --- /dev/null +++ b/LeanPool/CCDN/Noether/UniversalNoetherForms.lean @@ -0,0 +1,212 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.ForMathlib.IntegerPolynomialL1 +public import LeanPool.CCDN.ForMathlib.Matrix +public import LeanPool.CCDN.Noether.AmbientNoetherForms +public import Mathlib.FieldTheory.IsAlgClosed.AlgebraicClosure +import LeanPool.CCDN.Determinant.GenericPlaneEulerMinorCharZero +import LeanPool.CCDN.Determinant.UniversalPlaneMinor +import LeanPool.CCDN.Noether.AmbientNoetherHeight +import LeanPool.CCDN.Noether.AmbientNoetherVanishing +import LeanPool.CCDN.Noether.EulerPairSpecialization +import Mathlib.Data.Sym.Card + +/-! +A finite family of universal integer Noether forms, with the exact +degree and coefficient bound of CCDN Theorem 3.2.2. + +Characteristic-zero nonvanishing of the ambient integer +coefficient forms, descended from the algebraic closure. + +Every absolutely irreducible characteristic-zero hypersurface of degree +d at least two gives a nonzero Euler minor of order d²−1 in the universal +affine-plane parameter ring. +-/ + +@[expose] public section +namespace CCDN +noncomputable section +open MvPolynomial + +private lemma eulerClosedFormCoefficientMatrix_smul {R : Type*} [CommRing R] + (u : R) (k : ℕ) (f : Polynomial (Polynomial R)) : + eulerClosedFormCoefficientMatrix k (u • f) = u • eulerClosedFormCoefficientMatrix k f := by + ext row col + change eulerCoefficientEntryLinear k row col (u • f) = + u • eulerCoefficientEntryLinear k row col f + exact map_smul _ _ _ + +private lemma eulerClosedForm_minor_smul {R : Type*} [CommRing R] + (u : R) (k r : ℕ) (f : Polynomial (Polynomial R)) + (a : Fin r → ℕ × ℕ) (c : Fin r → EulerColumns k) : + ((eulerClosedFormCoefficientMatrix k (u • f)).submatrix a c).det = + u ^ r * ((eulerClosedFormCoefficientMatrix k f).submatrix a c).det := by + rw [eulerClosedFormCoefficientMatrix_smul] + change (u • (eulerClosedFormCoefficientMatrix k f).submatrix a c).det = _ + rw [Matrix.det_smul, Fintype.card_fin] + +/-- The universal affine-plane family has a nonzero determinant of +the precise Noether order. -/ +private theorem exists_universalPlane_euler_minor_charZero + {K σ : Type*} [Field K] [CharZero K] [IsAlgClosed K] [Finite σ] + (k : ℕ) (f : MvPolynomial (Option σ) K) (hf : Irreducible f) + (hd : f.totalDegree = k + 2) : + (eulerClosedFormCoefficientMatrix k (universalPlanePolynomial f)).HasNonzeroMinor + ((k + 2) ^ 2 - 1) := by + classical + let := Fintype.ofFinite σ + obtain ⟨a, b, hb, _, rows, cols, hdet⟩ := + exists_positioned_genericPlane_euler_minor_charZero k f hf hd + let R := MvPolynomial σ K + let F := FractionRing R + let φ : R →+* F := algebraMap R F + have hne : φ (((eulerClosedFormCoefficientMatrix k + (genericPlanePolynomial (normalizedHypersurfaceCoordinates a b hb f))).submatrix + rows cols).det) ≠ 0 := + (map_ne_zero_iff _ (IsFractionRing.injective R F)).mpr hdet + rw [eulerClosedForm_minor_map, normalized_genericPlane_universal_specialization, + eulerClosedForm_minor_smul, ← eulerClosedForm_minor_map] at hne + refine ⟨rows, cols, ?_⟩ + intro he + exact hne (by rw [he, map_zero, mul_zero]) + +end +end CCDN + +namespace CCDN +noncomputable section +open MvPolynomial + +/-- Some constructed universal form detects every absolutely irreducible +homogeneous equation in characteristic zero. -/ +theorem exists_ambientNoetherForm_nonzero {K σ : Type*} + [Field K] [CharZero K] [Fintype σ] [DecidableEq σ] + (k : ℕ) (f : MvPolynomial (Option σ) K) (hf : f.IsHomogeneous (k + 2)) + (hir : Irreducible (MvPolynomial.map (algebraMap K (AlgebraicClosure K)) f)) : + ∃ (rows : Fin ((k + 2) ^ 2 - 1) → ℕ × ℕ) + (cols : Fin ((k + 2) ^ 2 - 1) → EulerColumns k) + (m : PlaneParameters (Option σ) →₀ ℕ), + homogeneousCoefficientSpecialization (k + 2) f + (ambientNoetherForm (Option σ) k rows cols m) ≠ 0 := by + let φ := algebraMap K (AlgebraicClosure K) + have hd : (MvPolynomial.map φ f).totalDegree = k + 2 := (hf.map φ).totalDegree hir.ne_zero + obtain ⟨rows, cols, hdet⟩ := exists_universalPlane_euler_minor_charZero + k (MvPolynomial.map φ f) hir hd + obtain ⟨m, hm⟩ := exists_coeff_ne_zero hdet + refine ⟨rows, cols, m, ?_⟩ + intro hz + apply hm + rw [← ambientEuler_minor_map φ k f rows cols, coeff_map, + ← ambientNoetherForm_specialize k f hf rows cols m, hz, map_zero] + +end +end CCDN + +namespace CCDN +noncomputable section +open MvPolynomial + +private lemma finite_homogeneous_coefficientL1_bounded (σ : Type*) [Finite σ] + (r H : ℕ) : + {f : MvPolynomial σ ℤ | f.IsHomogeneous r ∧ l1Norm f ≤ H}.Finite := by + classical + let := Fintype.ofFinite σ + let C := ↥(Finset.Icc (-(H : ℤ)) (H : ℤ)) + let F : (Sym σ r → C) → MvPolynomial σ ℤ := + fun c => ∑ i, monomial (homogeneousCoefficientExponent σ r i) (c i).val + apply (Set.finite_range F).subset + intro f hf + let c : Sym σ r → C := fun i => ⟨f.coeff (homogeneousCoefficientExponent σ r i), by + have hh := (natAbs_coeff_le_l1Norm f (homogeneousCoefficientExponent σ r i)).trans hf.2 + have ha : |f.coeff (homogeneousCoefficientExponent σ r i)| ≤ (H : ℤ) := by + rw [← Int.natCast_natAbs] + exact_mod_cast hh + exact Finset.mem_Icc.mpr (abs_le.mp ha)⟩ + refine ⟨c, ?_⟩ + exact homogeneousCoefficientData_reconstruct r f hf.1 + +/-- For fixed `σ` and `k`, there are only finitely many ambient Noether forms. -/ +lemma finite_ambientNoetherForms (σ : Type*) [Fintype σ] [DecidableEq σ] (k : ℕ) : + {f | ∃ rows cols m, f = ambientNoetherForm σ k rows cols m}.Finite := by + apply (finite_homogeneous_coefficientL1_bounded (Sym σ (k + 2)) + ((k + 2) ^ 2 - 1) + ((k + 2) ^ (3 * (k + 2) ^ 2 - 3) * + (Fintype.card (Sym σ (k + 2)) * 3 ^ (k + 2)) ^ ((k + 2) ^ 2 - 1))).subset + rintro f ⟨rows,cols,m,rfl⟩ + exact ⟨ambientNoetherForm_homogeneous σ k rows cols m, + ambientNoetherForm_coefficientL1_le σ k rows cols m⟩ + +/-- The finite collection of universal Noether forms of degree `k + 2` in the variables `σ`: the +extracted plane-parameter coefficients of all Euler minors of the universal plane restriction of +the universal homogeneous form. -/ +def universalNoetherForms (σ : Type*) [Fintype σ] [DecidableEq σ] (k : ℕ) : + Finset (MvPolynomial (Sym σ (k + 2)) ℤ) := + (finite_ambientNoetherForms σ k).toFinset + +private lemma mem_universalNoetherForms {σ : Type*} [Fintype σ] [DecidableEq σ] + (k : ℕ) (f : MvPolynomial (Sym σ (k + 2)) ℤ) : + f ∈ universalNoetherForms σ k ↔ ∃ rows cols m, f = ambientNoetherForm σ k rows cols m := by + exact Set.Finite.mem_toFinset _ + +/-- Every universal Noether form is homogeneous of degree `(k + 2) ^ 2 - 1`. -/ +theorem universalNoetherForms_homogeneous {σ : Type*} [Fintype σ] [DecidableEq σ] + (k : ℕ) (g : MvPolynomial (Sym σ (k + 2)) ℤ) + (hg : g ∈ universalNoetherForms σ k) : + g.IsHomogeneous ((k + 2) ^ 2 - 1) := by + obtain ⟨rows,cols,m,rfl⟩ := (mem_universalNoetherForms k g).mp hg + exact ambientNoetherForm_homogeneous σ k rows cols m + +private lemma card_homogeneousCoefficientIndices (n d : ℕ) : + Fintype.card (Sym (Option (Fin n)) d) = (n + d).choose n := by + rw [Sym.card_sym_eq_choose, Fintype.card_option, Fintype.card_fin] + have he : n + 1 + d - 1 = n + d := by omega + rw [he, ← Nat.choose_symm (show d ≤ n + d by omega)] + congr 1 + omega + +/-- The printed integer coefficient bound: in `n + 1` homogeneous variables, every universal +Noether form has coefficient ℓ¹-norm at most +`(k + 2) ^ (3 * (k + 2) ^ 2 - 3) * (choose (n + k + 2) n * 3 ^ (k + 2)) ^ ((k + 2) ^ 2 - 1)`. -/ +theorem universalNoetherForms_coefficientL1_le (n k : ℕ) + (g : MvPolynomial (Sym (Option (Fin n)) (k + 2)) ℤ) + (hg : g ∈ universalNoetherForms (Option (Fin n)) k) : + l1Norm g ≤ + (k + 2) ^ (3 * (k + 2) ^ 2 - 3) * + ((n + (k + 2)).choose n * 3 ^ (k + 2)) ^ ((k + 2) ^ 2 - 1) := by + obtain ⟨rows,cols,m,rfl⟩ := (mem_universalNoetherForms k g).mp hg + simpa only [card_homogeneousCoefficientIndices] using + ambientNoetherForm_coefficientL1_le (Option (Fin n)) k rows cols m + +/-- Every universal Noether form vanishes on a geometrically reducible homogeneous equation of +degree `k + 2`, over an arbitrary field, with no characteristic assumption. -/ +theorem universalNoetherForms_vanish {K σ : Type*} [Field K] + [Fintype σ] [DecidableEq σ] + (k : ℕ) (f : MvPolynomial σ K) (hf : f.IsHomogeneous (k + 2)) + (hred : ¬Irreducible (MvPolynomial.map (algebraMap K (AlgebraicClosure K)) f)) + (g : MvPolynomial (Sym σ (k + 2)) ℤ) + (hg : g ∈ universalNoetherForms σ k) : + homogeneousCoefficientSpecialization (k + 2) f g = 0 := by + obtain ⟨rows,cols,m,rfl⟩ := (mem_universalNoetherForms k g).mp hg + exact ambientNoetherForm_eq_zero_of_not_absolutely_irreducible k f hf hred rows cols m + +/-- In characteristic zero, an absolutely irreducible homogeneous equation of degree `k + 2` +makes at least one universal Noether form nonzero. -/ +theorem universalNoetherForms_nonvanishing {K σ : Type*} [Field K] [CharZero K] + [Fintype σ] [DecidableEq σ] + (k : ℕ) (f : MvPolynomial (Option σ) K) (hf : f.IsHomogeneous (k + 2)) + (hir : Irreducible (MvPolynomial.map (algebraMap K (AlgebraicClosure K)) f)) : + ∃ g ∈ universalNoetherForms (Option σ) k, + homogeneousCoefficientSpecialization (k + 2) f g ≠ 0 := by + obtain ⟨rows,cols,m,hne⟩ := exists_ambientNoetherForm_nonzero k f hf hir + exact ⟨_, (mem_universalNoetherForms _ _).mpr ⟨rows,cols,m,rfl⟩, hne⟩ + +end +end CCDN diff --git a/LeanPool/CCDN/Noether/UniversalPlane.lean b/LeanPool/CCDN/Noether/UniversalPlane.lean new file mode 100644 index 0000000000..a3dd8eaafc --- /dev/null +++ b/LeanPool/CCDN/Noether/UniversalPlane.lean @@ -0,0 +1,165 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.Noether.BivariateTotalDegree +import LeanPool.CCDN.Determinant.PlaneMultiplicity +import LeanPool.CCDN.Noether.ClosedFormScaling +import LeanPool.CCDN.Noether.ShearNormalization + +/-! +# The universal affine plane + +The universal affine two-plane has independent coordinates for its base point and both +directions. Its specializations are affine-plane restrictions; its polynomial coefficients are +the objects to be counted. Every coefficient of the universal restriction has degree in the +plane parameters bounded by the total degree of the original equation. +-/ + +@[expose] public section +namespace CCDN +noncomputable section +open MvPolynomial + +/-- Indices of the parameters of an affine plane in the affine space with coordinates `σ`: the +pair `(i, 0)` indexes the `i`-th coordinate of the base point, and `(i, 1)`, `(i, 2)` those of +the two direction vectors. -/ +abbrev PlaneParameters (σ : Type*) := σ × Fin 3 + +/-- All affine planes a+x*b+y*c, with every coordinate an independent parameter. -/ +def universalPlaneMap (K σ : Type*) [CommRing K] : + MvPolynomial σ K →ₐ[K] MvPolynomial (Fin 2) (MvPolynomial (PlaneParameters σ) K) := + aeval (fun i => C (X (i,0)) + C (X (i,1)) * X 0 + C (X (i,2)) * X 1) + +/-- The restriction of `f` to the universal affine plane, viewed as a bivariate polynomial whose +coefficients are polynomials in the plane parameters. -/ +def universalPlanePolynomial {K σ : Type*} [CommRing K] (f : MvPolynomial σ K) : + Polynomial (Polynomial (MvPolynomial (PlaneParameters σ) K)) := + bivariatePolynomialEquiv _ (universalPlaneMap K σ f) + +/-- Evaluate all three blocks of affine-plane parameters. -/ +def planeParameterEval {K A σ : Type*} [CommRing K] [CommRing A] + (φ : K →+* A) (a b c : σ → A) : MvPolynomial (PlaneParameters σ) K →+* A := + eval₂Hom φ (fun i => ![a i.1, b i.1, c i.1] i.2) + +/-- Universal specialization is exactly the displayed affine substitution. -/ +theorem universalPlaneMap_specialize {K A σ : Type*} [CommRing K] [CommRing A] + (φ : K →+* A) (a b c : σ → A) (f : MvPolynomial σ K) : + MvPolynomial.map (planeParameterEval φ a b c) (universalPlaneMap K σ f) = + eval₂Hom (C.comp φ) (fun i => C (a i) + C (b i) * X 0 + C (c i) * X 1) f := by + have he : (MvPolynomial.map (planeParameterEval φ a b c)).comp (universalPlaneMap K σ).toRingHom = + eval₂Hom (C.comp φ) (fun i => C (a i) + C (b i) * X 0 + C (c i) * X 1) := by + apply MvPolynomial.ringHom_ext + · intro r + simp [universalPlaneMap, planeParameterEval] + · intro i + simp [universalPlaneMap, planeParameterEval] + exact RingHom.congr_fun he f + +/-- Specializing the plane parameters of the universal plane restriction of `f` to a base point +`a` and directions `b`, `c` gives the restriction of `f` to the plane `a + x • b + y • c`. -/ +lemma universalPlanePolynomial_specialize {K A σ : Type*} [CommRing K] [CommRing A] + (φ : K →+* A) (a b c : σ → A) (f : MvPolynomial σ K) : + bivariateMap (planeParameterEval φ a b c) (universalPlanePolynomial f) = + bivariatePolynomialEquiv A + (eval₂Hom (C.comp φ) (fun i => C (a i) + C (b i) * X 0 + C (c i) * X 1) f) := by + rw [universalPlanePolynomial, bivariatePolynomialEquiv_map, universalPlaneMap_specialize] + +/-- Scaling `f` by `u` multiplies every minor of order `r` of its closed-form coefficient matrix by +`u ^ r`. -/ +lemma closedForm_minor_smul {R : Type*} [CommRing R] + (u : R) (d r : ℕ) (f : Polynomial (Polynomial R)) + (a : Fin r → ℕ × ℕ) (c : Fin r → ClosedFormColumns d) : + ((closedFormCoefficientMatrix d (u • f)).submatrix a c).det = + u ^ r * ((closedFormCoefficientMatrix d f).submatrix a c).det := by + rw [closedFormCoefficientMatrix_smul] + change (u • (closedFormCoefficientMatrix d f).submatrix a c).det = _ + rw [Matrix.det_smul, Fintype.card_fin] + +end +end CCDN + +namespace CCDN +noncomputable section +open MvPolynomial + +/-- Under `commAlgEquiv`, which swaps the two sets of variables of an iterated multivariate +polynomial ring, the coefficient of `n` in the coefficient of `m` is the coefficient of `m` in +the coefficient of `n`. -/ +lemma commAlgEquiv_coeff_coeff {K σ τ : Type*} [CommRing K] + (f : MvPolynomial σ (MvPolynomial τ K)) (m : σ →₀ ℕ) (n : τ →₀ ℕ) : + ((commAlgEquiv K σ τ f).coeff n).coeff m = (f.coeff m).coeff n := by + classical + induction f using MvPolynomial.induction_on' with + | add f g hf hg => simp [hf, hg] + | monomial a b => + induction b using MvPolynomial.induction_on' with + | add b c hb hc => simp [hb, hc] + | monomial b c => + simp [commAlgEquiv, monomial, AddMonoidAlgebra.commAlgEquiv_single_single, + Finsupp.single_apply, eq_comm] + split_ifs <;> simp_all [AddMonoidAlgebra.coeff_single] + +private lemma coefficient_totalDegree_le_commAlgEquiv {K σ τ : Type*} [CommRing K] + (f : MvPolynomial σ (MvPolynomial τ K)) (m : σ →₀ ℕ) : + (f.coeff m).totalDegree ≤ (commAlgEquiv K σ τ f).totalDegree := by + classical + apply Finset.sup_le + intro n hn + apply le_totalDegree + rw [mem_support_iff] at hn ⊢ + intro he + apply hn + rw [← commAlgEquiv_coeff_coeff, he] + rfl + +private lemma universalPlaneMap_comm {K σ : Type*} [CommRing K] (f : MvPolynomial σ K) : + commAlgEquiv K (Fin 2) (PlaneParameters σ) (universalPlaneMap K σ f) = + eval₂Hom (C.comp (C : K →+* MvPolynomial (Fin 2) K)) + (fun i => X (i,0) + X (i,1) * C (X 0) + X (i,2) * C (X 1)) f := by + have he : (commAlgEquiv K (Fin 2) (PlaneParameters σ)).toRingHom.comp + (universalPlaneMap K σ).toRingHom = + eval₂Hom (C.comp (C : K →+* MvPolynomial (Fin 2) K)) + (fun i => X (i,0) + X (i,1) * C (X 0) + X (i,2) * C (X 1)) := by + apply MvPolynomial.ringHom_ext + · intro r + simp [universalPlaneMap] + · intro i + simp [universalPlaneMap] + exact RingHom.congr_fun he f + +private lemma universalPlaneMap_parameterDegree_le {K σ : Type*} [CommRing K] + (f : MvPolynomial σ K) : + (commAlgEquiv K (Fin 2) (PlaneParameters σ) (universalPlaneMap K σ f)).totalDegree ≤ + f.totalDegree := by + rw [universalPlaneMap_comm] + apply linear_eval₂_totalDegree_le + intro i + have hx (j : PlaneParameters σ) : + (X j : MvPolynomial (PlaneParameters σ) (MvPolynomial (Fin 2) K)).totalDegree ≤ 1 := by + simpa [X] using totalDegree_monomial_le (Finsupp.single j 1) (1 : MvPolynomial (Fin 2) K) + apply (totalDegree_add _ _).trans + apply max_le + · apply (totalDegree_add _ _).trans + apply max_le + · exact hx _ + · exact (totalDegree_mul _ _).trans (by simpa using hx (i,1)) + · exact (totalDegree_mul _ _).trans (by simpa using hx (i,2)) + +/-- Each coefficient of the universal plane restriction of `f` has total degree at most the +total degree of `f` in all plane parameters together. -/ +theorem universalPlanePolynomial_coefficient_degree {K σ : Type*} [CommRing K] + (f : MvPolynomial σ K) (i j : ℕ) : + (((universalPlanePolynomial f).coeff i).coeff j).totalDegree ≤ f.totalDegree := by + rw [universalPlanePolynomial, bivariatePolynomialEquiv_coeff] + exact (coefficient_totalDegree_le_commAlgEquiv _ _).trans + (universalPlaneMap_parameterDegree_le f) + +end +end CCDN diff --git a/LeanPool/CCDN/Noether/UniversalPlaneBadWitness.lean b/LeanPool/CCDN/Noether/UniversalPlaneBadWitness.lean new file mode 100644 index 0000000000..32a511166d --- /dev/null +++ b/LeanPool/CCDN/Noether/UniversalPlaneBadWitness.lean @@ -0,0 +1,188 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.Noether.UniversalPlaneWitness +import LeanPool.CCDN.AuxiliaryPolynomial.ConstantLeadingBadReduction +import LeanPool.CCDN.Determinant.ClosedFormIntegerMinor +import LeanPool.CCDN.Determinant.PlaneMultiplicity +import LeanPool.CCDN.Determinant.UniversalPlaneMinor +import LeanPool.CCDN.ForMathlib.MvPolynomial +import LeanPool.CCDN.Noether.ClosedFormParameterDegree +import LeanPool.CCDN.Noether.ClosedFormScaling + +/-! A nonzero polynomial of degree at most 2*d^3 detects all plane sections +that lose the full degree or fail geometric irreducibility. -/ + +@[expose] public section +namespace CCDN +noncomputable section +open MvPolynomial + +/-- The universal plane restriction of `f` has total degree at most the total degree of `f` in the +two plane coordinates. -/ +lemma universalPlaneMap_planeDegree_le {K σ : Type*} [CommRing K] + (f : MvPolynomial σ K) : (universalPlaneMap K σ f).totalDegree ≤ f.totalDegree := by + change (eval₂Hom (C.comp (C : K →+* MvPolynomial (PlaneParameters σ) K)) + (fun i => C (X (i,0)) + C (X (i,1)) * X 0 + C (X (i,2)) * X 1) f).totalDegree ≤ _ + apply linear_eval₂_totalDegree_le + intro i + have hx (j : Fin 2) : + (X j : MvPolynomial (Fin 2) (MvPolynomial (PlaneParameters σ) K)).totalDegree ≤ 1 := by + simpa [X] using + totalDegree_monomial_le (Finsupp.single j 1) (1 : MvPolynomial (PlaneParameters σ) K) + apply (totalDegree_add _ _).trans + apply max_le + · apply (totalDegree_add _ _).trans + apply max_le + · simp + · exact (totalDegree_mul _ _).trans (by simpa using hx 0) + · exact (totalDegree_mul _ _).trans (by simpa using hx 1) + +/-- Every specialization of the plane parameters of the universal plane restriction of `f` has +total degree at most the total degree of `f`. -/ +lemma universalPlanePolynomial_specialization_totalDegree {K L σ : Type*} + [CommRing K] [CommRing L] (f : MvPolynomial σ K) + (φ : MvPolynomial (PlaneParameters σ) K →+* L) : + bivariateTotalDegree (bivariateMap φ (universalPlanePolynomial f)) ≤ f.totalDegree := by + apply (bivariateTotalDegree_map_le φ _).trans + simpa only [universalPlanePolynomial, bivariateTotalDegree, AlgEquiv.symm_apply_apply] + using universalPlaneMap_planeDegree_le f + +/-- The bad-plane witness: the universal outer leading coefficient of the plane restriction of +`f` times the closed-form minor with rows `rows` and columns `cols`. The leading-coefficient +factor detects all outer-degree drops. -/ +def universalPlaneBadWitness {K σ : Type*} [CommRing K] (f : MvPolynomial σ K) + (rows : Fin (2 * f.totalDegree ^ 2 - 1) → ℕ × ℕ) + (cols : Fin (2 * f.totalDegree ^ 2 - 1) → ClosedFormColumns f.totalDegree) : + MvPolynomial (PlaneParameters σ) K := + universalPlaneLeading f * + ((closedFormCoefficientMatrix f.totalDegree (universalPlanePolynomial f)).submatrix + rows cols).det + +private lemma universalPlaneBadWitness_degree {K σ : Type*} [CommRing K] (f : MvPolynomial σ K) + (rows : Fin (2 * f.totalDegree ^ 2 - 1) → ℕ × ℕ) + (cols : Fin (2 * f.totalDegree ^ 2 - 1) → ClosedFormColumns f.totalDegree) : + (universalPlaneBadWitness f rows cols).totalDegree ≤ 2 * f.totalDegree ^ 3 := by + apply (totalDegree_mul _ _).trans + have hl := universalPlanePolynomial_coefficient_degree f f.totalDegree 0 + have hm := closedForm_minor_parameterDegree _ _ _ _ + (universalPlanePolynomial_coefficient_degree f) rows cols + change (universalPlaneLeading f).totalDegree ≤ f.totalDegree at hl + calc + _ ≤ f.totalDegree + (2 * f.totalDegree ^ 2 - 1) * f.totalDegree := Nat.add_le_add hl hm + _ ≤ 2 * f.totalDegree ^ 3 := by + by_cases h : f.totalDegree = 0 + · simp [h] + · have h1 : 1 ≤ 2 * f.totalDegree ^ 2 := by + have : 0 < f.totalDegree := Nat.pos_of_ne_zero h; nlinarith + have he := Nat.sub_add_cancel h1 + nlinarith + +/-- Nonvanishing certifies full degree and irreducibility over ANY target +field of characteristic zero or greater than d. In particular it applies in the +algebraic closure when detecting geometrically bad finite-field planes. -/ +theorem universalPlaneBadWitness_good_specialization + {K L σ : Type*} [CommRing K] [Field L] + (f : MvPolynomial σ K) (hpos : 0 < f.totalDegree) (hchar : CharZeroOrGT L f.totalDegree) + (rows : Fin (2 * f.totalDegree ^ 2 - 1) → ℕ × ℕ) + (cols : Fin (2 * f.totalDegree ^ 2 - 1) → ClosedFormColumns f.totalDegree) + (φ : MvPolynomial (PlaneParameters σ) K →+* L) + (hw : φ (universalPlaneBadWitness f rows cols) ≠ 0) : + (bivariateMap φ (universalPlanePolynomial f)).natDegree = f.totalDegree ∧ + bivariateTotalDegree (bivariateMap φ (universalPlanePolynomial f)) = f.totalDegree ∧ + Irreducible (bivariateMap φ (universalPlanePolynomial f)) := by + let g := bivariateMap φ (universalPlanePolynomial f) + have hh := mul_ne_zero_iff.mp (show φ (universalPlaneLeading f) * + φ (((closedFormCoefficientMatrix f.totalDegree (universalPlanePolynomial f)).submatrix + rows cols).det) ≠ 0 + by simpa only [universalPlaneBadWitness, map_mul] using hw) + have ht : bivariateTotalDegree g ≤ f.totalDegree := + universalPlanePolynomial_specialization_totalDegree f φ + have hc : ((g.coeff f.totalDegree).coeff 0) ≠ 0 := by + simpa only [g, bivariateMap_coeff, universalPlaneLeading] using hh.1 + have hd : g.natDegree = f.totalDegree := by + apply le_antisymm ((bivariate_natDegree_le_totalDegree g).trans ht) + apply Polynomial.le_natDegree_of_ne_zero + intro hz + exact hc (by rw [hz, Polynomial.coeff_zero]) + have he : bivariateTotalDegree g = f.totalDegree := + le_antisymm ht (hd ▸ bivariate_natDegree_le_totalDegree g) + refine ⟨hd, he, ?_⟩ + by_contra hred + have hl : g.leadingCoeff = Polynomial.C (φ (universalPlaneLeading f)) := by + rw [bivariate_leadingCoeff_eq_constant g (by omega)] + congr 1 + change (g.coeff g.natDegree).coeff 0 = _ + rw [hd] + exact bivariateMap_coeff φ _ _ _ + have hr := closedForm_rank_drop_of_constant_leading f.totalDegree hpos hchar + _ hh.1 g hl hred hd ht + have hdet : ((closedFormCoefficientMatrix f.totalDegree g).submatrix rows cols).det ≠ 0 := by + rw [← closedForm_minor_map] + exact hh.2 + have hrank := Matrix.le_rank_of_det_submatrix_ne_zero _ rows cols hdet + omega + +/-- A concrete cubic-degree bad-section witness over the original field. -/ +theorem exists_universalPlane_badWitness + {K σ : Type*} [Field K] [Finite σ] + (f : MvPolynomial (Option σ) K) + (hf : Irreducible (MvPolynomial.map (algebraMap K (AlgebraicClosure K)) f)) + (hpos : 0 < f.totalDegree) (hchar : CharZeroOrGT K (2 * f.totalDegree ^ 2)) : + ∃ (rows : Fin (2 * f.totalDegree ^ 2 - 1) → ℕ × ℕ) + (cols : Fin (2 * f.totalDegree ^ 2 - 1) → ClosedFormColumns f.totalDegree), + universalPlaneBadWitness f rows cols ≠ 0 ∧ + (universalPlaneBadWitness f rows cols).totalDegree ≤ 2 * f.totalDegree ^ 3 := by + classical + let := Fintype.ofFinite σ + obtain ⟨rows, cols, hdet, _⟩ := exists_universalPlane_minor_degree f hf hpos hchar + exact ⟨rows, cols, + mul_ne_zero (universalPlaneLeading_ne_zero f hf hpos + (hchar.mono (by nlinarith [Nat.le_mul_self f.totalDegree]))) hdet, + universalPlaneBadWitness_degree f rows cols⟩ + +/-- A nonzero integer polynomial in the full parameter space, +constructed for every geometrically irreducible integer equation. -/ +theorem exists_integer_universalPlane_badWitness + {L σ : Type*} [Field L] [CharZero L] [IsAlgClosed L] [Finite σ] + (f : MvPolynomial (Option σ) ℤ) + (hf : Irreducible (MvPolynomial.map (Int.castRingHom L) f)) + (hpos : 0 < f.totalDegree) : + ∃ (rows : Fin (2 * f.totalDegree ^ 2 - 1) → ℕ × ℕ) + (cols : Fin (2 * f.totalDegree ^ 2 - 1) → ClosedFormColumns f.totalDegree), + universalPlaneBadWitness f rows cols ≠ 0 ∧ + (universalPlaneBadWitness f rows cols).totalDegree ≤ 2 * f.totalDegree ^ 3 := by + classical + let := Fintype.ofFinite σ + let φ : ℤ →+* L := Int.castRingHom L + have ht : (MvPolynomial.map φ f).totalDegree = f.totalDegree := + totalDegree_map_of_injective (RingHom.injective_int _) f + have hex := exists_universalPlane_minor (MvPolynomial.map φ f) hf (ht ▸ hpos) + (.of_charZero _) + rw [ht] at hex + obtain ⟨rows, cols, hdet⟩ := hex + have hm : ((closedFormCoefficientMatrix f.totalDegree + (universalPlanePolynomial f)).submatrix rows cols).det ≠ 0 := by + intro he + rw [universalPlanePolynomial_map, ← closedForm_minor_map, he, map_zero] at hdet + exact hdet rfl + have hl : universalPlaneLeading f ≠ 0 := by + have hn := universalPlaneLeading_ne_zero_of_irreducible (MvPolynomial.map φ f) hf + (ht ▸ hpos) (.of_charZero _) + have he : universalPlaneLeading (MvPolynomial.map φ f) = + MvPolynomial.map φ (universalPlaneLeading f) := by + simp only [universalPlaneLeading, ht, universalPlanePolynomial_map, bivariateMap_coeff] + rw [he] at hn + intro hz + exact hn (by rw [hz, map_zero]) + exact ⟨rows, cols, mul_ne_zero hl hm, universalPlaneBadWitness_degree f rows cols⟩ + +end +end CCDN diff --git a/LeanPool/CCDN/Noether/UniversalPlaneL1.lean b/LeanPool/CCDN/Noether/UniversalPlaneL1.lean new file mode 100644 index 0000000000..8e0039e06e --- /dev/null +++ b/LeanPool/CCDN/Noether/UniversalPlaneL1.lean @@ -0,0 +1,111 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.ForMathlib.IntegerPolynomialL1 +public import LeanPool.CCDN.Noether.AmbientNoetherForms +import LeanPool.CCDN.Noether.AmbientNoetherVanishing +import LeanPool.CCDN.Noether.EulerSparseMatrix + +/-! Explicit coefficient-sum bounds for generic plane restrictions. +An original monomial of degree d contributes at most 3^d; an Euler matrix +entry contributes at most d times this quantity. -/ + +@[expose] public section +namespace CCDN +noncomputable section +open MvPolynomial + +private lemma universalPlane_coordinate_flatten_l1_le {σ : Type*} (i : σ) : + l1Norm ((sumAlgEquiv ℤ (Fin 2) (PlaneParameters σ)).symm + (C (X (i,0)) + C (X (i,1)) * X 0 + C (X (i,2)) * X 1)) ≤ 3 := by + simp only [map_add, map_mul, sumAlgEquiv_symm_C_X, sumAlgEquiv_symm_X] + have hx (j : Fin 2 ⊕ PlaneParameters σ) : + l1Norm (X j : MvPolynomial (Fin 2 ⊕ PlaneParameters σ) ℤ) = 1 := by + simp [X] + apply (l1Norm_add_le _ _).trans + have hab := (l1Norm_add_le + (X (Sum.inr (i,0)) : MvPolynomial (Fin 2 ⊕ PlaneParameters σ) ℤ) + (X (Sum.inr (i,1)) * X (Sum.inl 0))).trans + (Nat.add_le_add_left (l1Norm_mul_le _ _) _) + have hc := l1Norm_mul_le + (X (Sum.inr (i,2)) : MvPolynomial (Fin 2 ⊕ PlaneParameters σ) ℤ) (X (Sum.inl 1)) + simpa only [hx] using Nat.add_le_add hab hc + +private theorem universalPlane_monomial_flatten_l1_le {σ : Type*} (m : σ →₀ ℕ) : + l1Norm ((sumAlgEquiv ℤ (Fin 2) (PlaneParameters σ)).symm + (universalPlaneMap ℤ σ (monomial m 1))) ≤ 3 ^ m.degree := by + classical + rw [universalPlaneMap, aeval_monomial, map_one, one_mul] + change l1Norm ((sumAlgEquiv ℤ (Fin 2) (PlaneParameters σ)).symm + (∏ i ∈ m.support, + (C (X (i,0)) + C (X (i,1)) * X 0 + C (X (i,2)) * X 1) ^ m i)) ≤ _ + rw [map_prod] + simp only [map_pow] + calc + _ ≤ ∏ i ∈ m.support, l1Norm + (((sumAlgEquiv ℤ (Fin 2) (PlaneParameters σ)).symm + (C (X (i,0)) + C (X (i,1)) * X 0 + C (X (i,2)) * X 1)) ^ m i) := + l1Norm_prod_le _ _ + _ ≤ ∏ i ∈ m.support, 3 ^ m i := by + exact Finset.prod_le_prod fun i _ => + (l1Norm_pow_le _ _).trans + (Nat.pow_le_pow_left (universalPlane_coordinate_flatten_l1_le i) _) + _ = 3 ^ m.degree := by + rw [Finset.prod_pow_eq_pow_sum] + rfl + +private theorem universalPlane_monomial_coefficient_l1_le {σ : Type*} + (m : σ →₀ ℕ) (n : Fin 2 →₀ ℕ) : + l1Norm ((universalPlaneMap ℤ σ (monomial m 1)).coeff n) ≤ 3 ^ m.degree := + (l1Norm_coeff_le_l1Norm_sumAlgEquiv_symm _ _).trans (universalPlane_monomial_flatten_l1_le m) + +private theorem eulerClosedForm_entry_l1_le {σ : Type*} (k H : ℕ) + (f : Polynomial (Polynomial (MvPolynomial σ ℤ))) + (hf : bivariateTotalDegree f ≤ k + 2) + (hcoeff : ∀ i, l1Norm (eulerCoefficientData (k + 2) f i) ≤ H) + (row : ℕ × ℕ) (col : EulerColumns k) : + l1Norm (eulerClosedFormCoefficientMatrix k f row col) ≤ (k + 2) * H := by + have he := congrFun (congrFun (universalEulerMatrix_specialize k f hf) row) col + change eval₂Hom (Int.castRingHom (MvPolynomial σ ℤ)) + (eulerCoefficientData (k + 2) f) (universalEulerMatrix k row col) = _ at he + rw [← he] + obtain hz | ⟨i,c,hc,hm⟩ := universalEulerMatrix_sparse k row col + · rw [hz, map_zero, l1Norm_zero] + exact Nat.zero_le _ + · rw [hm, map_mul, eval₂Hom_C] + have hX : eval₂Hom (Int.castRingHom (MvPolynomial σ ℤ)) + (eulerCoefficientData (k + 2) f) (X i) = eulerCoefficientData (k + 2) f i := + eval₂_X _ _ _ + rw [hX] + change l1Norm (C c * eulerCoefficientData (k + 2) f i) ≤ _ + apply (l1Norm_mul_le _ _).trans + rw [l1Norm_C] + apply Nat.mul_le_mul _ (hcoeff i) + have hh : (c.natAbs : ℤ) ≤ (k + 2 : ℕ) := by simpa only [Int.natCast_natAbs] using hc + exact_mod_cast hh + +/-- For a monomial of degree `k + 2`, every Euler matrix entry of its universal plane restriction +has coefficient ℓ¹-norm at most `(k + 2) * 3 ^ (k + 2)`. -/ +theorem ambientEulerEntry_monomial_l1_le {σ : Type*} (k : ℕ) + (m : σ →₀ ℕ) (hm : m.degree = k + 2) (row : ℕ × ℕ) (col : EulerColumns k) : + l1Norm (ambientEulerEntryLinear ℤ σ k row col (monomial m 1)) ≤ + (k + 2) * 3 ^ (k + 2) := by + rw [ambientEulerEntryLinear_apply] + apply eulerClosedForm_entry_l1_le k (3 ^ (k + 2)) _ + · exact (universalPlanePolynomial_totalDegree_le _).trans + ((totalDegree_monomial_le m (1 : ℤ)).trans (by change m.degree ≤ k + 2; omega)) + · intro i + simpa only [eulerCoefficientData, universalPlanePolynomial, + AlgEquiv.symm_apply_apply, hm] using + universalPlane_monomial_coefficient_l1_le m + ((Finsupp.degreeLeEquivSym (Fin 2) (k + 2)).symm i).val + +end +end CCDN diff --git a/LeanPool/CCDN/Noether/UniversalPlaneSpecialization.lean b/LeanPool/CCDN/Noether/UniversalPlaneSpecialization.lean new file mode 100644 index 0000000000..f222669bdd --- /dev/null +++ b/LeanPool/CCDN/Noether/UniversalPlaneSpecialization.lean @@ -0,0 +1,123 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.Algebra.PolynomialPredicates +public import LeanPool.CCDN.Plane.FiniteFieldBadPlanes +import LeanPool.CCDN.Algebra.IntegerPolynomialAvoidance +import LeanPool.CCDN.Noether.ShearNormalization + +/-! +# Specializations of universal plane sections + +Evaluation of a universal section, including the three points obtained from the base point by +adding each direction. A nonzero universal witness certifies a geometrically irreducible plane +section in characteristic zero, and integer specialization can simultaneously avoid any +additional nonzero polynomial condition on the plane parameters. +-/ + +@[expose] public section +namespace CCDN +noncomputable section +open MvPolynomial + +/-- Construct an integer plane specialization which is geometrically + irreducible of full degree and avoids an additional algebraic condition. + The parameter bound is independent of the equation's coefficient height. -/ +theorem exists_integer_geometrically_good_plane_avoiding {σ : Type*} [Finite σ] + (f : MvPolynomial (Option σ) ℤ) + (hir : AbsolutelyIrreducible f) + (hd : 0 < f.totalDegree) + (P : MvPolynomial (PlaneParameters (Option σ)) ℤ) (hP : P ≠ 0) : + ∃ v : PlaneParameters (Option σ) → ℤ, + (∀ i, 0 ≤ v i ∧ v i ≤ 2 * f.totalDegree ^ 3 + P.totalDegree) ∧ + eval v P ≠ 0 ∧ + (bivariateMap (Int.castRingHom (AlgebraicClosure ℚ)) (universalPlaneSection f v)).natDegree = + f.totalDegree ∧ + bivariateTotalDegree + (bivariateMap (Int.castRingHom (AlgebraicClosure ℚ)) (universalPlaneSection f v)) = + f.totalDegree ∧ + Irreducible (bivariateMap (Int.castRingHom (AlgebraicClosure ℚ)) + (universalPlaneSection f v)) := by + classical + let := Fintype.ofFinite σ + obtain ⟨rows, cols, hW, hdegree⟩ := exists_integer_universalPlane_badWitness f hir hd + let W := universalPlaneBadWitness f rows cols + obtain ⟨v, hv, hvalue⟩ := exists_integer_grid_eval_ne_zero (W * P) (mul_ne_zero hW hP) + (2 * f.totalDegree ^ 3 + P.totalDegree) + ((totalDegree_mul W P).trans (Nat.add_le_add_right hdegree _)) + rw [eval_mul] at hvalue + obtain ⟨hvW, hvP⟩ := mul_ne_zero_iff.mp hvalue + let φ := (Int.castRingHom (AlgebraicClosure ℚ)).comp (eval v) + have hwφ : φ W ≠ 0 := by + simpa only [φ, RingHom.comp_apply, Int.coe_castRingHom, ne_eq, Int.cast_eq_zero] using hvW + have hgood := universalPlaneBadWitness_good_specialization f hd (.of_charZero _) rows cols φ hwφ + refine ⟨v, hv, hvP, ?_⟩ + simpa only [φ, bivariateMap_comp_apply, universalPlaneSection] using hgood + +end +end CCDN + +namespace CCDN +noncomputable section +open MvPolynomial + +/-- Evaluate the outer coordinate at x and the inner coordinate at y. -/ +def bivariateEval {K : Type*} [CommRing K] (x y : K) : Polynomial (Polynomial K) →+* K := + (Polynomial.evalRingHom x).comp (Polynomial.mapRingHom (Polynomial.evalRingHom y)) + +/-- Evaluating the universal plane section of `f` with parameters `v` at `(x, y)` gives `f` at the +point `a + x • b + y • c` of the plane with base point `a` and directions `b`, `c` given by `v`. -/ +lemma universalPlaneSection_eval {K σ : Type*} [CommRing K] + (f : MvPolynomial σ K) (v : PlaneParameters σ → K) (x y : K) : + bivariateEval x y (universalPlaneSection f v) = + eval (fun i => v (i,0) + v (i,1) * x + v (i,2) * y) f := by + have he : (bivariateEval x y).comp + ((bivariateMap (eval v)).comp + ((bivariatePolynomialEquiv _).toRingHom.comp (universalPlaneMap K σ).toRingHom)) = + eval (fun i => v (i,0) + v (i,1) * x + v (i,2) * y) := by + apply MvPolynomial.ringHom_ext + · intro r + simp [bivariateEval, universalPlaneMap, bivariatePolynomialEquiv_C, bivariateMap] + · intro i + simp [bivariateEval, universalPlaneMap, bivariatePolynomialEquiv_C, + bivariatePolynomialEquiv_X_zero, bivariatePolynomialEquiv_X_one, bivariateMap] + exact RingHom.congr_fun he f + +/-- The equivalence between plane parameters, a base point `a` with directions `b` and `c`, and +the three defining points `a`, `a + b` and `a + c` of the plane. -/ +def planeTriplePointsEquiv {K σ : Type*} [CommRing K] : + (PlaneParameters σ → K) ≃ (Fin 3 → σ → K) where + toFun v := ![fun i => v (i,0), fun i => v (i,0) + v (i,1), fun i => v (i,0) + v (i,2)] + invFun w := fun i => ![w 0 i.1, w 1 i.1 - w 0 i.1, w 2 i.1 - w 0 i.1] i.2 + left_inv v := by + funext i + rcases i with ⟨i,j⟩ + fin_cases j <;> simp + right_inv w := by + funext j i + fin_cases j <;> simp + +/-- A zero section contains all three defining points, including coincident +ones. This does not require the parameterization to be injective. -/ +lemma zero_universalPlaneSection_three_points {K σ : Type*} [CommRing K] + (f : MvPolynomial σ K) (v : PlaneParameters σ → K) + (hv : universalPlaneSection f v = 0) (j : Fin 3) : + eval (planeTriplePointsEquiv v j) f = 0 := by + have h0 := universalPlaneSection_eval f v 0 0 + have h1 := universalPlaneSection_eval f v 1 0 + have h2 := universalPlaneSection_eval f v 0 1 + rw [hv, map_zero] at h0 h1 h2 + fin_cases j + · simpa [planeTriplePointsEquiv] using h0.symm + · simpa [planeTriplePointsEquiv] using h1.symm + · simpa [planeTriplePointsEquiv] using h2.symm + +end +end CCDN diff --git a/LeanPool/CCDN/Noether/UniversalPlaneWitness.lean b/LeanPool/CCDN/Noether/UniversalPlaneWitness.lean new file mode 100644 index 0000000000..0a86b57026 --- /dev/null +++ b/LeanPool/CCDN/Noether/UniversalPlaneWitness.lean @@ -0,0 +1,169 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.Algebra.AffineLineLeading +public import LeanPool.CCDN.Algebra.CharZeroOrGT +public import LeanPool.CCDN.Noether.UniversalPlane +public import Mathlib.FieldTheory.IsAlgClosed.AlgebraicClosure +import LeanPool.CCDN.Determinant.ClosedFormIntegerMinor +import LeanPool.CCDN.Determinant.UniversalPlaneMinor +import LeanPool.CCDN.ForMathlib.MvPolynomial +import LeanPool.CCDN.Noether.ClosedFormParameterDegree +import LeanPool.CCDN.Noether.ShearNormalization + +/-! +A nonzero universal minor of cubic parameter degree, over the original coefficient field; the +nonvanishing proof descends from an algebraic closure. The leading coefficient of a universal +plane section, evaluated by the highest homogeneous form in the first direction, detects loss of +the full outer degree. +-/ + +@[expose] public section +namespace CCDN +noncomputable section +open MvPolynomial + +private lemma universalPlaneMap_map {K L σ : Type*} [CommRing K] [CommRing L] + (φ : K →+* L) (f : MvPolynomial σ K) : + universalPlaneMap L σ (MvPolynomial.map φ f) = + MvPolynomial.map (MvPolynomial.map φ) (universalPlaneMap K σ f) := by + have he : (universalPlaneMap L σ).toRingHom.comp (MvPolynomial.map φ) = + (MvPolynomial.map (MvPolynomial.map φ)).comp (universalPlaneMap K σ).toRingHom := by + apply MvPolynomial.ringHom_ext + · intro r + simp [universalPlaneMap] + · intro i + simp [universalPlaneMap] + exact RingHom.congr_fun he f + +/-- The universal plane restriction commutes with applying a coefficient ring homomorphism. -/ +lemma universalPlanePolynomial_map {K L σ : Type*} [CommRing K] [CommRing L] + (φ : K →+* L) (f : MvPolynomial σ K) : + universalPlanePolynomial (MvPolynomial.map φ f) = + bivariateMap (MvPolynomial.map φ) (universalPlanePolynomial f) := by + rw [universalPlanePolynomial, universalPlaneMap_map, universalPlanePolynomial, + bivariatePolynomialEquiv_map] + +/-- A geometrically irreducible equation yields a nonzero polynomial +in the full affine-plane parameter ring, of degree at most (2*d^2-1)*d. +There are no smoothness or positioning hypotheses on the input equation. -/ +theorem exists_universalPlane_minor_degree + {K σ : Type*} [Field K] [Finite σ] + (f : MvPolynomial (Option σ) K) + (hf : Irreducible (MvPolynomial.map (algebraMap K (AlgebraicClosure K)) f)) + (hpos : 0 < f.totalDegree) (hchar : CharZeroOrGT K (2 * f.totalDegree ^ 2)) : + ∃ (rows : Fin (2 * f.totalDegree ^ 2 - 1) → ℕ × ℕ) + (cols : Fin (2 * f.totalDegree ^ 2 - 1) → ClosedFormColumns f.totalDegree), + ((closedFormCoefficientMatrix f.totalDegree + (universalPlanePolynomial f)).submatrix rows cols).det ≠ 0 ∧ + (((closedFormCoefficientMatrix f.totalDegree + (universalPlanePolynomial f)).submatrix rows cols).det).totalDegree ≤ + (2 * f.totalDegree ^ 2 - 1) * f.totalDegree := by + classical + let := Fintype.ofFinite σ + let L := AlgebraicClosure K + let φ : K →+* L := algebraMap K L + have ht : (MvPolynomial.map φ f).totalDegree = f.totalDegree := + totalDegree_map_of_injective φ.injective _ + have hex := exists_universalPlane_minor + (MvPolynomial.map φ f) hf (ht ▸ hpos) (ht ▸ hchar.map φ) + rw [ht] at hex + obtain ⟨rows, cols, hdet⟩ := hex + refine ⟨rows, cols, ?_, ?_⟩ + · intro he + rw [universalPlanePolynomial_map, ← closedForm_minor_map, he, map_zero] at hdet + exact hdet rfl + · exact closedForm_minor_parameterDegree _ _ _ _ + (universalPlanePolynomial_coefficient_degree f) rows cols + +end +end CCDN + +namespace CCDN +noncomputable section +open MvPolynomial + +private lemma universalPlanePolynomial_specialize_axis {K σ : Type*} [CommRing K] + (a b c : σ → K) (f : MvPolynomial σ K) : + Polynomial.map (Polynomial.evalRingHom 0) + (bivariateMap (planeParameterEval (RingHom.id K) a b c) (universalPlanePolynomial f)) = + affineLinePolynomial a b f := by + rw [universalPlanePolynomial_specialize] + have he : (Polynomial.mapRingHom (Polynomial.evalRingHom (0 : K))).comp + ((bivariatePolynomialEquiv K).toRingHom.comp + (eval₂Hom (C.comp (RingHom.id K)) + (fun i => C (a i) + C (b i) * X 0 + C (c i) * X 1))) = affineLinePolynomial a b := by + apply MvPolynomial.ringHom_ext + · intro r + simp [affineLinePolynomial, bivariatePolynomialEquiv_C] + · intro i + simp [affineLinePolynomial, bivariatePolynomialEquiv_C, + bivariatePolynomialEquiv_X_zero, bivariatePolynomialEquiv_X_one] + exact RingHom.congr_fun he f + +/-- The coefficient of x^d y^0, as a polynomial in all plane parameters. -/ +def universalPlaneLeading {K σ : Type*} [CommRing K] (f : MvPolynomial σ K) : + MvPolynomial (PlaneParameters σ) K := + ((universalPlanePolynomial f).coeff f.totalDegree).coeff 0 + +private lemma universalPlaneLeading_specialize {K σ : Type*} [CommRing K] + (a b c : σ → K) (f : MvPolynomial σ K) : + planeParameterEval (RingHom.id K) a b c (universalPlaneLeading f) = + eval b (homogeneousComponent f.totalDegree f) := by + have h := congrArg (fun q : Polynomial K => q.coeff f.totalDegree) + (universalPlanePolynomial_specialize_axis a b c f) + rw [Polynomial.coeff_map, affineLinePolynomial_top_coefficient a b f _ le_rfl] at h + change Polynomial.eval 0 ((bivariateMap (planeParameterEval (RingHom.id K) a b c) + (universalPlanePolynomial f)).coeff f.totalDegree) = _ at h + rw [← Polynomial.coeff_zero_eq_eval_zero, bivariateMap_coeff] at h + exact h + +private lemma universalPlaneLeading_map {K L σ : Type*} [Field K] [Field L] + (φ : K →+* L) (f : MvPolynomial σ K) : + universalPlaneLeading (MvPolynomial.map φ f) = + MvPolynomial.map φ (universalPlaneLeading f) := by + have ht : (MvPolynomial.map φ f).totalDegree = f.totalDegree := + totalDegree_map_of_injective φ.injective _ + simp only [universalPlaneLeading, ht, universalPlanePolynomial_map, bivariateMap_coeff] + +/-- Over an algebraically closed field, irreducibility and the characteristic bound +suffice for the leading polynomial to be nonzero. -/ +theorem universalPlaneLeading_ne_zero_of_irreducible + {K σ : Type*} [Field K] [IsAlgClosed K] [Finite σ] + (f : MvPolynomial (Option σ) K) (hf : Irreducible f) (hpos : 0 < f.totalDegree) + (hchar : CharZeroOrGT K f.totalDegree) : + universalPlaneLeading f ≠ 0 := by + classical + let := Fintype.ofFinite σ + obtain ⟨a, b, _, hdir, _⟩ := exists_monic_smooth_hypersurface_coordinates f hf hpos hchar + intro hz + apply hdir + rw [← universalPlaneLeading_specialize a b 0, hz, map_zero] + +/-- Geometric irreducibility and the characteristic bound suffice for the +leading polynomial to be nonzero. -/ +theorem universalPlaneLeading_ne_zero + {K σ : Type*} [Field K] [Finite σ] + (f : MvPolynomial (Option σ) K) + (hf : Irreducible (MvPolynomial.map (algebraMap K (AlgebraicClosure K)) f)) + (hpos : 0 < f.totalDegree) (hchar : CharZeroOrGT K f.totalDegree) : + universalPlaneLeading f ≠ 0 := by + classical + let := Fintype.ofFinite σ + let φ : K →+* AlgebraicClosure K := algebraMap K (AlgebraicClosure K) + have ht : (MvPolynomial.map φ f).totalDegree = f.totalDegree := + totalDegree_map_of_injective φ.injective _ + have hn := universalPlaneLeading_ne_zero_of_irreducible (MvPolynomial.map φ f) hf (ht ▸ hpos) + (ht ▸ hchar.map φ) + rw [universalPlaneLeading_map] at hn + exact fun hz ↦ hn (by rw [hz, map_zero]) + +end +end CCDN diff --git a/LeanPool/CCDN/Plane/AffineBoxRanges.lean b/LeanPool/CCDN/Plane/AffineBoxRanges.lean new file mode 100644 index 0000000000..56f5ef1046 --- /dev/null +++ b/LeanPool/CCDN/Plane/AffineBoxRanges.lean @@ -0,0 +1,107 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.Plane.ScaledPlaneCount +import LeanPool.CCDN.ForMathlib.Log + +/-! Quantitative large-box and small-box reductions for affine scaling. -/ + +@[expose] public section +namespace CCDN +open Real + +/-- Above a fixed multiple of d², a logarithmic height of B/A is large enough +for the unscaled auxiliary-polynomial branch. -/ +lemma large_box_height_threshold {d : ℕ} (hd : 0 < d) {A B : ℝ} (hA : 1 ≤ A) + (hB : (12 * A) ^ 2 * (d : ℝ) ^ 2 ≤ B) : + 4 * (d : ℝ) * Real.log (((d : ℝ) + 1) * B) ≤ B / A := by + have hd1 : (1 : ℝ) ≤ d := by exact_mod_cast hd + have hd0 : (0 : ℝ) < d := by linarith + have hA0 : 0 < A := by linarith + have hB0 : 0 < B := lt_of_lt_of_le (by positivity) hB + have hs := Real.sqrt_nonneg B + have hs2 := Real.sq_sqrt hB0.le + have hslo : 12 * A * d ≤ Real.sqrt B := by nlinarith + have hdS : (d : ℝ) ≤ Real.sqrt B := by nlinarith + have hlogB : Real.log B ≤ 2 * Real.sqrt B := by + have h := Real.log_le_rpow_div hB0.le (by norm_num : (0 : ℝ) < 1 / 2) + rw [← Real.sqrt_eq_rpow] at h + linarith + have hlogd : Real.log ((d : ℝ) + 1) ≤ d := Real.log_add_one_le_self d.cast_nonneg + rw [Real.log_mul (by positivity) hB0.ne'] + apply (le_div_iff₀ hA0).mpr + have hh : 4 * (d : ℝ) * (Real.log ((d : ℝ) + 1) + Real.log B) * A ≤ + 12 * A * d * Real.sqrt B := by + have hlog : Real.log ((d : ℝ) + 1) + Real.log B ≤ 3 * Real.sqrt B := by linarith + have ht := mul_le_mul_of_nonneg_left hlog (show 0 ≤ 4 * (d : ℝ) * A by positivity) + nlinarith only [ht] + have ht := mul_le_mul_of_nonneg_right hslo hs + nlinarith + +/-- On B<=S*d² the 2/d box power has an absolute bound depending only on S. -/ +private lemma small_box_log_bound {d : ℕ} (hd : 0 < d) {S B : ℝ} (hS : 1 ≤ S) + (hB : 1 ≤ B) (hBS : B ≤ S * (d : ℝ) ^ 2) : + Real.log (((d : ℝ) + 1) * B) ≤ Real.log S + 3 * d := by + have hd0 : (0 : ℝ) < d := by exact_mod_cast hd + have hS0 : 0 < S := by linarith + have hB0 : 0 < B := by linarith + have hdplus : (0 : ℝ) < d + 1 := by positivity + have hlogd : Real.log (d : ℝ) ≤ d := by + have h := Real.log_le_sub_one_of_pos hd0 + linarith + have hlogplus : Real.log ((d : ℝ) + 1) ≤ d := Real.log_add_one_le_self d.cast_nonneg + have hlogB := Real.log_le_log hB0 hBS + rw [Real.log_mul hS0.ne' (by positivity), Real.log_pow] at hlogB + rw [Real.log_mul hdplus.ne' hB0.ne'] + norm_num at hlogB + linarith + +private lemma small_box_power_bound {d : ℕ} (hd : 0 < d) {S B : ℝ} (hS : 1 ≤ S) + (hB : 1 ≤ B) (hBS : B ≤ S * (d : ℝ) ^ 2) : + (((d : ℝ) + 1) * B) ^ (2 / (d : ℝ)) ≤ Real.exp (2 * Real.log S + 6) := by + have hd1 : (1 : ℝ) ≤ d := by exact_mod_cast hd + have hd0 : (0 : ℝ) < d := by linarith + have hlogS := Real.log_nonneg hS + have hlog := small_box_log_bound hd hS hB hBS + rw [Real.rpow_def_of_pos (by positivity)] + apply Real.exp_le_exp.mpr + rw [← mul_div_assoc] + apply (div_le_iff₀ hd0).mpr + nlinarith + +/-- In the small-box range `1 ≤ B ≤ S * d ^ 2` (with `1 ≤ S`), the unscaled auxiliary degree bound +`d * scaledPlaneAuxiliaryDegreeBound d C ((d + 1) * B) 1` is at most a constant depending only on +`S` and `C`, times `d ^ 4`. -/ +lemma small_box_unscaled_degree_bound {d : ℕ} (hd : 0 < d) {S B C : ℝ} + (hS : 1 ≤ S) (hB : 1 ≤ B) (hBS : B ≤ S * (d : ℝ) ^ 2) : + (d : ℝ) * scaledPlaneAuxiliaryDegreeBound d C (((d : ℝ) + 1) * B) 1 ≤ + (Real.log S + 6 + 2 * Real.exp (heightDeterminantError C + 23) * Real.exp 14 * + Real.exp (2 * Real.log S + 6)) * (d : ℝ) ^ 4 := by + have hd1 : (1 : ℝ) ≤ d := by exact_mod_cast hd + have hd0 : (0 : ℝ) < d := by linarith + have hd3 : (d : ℝ) ≤ (d : ℝ) ^ 3 := by nlinarith [sq_nonneg ((d : ℝ) - 1)] + have hlogS := Real.log_nonneg hS + have hlog := small_box_log_bound hd hS hB hBS + have hpow := small_box_power_bound hd hS hB hBS + have hrest : (d : ℝ) + Real.log (((d : ℝ) + 1) * B) + 2 ≤ + (Real.log S + 6) * (d : ℝ) ^ 3 := by nlinarith + have hterm := mul_le_mul_of_nonneg_left hpow (show 0 ≤ + 2 * Real.exp (heightDeterminantError C + 23) * Real.exp 14 * (d : ℝ) ^ 3 by positivity) + have hdeg : scaledPlaneAuxiliaryDegreeBound d C (((d : ℝ) + 1) * B) 1 ≤ + (Real.log S + 6 + 2 * Real.exp (heightDeterminantError C + 23) * Real.exp 14 * + Real.exp (2 * Real.log S + 6)) * (d : ℝ) ^ 3 := by + unfold scaledPlaneAuxiliaryDegreeBound + simp only [Real.log_one, zero_div, add_zero, mul_one, Real.one_rpow, div_one] + nlinarith only [hrest, hterm] + have ht := mul_le_mul_of_nonneg_left hdeg hd0.le + convert ht using 1 + ring + +end CCDN diff --git a/LeanPool/CCDN/Plane/AffineNonzeroConstant.lean b/LeanPool/CCDN/Plane/AffineNonzeroConstant.lean new file mode 100644 index 0000000000..e6dd6129b8 --- /dev/null +++ b/LeanPool/CCDN/Plane/AffineNonzeroConstant.lean @@ -0,0 +1,179 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.AuxiliaryPolynomial.ScalingPrime +public import LeanPool.CCDN.Determinant.HeightDeterminant +public import LeanPool.CCDN.Height.CountingHypotheses +public import LeanPool.CCDN.Height.PrimitiveIntegerPolynomial +public import LeanPool.CCDN.Statements +import LeanPool.CCDN.Plane.AffineBoxRanges +import LeanPool.CCDN.Plane.ScaledBoxExponent +import LeanPool.CCDN.Plane.UnscaledModelCount + +/-! +Combining the scaling-prime, large-coefficient and small-box branches. +The plane theorem assumes the sharp finite-field count and a nonzero +constant term; a later translation removes the second hypothesis. + +The affine exponent 1/d with the scaling prime constructed from Mertens. +This branch assumes a nonzero constant coefficient of small logarithmic height; +the sharp finite-field count is an explicit geometric hypothesis. +-/ + +@[expose] public section +namespace CCDN +open Real + +/-- Affine plane count with exponent `1 / d` when the constant coefficient is nonzero and of small +logarithmic height: if `f` is a primitive integer polynomial of degree `d ≥ 1`, absolutely +irreducible over an algebraically closed field of characteristic zero, with +`log |f(0)| < B / scalingPrimeRatio` and satisfying the finite-field residue count hypothesis, +then the number of its integer zeros in the box of size `B ≥ scalingPrimeRatio` is at most an +explicit constant times `d ^ 3 * B ^ (1 / d) * (log B + d)`. The proof reduces modulo a scaling +prime. -/ +theorem plane_finite_count_small_constant_coefficient + {F : Type*} [Field F] [CharZero F] [IsAlgClosed F] {t : ℕ} + (f : MvPolynomial (Fin 2) ℤ) (hprim : PrimitiveIntegerPolynomial f) + (hir : Irreducible (MvPolynomial.map (Int.castRingHom F) f)) + (hd : 0 < f.totalDegree) (hf0 : f.coeff 0 ≠ 0) + (z : Fin t → Fin 2 → ℤ) (hinj : Function.Injective z) + (hz : ∀ i, MvPolynomial.eval (z i) f = 0) + {B C : ℝ} (hB : scalingPrimeRatio ≤ B) + (hheight : Real.log |(f.coeff 0 : ℝ)| < B / scalingPrimeRatio) + (hbox : ∀ i, InBox (z i) B) (hC : 0 ≤ C) + (hcount : PlaneResidueCountBound C f.totalDegree f) : + (t : ℝ) ≤ + (38 + 8 * Real.exp (heightDeterminantError C + 20) + + 2 * Real.exp (heightDeterminantError C + 23) * Real.exp 14 * Real.exp 2 * + scalingPrimeRatio) * + (f.totalDegree : ℝ) ^ 3 * B ^ (1 / (f.totalDegree : ℝ)) * + (Real.log B + f.totalDegree) := by + obtain ⟨p, hp, hlo, hhi, hnd⟩ := exists_scaling_prime hB hf0 hheight + have hA := scalingPrimeRatio_one_le + have hA0 : 0 < scalingPrimeRatio := by linarith + have hBp : B ≤ scalingPrimeRatio * p := by + have h := (div_lt_iff₀ hA0).mp hlo + nlinarith + exact scaled_plane_finite_count_one_over_degree f hprim hir hd hp hnd z hinj + ⟨hA.trans hB, hz, hbox⟩ hA hhi hBp hC hcount + +end CCDN + +namespace CCDN +noncomputable section +open MvPolynomial Real + +/-- The threshold constant `(12 * scalingPrimeRatio) ^ 2` separating small boxes +`B ≤ affineSmallBoxScale * d ^ 2` from large boxes in the affine plane count. -/ +def affineSmallBoxScale : ℝ := (12 * scalingPrimeRatio) ^ 2 + +private lemma affineSmallBoxScale_one_le : 1 ≤ affineSmallBoxScale := by + have h := scalingPrimeRatio_one_le + unfold affineSmallBoxScale + nlinarith + +/-- The constant of `plane_finite_count_nonzero_constant` for residue constant `C`, the sum of +the constants of its regimes, with `E = heightDeterminantError C`: the scaled small-coefficient +regime (`plane_finite_count_small_constant_coefficient`) costs +`38 + 8 exp (E + 20) + 2 exp (E + 23) e¹⁴ e² · scalingPrimeRatio`, and the small-box regime +(`small_box_unscaled_degree_bound`) costs `log S + 6 + 2 exp (E + 23) e¹⁴ exp (2 log S + 6)` +with `S = affineSmallBoxScale`. The large-coefficient constant `34 + 8 exp (E + 20)` is +dominated by the first. The numerical constants are explained in +`projectivePlaneAbsoluteConstant`. -/ +def affineNonzeroConstantBound (C : ℝ) : ℝ := + (38 + 8 * Real.exp (heightDeterminantError C + 20) + + 2 * Real.exp (heightDeterminantError C + 23) * Real.exp 14 * Real.exp 2 * scalingPrimeRatio) + + (Real.log affineSmallBoxScale + 6 + 2 * Real.exp (heightDeterminantError C + 23) * Real.exp 14 * + Real.exp (2 * Real.log affineSmallBoxScale + 6)) + +/-- The constant `affineNonzeroConstantBound C` is positive. -/ +lemma affineNonzeroConstantBound_pos (C : ℝ) : 0 < affineNonzeroConstantBound C := by + have hA : 0 < scalingPrimeRatio := zero_lt_one.trans_le scalingPrimeRatio_one_le + have hS := Real.log_nonneg affineSmallBoxScale_one_le + unfold affineNonzeroConstantBound + positivity + +/-- For `d ≥ 1` and `B ≥ 1`, the curve bound `curveBound d B` is at least `d ^ 4`. -/ +lemma degree_four_le_curveBound {d : ℕ} (hd : 0 < d) {B : ℝ} (hB : 1 ≤ B) : + (d : ℝ) ^ 4 ≤ curveBound d B := by + have hd0 : (0 : ℝ) < d := by exact_mod_cast hd + have hlog := Real.log_nonneg hB + have hp : 1 ≤ B ^ (1 / (d : ℝ)) := Real.one_le_rpow hB (by positivity) + have hh : (d : ℝ) ≤ B ^ (1 / (d : ℝ)) * (Real.log B + d) := by nlinarith + have ht := mul_le_mul_of_nonneg_left hh (pow_nonneg hd0.le 3) + unfold curveBound + nlinarith only [ht] + +/-- Coefficient-uniform affine plane count for nonzero constant term and every +B>=1. -/ +theorem plane_finite_count_nonzero_constant + {F : Type*} [Field F] [CharZero F] [IsAlgClosed F] {t : ℕ} + (f : MvPolynomial (Fin 2) ℤ) (hprim : PrimitiveIntegerPolynomial f) + (hir : Irreducible (MvPolynomial.map (Int.castRingHom F) f)) + (hd : 0 < f.totalDegree) (hf0 : f.coeff 0 ≠ 0) + (z : Fin t → Fin 2 → ℤ) (hinj : Function.Injective z) {B C : ℝ} (hzB : IsBoxZeroFamily f B z) + (hC : 0 ≤ C) (hcount : PlaneResidueCountBound C f.totalDegree f) : + (t : ℝ) ≤ affineNonzeroConstantBound C * curveBound f.totalDegree B := by + obtain ⟨hB, hz, hbox⟩ := hzB + let d := f.totalDegree + let K₁ := 38 + 8 * Real.exp (heightDeterminantError C + 20) + + 2 * Real.exp (heightDeterminantError C + 23) * Real.exp 14 * Real.exp 2 * scalingPrimeRatio + let K₂ := Real.log affineSmallBoxScale + 6 + + 2 * Real.exp (heightDeterminantError C + 23) * Real.exp 14 * + Real.exp (2 * Real.log affineSmallBoxScale + 6) + let K₃ := 34 + 8 * Real.exp (heightDeterminantError C + 20) + have hA := scalingPrimeRatio_one_le + have hA0 : 0 < scalingPrimeRatio := by linarith + have hd1 : (1 : ℝ) ≤ d := by exact_mod_cast hd + have hK₁ : 0 ≤ K₁ := by dsimp [K₁]; positivity + have hK₂ : 0 ≤ K₂ := by + have := Real.log_nonneg affineSmallBoxScale_one_le + dsimp [K₂] + positivity + have hK₃ : 0 ≤ K₃ := by dsimp [K₃]; positivity + have h31 : K₃ ≤ K₁ := by + have hh : 0 ≤ 2 * Real.exp (heightDeterminantError C + 23) * Real.exp 14 * Real.exp 2 * + scalingPrimeRatio := by positivity + dsimp [K₃, K₁] + linarith + have hpow := degree_four_le_curveBound hd hB + have hG : 0 ≤ curveBound d B := (by positivity : (0 : ℝ) ≤ (d : ℝ) ^ 4).trans hpow + have hsum : affineNonzeroConstantBound C = K₁ + K₂ := rfl + rw [hsum] + by_cases hsmallB : B ≤ affineSmallBoxScale * (d : ℝ) ^ 2 + · have hh := unscaled_plane_finite_count f hprim hir hd z hinj ⟨hB, hz, hbox⟩ hC hcount + have hs := small_box_unscaled_degree_bound hd affineSmallBoxScale_one_le hB hsmallB (C := C) + apply hh.trans + apply max_le + · apply hs.trans + have ht := mul_le_mul_of_nonneg_left hpow hK₂ + exact ht.trans (mul_le_mul_of_nonneg_right (by linarith) hG) + · exact (mul_le_mul_of_nonneg_left hpow hK₃).trans + (mul_le_mul_of_nonneg_right (by linarith) hG) + · have hlargeB : (12 * scalingPrimeRatio) ^ 2 * (d : ℝ) ^ 2 ≤ B := (lt_of_not_ge hsmallB).le + by_cases hsmallH : Real.log |(f.coeff 0 : ℝ)| < B / scalingPrimeRatio + · have hBA : scalingPrimeRatio ≤ B := by + have hd2 : (1 : ℝ) ≤ (d : ℝ) ^ 2 := by nlinarith + nlinarith [mul_le_mul_of_nonneg_left hd2 (sq_nonneg (12 * scalingPrimeRatio))] + have hh := plane_finite_count_small_constant_coefficient f hprim hir hd hf0 z hinj hz hBA + hsmallH hbox hC hcount + have ht : (t : ℝ) ≤ K₁ * curveBound d B := by + convert hh using 1 + simp only [K₁, curveBound, d] + ring + exact ht.trans (mul_le_mul_of_nonneg_right (by linarith) hG) + · have hheight := (large_box_height_threshold hd hA hlargeB).trans (le_of_not_gt hsmallH) + have hh := unscaled_plane_finite_count_large_coefficient f hprim hir hd z hinj + ⟨hB, hz, hbox⟩ hC 0 hf0 hheight hcount + exact hh.trans ((mul_le_mul_of_nonneg_left hpow hK₃).trans + (mul_le_mul_of_nonneg_right (by linarith) hG)) + +end +end CCDN diff --git a/LeanPool/CCDN/Plane/AffinePlaneAssembly.lean b/LeanPool/CCDN/Plane/AffinePlaneAssembly.lean new file mode 100644 index 0000000000..3713e71282 --- /dev/null +++ b/LeanPool/CCDN/Plane/AffinePlaneAssembly.lean @@ -0,0 +1,121 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.Plane.AffineNonzeroConstant +import LeanPool.CCDN.AuxiliaryPolynomial.AffineTranslationCount +import LeanPool.CCDN.AuxiliaryPolynomial.PlaneAuxiliaryCount +import LeanPool.CCDN.ForMathlib.Log + +/-! All affine scaling, coefficient-height and zero-constant-term reductions +for primitive absolutely irreducible plane equations. The resulting bound +assumes the sharp finite-field estimate. -/ + +@[expose] public section +namespace CCDN +noncomputable section +open MvPolynomial Real + +private lemma curveBound_translation_radius {d : ℕ} (hd : 0 < d) {B : ℝ} (hB : 1 ≤ B) : + curveBound d (((d : ℝ) + 1) * B) ≤ 2 * Real.exp 1 * curveBound d B := by + have hd0 : (0 : ℝ) < d := by exact_mod_cast hd + have hB0 : 0 < B := by linarith + have hdplus : (0 : ℝ) < d + 1 := by positivity + have hlogB := Real.log_nonneg hB + have hlogd : Real.log ((d : ℝ) + 1) ≤ d := Real.log_add_one_le_self d.cast_nonneg + have hpow : ((d : ℝ) + 1) ^ (1 / (d : ℝ)) ≤ Real.exp 1 := by + rw [Real.rpow_def_of_pos hdplus, mul_one_div] + apply Real.exp_le_exp.mpr + exact (div_le_one hd0).mpr hlogd + have hlog : Real.log (((d : ℝ) + 1) * B) + d ≤ 2 * (Real.log B + d) := by + rw [Real.log_mul hdplus.ne' hB0.ne'] + linarith + unfold curveBound + rw [Real.mul_rpow hdplus.le hB0.le] + have hh := mul_le_mul hpow hlog + (by + have hR : 1 ≤ ((d : ℝ) + 1) * B := one_le_mul_of_one_le_of_one_le (by linarith) hB + have := Real.log_nonneg hR + positivity) + (Real.exp_pos 1).le + have ht := mul_le_mul_of_nonneg_left hh + (show 0 ≤ (d : ℝ) ^ 3 * B ^ (1 / (d : ℝ)) by positivity) + convert ht using 1 <;> ring + +/-- Coefficient-uniform point count for a primitive absolutely irreducible +integer plane equation, assuming the sharp finite-field point estimate at +primes above 27*d^4. -/ +private theorem plane_finite_count_of_residue_bound + {F : Type*} [Field F] [CharZero F] [IsAlgClosed F] {t : ℕ} + (f : MvPolynomial (Fin 2) ℤ) (hprim : PrimitiveIntegerPolynomial f) + (hir : Irreducible (MvPolynomial.map (Int.castRingHom F) f)) (hd : 0 < f.totalDegree) + (z : Fin t → Fin 2 → ℤ) (hinj : Function.Injective z) {B C : ℝ} (hzB : IsBoxZeroFamily f B z) + (hC : 0 ≤ C) (hcount : PlaneResidueCountBound C f.totalDegree f) : + (t : ℝ) ≤ (2 * Real.exp 1 * affineNonzeroConstantBound C) * curveBound f.totalDegree B := by + obtain ⟨hB, hz, hbox⟩ := hzB + have hf0 : f ≠ 0 := fun h => hir.ne_zero (by simp [h]) + obtain ⟨a, ha, ha0⟩ := exists_bounded_translation_nonzero_constant f hf0 + let q := affineTranslation ℤ a f + let z' : Fin t → Fin 2 → ℤ := fun i => z i - a + let R := ((f.totalDegree : ℝ) + 1) * B + have hdeg : q.totalDegree = f.totalDegree := affineTranslation_totalDegree a f + have hprimq : PrimitiveIntegerPolynomial q := hprim.translate_affine a + have hirq : Irreducible (MvPolynomial.map (Int.castRingHom F) q) := by + dsimp [q] + rw [affineTranslation_map, affineTranslation_irreducible_iff] + exact hir + have hinj' : Function.Injective z' := by + intro i j hij + apply hinj + have hh := congrArg (fun v : Fin 2 → ℤ => v + a) hij + simpa only [z', sub_add_cancel] using hh + have hz' : ∀ i, eval (z' i) q = 0 := by + intro i + dsimp [q, z'] + rw [affineTranslation_eval, sub_add_cancel] + exact hz i + have hR : 1 ≤ R := one_le_mul_of_one_le_of_one_le + (by have := Nat.cast_nonneg (α := ℝ) f.totalDegree; linarith) hB + have hbox' : ∀ i j, |(z' i j : ℝ)| ≤ R := by + intro i j + exact homogeneousTranslation_integer_point_box hB a (z i) ha (hbox i) j + have hcount' : PlaneResidueCountBound C q.totalDegree q := by + intro ℓ hℓ hsize hirℓ + rw [hdeg] at hsize ⊢ + have hirf : Irreducible (MvPolynomial.map (Int.castRingHom (ZMod ℓ)) f) := by + dsimp [q] at hirℓ + rwa [affineTranslation_map, affineTranslation_irreducible_iff] at hirℓ + rw [show Nat.card (PlaneResidue q ℓ) = Nat.card (PlaneResidue f ℓ) from + affineTranslation_residue_card a f ℓ] + exact hcount ℓ hℓ hsize hirf + have hh := plane_finite_count_nonzero_constant q hprimq hirq (by rwa [hdeg]) ha0 z' hinj' + ⟨hR, hz', hbox'⟩ hC hcount' + rw [hdeg] at hh + have ht := hh.trans (mul_le_mul_of_nonneg_left (curveBound_translation_radius hd hB) + (affineNonzeroConstantBound_pos C).le) + convert ht using 1 + ring + +/-- For a primitive absolutely irreducible integer plane equation, the number of integer +zeros of height at most a real `B ≥ 1` is bounded by a constant multiple of `curveBound`, +assuming the finite-field point estimate `p + C d² √p` at primes above `27 d⁴`. -/ +theorem count_plane_of_residue_bound + {F : Type*} [Field F] [CharZero F] [IsAlgClosed F] + (f : MvPolynomial (Fin 2) ℤ) (hprim : PrimitiveIntegerPolynomial f) + (hir : Irreducible (MvPolynomial.map (Int.castRingHom F) f)) (hd : 0 < f.totalDegree) + {B C : ℝ} (hB : 1 ≤ B) (hC : 0 ≤ C) + (hcount : PlaneResidueCountBound C f.totalDegree f) : + (count (zeroLocus (MvPolynomial.map (Int.castRingHom ℚ) f)) B : ℝ) ≤ + (2 * Real.exp 1 * affineNonzeroConstantBound C) * curveBound f.totalDegree B := by + apply integer_zero_count_le_of_family_bound + intro t z hinj hz hbox + exact plane_finite_count_of_residue_bound f hprim hir hd z hinj ⟨hB, hz, hbox⟩ hC hcount + +end +end CCDN diff --git a/LeanPool/CCDN/Plane/AffinePlaneBadPrimeCount.lean b/LeanPool/CCDN/Plane/AffinePlaneBadPrimeCount.lean new file mode 100644 index 0000000000..3bbef90178 --- /dev/null +++ b/LeanPool/CCDN/Plane/AffinePlaneBadPrimeCount.lean @@ -0,0 +1,102 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.AuxiliaryPolynomial.AffineHypersurfaceAuxiliary +import LeanPool.CCDN.AuxiliaryPolynomial.PlaneAuxiliaryCount +import LeanPool.CCDN.Height.ScaledHypersurfaceHeight +import LeanPool.CCDN.Plane.Bezout + +/-! A logarithm-free plane-curve estimate in terms of the +geometric bad-prime factor. Uniformly bounded bad-prime factors therefore +give a coefficient-independent logarithm-free counting theorem. -/ + +@[expose] public section +namespace CCDN +noncomputable section +open MvPolynomial + +private theorem affine_plane_expression_le_bad_prime {d : ℕ} (hd : 0 < d) + {B L b : ℝ} (hB : 1 ≤ B) (hL : 1 ≤ L) (hb : 0 ≤ b) : + affineAuxiliaryDegreeExpression 0 d B L b ≤ + (d : ℝ) ^ 3 * (b + 2) * B ^ (1 / (d : ℝ)) := by + have hdR : (1 : ℝ) ≤ d := by exact_mod_cast hd + have hd0 : (0 : ℝ) < d := zero_lt_one.trans_le hdR + have hB0 : 0 < B := zero_lt_one.trans_le hB + have hlogB := Real.log_nonneg hB + have hlogL := Real.log_nonneg hL + have hpow : 1 ≤ B ^ (1 / (d : ℝ)) := Real.one_le_rpow hB (by positivity) + have hden : 1 ≤ L ^ (1 / ((d : ℝ) * d)) := Real.one_le_rpow hL (by positivity) + have hmin : 0 ≤ min (Real.log L + d * Real.log B + (d : ℝ) ^ 2) ((d : ℝ) ^ 2 * b) := + le_min (by positivity) (by positivity) + have hfrac : + (d : ℝ) * min (Real.log L + d * Real.log B + (d : ℝ) ^ 2) ((d : ℝ) ^ 2 * b) * + B ^ (1 / (d : ℝ)) / L ^ (1 / ((d : ℝ) * d)) ≤ + (d : ℝ) ^ 3 * b * B ^ (1 / (d : ℝ)) := by + calc + _ ≤ (d : ℝ) * + min (Real.log L + d * Real.log B + (d : ℝ) ^ 2) ((d : ℝ) ^ 2 * b) * + B ^ (1 / (d : ℝ)) := div_le_self (by positivity) hden + _ ≤ (d : ℝ) * ((d : ℝ) ^ 2 * b) * B ^ (1 / (d : ℝ)) := by + gcongr + exact min_le_right _ _ + _ = _ := by ring + have hlog : Real.log B ≤ (d : ℝ) * B ^ (1 / (d : ℝ)) := + log_le_dimension_root hd hB0 + have hdcube : (d : ℝ) ≤ (d : ℝ) ^ 3 := by + simpa only [pow_one] using pow_le_pow_right₀ hdR (by omega : 1 ≤ 3) + have hlog' := hlog.trans (mul_le_mul_of_nonneg_right hdcube (by positivity)) + have hcube := mul_le_mul_of_nonneg_left hpow (show 0 ≤ (d : ℝ) ^ 3 by positivity) + unfold affineAuxiliaryDegreeExpression + norm_num only [Nat.cast_zero, zero_add, div_one, Real.rpow_one, Real.rpow_zero, + one_mul, sub_self, mul_one, show (2 - 1 : ℝ) = 1 by norm_num, + show (4 - 1 : ℝ) = 3 by norm_num, Real.rpow_natCast, Real.rpow_ofNat] + nlinarith + +/-- The plane auxiliary and Bezout prove the bad-prime bound for +integer zeros. -/ +theorem affine_plane_count_bad_prime + (f : MvPolynomial (Fin 2) ℤ) (hprim : PrimitiveIntegerPolynomial f) + (hir : Irreducible (map (Int.castRingHom ℚ) f)) + (hd : 0 < f.totalDegree) {B : ℝ} (hB : 1 ≤ B) : + (count (zeroLocus (map (Int.castRingHom ℚ) f)) B : ℝ) ≤ + affineHypersurfaceAuxiliaryConstant 0 * (f.totalDegree : ℝ) ^ 4 * + (hypersurfaceBadPrimeFactor f + 2) * B ^ (1 / (f.totalDegree : ℝ)) := by + obtain ⟨g, ⟨_, hfg, hgz⟩, hgdeg⟩ := + affine_hypersurface_auxiliary f hprim (hprim.irreducible_of_rational_map hir) hd hB + have hf0 : f ≠ 0 := ne_zero_of_irreducible_map hir + have hdegree := hgdeg.trans (mul_le_mul_of_nonneg_left + (affine_plane_expression_le_bad_prime hd hB (leading_height_one_le f hf0) + (hypersurfaceBadPrimeFactor_nonneg f)) (affineHypersurfaceAuxiliaryConstant_pos 0).le) + have hfgQ : ¬ map (Int.castRingHom ℚ) f ∣ map (Int.castRingHom ℚ) g := + fun h => hfg (hprim.rational_map_dvd_iff.mp h) + apply integer_zero_count_le_of_family_bound + intro t z hinj hzf hbox + have hzQ : Function.Injective (fun i => rationalPoint (z i)) := by + intro i j h + apply hinj + funext k + exact Int.cast_injective (congrFun h k) + have hmapzero (p : MvPolynomial (Fin 2) ℤ) (i : Fin t) (hp : eval (z i) p = 0) : + eval (rationalPoint (z i)) (map (Int.castRingHom ℚ) p) = 0 := by + have hh := congrArg (Int.castRingHom ℚ) hp + rw [map_eval, map_zero] at hh + exact hh + have hbezout := plane_common_zeros_card_le (map (Int.castRingHom ℚ) f) + (map (Int.castRingHom ℚ) g) hir hfgQ (fun i => rationalPoint (z i)) hzQ + (fun i => hmapzero f i (hzf i)) (fun i => hmapzero g i (hgz (z i) (hbox i) (hzf i))) + rw [Fintype.card_fin, rational_map_totalDegree, rational_map_totalDegree] at hbezout + have hcount : (t : ℝ) ≤ (f.totalDegree : ℝ) * g.totalDegree := by exact_mod_cast hbezout + have hm := mul_le_mul_of_nonneg_left hdegree (Nat.cast_nonneg (α := ℝ) f.totalDegree) + apply hcount.trans + convert hm using 1 + ring + +end +end CCDN diff --git a/LeanPool/CCDN/Plane/Bezout.lean b/LeanPool/CCDN/Plane/Bezout.lean new file mode 100644 index 0000000000..de074fa356 --- /dev/null +++ b/LeanPool/CCDN/Plane/Bezout.lean @@ -0,0 +1,67 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.Algebra.IdealZeroLocus +public import LeanPool.CCDN.Height.Box +public import LeanPool.CCDN.Hilbert.AffineCurveDegree +public import Mathlib.Analysis.SpecialFunctions.Pow.Real +import LeanPool.CCDN.Algebra.PolynomialSection +import LeanPool.CCDN.Hilbert.PlaneHilbert +import Mathlib.RingTheory.Polynomial.UniqueFactorization + +/-! Sharp bounds for distinct points in proper polynomial sections. In the plane, +the principal Hilbert function gives the usual product-of-degrees bound. -/ + +@[expose] public section +namespace CCDN +noncomputable section + +/-- An irreducible plane curve and a polynomial not divisible by its equation +have at most the product of their degrees many distinct common zeros. -/ +theorem plane_common_zeros_card_le {K ι : Type*} [Field K] [Fintype ι] + (f g : MvPolynomial (Fin 2) K) (hf : Irreducible f) (hfg : ¬ f ∣ g) + (z : ι → Fin 2 → K) (hz : Function.Injective z) + (hzf : ∀ i, MvPolynomial.eval (z i) f = 0) + (hzg : ∀ i, MvPolynomial.eval (z i) g = 0) : + Fintype.card ι ≤ f.totalDegree * g.totalDegree := by + classical + let I : Ideal (MvPolynomial (Fin 2) K) := Ideal.span {f} + have : I.IsPrime := Ideal.isPrime_span_singleton_of_prime hf.prime + apply polynomial_section_card_le_degree I f.totalDegree + ⟨f.totalDegree, (f.totalDegree : ℤ) - f.totalDegree.choose 2, + fun N hN => principal_plane_hilbert_function f hf.ne_zero hN⟩ + g (by simpa only [I, Ideal.mem_span_singleton] using hfg) + g.totalDegree le_rfl z hz _ hzg + intro i h hh + obtain ⟨q, rfl⟩ := Ideal.mem_span_singleton.mp hh + rw [MvPolynomial.eval_mul, hzf, zero_mul] + +/-- The section bound directly in the real-radius integer counting interface. -/ +theorem count_curve_section_le {n d : ℕ} (I : Ideal (CoordinatePolynomial n)) + [I.IsPrime] (hd : HasAffineCurveDegree I d) + (g : CoordinatePolynomial n) (hg : g ∉ I) (B : ℝ) : + count (idealZeroLocus I ∩ zeroLocus g) B ≤ d * g.totalDegree := by + classical + let S := integralPoints (idealZeroLocus I ∩ zeroLocus g) B + let z : S → RatPoint n := fun x => rationalPoint x.val + have hz : Function.Injective z := by + intro x y h + apply Subtype.ext + funext j + exact Int.cast_injective (congrFun h j) + have hzp (i : S) : z i ∈ idealZeroLocus I := + (mem_integralPoints.mp i.property).2.1 + have hzg (i : S) : MvPolynomial.eval (z i) g = 0 := + (mem_integralPoints.mp i.property).2.2 + have h := polynomial_section_card_le_degree I d hd g hg g.totalDegree le_rfl z hz hzp hzg + simpa only [Fintype.card_coe, S, count] using h + +end +end CCDN diff --git a/LeanPool/CCDN/Plane/CurveCount.lean b/LeanPool/CCDN/Plane/CurveCount.lean new file mode 100644 index 0000000000..4327579011 --- /dev/null +++ b/LeanPool/CCDN/Plane/CurveCount.lean @@ -0,0 +1,197 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.Plane.RationalPlaneAssembly +import LeanPool.CCDN.ForMathlib.MvPolynomial +import LeanPool.CCDN.Plane.NonabsolutePlaneCount +import LeanPool.CCDN.Plane.StepanovBound +import LeanPool.CCDN.Plane.StepanovDimension +import LeanPool.CCDN.Plane.StepanovParameters + +/-! +Sharp finite-field counting and the complete rational-plane +integral-point bound. The finite-field proof uses the constructed Stepanov +auxiliary and retains an explicit absolute error constant. + +The complete rational plane reduction to sharp finite-field counting. +The non-absolutely-irreducible case is bounded by d². + +The explicit real-valued error term for the integer Stepanov parameters. + +The affine residue points of an integer plane equation are the +zeros of its reduction. +-/ + +@[expose] public section +namespace CCDN +noncomputable section +open MvPolynomial + +private lemma planeResidue_card_eq_zero_card (f : MvPolynomial (Fin 2) ℤ) (p : ℕ) : + Nat.card (PlaneResidue f p) = + Nat.card {x : Fin 2 → ZMod p // + eval x (MvPolynomial.map (Int.castRingHom (ZMod p)) f) = 0} := by + apply Nat.card_congr + apply Equiv.subtypeEquivRight + intro x + simp only [eval_map] + +end +end CCDN + +namespace CCDN + +/-- The Stepanov count `t N ≤ d (D + p A) + d (d - 1) N` gives the plane-curve bound +`t ≤ p + 161 d² √p`; see `planeResidueConstant` for the derivation of `161`. -/ +lemma stepanov_real_bound (d p A D N t : ℕ) (hd : 0 < d) + (hp : stepanovThreshold d < p) (hD : d * D < p) + (hN : N + 4 * d ^ 2 = d * A) (hNpos : 0 < N) + (hpx : p < (d * A + d) ^ 2) + (hcount : t * N ≤ d * (D + p * A) + d * (d - 1) * N) : + (t : ℝ) ≤ p + planeResidueConstant * (d : ℝ) ^ 2 * Real.sqrt p := by + have hdR : (1 : ℝ) ≤ d := by exact_mod_cast hd + have hd2 : (1 : ℝ) ≤ (d : ℝ) ^ 2 := by nlinarith only [hdR] + have hdle : (d : ℝ) ≤ (d : ℝ) ^ 2 := by nlinarith only [hdR] + unfold stepanovThreshold at hp + have hpR : 27 * (d : ℝ) ^ 4 < p := by exact_mod_cast hp + have hp0 : (0 : ℝ) < p := by nlinarith only [hpR, pow_nonneg (Nat.cast_nonneg d : (0 : ℝ) ≤ d) 4] + let s := Real.sqrt (p : ℝ) + have hs0 : 0 ≤ s := Real.sqrt_nonneg _ + have hs2 : s ^ 2 = p := Real.sq_sqrt (le_of_lt hp0) + have hs1 : 1 ≤ s := by nlinarith only [hs0, hs2, hpR, hd2] + have hNR : (N : ℝ) + 4 * (d : ℝ) ^ 2 = (d : ℝ) * A := by exact_mod_cast hN + have hpxR : (p : ℝ) < ((d : ℝ) * A + d) ^ 2 := by exact_mod_cast hpx + have hxp0 : (0 : ℝ) ≤ (d : ℝ) * A + d := by positivity + have hsnear : s < (N : ℝ) + 5 * (d : ℝ) ^ 2 := by + have hh : s < (d : ℝ) * A + d := by nlinarith only [hpxR, hs2, hs0, hxp0] + nlinarith only [hh, hNR, hdle] + have hsN : s < 32 * (N : ℝ) := by + by_contra! hn + have hh : 31 * s < 160 * (d : ℝ) ^ 2 := by linarith only [hn, hsnear] + have hsq := mul_self_lt_mul_self (by positivity : 0 ≤ 31 * s) hh + nlinarith only [hsq, hpR, hs2] + have hsp : (p : ℝ) ≤ 32 * N * s := by + have hh := mul_le_mul_of_nonneg_right (le_of_lt hsN) hs0 + nlinarith only [hh, hs2] + have hcoef : 4 * (d : ℝ) ^ 2 + 1 ≤ 5 * (d : ℝ) ^ 2 := by linarith only [hd2] + have hprod1 := mul_le_mul_of_nonneg_right hcoef (le_of_lt hp0) + have hprod2 := mul_le_mul_of_nonneg_left hsp (by positivity : 0 ≤ 5 * (d : ℝ) ^ 2) + have hsmall := mul_le_mul_of_nonneg_left hs1 (by positivity : 0 ≤ (d : ℝ) ^ 2 * N) + have hsub : d * (d - 1) ≤ d ^ 2 := by nlinarith only [Nat.sub_le d 1] + have hc : t * N ≤ d * D + p * (d * A) + d ^ 2 * N := by + have hh := Nat.mul_le_mul_right N hsub + nlinarith only [hcount, hh] + have hcR : (t : ℝ) * N ≤ (d : ℝ) * D + p * ((d : ℝ) * A) + (d : ℝ) ^ 2 * N := by + exact_mod_cast hc + have hDR : (d : ℝ) * D ≤ p := by exact_mod_cast hD.le + rw [← hNR] at hcR + have hfin : (t : ℝ) * N ≤ (p + 161 * (d : ℝ) ^ 2 * s) * N := by + nlinarith only [hcR, hDR, hprod1, hprod2, hsmall] + exact (mul_le_mul_iff_left₀ (by exact_mod_cast hNpos : (0 : ℝ) < N)).mp hfin + +end CCDN + +namespace CCDN +noncomputable section +open MvPolynomial Real + +/-- Every box on a rational irreducible plane curve which splits over a field +extension has at most d² integer points, independently of the radius. -/ +private theorem count_nonabsolute_plane_le {F : Type*} [Field F] [CharZero F] + (f : MvPolynomial (Fin 2) ℚ) (hf : Irreducible f) (hd : 0 < f.totalDegree) + (hred : ¬ Irreducible (MvPolynomial.map (algebraMap ℚ F) f)) (B : ℝ) : + count (zeroLocus f) B ≤ f.totalDegree ^ 2 := by + classical + let S := integralPoints (zeroLocus f) B + let z : S → Fin 2 → ℚ := fun i => rationalPoint i.val + have hi : Function.Injective z := by + intro i j he + apply Subtype.ext + funext k + exact Int.cast_injective (congrFun he k) + have hz : ∀ i, eval (z i) f = 0 := fun i => (mem_integralPoints.mp i.property).2 + have h := nonabsolute_plane_points_card_le f hf hd hred z hi hz + simpa only [Fintype.card_coe, S, count] using h + +/-- The exact PlaneCurveBound target follows from the sharp finite-field +estimate. The rational nonabsolute case is covered by the d² bound. -/ +private theorem planeCurveBound_of_residue_bound {C : ℝ} + (hC : 0 ≤ C) (hres : PlaneResidueBound C) : PlaneCurveBound := by + let K := 2 * Real.exp 1 * affineNonzeroConstantBound C + have hK : 0 < K := mul_pos (by positivity) (affineNonzeroConstantBound_pos C) + refine ⟨K + 1, by linarith, ?_⟩ + intro f hf hd B hB + have hG : 0 ≤ curveBound f.totalDegree B := by + have h := degree_four_le_curveBound hd hB + exact (by positivity : (0 : ℝ) ≤ (f.totalDegree : ℝ) ^ 4).trans h + by_cases habs : AbsolutelyIrreducibleRat f + · exact (count_rational_plane_of_residue_bound f habs hd hB hC hres).trans + (mul_le_mul_of_nonneg_right (by linarith) hG) + · have hh := count_nonabsolute_plane_le f hf hd habs B + have ht : (count (zeroLocus f) B : ℝ) ≤ (f.totalDegree : ℝ) ^ 2 := by exact_mod_cast hh + have hd1 : (1 : ℝ) ≤ f.totalDegree := by exact_mod_cast hd + have hpow : (f.totalDegree : ℝ) ^ 2 ≤ (f.totalDegree : ℝ) ^ 4 := by + nlinarith [sq_nonneg ((f.totalDegree : ℝ) ^ 2 - 1)] + have hb := ht.trans (hpow.trans (degree_four_le_curveBound hd hB)) + exact hb.trans (by nlinarith) + +end +end CCDN + +namespace CCDN +noncomputable section +open MvPolynomial + +/-- An irreducible plane equation over the prime field has at most +`p + planeResidueConstant * d² * √p` rational points when `p > stepanovThreshold d`. -/ +theorem plane_finite_field_card_bound {ι : Type*} [Fintype ι] + (p : ℕ) [Fact p.Prime] (f : MvPolynomial (Fin 2) (ZMod p)) (hf : Irreducible f) + (hsize : stepanovThreshold (f.totalDegree : ℝ) < p) + (z : ι → Fin 2 → ZMod p) (hz : Function.Injective z) + (hzero : ∀ i, eval (z i) f = 0) : + (Fintype.card ι : ℝ) ≤ p + planeResidueConstant * (f.totalDegree : ℝ) ^ 2 * Real.sqrt p := by + classical + have hd := totalDegree_pos_of_ne_zero_of_not_isUnit hf.ne_zero hf.not_isUnit + have hp : 27 * f.totalDegree ^ 4 < p := by exact_mod_cast hsize + have hchar : f.totalDegree < p := by + have h1 := Nat.le_mul_self f.totalDegree + have h2 := Nat.le_mul_self (f.totalDegree * f.totalDegree) + nlinarith only [hp, h1, h2] + obtain ⟨A, D, N, hA, hD, hDp, hNp, hN, hNpos, hx, hy, hpx⟩ := + exists_stepanov_integer_parameters f.totalDegree p hd hp + have hdim := stepanov_hilbert_dimension_inequality f hf.ne_zero p A D N hd hp hA hD hx hy hN hNpos + have hc := plane_stepanov_bound p f hf hd hchar z hz hzero hDp hNp hdim + exact stepanov_real_bound f.totalDegree p A D N (Fintype.card ι) hd hp hDp hN hNpos hpx hc + +/-- The sharp finite-field hypothesis used by the affine determinant argument +holds with the absolute constant `planeResidueConstant = 161`. -/ +theorem planeResidueBound : PlaneResidueBound planeResidueConstant := by + intro f _hd p hp hsize hir + let : Fact p.Prime := ⟨hp⟩ + classical + let q := MvPolynomial.map (Int.castRingHom (ZMod p)) f + have hqd : q.totalDegree ≤ f.totalDegree := totalDegree_map_le _ _ + have hs : stepanovThreshold (q.totalDegree : ℝ) < (p : ℝ) := by + apply lt_of_le_of_lt _ hsize + gcongr + let S := {x : Fin 2 → ZMod p // eval x q = 0} + have hh := plane_finite_field_card_bound p q hir hs (fun x : S => x.val) + Subtype.val_injective (fun x => x.property) + rw [planeResidue_card_eq_zero_card, Nat.card_eq_fintype_card] + apply hh.trans + gcongr + exact planeResidueConstant_pos.le + +/-- The full coefficient-uniform rational-plane integral-point bound. -/ +theorem planeCurveBound : PlaneCurveBound := + planeCurveBound_of_residue_bound planeResidueConstant_pos.le planeResidueBound + +end +end CCDN diff --git a/LeanPool/CCDN/Plane/DegreeOneAffineLine.lean b/LeanPool/CCDN/Plane/DegreeOneAffineLine.lean new file mode 100644 index 0000000000..424d447038 --- /dev/null +++ b/LeanPool/CCDN/Plane/DegreeOneAffineLine.lean @@ -0,0 +1,202 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.Algebra.IdealZeroLocus +public import LeanPool.CCDN.ForMathlib.KrullHeight +public import LeanPool.CCDN.Hilbert.AffineCurveDegree +public import LeanPool.CCDN.Plane.IntegralAffineLine +import LeanPool.CCDN.Algebra.CoordinatePowerGeneration +import LeanPool.CCDN.Hilbert.CriticalFilteredGrowth +import LeanPool.CCDN.Hilbert.PrimeComponentTranscendence + +/-! +# Degree-one prime curves are affine lines + +The eventual Hilbert-degree definition implies a one-dimensional increase in +successive filtered spaces. Critical growth forces every coordinate to be affine +in one transcendental coordinate, giving polynomial containment of an affine line. +-/ + +@[expose] public section +namespace CCDN +noncomputable section +open MvPolynomial + +private lemma coordinate_mul_filtered_mem {K σ : Type*} [Field K] + (I : Ideal (MvPolynomial σ K)) (j : σ) (N : ℕ) + {u : MvPolynomial σ K ⧸ I} (hu : u ∈ filteredCoordinateSpace I N) : + (Ideal.Quotient.mk I) (X j) * u ∈ filteredCoordinateSpace I (N + 1) := by + obtain ⟨f, hf, rfl⟩ := Submodule.mem_map.mp hu + apply Submodule.mem_map.mpr + refine ⟨X j * f, ?_, by simp⟩ + rw [mem_restrictTotalDegree] at hf ⊢ + apply (totalDegree_mul _ _).trans + rw [totalDegree_X] + omega + +/-- Affine coordinate formulas in a prime quotient give polynomial containment +of an affine line through each base-field-valued point. -/ +private theorem affine_coordinates_line_polynomial_identity {K : Type*} [Field K] + (p : Ideal (MvPolynomial (Fin 3) K)) + (j : Fin 3) (c d : Fin 3 → K) + (hj : Transcendental K ((Ideal.Quotient.mk p) (X j))) + (hcd : ∀ k, (Ideal.Quotient.mk p) (X k) = + c k • (Ideal.Quotient.mk p) (X j) + algebraMap K (MvPolynomial (Fin 3) K ⧸ p) (d k)) + (z : Fin 3 → K) (hz : z ∈ idealZeroLocus p) : + ∀ f ∈ p, + MvPolynomial.aeval + (fun k => Polynomial.C (z k) + Polynomial.C (c k) * Polynomial.X) f = 0 := by + let x := (Ideal.Quotient.mk p) (X j) + let φ : MvPolynomial (Fin 3) K →ₐ[K] Polynomial K := + MvPolynomial.aeval (fun k => Polynomial.C (d k) + Polynomial.C (c k) * Polynomial.X) + have hcomp : (Polynomial.aeval x).comp φ = Ideal.Quotient.mkₐ K p := by + apply MvPolynomial.algHom_ext + intro k + simp only [AlgHom.comp_apply, φ, aeval_X, map_add, map_mul, + Polynomial.aeval_C, Polynomial.aeval_X] + rw [Ideal.Quotient.mkₐ_eq_mk, hcd k] + have hsmul : c k • (Ideal.Quotient.mk p) (X j) = + algebraMap K _ (c k) * (Ideal.Quotient.mk p) (X j) := + Algebra.smul_def (R := K) (A := MvPolynomial (Fin 3) K ⧸ p) (c k) + ((Ideal.Quotient.mk p) (X j)) + rw [hsmul] + exact add_comm _ _ + have hφ (f : MvPolynomial (Fin 3) K) (hf : f ∈ p) : φ f = 0 := by + apply (transcendental_iff_injective.mp hj) + change ((Polynomial.aeval x).comp φ) f = _ + rw [hcomp] + simpa using Ideal.Quotient.eq_zero_iff_mem.mpr hf + have hpoint (k : Fin 3) : z k = d k + c k * z j := by + have hrel : X k - (C (d k) + C (c k) * X j) ∈ p := by + apply Ideal.Quotient.eq_zero_iff_mem.mp + simp only [map_sub, map_add, map_mul] + change (Ideal.Quotient.mk p) (X k) - + (algebraMap K _ (d k) + algebraMap K _ (c k) * (Ideal.Quotient.mk p) (X j)) = 0 + rw [hcd k] + have hsmul : c k • (Ideal.Quotient.mk p) (X j) = + algebraMap K _ (c k) * (Ideal.Quotient.mk p) (X j) := + Algebra.smul_def (R := K) (A := MvPolynomial (Fin 3) K ⧸ p) (c k) + ((Ideal.Quotient.mk p) (X j)) + rw [hsmul] + ring + have he := hz _ hrel + simpa only [eval_sub, eval_add, eval_mul, eval_X, eval_C, sub_eq_zero] using he + have htranslate : + (Polynomial.aeval (Polynomial.C (z j) + Polynomial.X)).comp φ = + MvPolynomial.aeval (fun k => Polynomial.C (z k) + Polynomial.C (c k) * Polynomial.X) := by + apply MvPolynomial.algHom_ext + intro k + simp only [AlgHom.comp_apply, φ, aeval_X, map_add, map_mul, + Polynomial.aeval_C, Polynomial.aeval_X] + rw [hpoint k] + simp only [Polynomial.C_add, Polynomial.C_mul, Polynomial.algebraMap_eq] + ring + intro f hf + rw [← htranslate, AlgHom.comp_apply, hφ f hf, map_zero] + +/-- A degree-one prime curve containing a base-field point has affine coordinate +formulas over that same field. In particular this holds directly over `ℚ`. -/ +private theorem degree_one_coordinates_affine_of_point {K : Type*} [Field K] + (p : Ideal (MvPolynomial (Fin 3) K)) [p.IsPrime] (hp : p.height = 2) + (hdegree : HasFilteredCurveDegree p 1) + (z : Fin 3 → K) (hz : z ∈ idealZeroLocus p) : + ∃ (j : Fin 3) (c d : Fin 3 → K), c j ≠ 0 ∧ + Transcendental K ((Ideal.Quotient.mk p) (X j)) ∧ ∀ k, + (Ideal.Quotient.mk p) (X k) = + c k • (Ideal.Quotient.mk p) (X j) + algebraMap K (MvPolynomial (Fin 3) K ⧸ p) (d k) := by + obtain ⟨N, b, hN⟩ := hdegree + obtain ⟨j, hj⟩ := height_two_transcendental_coordinate_of_point p hp z hz + have hdim : Module.finrank K (filteredCoordinateSpace p (N + 1)) = + Module.finrank K (filteredCoordinateSpace p N) + 1 := by + have h0 := hN N le_rfl + have h1 := hN (N + 1) (by omega) + push_cast at h0 h1 + omega + have hUone : (1 : MvPolynomial (Fin 3) K ⧸ p) ∈ filteredCoordinateSpace p N := by + apply Submodule.mem_map.mpr + refine ⟨1, ?_, by simp⟩ + rw [mem_restrictTotalDegree, totalDegree_one] + omega + have hex (k : Fin 3) : ∃ c d : K, + (Ideal.Quotient.mk p) (X k) = c • (Ideal.Quotient.mk p) (X j) + + algebraMap K (MvPolynomial (Fin 3) K ⧸ p) d := + affine_of_critical_growth_of_point (coordinatePoint p z hz) + (filteredCoordinateSpace p N) (filteredCoordinateSpace p (N + 1)) + (filteredCoordinateSpace_mono p (by omega)) hUone hdim hj + (fun _ hu => coordinate_mul_filtered_mem p j N hu) + (fun _ hu => coordinate_mul_filtered_mem p k N hu) + choose c d hcd using hex + have hcj : c j ≠ 0 := by + intro hc + have he := hcd j + rw [hc, zero_smul, zero_add] at he + apply hj + rw [he] + exact isAlgebraic_algebraMap (d j) + exact ⟨j, c, d, hcj, hj, hcd⟩ + +/-- Affine coordinate identities identify the entire base-field zero set +with the parameterized line, in both directions. -/ +private theorem zeroLocus_eq_line_of_affine_coordinates {K : Type*} [Field K] + (p : Ideal (MvPolynomial (Fin 3) K)) + (j : Fin 3) (c d : Fin 3 → K) + (hj : Transcendental K ((Ideal.Quotient.mk p) (X j))) + (hcd : ∀ k, (Ideal.Quotient.mk p) (X k) = + c k • (Ideal.Quotient.mk p) (X j) + algebraMap K (MvPolynomial (Fin 3) K ⧸ p) (d k)) + (z : Fin 3 → K) (hz : z ∈ idealZeroLocus p) : + idealZeroLocus p = {x | ∃ t : K, x = z + t • c} := by + have hpoint (x : Fin 3 → K) (hx : x ∈ idealZeroLocus p) (k : Fin 3) : + x k = c k * x j + d k := by + have he := congrArg (coordinatePoint p x hx) (hcd k) + simpa only [coordinatePoint_mk, eval_X, map_add, map_smul, + AlgHom.commutes, Algebra.algebraMap_self, RingHom.id_apply, smul_eq_mul] using he + ext x + constructor + · intro hx + refine ⟨x j - z j, ?_⟩ + funext k + change x k = z k + (x j - z j) * c k + rw [hpoint x hx k, hpoint z hz k] + ring + · rintro ⟨t, rfl⟩ f hf + have hpoly := affine_coordinates_line_polynomial_identity p j c d hj hcd z hz f hf + have he : (Polynomial.aeval t).comp + (MvPolynomial.aeval (fun k => Polynomial.C (z k) + Polynomial.C (c k) * Polynomial.X)) = + MvPolynomial.aeval (z + t • c) := by + apply MvPolynomial.algHom_ext + intro k + simp only [AlgHom.comp_apply, aeval_X, map_add, map_mul, + Polynomial.aeval_C, Polynomial.aeval_X, Pi.add_apply, Pi.smul_apply, smul_eq_mul, + Algebra.algebraMap_self, RingHom.id_apply] + ring + have hz' := congrArg (Polynomial.aeval t) hpoly + change ((Polynomial.aeval t).comp + (MvPolynomial.aeval (fun k => Polynomial.C (z k) + Polynomial.C (c k) * Polynomial.X))) f = + (Polynomial.aeval t) 0 at hz' + rw [he, map_zero, MvPolynomial.aeval_eq_eval] at hz' + exact hz' + +/-- A degree-one prime component with a rational point is exactly a +rational affine line. This is equality of zero sets, not just containment. -/ +theorem degree_one_zeroLocus_eq_rationalAffineLine + (p : Ideal (CoordinatePolynomial 3)) [p.IsPrime] (hp : p.height = 2) + (hdegree : HasAffineCurveDegree p 1) + (z : RatPoint 3) (hz : z ∈ idealZeroLocus p) : + ∃ v : RatPoint 3, v ≠ 0 ∧ idealZeroLocus p = rationalAffineLine z v ∧ + ∀ f ∈ p, MvPolynomial.aeval + (fun k => Polynomial.C (z k) + Polynomial.C (v k) * Polynomial.X) f = 0 := by + obtain ⟨j, c, d, hcj, hj, hcd⟩ := + degree_one_coordinates_affine_of_point p hp hdegree z hz + refine ⟨c, fun h => hcj (congrFun h j), ?_, + affine_coordinates_line_polynomial_identity p j c d hj hcd z hz⟩ + exact zeroLocus_eq_line_of_affine_coordinates p j c d hj hcd z hz + +end +end CCDN diff --git a/LeanPool/CCDN/Plane/FiniteFieldBadPlanes.lean b/LeanPool/CCDN/Plane/FiniteFieldBadPlanes.lean new file mode 100644 index 0000000000..f57a74a07f --- /dev/null +++ b/LeanPool/CCDN/Plane/FiniteFieldBadPlanes.lean @@ -0,0 +1,108 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.Noether.UniversalPlaneBadWitness +import LeanPool.CCDN.Plane.FiniteFieldPolynomialZeros + +/-! Counting all affine-plane parameter choices whose section drops degree +or is not geometrically irreducible. This includes zero restrictions, but +incidence averaging uses a sharper separate bound on those. -/ + +@[expose] public section +namespace CCDN +noncomputable section +open MvPolynomial + +/-- The bivariate restriction at a point of the full parameter space. -/ +def universalPlaneSection {K σ : Type*} [CommRing K] + (f : MvPolynomial σ K) (v : PlaneParameters σ → K) : Polynomial (Polynomial K) := + bivariateMap (eval v) (universalPlanePolynomial f) + +/-- The plane parameters (possibly degenerate) at which the plane section of `f` drops total degree +or is not geometrically irreducible. -/ +def BadPlaneParameters {K σ : Type*} [Field K] (f : MvPolynomial σ K) := + {v : PlaneParameters σ → K // + bivariateTotalDegree (universalPlaneSection f v) ≠ f.totalDegree ∨ + ¬Irreducible (bivariateMap (algebraMap K (AlgebraicClosure K)) (universalPlaneSection f v))} + +/-- Mapping the coefficients of a nested bivariate polynomial along a composite ring +homomorphism `ψ.comp φ` is the same as mapping along `φ` and then along `ψ`. -/ +lemma bivariateMap_comp_apply {R S T : Type*} [CommRing R] [CommRing S] [CommRing T] + (φ : R →+* S) (ψ : S →+* T) (f : Polynomial (Polynomial R)) : + bivariateMap (ψ.comp φ) f = bivariateMap ψ (bivariateMap φ f) := by + ext i j + simp only [bivariateMap_coeff, RingHom.comp_apply] + +private lemma badPlaneParameters_witness_zero {K σ : Type*} [Field K] {p : ℕ} [CharP K p] + (f : MvPolynomial σ K) (hpos : 0 < f.totalDegree) (hchar : f.totalDegree < p) + (rows : Fin (2 * f.totalDegree ^ 2 - 1) → ℕ × ℕ) + (cols : Fin (2 * f.totalDegree ^ 2 - 1) → ClosedFormColumns f.totalDegree) + (v : BadPlaneParameters f) : eval v.val (universalPlaneBadWitness f rows cols) = 0 := by + by_contra h + have hbase := universalPlaneBadWitness_good_specialization f hpos (.of_charP hchar) rows cols + (eval v.val) h + rcases v.property with hdeg | hir + · exact hdeg hbase.2.1 + · let L := AlgebraicClosure K + let φ : K →+* L := algebraMap K L + have hw : (φ.comp (eval v.val)) (universalPlaneBadWitness f rows cols) ≠ 0 := + (map_ne_zero_iff φ φ.injective).mpr h + have hh := universalPlaneBadWitness_good_specialization f hpos + ((CharZeroOrGT.of_charP hchar).map φ) rows cols _ hw + apply hir + simpa only [bivariateMap_comp_apply, universalPlaneSection, φ] using hh.2.2 + +/-- The proportion of geometrically bad plane parameters is at most 2*d^3/q. -/ +private theorem finite_field_bad_plane_parameters_mul_card + {K σ : Type*} [Field K] [Fintype K] [Fintype σ] {p : ℕ} [CharP K p] + (f : MvPolynomial (Option σ) K) + (hf : Irreducible (MvPolynomial.map (algebraMap K (AlgebraicClosure K)) f)) + (hpos : 0 < f.totalDegree) (hchar : 2 * f.totalDegree ^ 2 < p) : + Nat.card (BadPlaneParameters f) * Fintype.card K ≤ + (2 * f.totalDegree ^ 3) * Fintype.card K ^ Fintype.card (PlaneParameters (Option σ)) := by + classical + obtain ⟨rows, cols, hw, hdeg⟩ := exists_universalPlane_badWitness f hf hpos + (.of_charP hchar) + let W := universalPlaneBadWitness f rows cols + have hdp : f.totalDegree < p := by nlinarith + let ψ : BadPlaneParameters f → {v : PlaneParameters (Option σ) → K // eval v W = 0} := + fun v => ⟨v.val, badPlaneParameters_witness_zero f hpos hdp rows cols v⟩ + have hψ : Function.Injective ψ := by + intro x y h + apply Subtype.ext + change (ψ x).val = (ψ y).val + exact congrArg Subtype.val h + have hcard := Nat.card_le_card_of_injective ψ hψ + have hb := finite_field_polynomial_zeros_mul_card W hw + exact ((Nat.mul_le_mul_right (Fintype.card K) hcard).trans hb).trans + (Nat.mul_le_mul_right _ hdeg) + +/-- In m ambient coordinates, at most 2*d^3*q^(3*m-1) parameter choices +are bad. This counts triples (base point, first direction, second direction). -/ +theorem finite_field_bad_plane_parameters_card + {K σ : Type*} [Field K] [Fintype K] [Fintype σ] {p : ℕ} [CharP K p] + (f : MvPolynomial (Option σ) K) + (hf : Irreducible (MvPolynomial.map (algebraMap K (AlgebraicClosure K)) f)) + (hpos : 0 < f.totalDegree) (hchar : 2 * f.totalDegree ^ 2 < p) : + Nat.card (BadPlaneParameters f) ≤ + (2 * f.totalDegree ^ 3) * Fintype.card K ^ (3 * (Fintype.card σ + 1) - 1) := by + have h := finite_field_bad_plane_parameters_mul_card f hf hpos hchar + have hN : 1 ≤ Fintype.card (PlaneParameters (Option σ)) := Fintype.card_pos + have he : Fintype.card K ^ Fintype.card (PlaneParameters (Option σ)) = + Fintype.card K ^ (Fintype.card (PlaneParameters (Option σ)) - 1) * Fintype.card K := by + rw [← pow_succ, Nat.sub_add_cancel hN] + rw [he, ← Nat.mul_assoc] at h + have hc : Nat.card (BadPlaneParameters f) ≤ + (2 * f.totalDegree ^ 3) * Fintype.card K ^ (Fintype.card (PlaneParameters (Option σ)) - 1) := + Nat.le_of_mul_le_mul_right h Fintype.card_pos + simpa [PlaneParameters, Nat.mul_comm] using hc + +end +end CCDN diff --git a/LeanPool/CCDN/Plane/FiniteFieldConstants.lean b/LeanPool/CCDN/Plane/FiniteFieldConstants.lean new file mode 100644 index 0000000000..7d3ea658d6 --- /dev/null +++ b/LeanPool/CCDN/Plane/FiniteFieldConstants.lean @@ -0,0 +1,133 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import Mathlib.Basic.Real.Basic +import Mathlib.Tactic.Linarith +import Mathlib.Tactic.Ring + +/-! The prime cutoff `27 d⁴` and the absolute constants of the finite-field point counts. + +Every count below holds for primes `p > stepanovThreshold d = 27 d⁴` and has the shape +`main term + C d² √p · (lower-order power of p)`. The constants are derived from the plane +constant `planeResidueConstant = 161` by explicit additive corrections, recorded in the +docstrings and in the `_eq_add` lemmas. They are defined as numerals, so statements quoting +the numerals are definitionally equal to statements using the names. -/ + +@[expose] public section +namespace CCDN +noncomputable section + +/-- The prime cutoff `27 d⁴` of the Stepanov method. For `p > 27 d⁴` the integer Stepanov +parameters `A = ⌊√p / d⌋`, `D = ⌊(p - 1) / d⌋`, `N = d A - 4 d²` exist with `N > 0` +(`exists_stepanov_integer_parameters`), the Hilbert-dimension inequality holds +(`stepanov_numeric_dimension`), and `√p < 32 N` (`stepanov_real_bound`), since +`31² · 27 > 160²`. Bad primes are only counted above this cutoff; below it the trivial +bounds are used. The definition is polymorphic so that it reads the same over `ℕ` and `ℝ`. -/ +def stepanovThreshold {R : Type*} [Semiring R] (d : R) : R := 27 * d ^ 4 + +/-- The cast of the natural-number cutoff `stepanovThreshold d` is the cutoff of the cast of `d`. -/ +@[simp, norm_cast] +lemma Nat.cast_stepanovThreshold {R : Type*} [Semiring R] (d : ℕ) : + ((stepanovThreshold d : ℕ) : R) = stepanovThreshold (d : R) := by + simp [stepanovThreshold] + +/-- The cutoff `stepanovThreshold` is monotone on nonnegative arguments. -/ +@[gcongr] +lemma stepanovThreshold_le_stepanovThreshold {R : Type*} [Semiring R] [PartialOrder R] + [IsOrderedRing R] {a b : R} (ha : 0 ≤ a) (hab : a ≤ b) : + stepanovThreshold a ≤ stepanovThreshold b := by + unfold stepanovThreshold + have : (0 : R) ≤ 27 := Nat.ofNat_nonneg 27 + gcongr + +/-- The constant `161` of the affine plane-curve count `#C(𝔽_p) ≤ p + 161 d² √p` +(`plane_finite_field_card_bound`). In `stepanov_real_bound` the Stepanov count +`t N ≤ d D + p (N + 4 d²) + d² N` is bounded using `d D < p`, `(4 d² + 1) p ≤ 5 d² p`, +`p ≤ 32 N √p` and `1 ≤ √p`, giving `t ≤ p + (5 · 32 + 1) d² √p`. -/ +def planeResidueConstant : ℝ := 161 + +/-- The constant `162 = 161 + 1` of the projective plane-curve count +(`projective_plane_finite_field_card_bound`): the affine chart contributes +`p + 161 d² √p` and the line at infinity at most `d ≤ d² √p` further points +(`projectivePlaneResidueConstant_eq_add`). -/ +def projectivePlaneResidueConstant : ℝ := 162 + +/-- The constant `C = 164 = 162 + 2` for which the projective plane total multiplicity is +at most `p + (C + 1) d² √p` (`projectivePlaneTotalMultiplicity_le`). The singular +correction `3 d² (d - 1) ≤ 3 d² √p` adds `3` to `162`; the shift by one matches the affine +convention of `degreeFourDeterminantError`, whose singular correction `d² (d - 1)` costs `1` +(`projectivePlaneDeterminantConstant_eq_add`). -/ +def projectivePlaneDeterminantConstant : ℝ := 164 + +/-- The constant `164 = 161 + 3` of the affine hypersurface count +`#X(𝔽_p) ≤ p^(m-1) + 164 d² p^(m-2) √p` (`affine_hypersurface_finite_field_bound`). Averaging +the plane count over plane sections gives `161`; bad and zero plane sections cost at most +`3 d² p √p` (`hypersurface_bad_section_error`, `hypersurfaceResidueConstant_eq_add`). -/ +def hypersurfaceResidueConstant : ℝ := 164 + +/-- The constant `330 = 2 (164 + 1)` of the projective hypersurface count +(`projective_hypersurface_finite_field_bound`). Applied to the affine cone count with error +`H = 164 d² √p`, `homogeneous_projective_count_of_cone_bound` gives the projective error +`2 (H + 1) p^(m-2)`, and `1 ≤ d² √p` (`projectiveHypersurfaceResidueConstant_eq_add`). -/ +def projectiveHypersurfaceResidueConstant : ℝ := 330 + +/-- The value of `planeResidueConstant` is `161`. -/ +lemma planeResidueConstant_eq : planeResidueConstant = 161 := rfl + +/-- The value of `projectivePlaneResidueConstant` is `162`. -/ +lemma projectivePlaneResidueConstant_eq : projectivePlaneResidueConstant = 162 := rfl + +/-- The value of `projectivePlaneDeterminantConstant` is `164`. -/ +lemma projectivePlaneDeterminantConstant_eq : projectivePlaneDeterminantConstant = 164 := rfl + +/-- The value of `hypersurfaceResidueConstant` is `164`. -/ +lemma hypersurfaceResidueConstant_eq : hypersurfaceResidueConstant = 164 := rfl + +/-- The value of `projectiveHypersurfaceResidueConstant` is `330`. -/ +lemma projectiveHypersurfaceResidueConstant_eq : projectiveHypersurfaceResidueConstant = 330 := + rfl + +/-- The projective plane constant exceeds the affine plane constant by `1`, accounting for the +points on the line at infinity. -/ +lemma projectivePlaneResidueConstant_eq_add : + projectivePlaneResidueConstant = planeResidueConstant + 1 := by + norm_num [projectivePlaneResidueConstant, planeResidueConstant] + +/-- The projective plane determinant constant exceeds the projective plane residue constant +by `2`. -/ +lemma projectivePlaneDeterminantConstant_eq_add : + projectivePlaneDeterminantConstant = projectivePlaneResidueConstant + 2 := by + norm_num [projectivePlaneDeterminantConstant, projectivePlaneResidueConstant] + +/-- The affine hypersurface constant exceeds the plane constant by `3`, accounting for bad and +zero plane sections. -/ +lemma hypersurfaceResidueConstant_eq_add : + hypersurfaceResidueConstant = planeResidueConstant + 3 := by + norm_num [hypersurfaceResidueConstant, planeResidueConstant] + +/-- The projective hypersurface constant is twice the affine hypersurface constant plus one. -/ +lemma projectiveHypersurfaceResidueConstant_eq_add : + projectiveHypersurfaceResidueConstant = 2 * (hypersurfaceResidueConstant + 1) := by + norm_num [projectiveHypersurfaceResidueConstant, hypersurfaceResidueConstant] + +/-- The plane constant `planeResidueConstant` is positive. -/ +lemma planeResidueConstant_pos : 0 < planeResidueConstant := by + norm_num [planeResidueConstant] + +/-- The constant `projectivePlaneDeterminantConstant` is positive. -/ +lemma projectivePlaneDeterminantConstant_pos : 0 < projectivePlaneDeterminantConstant := by + norm_num [projectivePlaneDeterminantConstant] + +/-- The constant `projectiveHypersurfaceResidueConstant` is positive. -/ +lemma projectiveHypersurfaceResidueConstant_pos : 0 < projectiveHypersurfaceResidueConstant := by + norm_num [projectiveHypersurfaceResidueConstant] + +end +end CCDN diff --git a/LeanPool/CCDN/Plane/FiniteFieldPolynomialZeros.lean b/LeanPool/CCDN/Plane/FiniteFieldPolynomialZeros.lean new file mode 100644 index 0000000000..18cc94d8b7 --- /dev/null +++ b/LeanPool/CCDN/Plane/FiniteFieldPolynomialZeros.lean @@ -0,0 +1,34 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import Mathlib.Algebra.MvPolynomial.Degrees +public import Mathlib.SetTheory.Cardinal.Finite +import LeanPool.CCDN.ForMathlib.SchwartzZippel + +/-! The polynomial zero bound on the full affine parameter space, with its +field-size saving retained in multiplicative form. This is the Schwartz–Zippel +lemma `MvPolynomial.schwartz_zippel_totalDegree_card` applied to the whole finite field. -/ + +@[expose] public section +namespace CCDN +noncomputable section +open MvPolynomial + +/-- Schwartz--Zippel for an arbitrary finite coordinate type. -/ +theorem finite_field_polynomial_zeros_mul_card {K τ : Type*} [Field K] [Fintype K] + [Fintype τ] (f : MvPolynomial τ K) (hf : f ≠ 0) : + Nat.card {x : τ → K // eval x f = 0} * Fintype.card K ≤ + f.totalDegree * Fintype.card K ^ Fintype.card τ := by + classical + simpa [Nat.card_eq_fintype_card, Fintype.card_subtype] using + schwartz_zippel_totalDegree_card hf (Finset.univ : Finset K) + +end +end CCDN diff --git a/LeanPool/CCDN/Plane/FiniteFieldZeroPlanes.lean b/LeanPool/CCDN/Plane/FiniteFieldZeroPlanes.lean new file mode 100644 index 0000000000..cee1a3cf27 --- /dev/null +++ b/LeanPool/CCDN/Plane/FiniteFieldZeroPlanes.lean @@ -0,0 +1,75 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.Plane.FiniteFieldBadPlanes +import LeanPool.CCDN.Noether.UniversalPlaneSpecialization +import LeanPool.CCDN.Plane.FiniteFieldPolynomialZeros + +/-! Identically zero sections are bounded separately using their three +defining points. This gives three powers of the field size in the +denominator, even with degenerate parameterizations included. -/ + +@[expose] public section +namespace CCDN +noncomputable section +open MvPolynomial + +/-- The usual degree-times-q^(m-1) bound, for arbitrary nonempty finite +coordinate types and affine zeros. -/ +lemma finite_field_polynomial_zeros_card {K τ : Type*} [Field K] [Fintype K] + [Fintype τ] [Nonempty τ] (f : MvPolynomial τ K) (hf : f ≠ 0) : + Nat.card {x : τ → K // eval x f = 0} ≤ + f.totalDegree * Fintype.card K ^ (Fintype.card τ - 1) := by + have h := finite_field_polynomial_zeros_mul_card f hf + have he : Fintype.card K ^ Fintype.card τ = + Fintype.card K ^ (Fintype.card τ - 1) * Fintype.card K := by + rw [← pow_succ, Nat.sub_add_cancel Fintype.card_pos] + rw [he, ← Nat.mul_assoc] at h + exact Nat.le_of_mul_le_mul_right h Fintype.card_pos + +/-- All parameter triples whose restricted polynomial is identically zero. -/ +def ZeroPlaneParameters {K σ : Type*} [CommRing K] (f : MvPolynomial σ K) := + {v : PlaneParameters σ → K // universalPlaneSection f v = 0} + +/-- Sending a zero section to its three defining hypersurface points is +injective. -/ +private theorem zero_plane_parameters_card_le_cube {K σ : Type*} [CommRing K] + [Finite K] [Finite σ] (f : MvPolynomial σ K) : + Nat.card (ZeroPlaneParameters f) ≤ + (Nat.card {x : σ → K // eval x f = 0}) ^ 3 := by + classical + let := Fintype.ofFinite K + let := Fintype.ofFinite σ + let Z := {x : σ → K // eval x f = 0} + let ψ : ZeroPlaneParameters f → (Fin 3 → Z) := fun v j => + ⟨planeTriplePointsEquiv v.val j, zero_universalPlaneSection_three_points f v.val v.property j⟩ + have hψ : Function.Injective ψ := by + intro x y h + apply Subtype.ext + apply planeTriplePointsEquiv.injective + funext j + exact congrArg Subtype.val (congrFun h j) + have h := Nat.card_le_card_of_injective ψ hψ + simpa only [Nat.card_eq_fintype_card, Fintype.card_fun, Fintype.card_fin] using h + +/-- In m ambient coordinates, zero restrictions have at most d^3*q^(3*m-3) +parameter triples. This is substantially smaller than the general bad-plane +bound and controls their q^2-point contribution to incidence averaging. -/ +theorem finite_field_zero_plane_parameters_card + {K σ : Type*} [Field K] [Fintype K] [Fintype σ] + (f : MvPolynomial (Option σ) K) (hf : f ≠ 0) : + Nat.card (ZeroPlaneParameters f) ≤ + f.totalDegree ^ 3 * Fintype.card K ^ (3 * Fintype.card σ) := by + have h := (zero_plane_parameters_card_le_cube f).trans + (Nat.pow_le_pow_left (finite_field_polynomial_zeros_card f hf) 3) + simpa only [Fintype.card_option, Nat.add_sub_cancel, mul_pow, ← pow_mul, Nat.mul_comm] using h + +end +end CCDN diff --git a/LeanPool/CCDN/Plane/FrobeniusSystem.lean b/LeanPool/CCDN/Plane/FrobeniusSystem.lean new file mode 100644 index 0000000000..b7a0ee8fb6 --- /dev/null +++ b/LeanPool/CCDN/Plane/FrobeniusSystem.lean @@ -0,0 +1,113 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.Hilbert.FilteredIntersection +public import LeanPool.CCDN.Noether.FormalPlaneDifferentiation +import LeanPool.CCDN.Determinant.PlaneSingularities +import Mathlib.LinearAlgebra.FiniteDimensional.Lemmas + +/-! The finite linear system producing Frobenius auxiliary coefficients. +A strict dimension inequality gives a nontrivial solution. -/ + +@[expose] public section +namespace CCDN +noncomputable section +open MvPolynomial + +private lemma planeTangent_totalDegree_le {K : Type*} [Field K] + (f h : MvPolynomial (Fin 2) K) (j : Fin 2) : + (planeTangent f j h).totalDegree ≤ h.totalDegree + f.totalDegree := by + rw [planeTangent_apply] + apply (totalDegree_sub _ _).trans + apply max_le + · exact (totalDegree_mul _ _).trans (by + have h1 := pderiv_totalDegree_le f j.rev + have h2 := pderiv_totalDegree_le h j + omega) + · exact (totalDegree_mul _ _).trans (by + have h1 := pderiv_totalDegree_le f j + have h2 := pderiv_totalDegree_le h j.rev + omega) + +private lemma planeTangent_iterate_totalDegree_le {K : Type*} [Field K] + (f h : MvPolynomial (Fin 2) K) (j : Fin 2) (r : ℕ) : + ((planeTangent f j)^[r] h).totalDegree ≤ h.totalDegree + r * f.totalDegree := by + induction r with + | zero => simp + | succ r hr => + rw [Function.iterate_succ_apply'] + exact (planeTangent_totalDegree_le f _ j).trans (by nlinarith) + +/-- Independent bounded-degree coefficient classes and more unknowns than +constraints yield bounded-degree auxiliary coefficients. -/ +theorem exists_planeFrobenius_coefficients {K ι κ : Type*} [Field K] + [Fintype ι] [Fintype κ] + (f : MvPolynomial (Fin 2) K) (j : Fin 2) + (h : ι → MvPolynomial (Fin 2) K) (b : κ → MvPolynomial (Fin 2) K) + {D A N : ℕ} (hd : ∀ i, (h i).totalDegree ≤ D) (hb : ∀ k, (b k).totalDegree ≤ A) + (hbi : LinearIndependent K (fun k => (Ideal.Quotient.mkₐ K (Ideal.span {f})) (b k))) + (hdim : (∑ r : Fin N, Module.finrank K + (filteredCoordinateSpace (Ideal.span {f}) (D + A + r.val * f.totalDegree))) < + Fintype.card ι * Fintype.card κ) : + ∃ a : ι → MvPolynomial (Fin 2) K, + (∀ i, (a i).totalDegree ≤ A) ∧ (∃ i, ¬ f ∣ a i) ∧ + ∀ r < N, f ∣ ∑ i, (planeTangent f j)^[r] (h i) * a i := by + classical + let I : Ideal (MvPolynomial (Fin 2) K) := Ideal.span {f} + let π := Ideal.Quotient.mkₐ K I + let V (r : Fin N) := filteredCoordinateSpace I (D + A + r.val * f.totalDegree) + let v (r : Fin N) (t : ι × κ) : V r := + ⟨π ((planeTangent f j)^[r.val] (h t.1) * b t.2), by + apply Submodule.mem_map.mpr + refine ⟨_, ?_, rfl⟩ + rw [mem_restrictTotalDegree] + exact (totalDegree_mul _ _).trans (by + have hh := planeTangent_iterate_totalDegree_le f (h t.1) j r.val + have hh' := hd t.1 + have hb' := hb t.2 + omega)⟩ + let F : (ι × κ → K) →ₗ[K] (∀ r, V r) := + { toFun := fun c r => ∑ t, c t • v r t + map_add' := by intros; funext r; simp only [Pi.add_apply, add_smul, Finset.sum_add_distrib] + map_smul' := by + intros + funext r + simp only [Pi.smul_apply, smul_eq_mul, mul_smul, Finset.smul_sum, RingHom.id_apply] } + have hdim' : Module.finrank K (∀ r, V r) < Module.finrank K (ι × κ → K) := by + rw [Module.finrank_pi, Fintype.card_prod, Module.finrank_pi_fintype] + exact hdim + obtain ⟨c, hFc, hc⟩ := + Submodule.exists_mem_ne_zero_of_ne_bot (LinearMap.ker_ne_bot_of_finrank_lt (f := F) hdim') + rw [LinearMap.mem_ker] at hFc + let a : ι → MvPolynomial (Fin 2) K := fun i => ∑ k, c (i, k) • b k + refine ⟨a, ?_, ?_, ?_⟩ + · intro i + apply totalDegree_finsetSum_le + intro k _ + exact (totalDegree_smul_le _ _).trans (hb k) + · by_contra! hn + apply hc + funext t + have he : π (a t.1) = 0 := + Ideal.Quotient.eq_zero_iff_mem.mpr (Ideal.mem_span_singleton.mpr (hn t.1)) + have hs : ∑ k, c (t.1, k) • π (b k) = 0 := by + simpa only [a, map_sum, map_smul] using he + exact Fintype.linearIndependent_iff.mp hbi (fun k => c (t.1, k)) hs t.2 + · intro r hr + have he := congrArg Subtype.val (congrFun hFc (⟨r, hr⟩ : Fin N)) + apply Ideal.mem_span_singleton.mp + apply Ideal.Quotient.eq_zero_iff_mem.mp + change π (∑ i, (planeTangent f j)^[r] (h i) * a i) = 0 + simpa only [F, LinearMap.coe_mk, AddHom.coe_mk, Submodule.coe_sum, + Submodule.coe_smul, v, Fintype.sum_prod_type, a, Finset.mul_sum, + mul_smul_comm, map_sum, map_smul, Pi.zero_apply, ZeroMemClass.coe_zero] using he + +end +end CCDN diff --git a/LeanPool/CCDN/Plane/FullDegreeSectionIrreducibility.lean b/LeanPool/CCDN/Plane/FullDegreeSectionIrreducibility.lean new file mode 100644 index 0000000000..a404fda334 --- /dev/null +++ b/LeanPool/CCDN/Plane/FullDegreeSectionIrreducibility.lean @@ -0,0 +1,79 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.Plane.FiniteFieldBadPlanes +import LeanPool.CCDN.ForMathlib.MvPolynomial +import LeanPool.CCDN.Plane.SectionBounds +import Mathlib.Algebra.MvPolynomial.Nilpotent +import Mathlib.Algebra.MvPolynomial.NoZeroDivisors + +/-! An irreducible section retaining the full total degree forces the +original equation to be irreducible. Degree additivity prevents either +nonconstant ambient factor from disappearing under restriction. -/ + +@[expose] public section +namespace CCDN +noncomputable section +open MvPolynomial + +private lemma polynomial_isUnit_of_nonzero_degree_zero {K σ : Type*} [Field K] + (f : MvPolynomial σ K) (hf : f ≠ 0) (hd : f.totalDegree = 0) : IsUnit f := by + by_contra hu + exact (totalDegree_pos_of_ne_zero_of_not_isUnit hf hu).ne' hd + +/-- A degree-nonincreasing polynomial ring homomorphism reflects irreducibility +at every equation whose full degree is retained. -/ +theorem irreducible_of_full_degree_map {K σ τ : Type*} [Field K] + (ψ : MvPolynomial σ K →+* MvPolynomial τ K) + (hψ : ∀ g, (ψ g).totalDegree ≤ g.totalDegree) + (f : MvPolynomial σ K) (hir : Irreducible (ψ f)) + (hdeg : (ψ f).totalDegree = f.totalDegree) : Irreducible f := by + refine ⟨fun hu => hir.not_isUnit (hu.map ψ), ?_⟩ + intro a b he + have he' : ψ f = ψ a * ψ b := by rw [he, map_mul] + have ha : ψ a ≠ 0 := left_ne_zero_of_mul (he' ▸ hir.ne_zero) + have hb : ψ b ≠ 0 := right_ne_zero_of_mul (he' ▸ hir.ne_zero) + have ha0 : a ≠ 0 := by intro hz; exact ha (by rw [hz, map_zero]) + have hb0 : b ≠ 0 := by intro hz; exact hb (by rw [hz, map_zero]) + have ht := totalDegree_mul_of_isDomain ha0 hb0 + have ht' := totalDegree_mul_of_isDomain ha hb + rw [← he] at ht + rw [← he', hdeg] at ht' + have hda := hψ a + have hdb := hψ b + rcases hir.isUnit_or_isUnit he' with hu | hu + · left + have hz := (isUnit_iff_totalDegree_of_isReduced.mp hu).2 + exact polynomial_isUnit_of_nonzero_degree_zero a ha0 (by omega) + · right + have hz := (isUnit_iff_totalDegree_of_isReduced.mp hu).2 + exact polynomial_isUnit_of_nonzero_degree_zero b hb0 (by omega) + +/-- The ring homomorphism restricting a polynomial in the coordinates `σ` to the affine plane +`a + s • b + t • c` whose base point and directions are given by the parameters `v`, as a +polynomial in the plane coordinates `s` and `t`. -/ +def planeSectionPolynomialHom {K σ : Type*} [CommRing K] (v : PlaneParameters σ → K) : + MvPolynomial σ K →+* MvPolynomial (Fin 2) K := + (bivariatePolynomialEquiv K).symm.toRingHom.comp + ((bivariateMap (eval v)).comp + ((bivariatePolynomialEquiv _).toRingHom.comp (universalPlaneMap K σ).toRingHom)) + +/-- If the plane section of `f` at parameters `v` is irreducible and keeps the total degree of `f`, +then `f` is irreducible; the parameters `v` may be degenerate. -/ +theorem irreducible_of_full_degree_planeSection {K σ : Type*} [Field K] + (f : MvPolynomial σ K) (v : PlaneParameters σ → K) + (hir : Irreducible (universalPlaneSection f v)) + (hdeg : bivariateTotalDegree (universalPlaneSection f v) = f.totalDegree) : Irreducible f := by + apply irreducible_of_full_degree_map (planeSectionPolynomialHom v) + (fun g => planeSectionPolynomial_totalDegree g v) f _ hdeg + exact (MulEquiv.irreducible_iff (f := (bivariatePolynomialEquiv K).symm)).mpr hir + +end +end CCDN diff --git a/LeanPool/CCDN/Plane/GridHeight.lean b/LeanPool/CCDN/Plane/GridHeight.lean new file mode 100644 index 0000000000..fb114fc4f0 --- /dev/null +++ b/LeanPool/CCDN/Plane/GridHeight.lean @@ -0,0 +1,83 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.Noether.BivariateTotalDegree +public import Mathlib.Basic.Real.Basic +import LeanPool.CCDN.Height.MultivariateGridHeight +import LeanPool.CCDN.Noether.ShearNormalization + +/-! A coefficient-height witness on the bounded integer plane grid: the case n = 2 of +`CCDN.Height.MultivariateGridHeight`. -/ + +@[expose] public section +namespace CCDN +noncomputable section +open Polynomial + +/-- Evaluating the nested-polynomial form of a bivariate polynomial `f` at `(x, y)` (inner +variable at `y`, outer variable at `x`) agrees with evaluating `f` at `![x, y]`. -/ +lemma bivariatePolynomialEquiv_eval {R : Type*} [CommRing R] + (f : MvPolynomial (Fin 2) R) (x y : R) : + (Polynomial.map (Polynomial.evalRingHom y) (bivariatePolynomialEquiv R f)).eval x = + MvPolynomial.eval ![x, y] f := by + have he : (Polynomial.evalRingHom x).comp + ((Polynomial.mapRingHom (Polynomial.evalRingHom y)).comp + (bivariatePolynomialEquiv R).toRingHom) = + MvPolynomial.eval ![x, y] := by + apply MvPolynomial.ringHom_ext + · intro c + simp [bivariatePolynomialEquiv_C] + · intro i + fin_cases i <;> simp [bivariatePolynomialEquiv_X_zero, bivariatePolynomialEquiv_X_one] + exact RingHom.congr_fun he f + +/-- Natural-number points of `Fin 2 → ℕ`, written as vectors of length two. -/ +private lemma natCast_fin_two_eq {R : Type*} [CommRing R] (a : Fin 2 → ℕ) : + (fun i => (a i : R)) = ![(a 0 : R), (a 1 : R)] := by + funext i + fin_cases i <;> rfl + +/-- The plane case of `multivariate_coefficient_bound_of_integer_grid`, for nested +polynomials. -/ +theorem bivariate_coefficient_bound_of_integer_grid (f : ℝ[X][X]) {d : ℕ} + (hd : bivariateTotalDegree f ≤ d) {H : ℝ} (hH : 0 ≤ H) + (heval : ∀ x y : ℕ, x ≤ d → y ≤ d → + |(Polynomial.map (Polynomial.evalRingHom (y : ℝ)) f).eval (x : ℝ)| ≤ H) (i j : ℕ) : + |(f.coeff i).coeff j| ≤ ((d : ℝ) + 1) ^ (2 * (d + 1)) * H := by + have h := multivariate_coefficient_bound_of_integer_grid + ((bivariatePolynomialEquiv ℝ).symm f) hd hH (fun a ha => by + have hh := heval (a 0) (a 1) (ha 0) (ha 1) + rwa [← (bivariatePolynomialEquiv ℝ).apply_symm_apply f, bivariatePolynomialEquiv_eval, + ← natCast_fin_two_eq] at hh) (Finsupp.cons i (Finsupp.single 0 j)) + rwa [← bivariatePolynomialEquiv_coeff, AlgEquiv.apply_symm_apply] at h + +/-- A nonzero integer plane polynomial of total degree at most `d` has a nonzero value at some +natural point `(x, y)` with `x, y ≤ d`, and every coefficient is bounded by +`(d + 1) ^ (2 * (d + 1))` times the absolute value of that value. -/ +theorem exists_integer_plane_grid_height_witness (f : MvPolynomial (Fin 2) ℤ) + (hf : f ≠ 0) {d : ℕ} (hd : f.totalDegree ≤ d) : + ∃ x y : ℕ, x ≤ d ∧ y ≤ d ∧ MvPolynomial.eval ![(x : ℤ), (y : ℤ)] f ≠ 0 ∧ ∀ m, + |(f.coeff m : ℝ)| ≤ ((d : ℝ) + 1) ^ (2 * (d + 1)) * + |(MvPolynomial.eval ![(x : ℤ), (y : ℤ)] f : ℝ)| := by + obtain ⟨a, ha, hne, hc⟩ := exists_integer_multivariate_grid_height_witness f hf hd + lift a to Fin 2 → ℕ using fun i => (ha i).1 + rw [natCast_fin_two_eq] at hne hc + exact ⟨a 0, a 1, Int.ofNat_le.mp (ha 0).2, Int.ofNat_le.mp (ha 1).2, hne, hc⟩ + +/-- The plane case of `integer_multivariate_coefficient_bound_of_grid`. -/ +theorem integer_plane_coefficient_bound_of_grid (f : MvPolynomial (Fin 2) ℤ) {d : ℕ} + (hd : f.totalDegree ≤ d) {H : ℝ} (hH : 0 ≤ H) + (heval : ∀ x y : ℕ, x ≤ d → y ≤ d → |(MvPolynomial.eval ![(x : ℤ), (y : ℤ)] f : ℝ)| ≤ H) + (m : Fin 2 →₀ ℕ) : |(f.coeff m : ℝ)| ≤ ((d : ℝ) + 1) ^ (2 * (d + 1)) * H := + integer_multivariate_coefficient_bound_of_grid f hd hH (fun a ha => by + rw [natCast_fin_two_eq]; exact heval (a 0) (a 1) (ha 0) (ha 1)) m + +end +end CCDN diff --git a/LeanPool/CCDN/Plane/HomogeneousLeadingCoefficient.lean b/LeanPool/CCDN/Plane/HomogeneousLeadingCoefficient.lean new file mode 100644 index 0000000000..dce689b975 --- /dev/null +++ b/LeanPool/CCDN/Plane/HomogeneousLeadingCoefficient.lean @@ -0,0 +1,126 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.Algebra.HomogeneousTranslation +public import LeanPool.CCDN.Height.IntegerCoefficientHeight +public import Mathlib.RingTheory.MvPolynomial.MonomialOrder +import LeanPool.CCDN.Algebra.HomogeneousChart +import LeanPool.CCDN.Plane.GridHeight + +/-! Positioning a coefficient-height witness as the lexicographic leading +coefficient by a bounded integral translation of the affine chart. -/ + +@[expose] public section +namespace CCDN +noncomputable section +open MvPolynomial +open scoped MonomialOrder + +/-- Every exponent of total degree at most `d` is lexicographically at most the pure power +`x₀ ^ d` of the first variable. -/ +lemma exponent_le_pure_first {n d : ℕ} (m : Fin (n + 1) →₀ ℕ) + (hm : m.sum (fun _ e => e) ≤ d) : m ≼[MonomialOrder.lex] Finsupp.single 0 d := by + have hs := exponent_degree_split m + by_cases he : m 0 = d + · have ht : chartExponent m = 0 := by + apply (Finsupp.degree_eq_zero_iff _).mp + change (chartExponent m).sum (fun _ e => e) = 0 + omega + have hm' : m = Finsupp.single 0 d := by + ext i + refine Fin.cases ?_ (fun j => ?_) i + · simpa using he + · have h := congrArg (fun t => t j) ht + simpa only [chartExponent_apply, Finsupp.coe_zero, Pi.zero_apply, Finsupp.single_eq_of_ne + (show j.succ ≠ (0 : Fin (n + 1)) from Fin.succ_ne_zero j)] using h + rw [hm'] + · apply le_of_lt + change toLex m < toLex (Finsupp.single (0 : Fin (n + 1)) d) + refine ⟨0, ?_, ?_⟩ + · intro i hi + exact False.elim (not_lt_of_ge (Fin.zero_le i) hi) + · simp only [ofLex_toLex, Finsupp.single_eq_same] + omega + +/-- If `f` has total degree at most `d` and a nonzero coefficient at `x₀ ^ d`, then its +lexicographic leading coefficient is that coefficient. -/ +lemma lex_leadingCoeff_eq_pure_first {R : Type*} [CommRing R] {n d : ℕ} + (f : MvPolynomial (Fin (n + 1)) R) (hd : f.totalDegree ≤ d) + (hc : f.coeff (Finsupp.single 0 d) ≠ 0) : + MonomialOrder.lex.leadingCoeff f = f.coeff (Finsupp.single 0 d) := by + have he : MonomialOrder.lex.degree f = Finsupp.single 0 d := by + apply MonomialOrder.lex.toSyn.injective + apply le_antisymm + · apply MonomialOrder.degree_le_iff.mpr + intro m hm + exact exponent_le_pure_first m ((le_totalDegree hm).trans hd) + · exact MonomialOrder.lex.le_degree (mem_support_iff.mpr hc) + change f.coeff (MonomialOrder.lex.degree f) = _ + rw [he] + +private lemma homogeneous_pure_first_coefficient {R : Type*} [CommRing R] {n d : ℕ} + (f : MvPolynomial (Fin (n + 1)) R) (hf : f.IsHomogeneous d) : + f.coeff (Finsupp.single 0 d) = eval (Fin.cons 1 (0 : Fin n → R)) f := by + have hd : (Finsupp.single (0 : Fin (n + 1)) d).degree = d := by simp + obtain ⟨j, hj⟩ := homogeneousMonomials_surjective n d (Finsupp.single 0 d) hd + have hc := affineChart_homogeneous_coefficient f hf j + rw [hj] at hc + have ht : chartExponent (Finsupp.single (0 : Fin (n + 1)) d) = 0 := by + ext i + simp [chartExponent_apply] + rw [ht] at hc + rw [← affineChart_eval, eval_zero] + exact hc.symm + +/-- For `f` homogeneous of degree `d`, the coefficient of `x₀ ^ d` in the translate +`homogeneousTranslation R a f` is the value `f (1, a)`. -/ +lemma homogeneousTranslation_pure_first {R : Type*} [CommRing R] {n d : ℕ} + (a : Fin n → R) (f : MvPolynomial (Fin (n + 1)) R) (hf : f.IsHomogeneous d) : + (homogeneousTranslation R a f).coeff (Finsupp.single 0 d) = eval (Fin.cons 1 a) f := by + rw [homogeneous_pure_first_coefficient _ (homogeneousTranslation_homogeneous a hf), + homogeneousTranslation_eval_chart, zero_add] + +/-- Every original coefficient is controlled by the leading coefficient +obtained by a translation with parameters in 0,...,d. -/ +theorem exists_bounded_leading_coefficient_translation {d : ℕ} + (f : MvPolynomial (Fin 3) ℤ) (hf : f.IsHomogeneous d) (hf0 : f ≠ 0) : + ∃ a : Fin 2 → ℤ, (∀ j, 0 ≤ a j ∧ a j ≤ d) ∧ + MonomialOrder.lex.leadingCoeff (homogeneousTranslation ℤ a f) ≠ 0 ∧ + CoefficientHeightLE f (((d : ℝ) + 1) ^ (2 * (d + 1)) * + |((MonomialOrder.lex.leadingCoeff (homogeneousTranslation ℤ a f) : ℤ) : ℝ)|) := by + have hc0 : affineChart 2 f ≠ 0 := by + intro hz + exact hf0 (affineChart_injective_on_homogeneous hf (isHomogeneous_zero _ _ d) + (by simpa only [map_zero] using hz)) + obtain ⟨x, y, hx, hy, hval, hcoeff⟩ := exists_integer_plane_grid_height_witness (affineChart 2 f) + hc0 ((affineChart_totalDegree_le f).trans hf.totalDegree_le) + let a : Fin 2 → ℤ := ![(x : ℤ), (y : ℤ)] + have he : (homogeneousTranslation ℤ a f).coeff (Finsupp.single 0 d) = + eval ![(x : ℤ), (y : ℤ)] (affineChart 2 f) := by + rw [homogeneousTranslation_pure_first a f hf, affineChart_eval] + have hlc := lex_leadingCoeff_eq_pure_first (homogeneousTranslation ℤ a f) + (homogeneousTranslation_homogeneous a hf).totalDegree_le (he ▸ hval) + refine ⟨a, ?_, by rw [hlc, he]; exact hval, ?_⟩ + · intro j + fin_cases j + · change 0 ≤ (x : ℤ) ∧ (x : ℤ) ≤ d + exact ⟨by positivity, by exact_mod_cast hx⟩ + · change 0 ≤ (y : ℤ) ∧ (y : ℤ) ≤ d + exact ⟨by positivity, by exact_mod_cast hy⟩ + · intro m + by_cases hm : m.degree = d + · obtain ⟨j, rfl⟩ := homogeneousMonomials_surjective 2 d m hm + rw [← affineChart_homogeneous_coefficient f hf j, hlc, he] + exact hcoeff _ + · rw [hf.coeff_eq_zero hm, Int.cast_zero, abs_zero] + positivity + +end +end CCDN diff --git a/LeanPool/CCDN/Plane/HyperplanePlaneAvoidance.lean b/LeanPool/CCDN/Plane/HyperplanePlaneAvoidance.lean new file mode 100644 index 0000000000..61a01e04a3 --- /dev/null +++ b/LeanPool/CCDN/Plane/HyperplanePlaneAvoidance.lean @@ -0,0 +1,75 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.Algebra.PolynomialPredicates +public import LeanPool.CCDN.Plane.FiniteFieldBadPlanes +import LeanPool.CCDN.Noether.ClosedFormParameterDegree +import LeanPool.CCDN.Noether.UniversalPlaneSpecialization + +/-! A bounded geometrically good plane whose three parameter columns have +an invertible minor in three selected ambient coordinates. This additional +condition allows an effective containing hyperplane to be constructed. -/ + +@[expose] public section +namespace CCDN +noncomputable section +open MvPolynomial + +private def planeAnchorMinor (n : ℕ) : + MvPolynomial (PlaneParameters (Option (Fin (3 + n)))) ℤ := + Matrix.det (Matrix.of (fun i j : Fin 3 => X (some (i.castAdd n), j))) + +private lemma planeAnchorMinor_eval (n : ℕ) + (v : PlaneParameters (Option (Fin (3 + n))) → ℤ) : + eval v (planeAnchorMinor n) = + Matrix.det (Matrix.of (fun i j : Fin 3 => v (some (i.castAdd n), j))) := by + simp [planeAnchorMinor, Matrix.det_fin_three] + +private lemma planeAnchorMinor_degree (n : ℕ) : (planeAnchorMinor n).totalDegree ≤ 3 := by + have hh := matrix_det_parameterDegree + (Matrix.of (fun i j : Fin 3 => (X (some (i.castAdd n), j) : + MvPolynomial (PlaneParameters (Option (Fin (3 + n)))) ℤ))) 1 (by intros; simp) + simpa only [planeAnchorMinor, Fintype.card_fin, Nat.mul_one] using hh + +private lemma planeAnchorMinor_ne_zero (n : ℕ) : planeAnchorMinor n ≠ 0 := by + let v : PlaneParameters (Option (Fin (3 + n))) → ℤ := + fun p => Option.elim p.1 0 (fun i => if i.val = p.2.val then 1 else 0) + have hv : eval v (planeAnchorMinor n) = 1 := by + rw [planeAnchorMinor_eval] + norm_num [v, Matrix.det_fin_three] + intro hz + rw [hz, map_zero] at hv + norm_num at hv + +/-- The extra determinant condition costs only three in the integer grid +size. -/ +theorem exists_integer_good_plane_with_anchor {n : ℕ} + (f : MvPolynomial (Option (Fin (3 + n))) ℤ) + (hir : AbsolutelyIrreducible f) + (hd : 0 < f.totalDegree) : + ∃ v : PlaneParameters (Option (Fin (3 + n))) → ℤ, + (∀ p, 0 ≤ v p ∧ v p ≤ 2 * f.totalDegree ^ 3 + 3) ∧ + Matrix.det (Matrix.of (fun i j : Fin 3 => v (some (i.castAdd n), j))) ≠ 0 ∧ + (bivariateMap (Int.castRingHom (AlgebraicClosure ℚ)) + (universalPlaneSection f v)).natDegree = f.totalDegree ∧ + bivariateTotalDegree (bivariateMap (Int.castRingHom (AlgebraicClosure ℚ)) + (universalPlaneSection f v)) = f.totalDegree ∧ + Irreducible (bivariateMap (Int.castRingHom (AlgebraicClosure ℚ)) + (universalPlaneSection f v)) := by + obtain ⟨v, hv, hminor, hgood⟩ := exists_integer_geometrically_good_plane_avoiding + f hir hd (planeAnchorMinor n) (planeAnchorMinor_ne_zero n) + refine ⟨v, ?_, ?_, hgood⟩ + · intro p + refine ⟨(hv p).1, (hv p).2.trans ?_⟩ + exact_mod_cast Nat.add_le_add_left (planeAnchorMinor_degree n) (2 * f.totalDegree ^ 3) + · simpa only [planeAnchorMinor_eval] using hminor + +end +end CCDN diff --git a/LeanPool/CCDN/Plane/IntegerPlaneHeight.lean b/LeanPool/CCDN/Plane/IntegerPlaneHeight.lean new file mode 100644 index 0000000000..16f37f0e51 --- /dev/null +++ b/LeanPool/CCDN/Plane/IntegerPlaneHeight.lean @@ -0,0 +1,97 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.Height.Box +public import LeanPool.CCDN.Height.IntegerCoefficientHeight +public import LeanPool.CCDN.Noether.ClosedFormHeight +public import LeanPool.CCDN.Plane.FiniteFieldBadPlanes +import LeanPool.CCDN.Height.PolynomialBoxHeight +import LeanPool.CCDN.Noether.UniversalPlaneSpecialization +import LeanPool.CCDN.Plane.GridHeight + +/-! Coefficient height of an integer plane section with bounded +parameters, and the resulting bound on the universal bad-prime witness. -/ + +@[expose] public section +namespace CCDN +noncomputable section +open MvPolynomial + +/-- Taking plane sections commutes with mapping coefficients along a ring homomorphism `φ`: the +section of `map φ f` at `φ ∘ v` is the coefficient-wise image of the section of `f` at `v`. -/ +lemma universalPlaneSection_map {R S σ : Type*} [CommRing R] [CommRing S] + (φ : R →+* S) (f : MvPolynomial σ R) (v : PlaneParameters σ → R) : + universalPlaneSection (MvPolynomial.map φ f) (φ ∘ v) = + bivariateMap φ (universalPlaneSection f v) := by + unfold universalPlaneSection + rw [universalPlanePolynomial_map, ← bivariateMap_comp_apply, + ← bivariateMap_comp_apply] + congr 2 + apply MvPolynomial.ringHom_ext + · intro r + simp + · intro i + simp + +/-- Height supplied by interpolation on the plane grid. -/ +def integerPlaneHeight (m d : ℕ) (H A : ℝ) : ℝ := + ((d : ℝ) + 1) ^ (2 * (d + 1)) * + (((d : ℝ) + 1) ^ m * H * (A * (1 + 2 * d)) ^ d) + +/-- Each coefficient of a bounded integer plane section is bounded explicitly +in terms of the original coefficient height, degree and ambient dimension. -/ +theorem integer_plane_section_coefficient_bound {σ : Type*} [Fintype σ] + (f : MvPolynomial σ ℤ) (H A : ℝ) (hH : 0 ≤ H) (hA : 1 ≤ A) + (hf : CoefficientHeightLE f H) + (v : PlaneParameters σ → ℤ) (hv : InBox v A) : + BivariateCoefficientBound (universalPlaneSection f v) + (integerPlaneHeight (Fintype.card σ) f.totalDegree H A) := by + let φ := Int.castRingHom ℝ + let g := universalPlaneSection (MvPolynomial.map φ f) (φ ∘ v) + have he : g = bivariateMap φ (universalPlaneSection f v) := + universalPlaneSection_map φ f v + have hd : bivariateTotalDegree g ≤ f.totalDegree := by + rw [he] + exact (bivariateTotalDegree_map_le _ _).trans + (universalPlanePolynomial_specialization_totalDegree f (eval v)) + have hB : 1 ≤ A * (1 + 2 * (f.totalDegree : ℝ)) := by + nlinarith [Nat.cast_nonneg (α := ℝ) f.totalDegree] + have hgrid (x y : ℕ) (hx : x ≤ f.totalDegree) (hy : y ≤ f.totalDegree) : + |(Polynomial.map (Polynomial.evalRingHom (y : ℝ)) g).eval (x : ℝ)| ≤ + ((f.totalDegree : ℝ) + 1) ^ Fintype.card σ * H * + (A * (1 + 2 * f.totalDegree)) ^ f.totalDegree := by + change |bivariateEval (x : ℝ) (y : ℝ) g| ≤ _ + dsimp only [g] + rw [universalPlaneSection_eval, eval_map] + apply polynomial_eval₂_box_bound f H _ hH hB hf + intro i + have hxR : (x : ℝ) ≤ f.totalDegree := by exact_mod_cast hx + have hyR : (y : ℝ) ≤ f.totalDegree := by exact_mod_cast hy + change |(v (i,0) : ℝ) + (v (i,1) : ℝ) * x + (v (i,2) : ℝ) * y| ≤ _ + calc + _ ≤ |(v (i,0) : ℝ)| + |(v (i,1) : ℝ)| * x + |(v (i,2) : ℝ)| * y := by + simpa only [abs_mul, abs_of_nonneg (Nat.cast_nonneg (α := ℝ) x), + abs_of_nonneg (Nat.cast_nonneg (α := ℝ) y)] using + (abs_add_le ((v (i,0) : ℝ) + (v (i,1) : ℝ) * x) ((v (i,2) : ℝ) * y)).trans + (add_le_add (abs_add_le (v (i,0) : ℝ) ((v (i,1) : ℝ) * x)) le_rfl) + _ ≤ A + A * x + A * y := by + gcongr + · exact hv _ + · exact hv _ + · exact hv _ + _ ≤ A * (1 + 2 * f.totalDegree) := by nlinarith + intro i j + have hh := bivariate_coefficient_bound_of_integer_grid g hd + (by positivity) hgrid i j + rw [he, bivariateMap_coeff] at hh + exact hh + +end +end CCDN diff --git a/LeanPool/CCDN/Plane/IntegerPlaneLogHeight.lean b/LeanPool/CCDN/Plane/IntegerPlaneLogHeight.lean new file mode 100644 index 0000000000..0ddff9ab7a --- /dev/null +++ b/LeanPool/CCDN/Plane/IntegerPlaneLogHeight.lean @@ -0,0 +1,129 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.Plane.IntegerPlaneHeight +public import Mathlib.Analysis.SpecialFunctions.Log.Basic +import LeanPool.CCDN.ForMathlib.Log + +/-! The bounded-parameter section loses only O_m(d²) in logarithmic height. +The resulting nonzero integer witness fits the fourth-power prime cutoff. -/ + +@[expose] public section +namespace CCDN +noncomputable section +open MvPolynomial + +/-- For `d ≥ 1` and `H ≥ 1`, the interpolation height `integerPlaneHeight m d H (2 * d ^ 3)` is at +least `1`. -/ +lemma integerPlaneHeight_one_le (m d : ℕ) (hd : 1 ≤ d) + (H : ℝ) (hH : 1 ≤ H) : + 1 ≤ integerPlaneHeight m d H (2 * (d : ℝ) ^ 3) := by + have hdR : (1 : ℝ) ≤ d := by exact_mod_cast hd + have hA : 1 ≤ 2 * (d : ℝ) ^ 3 := by nlinarith [one_le_pow₀ hdR (n := 3)] + unfold integerPlaneHeight + apply one_le_mul_of_one_le_of_one_le + · exact one_le_pow₀ (by linarith) + · apply one_le_mul_of_one_le_of_one_le + · exact one_le_mul_of_one_le_of_one_le (one_le_pow₀ (by linarith)) hH + · exact one_le_pow₀ (one_le_mul_of_one_le_of_one_le hA (by linarith)) + +private lemma integerPlaneHeight_log_le (m d : ℕ) (hd : 1 ≤ d) + (H : ℝ) (hH : 1 ≤ H) : + Real.log (integerPlaneHeight m d H (2 * (d : ℝ) ^ 3)) ≤ + Real.log H + (m : ℝ) * d + 11 * (d : ℝ) ^ 2 := by + have hdR : (1 : ℝ) ≤ d := by exact_mod_cast hd + have hd0 : (0 : ℝ) < d := by linarith + have hH0 : 0 < H := by linarith + have hl1 : Real.log ((d : ℝ) + 1) ≤ d := Real.log_add_one_le_self hd0.le + have hl2 : Real.log (1 + 2 * (d : ℝ)) ≤ 2 * d := by + rw [add_comm] + exact Real.log_add_one_le_self (by positivity) + have hld := Real.log_le_self hd0.le + have hltwo := Real.log_le_self (by norm_num : (0 : ℝ) ≤ 2) + have hA : Real.log (2 * (d : ℝ) ^ 3 * (1 + 2 * d)) ≤ 7 * d := by + rw [Real.log_mul (by positivity) (by positivity), + Real.log_mul (by norm_num) (by positivity), Real.log_pow] + norm_num + linarith + unfold integerPlaneHeight + rw [Real.log_mul (by positivity) (by positivity), + Real.log_mul (by positivity) (by positivity), + Real.log_mul (by positivity) hH0.ne', Real.log_pow, Real.log_pow, Real.log_pow] + push_cast + calc + _ ≤ (2 * ((d : ℝ) + 1)) * d + ((m : ℝ) * d + Real.log H + d * (7 * d)) := by + gcongr + _ ≤ _ := by nlinarith + +/-- A dimension-dependent constant at the d⁴ cutoff. -/ +def hypersurfaceHeightConstant (m : ℕ) : ℝ := 28 * (m + 12) + +/-- The constant `hypersurfaceHeightConstant m` is at least `1`. -/ +lemma hypersurfaceHeightConstant_one_le (m : ℕ) : 1 ≤ hypersurfaceHeightConstant m := by + unfold hypersurfaceHeightConstant + have := Nat.cast_nonneg (α := ℝ) m + linarith + +/-- For `d ≥ 1` and `H ≥ 1`, with `L` the logarithm of the interpolation height +`integerPlaneHeight m d H (2 * d ^ 3)`, the quantity `L + 27 d⁴ (1 + L / d²)` is at most +`27 d⁴ · hypersurfaceHeightConstant m · (1 + log H / d²)`. -/ +lemma integerPlaneHeight_scaled_log_le (m d : ℕ) (hd : 1 ≤ d) + (H : ℝ) (hH : 1 ≤ H) : + Real.log (integerPlaneHeight m d H (2 * (d : ℝ) ^ 3)) + + (27 * (d : ℝ) ^ 4) * + (1 + Real.log (integerPlaneHeight m d H (2 * (d : ℝ) ^ 3)) / (d : ℝ) ^ 2) ≤ + (27 * (d : ℝ) ^ 4) * + (hypersurfaceHeightConstant m * (1 + Real.log H / (d : ℝ) ^ 2)) := by + have hd1 : (1 : ℝ) ≤ d := by exact_mod_cast hd + have hd0 : (0 : ℝ) < d := by linarith + have hlog := Real.log_nonneg hH + have hc := integerPlaneHeight_log_le m d hd H hH + have hd2 : (d : ℝ) ^ 2 ≤ (d : ℝ) ^ 4 := by + nlinarith [sq_nonneg ((d : ℝ) ^ 2 - 1)] + have hd3 : (d : ℝ) ^ 3 ≤ (d : ℝ) ^ 4 := by + nlinarith [mul_nonneg (show 0 ≤ (d : ℝ) ^ 3 by positivity) (sub_nonneg.mpr hd1)] + have hd12 : (d : ℝ) ≤ (d : ℝ) ^ 2 := by nlinarith + have hmul := mul_le_mul_of_nonneg_left hc (show 0 ≤ 1 + 27 * (d : ℝ) ^ 2 by positivity) + have hm : (m : ℝ) * d ≤ m * (d : ℝ) ^ 4 := + mul_le_mul_of_nonneg_left (hd12.trans hd2) (Nat.cast_nonneg m) + have hm3 : (m : ℝ) * (d : ℝ) ^ 3 ≤ m * (d : ℝ) ^ 4 := + mul_le_mul_of_nonneg_left hd3 (Nat.cast_nonneg m) + have hlog2 : Real.log H ≤ (d : ℝ) ^ 2 * Real.log H := + le_mul_of_one_le_left hlog (one_le_pow₀ hd1) + have hmid : (1 + 27 * (d : ℝ) ^ 2) * + (Real.log H + (m : ℝ) * d + 11 * (d : ℝ) ^ 2) + 27 * (d : ℝ) ^ 4 ≤ + 28 * (d : ℝ) ^ 2 * Real.log H + hypersurfaceHeightConstant m * (d : ℝ) ^ 4 := by + unfold hypersurfaceHeightConstant + nlinarith + have hconst : 28 ≤ hypersurfaceHeightConstant m := by + unfold hypersurfaceHeightConstant + have := Nat.cast_nonneg (α := ℝ) m + linarith + have hlast : 28 * (d : ℝ) ^ 2 * Real.log H + hypersurfaceHeightConstant m * (d : ℝ) ^ 4 ≤ + 27 * hypersurfaceHeightConstant m * (d : ℝ) ^ 4 + + 27 * hypersurfaceHeightConstant m * (d : ℝ) ^ 2 * Real.log H := by + have h1 := mul_le_mul_of_nonneg_right hconst (mul_nonneg (sq_nonneg (d : ℝ)) hlog) + have h2 := mul_nonneg (show 0 ≤ hypersurfaceHeightConstant m by linarith) + (show 0 ≤ (d : ℝ) ^ 4 by positivity) + nlinarith + have he (S : ℝ) : S + 27 * (d : ℝ) ^ 4 * (1 + S / (d : ℝ) ^ 2) = + (1 + 27 * (d : ℝ) ^ 2) * S + 27 * (d : ℝ) ^ 4 := by + field_simp + ring + rw [he] + have hr : 27 * (d : ℝ) ^ 4 * + (hypersurfaceHeightConstant m * (1 + Real.log H / (d : ℝ) ^ 2)) = + 27 * hypersurfaceHeightConstant m * (d : ℝ) ^ 4 + + 27 * hypersurfaceHeightConstant m * (d : ℝ) ^ 2 * Real.log H := by field_simp + rw [hr] + exact (add_le_add hmul le_rfl).trans (hmid.trans hlast) + +end +end CCDN diff --git a/LeanPool/CCDN/Plane/IntegralAffineLine.lean b/LeanPool/CCDN/Plane/IntegralAffineLine.lean new file mode 100644 index 0000000000..fd43333903 --- /dev/null +++ b/LeanPool/CCDN/Plane/IntegralAffineLine.lean @@ -0,0 +1,299 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.Height.ProjectiveBox + +/-! Integer points on rational affine lines. Primitive integer +directions make the line parameter integral, giving a spacing bound. -/ + +@[expose] public section +namespace CCDN +noncomputable section + +/-- The rational affine line `{a + t • v | t ∈ ℚ}` through `a` with direction `v`. -/ +def rationalAffineLine {n : ℕ} (a v : RatPoint n) : Set (RatPoint n) := + {x | ∃ t : ℚ, x = a + t • v} + +/-- The height `max_j |v j|` of an integer vector `v`. -/ +def integerDirectionHeight {n : ℕ} (v : IntPoint n) : ℕ := + Finset.univ.sup (fun j => (v j).natAbs) + +/-- Scaling the direction by a nonzero rational does not change the affine line. -/ +lemma rationalAffineLine_smul_direction {n : ℕ} (a v : RatPoint n) {c : ℚ} (hc : c ≠ 0) : + rationalAffineLine a (c • v) = rationalAffineLine a v := by + ext x + constructor + · rintro ⟨t, ht⟩ + exact ⟨t * c, by simpa only [smul_smul] using ht⟩ + · rintro ⟨t, ht⟩ + refine ⟨t / c, ?_⟩ + simpa only [smul_smul, div_mul_cancel₀ t hc] using ht + +/-- Moving the base point along the direction does not change the affine line. -/ +lemma rationalAffineLine_shift_base {n : ℕ} (a v : RatPoint n) (s : ℚ) : + rationalAffineLine (a + s • v) v = rationalAffineLine a v := by + ext x + constructor + · rintro ⟨t, ht⟩ + exact ⟨s + t, by simpa only [add_smul, add_assoc] using ht⟩ + · rintro ⟨t, ht⟩ + refine ⟨t - s, ht.trans ?_⟩ + funext j + dsimp + ring + +/-- Any two distinct points determine the entire parameterized rational line. -/ +private lemma rationalAffineLine_eq_of_distinct_points {n : ℕ} {a v x y : RatPoint n} + (hx : x ∈ rationalAffineLine a v) (hy : y ∈ rationalAffineLine a v) (hxy : x ≠ y) : + rationalAffineLine x (y - x) = rationalAffineLine a v := by + obtain ⟨s, rfl⟩ := hx + obtain ⟨t, rfl⟩ := hy + have hts : t - s ≠ 0 := by + intro he + have he' : t = s := sub_eq_zero.mp he + exact hxy (by rw [he']) + have he : (a + t • v) - (a + s • v) = (t - s) • v := by + funext j + dsimp + ring + rw [he, rationalAffineLine_smul_direction _ _ hts, rationalAffineLine_shift_base] + +/-- A nonzero integer vector has positive height. -/ +lemma integerDirectionHeight_pos {n : ℕ} {v : IntPoint n} (hv : v ≠ 0) : + 0 < integerDirectionHeight v := by + obtain ⟨j, hj⟩ := Function.ne_iff.mp hv + exact (Int.natAbs_pos.mpr hj).trans_le + (Finset.le_sup (f := fun j => (v j).natAbs) (Finset.mem_univ j)) + +private lemma exists_coordinate_integerDirectionHeight {n : ℕ} {v : IntPoint n} (hv : v ≠ 0) : + ∃ j, |(v j : ℝ)| = integerDirectionHeight v := by + obtain ⟨j, _⟩ := Function.ne_iff.mp hv + obtain ⟨k, _, hk⟩ := Finset.exists_mem_eq_sup (Finset.univ : Finset (Fin n)) + ⟨j, Finset.mem_univ j⟩ (fun j => (v j).natAbs) + refine ⟨k, ?_⟩ + have hh : ((v k).natAbs : ℝ) = integerDirectionHeight v := by exact_mod_cast hk.symm + simpa only [Nat.cast_natAbs, Int.cast_abs] using hh + +/-- Bezout's identity for a primitive direction forces a rational integral +multiple of that direction to have an integer multiplier. -/ +private theorem rational_scalar_integral_of_primitive {n : ℕ} (v : IntPoint n) + (hprim : Finset.univ.gcd v = 1) {t : ℚ} (w : IntPoint n) + (hw : ∀ j, (w j : ℚ) = t * v j) : + ∃ k : ℤ, (k : ℚ) = t := by + obtain ⟨c, hc⟩ := Finset.gcd_eq_sum_mul Finset.univ v + have hcQ : (∑ j, (v j : ℚ) * c j) = 1 := by exact_mod_cast (hc.symm.trans hprim) + refine ⟨∑ j, w j * c j, ?_⟩ + push_cast + simp_rw [hw] + rw [show (∑ j, t * (v j : ℚ) * c j) = t * ∑ j, (v j : ℚ) * c j by + rw [Finset.mul_sum]; apply Finset.sum_congr rfl; intro j _; ring, hcQ, mul_one] + +private theorem integer_mem_rationalAffineLine_iff {n : ℕ} (a v x : IntPoint n) + (hprim : Finset.univ.gcd v = 1) : + rationalPoint x ∈ rationalAffineLine (rationalPoint a) (rationalPoint v) ↔ + ∃ k : ℤ, ∀ j, x j = a j + k * v j := by + constructor + · rintro ⟨t, ht⟩ + have hw (j) : ((x - a) j : ℚ) = t * v j := by + have hh := congrFun ht j + change (x j : ℚ) = (a j : ℚ) + t * v j at hh + change ((x j - a j : ℤ) : ℚ) = _ + push_cast + linarith + obtain ⟨k, hk⟩ := rational_scalar_integral_of_primitive v hprim (x - a) hw + refine ⟨k, fun j => ?_⟩ + apply Int.cast_injective (α := ℚ) + push_cast + rw [hk] + exact congrFun ht j + · rintro ⟨k, hk⟩ + refine ⟨(k : ℚ), ?_⟩ + funext j + change (x j : ℚ) = (a j : ℚ) + (k : ℚ) * v j + exact_mod_cast hk j + +private lemma int_finset_card_le_span (T : Finset ℤ) (hT : T.Nonempty) : + (T.card : ℝ) ≤ (T.max' hT : ℝ) - (T.min' hT : ℝ) + 1 := by + have hsub : T ⊆ Finset.Icc (T.min' hT) (T.max' hT) := by + intro k hk + exact Finset.mem_Icc.mpr ⟨T.min'_le k hk, T.le_max' k hk⟩ + have hle := Finset.card_le_card hsub + rw [Int.card_Icc] at hle + have hnonneg : 0 ≤ T.max' hT - T.min' hT + 1 := by + have hh := T.min'_le (T.max' hT) (T.max'_mem hT) + omega + have hh : (T.card : ℤ) ≤ T.max' hT - T.min' hT + 1 := by + have hh : (T.card : ℤ) ≤ ((T.max' hT + 1 - T.min' hT).toNat : ℤ) := by exact_mod_cast hle + rw [Int.toNat_of_nonneg (by omega)] at hh + omega + exact_mod_cast hh + +/-- Integer points on a primitive line are spaced by its largest direction +coordinate. This counts the line's intersection with the integer box. -/ +private theorem integral_rationalAffineLine_count {n : ℕ} (a v : IntPoint n) + (hv : v ≠ 0) (hprim : Finset.univ.gcd v = 1) {B : ℝ} (hB : 0 ≤ B) : + (count (rationalAffineLine (rationalPoint a) (rationalPoint v)) B : ℝ) ≤ + 1 + 2 * B / integerDirectionHeight v := by + classical + let S := integralPoints (rationalAffineLine (rationalPoint a) (rationalPoint v)) B + have hrep (x : S) : ∃ k : ℤ, ∀ j, x.val j = a j + k * v j := + (integer_mem_rationalAffineLine_iff a v x.val hprim).mp (mem_integralPoints.mp x.property).2 + choose k hk using hrep + have hkinj : Function.Injective k := by + intro x y h + apply Subtype.ext + funext j + rw [hk x j, hk y j, h] + let T : Finset ℤ := Finset.univ.image k + have hcard : T.card = S.card := by + rw [Finset.card_image_of_injective _ hkinj] + simp only [Finset.card_univ, Fintype.card_coe] + by_cases hT : T.Nonempty + · have hTmem (t : ℤ) (ht : t ∈ T) : ∃ x : S, k x = t := by + obtain ⟨x, _, hx⟩ := Finset.mem_image.mp ht + exact ⟨x, hx⟩ + obtain ⟨xlo, hxlo⟩ := hTmem (T.min' hT) (T.min'_mem hT) + obtain ⟨xhi, hxhi⟩ := hTmem (T.max' hT) (T.max'_mem hT) + obtain ⟨j, hj⟩ := exists_coordinate_integerDirectionHeight hv + have hlo := (mem_integralPoints.mp xlo.property).1 j + have hhi := (mem_integralPoints.mp xhi.property).1 j + have hspan : ((T.max' hT : ℝ) - (T.min' hT : ℝ)) * integerDirectionHeight v ≤ 2 * B := by + have he : (xhi.val j : ℝ) - (xlo.val j : ℝ) = + ((T.max' hT : ℝ) - (T.min' hT : ℝ)) * (v j : ℝ) := by + rw [hk xhi j, hk xlo j, hxhi, hxlo] + push_cast + ring + have habs := (abs_sub (xhi.val j : ℝ) (xlo.val j : ℝ)).trans (add_le_add hhi hlo) + rw [he, abs_mul, hj, abs_of_nonneg (by + have hh := T.min'_le (T.max' hT) (T.max'_mem hT) + exact sub_nonneg.mpr (by exact_mod_cast hh))] at habs + linarith + have hheight : (0 : ℝ) < integerDirectionHeight v := by + exact_mod_cast integerDirectionHeight_pos hv + have hdif := (le_div_iff₀ hheight).mpr hspan + have hcnt := int_finset_card_le_span T hT + rw [hcard] at hcnt + change (S.card : ℝ) ≤ _ + linarith + · have hz : T = ∅ := Finset.not_nonempty_iff_eq_empty.mp hT + have hS : S.card = 0 := by simpa only [hz, Finset.card_empty] using hcard.symm + change (S.card : ℝ) ≤ _ + rw [hS, Nat.cast_zero] + positivity + +/-- Divide an integer direction by the gcd of its coordinates. This gives +a primitive direction of no greater height on the same rational line. -/ +private theorem exists_primitive_integer_direction {n : ℕ} (w : IntPoint n) (hw : w ≠ 0) : + ∃ v : IntPoint n, v ≠ 0 ∧ Finset.univ.gcd v = 1 ∧ + (∀ j, |(v j : ℝ)| ≤ |(w j : ℝ)|) ∧ + ∀ a : RatPoint n, rationalAffineLine a (rationalPoint w) = + rationalAffineLine a (rationalPoint v) := by + classical + obtain ⟨j, _⟩ := Function.ne_iff.mp hw + obtain ⟨v, hv, hprim⟩ := Finset.extract_gcd w + (show (Finset.univ : Finset (Fin n)).Nonempty from ⟨j, Finset.mem_univ j⟩) + let c := Finset.univ.gcd w + have hvc (i) : w i = c * v i := hv i (Finset.mem_univ i) + have hc : c ≠ 0 := by + intro he + apply hw + funext i + simpa only [he, zero_mul, Pi.zero_apply] using hvc i + have hv0 : v ≠ 0 := by + intro he + apply hw + funext i + simpa only [he, Pi.zero_apply, mul_zero] using hvc i + have hQ : rationalPoint w = (c : ℚ) • rationalPoint v := by + funext i + change (w i : ℚ) = (c : ℚ) * v i + exact_mod_cast hvc i + refine ⟨v, hv0, hprim, ?_, fun a => ?_⟩ + · intro i + have hc1 : (1 : ℝ) ≤ |(c : ℝ)| := by exact_mod_cast Int.one_le_abs hc + rw [hvc i, Int.cast_mul, abs_mul] + nlinarith [abs_nonneg (v i : ℝ)] + · rw [hQ] + exact rationalAffineLine_smul_direction _ _ (by exact_mod_cast hc) + +/-- Two bounded integer points give a primitive integer direction of +height at most twice the box radius, representing their full rational line. -/ +private theorem exists_primitive_direction_of_two_integer_points {n : ℕ} + (a b : IntPoint n) (hab : a ≠ b) {B : ℝ} + (ha : InBox a B) (hb : InBox b B) : + ∃ v : IntPoint n, v ≠ 0 ∧ Finset.univ.gcd v = 1 ∧ + (integerDirectionHeight v : ℝ) ≤ 2 * B ∧ + rationalAffineLine (rationalPoint a) (rationalPoint b - rationalPoint a) = + rationalAffineLine (rationalPoint a) (rationalPoint v) := by + have hw : b - a ≠ 0 := fun h => hab (sub_eq_zero.mp h).symm + obtain ⟨v, hv, hprim, hle, he⟩ := exists_primitive_integer_direction (b - a) hw + refine ⟨v, hv, hprim, ?_, ?_⟩ + · obtain ⟨j, hj⟩ := exists_coordinate_integerDirectionHeight hv + rw [← hj] + apply (hle j).trans + change |((b j - a j : ℤ) : ℝ)| ≤ _ + rw [Int.cast_sub] + have hh := (abs_sub (b j : ℝ) (a j : ℝ)).trans (add_le_add (hb j) (ha j)) + linarith + · have hcast : rationalPoint (b - a) = rationalPoint b - rationalPoint a := by + funext j + exact Int.cast_sub _ _ + simpa only [hcast] using he (rationalPoint a) + +/-- For a nonzero primitive integer vector `v`, the multiplicative height of the projective point +`[v]` equals `max_j |v j|`. -/ +theorem projective_direction_height {n : ℕ} (v : IntPoint (n + 1)) + (hv : v ≠ 0) (hprim : Finset.univ.gcd v = 1) : + (Projectivization.mk ℚ (rationalPoint v) (rationalPoint_ne_zero hv)).mulHeight = + (integerDirectionHeight v : ℝ) := by + rw [projective_height_primitive v hv hprim] + have hsup : (⨆ j, |v j| : ℤ) = (integerDirectionHeight v : ℤ) := by + apply le_antisymm + · apply ciSup_le + intro j + have hh : (v j).natAbs ≤ integerDirectionHeight v := + Finset.le_sup (f := fun j => (v j).natAbs) (Finset.mem_univ j) + have hh' : ((v j).natAbs : ℤ) ≤ (integerDirectionHeight v : ℤ) := by exact_mod_cast hh + simpa only [Int.natCast_natAbs] using hh' + · obtain ⟨j, hj⟩ := exists_coordinate_integerDirectionHeight hv + have hjZ : |v j| = (integerDirectionHeight v : ℤ) := by exact_mod_cast hj + rw [← hjZ] + exact le_ciSup (Finite.bddAbove_range (fun j => |v j|)) j + rw [hsup, Int.cast_natCast] + +/-- Any rational affine line containing two bounded integer points has an +integer base point and primitive integer direction, with the exact spacing +estimate and the direction-height cutoff needed for summing line contributions. -/ +theorem rationalAffineLine_integer_rep_and_count {n : ℕ} + (a v : RatPoint n) {B : ℝ} (hB : 0 ≤ B) + (hcount : 1 < count (rationalAffineLine a v) B) : + ∃ (z w : IntPoint n), w ≠ 0 ∧ Finset.univ.gcd w = 1 ∧ + InBox z B ∧ (integerDirectionHeight w : ℝ) ≤ 2 * B ∧ + rationalAffineLine a v = rationalAffineLine (rationalPoint z) (rationalPoint w) ∧ + (count (rationalAffineLine a v) B : ℝ) ≤ 1 + 2 * B / integerDirectionHeight w := by + obtain ⟨x, hx, y, hy, hxy⟩ := Finset.one_lt_card.mp hcount + obtain ⟨hxB, hxline⟩ := mem_integralPoints.mp hx + obtain ⟨hyB, hyline⟩ := mem_integralPoints.mp hy + obtain ⟨w, hw, hprim, hheight, he⟩ := + exists_primitive_direction_of_two_integer_points x y hxy hxB hyB + have hxyQ : rationalPoint x ≠ rationalPoint y := by + intro h + apply hxy + funext j + exact Int.cast_injective (congrFun h j) + have hline : rationalAffineLine a v = + rationalAffineLine (rationalPoint x) (rationalPoint w) := + (rationalAffineLine_eq_of_distinct_points hxline hyline hxyQ).symm.trans he + refine ⟨x, w, hw, hprim, hxB, hheight, hline, ?_⟩ + rw [hline] + exact integral_rationalAffineLine_count x w hw hprim hB + +end +end CCDN diff --git a/LeanPool/CCDN/Plane/LeadingPositionHeight.lean b/LeanPool/CCDN/Plane/LeadingPositionHeight.lean new file mode 100644 index 0000000000..c211776d1f --- /dev/null +++ b/LeanPool/CCDN/Plane/LeadingPositionHeight.lean @@ -0,0 +1,180 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.Algebra.HomogeneousChart +public import LeanPool.CCDN.Algebra.HomogeneousTranslation +public import LeanPool.CCDN.Height.IntegerCoefficientHeight +public import Mathlib.Analysis.SpecialFunctions.Log.Basic +public import Mathlib.RingTheory.MvPolynomial.MonomialOrder +import LeanPool.CCDN.Algebra.HomogeneousEvaluation +import LeanPool.CCDN.Algebra.HomogeneousRankDichotomy +import LeanPool.CCDN.ForMathlib.Log +import LeanPool.CCDN.Plane.GridHeight +import LeanPool.CCDN.Plane.HomogeneousLeadingCoefficient + +/-! +The bounded leading-coefficient translation has a controlled height cost. +Its logarithmic coefficient-position loss is at most 14d². + +Coefficient height controls homogeneous evaluations, uniformly on the +integer point box, with the exact number of monomials. +-/ + +@[expose] public section +namespace CCDN +noncomputable section +open MvPolynomial + +private theorem homogeneous_integer_evaluation_bound {n d : ℕ} + (f : MvPolynomial (Fin (n + 1)) ℤ) (hf : f.IsHomogeneous d) + {H R : ℝ} (hH : 0 ≤ H) (hR : 1 ≤ R) (hcoeff : CoefficientHeightLE f H) + (x : Fin (n + 1) → ℤ) (hx : InBox x R) : + |(eval x f : ℝ)| ≤ ((n + d).choose d : ℝ) * H * R ^ d := by + classical + conv_lhs => rw [homogeneous_eq_sum_coeff f hf] + simp only [map_sum, eval_monomial, Int.cast_sum, Int.cast_mul] + calc + _ ≤ ∑ j, |(f.coeff (homogeneousMonomials n d j) : ℝ) * + (((homogeneousMonomials n d j).prod (fun i k => x i ^ k) : ℤ) : ℝ)| := + Finset.abs_sum_le_sum_abs _ _ + _ ≤ ∑ _j : Fin ((n + d).choose d), H * R ^ d := by + apply Finset.sum_le_sum + intro j _ + rw [abs_mul] + exact mul_le_mul (hcoeff _) (abs_monomial_value_le _ x hR hx + (homogeneousMonomials_degree n d j).le) (abs_nonneg _) hH + _ = _ := by simp [mul_assoc] + +private lemma ternary_homogeneous_dimension_le_square (d : ℕ) : + ((2 + d).choose d : ℝ) ≤ ((d : ℝ) + 2) ^ 2 := by + have h : 2 * ((2 + d).choose d : ℝ) = ((d : ℝ) + 2) * (d + 1) := by + exact_mod_cast ternary_homogeneous_dimension d + nlinarith [show (0 : ℝ) ≤ d by positivity] + +end +end CCDN + +namespace CCDN +noncomputable section +open MvPolynomial Real + +/-- The height-loss factor `(d + 1) ^ (4 (d + 1)) (d + 2) ^ 2 (2 d + 1) ^ d` by which the +coefficients of the affine chart of a ternary form of degree `d`, after a translation with +parameters in `[0, d]`, may exceed a coefficient height `L` of the form (measured as +`(d + 1) ^ (2 (d + 1)) * L`); see `homogeneousTranslation_chart_coefficient_bound`. -/ +def leadingPositionHeightFactor (d : ℕ) : ℝ := + ((d : ℝ) + 1) ^ (4 * (d + 1)) * ((d : ℝ) + 2) ^ 2 * (2 * (d : ℝ) + 1) ^ d + +/-- The factor `leadingPositionHeightFactor d` is at least `1`. -/ +lemma leadingPositionHeightFactor_one_le (d : ℕ) : 1 ≤ leadingPositionHeightFactor d := by + unfold leadingPositionHeightFactor + apply one_le_mul_of_one_le_of_one_le + · apply one_le_mul_of_one_le_of_one_le + · exact one_le_pow₀ (by have := Nat.cast_nonneg (α := ℝ) d; linarith) + · exact one_le_pow₀ (by have := Nat.cast_nonneg (α := ℝ) d; linarith) + · exact one_le_pow₀ (by have := Nat.cast_nonneg (α := ℝ) d; linarith) + +/-- For `d ≥ 1`, the logarithm of `leadingPositionHeightFactor d` is at most `14 d²`. -/ +lemma leadingPositionHeightFactor_log_le {d : ℕ} (hd : 0 < d) : + Real.log (leadingPositionHeightFactor d) ≤ 14 * (d : ℝ) ^ 2 := by + have hdR : (1 : ℝ) ≤ d := by exact_mod_cast hd + have h₁ : Real.log ((d : ℝ) + 1) ≤ d := Real.log_add_one_le_self d.cast_nonneg + have h₂ : Real.log ((d : ℝ) + 2) ≤ d + 1 := by + rw [show (d : ℝ) + 2 = (d + 1) + 1 by ring] + exact Real.log_add_one_le_self (by positivity) + have h₃ : Real.log (2 * (d : ℝ) + 1) ≤ 2 * d := Real.log_add_one_le_self (by positivity) + unfold leadingPositionHeightFactor + rw [Real.log_mul (by positivity) (by positivity), Real.log_mul (by positivity) (by positivity), + Real.log_pow, Real.log_pow, Real.log_pow] + push_cast + calc + _ ≤ 4 * ((d : ℝ) + 1) * d + 2 * ((d : ℝ) + 1) + d * (2 * d) := by gcongr + _ ≤ _ := by nlinarith + +/-- Control the transformed coefficients through interpolation of translated +grid values. -/ +theorem homogeneousTranslation_chart_coefficient_bound {d : ℕ} + (f : MvPolynomial (Fin 3) ℤ) (hf : f.IsHomogeneous d) + (a : Fin 2 → ℤ) (ha : ∀ j, 0 ≤ a j ∧ a j ≤ d) + {L : ℝ} (hL : 0 ≤ L) + (hcoeff : CoefficientHeightLE f (((d : ℝ) + 1) ^ (2 * (d + 1)) * L)) + (m : Fin 2 →₀ ℕ) : + |((affineChart 2 (homogeneousTranslation ℤ a f)).coeff m : ℝ)| ≤ + leadingPositionHeightFactor d * L := by + let K := ((d : ℝ) + 1) ^ (2 * (d + 1)) + let V := ((d : ℝ) + 2) ^ 2 * (K * L) * (2 * (d : ℝ) + 1) ^ d + have heval (x y : ℕ) (hx : x ≤ d) (hy : y ≤ d) : + |(eval ![(x : ℤ), (y : ℤ)] (affineChart 2 (homogeneousTranslation ℤ a f)) : ℝ)| ≤ V := by + let z : Fin 2 → ℤ := ![(x : ℤ), (y : ℤ)] + have hz : ∀ j, 0 ≤ z j ∧ z j ≤ d := by + intro j + fin_cases j + · change 0 ≤ (x : ℤ) ∧ (x : ℤ) ≤ d + exact ⟨by positivity, by exact_mod_cast hx⟩ + · change 0 ≤ (y : ℤ) ∧ (y : ℤ) ≤ d + exact ⟨by positivity, by exact_mod_cast hy⟩ + have hbox : ∀ j, |((Fin.cons 1 (z + a) : Fin 3 → ℤ) j : ℝ)| ≤ 2 * d + 1 := by + intro j + refine Fin.cases ?_ (fun k => ?_) j + · simp only [Fin.cons_zero, Int.cast_one, abs_one] + have := Nat.cast_nonneg (α := ℝ) d + linarith + · simp only [Fin.cons_succ, Pi.add_apply, Int.cast_add] + have hz0 : (0 : ℝ) ≤ z k := by exact_mod_cast (hz k).1 + have ha0 : (0 : ℝ) ≤ a k := by exact_mod_cast (ha k).1 + have hzd : (z k : ℝ) ≤ d := by exact_mod_cast (hz k).2 + have had : (a k : ℝ) ≤ d := by exact_mod_cast (ha k).2 + rw [abs_of_nonneg (by linarith)] + linarith + rw [affineChart_eval, homogeneousTranslation_eval_chart] + have h := homogeneous_integer_evaluation_bound f hf (show 0 ≤ K * L by dsimp [K]; positivity) + (show (1 : ℝ) ≤ 2 * d + 1 by have := Nat.cast_nonneg (α := ℝ) d; linarith) + hcoeff (Fin.cons 1 (z + a)) hbox + apply h.trans + exact mul_le_mul_of_nonneg_right + (mul_le_mul_of_nonneg_right (ternary_homogeneous_dimension_le_square d) (by positivity)) + (by positivity) + have h := integer_plane_coefficient_bound_of_grid (affineChart 2 (homogeneousTranslation ℤ a f)) + ((affineChart_totalDegree_le _).trans (homogeneousTranslation_homogeneous a hf).totalDegree_le) + (show 0 ≤ V by dsimp [V, K]; positivity) heval m + convert h using 1 + dsimp only [leadingPositionHeightFactor, V, K] + rw [show 4 * (d + 1) = 2 * (d + 1) + 2 * (d + 1) by omega, pow_add] + ring + +/-- A bounded integer translation supplies the leading-position +hypothesis with absolute constant 14, and a coefficient height at least one. -/ +theorem exists_translated_coefficient_position {d : ℕ} (hd : 0 < d) + (f : MvPolynomial (Fin 3) ℤ) (hf : f.IsHomogeneous d) (hf0 : f ≠ 0) : + ∃ (a : Fin 2 → ℤ) (H : ℝ), (∀ j, 0 ≤ a j ∧ a j ≤ d) ∧ 1 ≤ H ∧ + CoefficientHeightLE (affineChart 2 (homogeneousTranslation ℤ a f)) H ∧ + Real.log |((MonomialOrder.lex.leadingCoeff (homogeneousTranslation ℤ a f) : ℤ) : ℝ)| ≤ + Real.log H ∧ + Real.log H ≤ + Real.log |((MonomialOrder.lex.leadingCoeff (homogeneousTranslation ℤ a f) : ℤ) : ℝ)| + + 14 * (d : ℝ) ^ 2 := by + obtain ⟨a, ha, hne, hcoeff⟩ := exists_bounded_leading_coefficient_translation f hf hf0 + let L := |((MonomialOrder.lex.leadingCoeff (homogeneousTranslation ℤ a f) : ℤ) : ℝ)| + have hL : 1 ≤ L := by + dsimp only [L] + exact_mod_cast Int.one_le_abs hne + have hP := leadingPositionHeightFactor_one_le d + have hpos : 0 < leadingPositionHeightFactor d := by linarith + have hLpos : 0 < L := by linarith + refine ⟨a, leadingPositionHeightFactor d * L, ha, one_le_mul_of_one_le_of_one_le hP hL, + homogeneousTranslation_chart_coefficient_bound f hf a ha hLpos.le hcoeff, ?_, ?_⟩ + · apply Real.log_le_log hLpos + nlinarith + · rw [Real.log_mul hpos.ne' hLpos.ne'] + have h := leadingPositionHeightFactor_log_le hd + linarith + +end +end CCDN diff --git a/LeanPool/CCDN/Plane/LinearDeformation.lean b/LeanPool/CCDN/Plane/LinearDeformation.lean new file mode 100644 index 0000000000..688e2627eb --- /dev/null +++ b/LeanPool/CCDN/Plane/LinearDeformation.lean @@ -0,0 +1,68 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import Mathlib.Algebra.MvPolynomial.PDeriv +import Mathlib.RingTheory.MvPolynomial.IrreducibleQuadratic + +/-! Constant and first coefficients of a plane equation under a linear +one-parameter displacement of one coordinate. These are algebraic coefficient +identities, valid for arbitrary commutative target algebras. -/ + +@[expose] public section +namespace CCDN +noncomputable section +open MvPolynomial + +private lemma polynomial_plane_coeff_one_chain {K A : Type*} [CommRing K] [CommRing A] + [Algebra K A] (ψ : MvPolynomial (Fin 2) K →ₐ[K] Polynomial A) + (f : MvPolynomial (Fin 2) K) : + (ψ f).coeff 1 = (ψ (pderiv 0 f)).coeff 0 * (ψ (X 0)).coeff 1 + + (ψ (pderiv 1 f)).coeff 0 * (ψ (X 1)).coeff 1 := by + have hC (c : K) : ψ (C c) = Polynomial.C (algebraMap K A c) := ψ.commutes c + induction f using MvPolynomial.induction_on with + | C c => simp [hC] + | add f g hf hg => simp only [map_add, Polynomial.coeff_add, hf, hg]; ring + | mul_X f k hf => + fin_cases k <;> + simp [map_mul, Derivation.leibniz, smul_eq_mul, Polynomial.mul_coeff_zero, + Polynomial.mul_coeff_one, hf] <;> ring + +/-- Vary one plane coordinate by t*w in an arbitrary target algebra. -/ +def planeLinearDeformation {K A : Type*} [CommRing K] [CommRing A] [Algebra K A] + (a : Fin 2 → A) (k : Fin 2) (w : A) : + MvPolynomial (Fin 2) K →ₐ[K] Polynomial A := + aeval (fun j => Polynomial.C (a j) + if j = k then Polynomial.X * Polynomial.C w else 0) + +/-- The constant coefficient of the linear deformation of `f` at `a` is the value `f(a)`. -/ +lemma planeLinearDeformation_coeff_zero {K A : Type*} [CommRing K] [CommRing A] + [Algebra K A] (a : Fin 2 → A) (k : Fin 2) (w : A) + (f : MvPolynomial (Fin 2) K) : + (planeLinearDeformation a k w f).coeff 0 = aeval a f := by + have hC (c : K) : planeLinearDeformation a k w (C c) = + Polynomial.C (algebraMap K A c) := (planeLinearDeformation a k w).commutes c + have hX (j : Fin 2) : (planeLinearDeformation (K := K) a k w (X j)).coeff 0 = a j := by + fin_cases j <;> fin_cases k <;> simp [planeLinearDeformation] + induction f using MvPolynomial.induction_on with + | C c => simp only [hC, Polynomial.coeff_C_zero, aeval_C] + | add f g hf hg => simp only [map_add, Polynomial.coeff_add, hf, hg] + | mul_X f j hf => simp only [map_mul, Polynomial.mul_coeff_zero, hf, hX, aeval_X] + +/-- The linear coefficient of the deformation of `f` at `a` along coordinate `k` with speed `w` is +`(∂f/∂x_k)(a) * w`. -/ +lemma planeLinearDeformation_coeff_one {K A : Type*} [CommRing K] [CommRing A] + [Algebra K A] (a : Fin 2 → A) (k : Fin 2) (w : A) + (f : MvPolynomial (Fin 2) K) : + (planeLinearDeformation a k w f).coeff 1 = aeval a (pderiv k f) * w := by + rw [polynomial_plane_coeff_one_chain, planeLinearDeformation_coeff_zero, + planeLinearDeformation_coeff_zero] + fin_cases k <;> simp [planeLinearDeformation, Polynomial.mul_coeff_one] + +end +end CCDN diff --git a/LeanPool/CCDN/Plane/NonabsolutePlaneCount.lean b/LeanPool/CCDN/Plane/NonabsolutePlaneCount.lean new file mode 100644 index 0000000000..8a4d8ae55e --- /dev/null +++ b/LeanPool/CCDN/Plane/NonabsolutePlaneCount.lean @@ -0,0 +1,104 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import Mathlib.Algebra.MvPolynomial.Degrees +import LeanPool.CCDN.Algebra.PolynomialLinearDescent +import LeanPool.CCDN.ForMathlib.MvPolynomial +import LeanPool.CCDN.Plane.Bezout +import Mathlib.Algebra.MvPolynomial.Nilpotent + +/-! Irreducible base-field plane curves that split over an extension have at +most d² base-field points. The proof uses linear coefficient descent and +Bézout. -/ + +@[expose] public section +namespace CCDN +noncomputable section +open MvPolynomial + +private lemma plane_extension_section_card_le {k K ι : Type*} + [Field k] [Field K] [Algebra k K] [Fintype ι] + (f : MvPolynomial (Fin 2) k) (hf : Irreducible f) + (g : MvPolynomial (Fin 2) K) (hg : g ≠ 0) (hdeg : g.totalDegree < f.totalDegree) + (z : ι → Fin 2 → k) (hinj : Function.Injective z) + (hzf : ∀ i, eval (z i) f = 0) + (hzg : ∀ i, eval (algebraMap k K ∘ z i) g = 0) : + Fintype.card ι ≤ f.totalDegree * g.totalDegree := by + classical + obtain ⟨g₀, hg₀, hdeg₀, hzero⟩ := exists_nonzero_polynomial_linear_descent (k := k) g hg + have hnd : ¬ f ∣ g₀ := by + intro hdiv + have h := totalDegree_le_of_dvd_of_isDomain hdiv hg₀ + omega + exact (plane_common_zeros_card_le f g₀ hf hnd z hinj hzf (fun i => hzero (z i) (hzg i))).trans + (Nat.mul_le_mul_left _ hdeg₀) + +private lemma plane_points_card_le_of_extension_factorization {k K ι : Type*} + [Field k] [Field K] [Algebra k K] [Fintype ι] + (f : MvPolynomial (Fin 2) k) (hf : Irreducible f) + (g h : MvPolynomial (Fin 2) K) (hg : g ≠ 0) (hh : h ≠ 0) + (hgd : 0 < g.totalDegree) (hhd : 0 < h.totalDegree) + (hfac : MvPolynomial.map (algebraMap k K) f = g * h) + (z : ι → Fin 2 → k) (hinj : Function.Injective z) (hz : ∀ i, eval (z i) f = 0) : + Fintype.card ι ≤ f.totalDegree ^ 2 := by + classical + have hdegree : g.totalDegree + h.totalDegree = f.totalDegree := by + have hm : (MvPolynomial.map (algebraMap k K) f).totalDegree = f.totalDegree := + totalDegree_map_of_injective (algebraMap k K).injective _ + rw [hfac, totalDegree_mul_of_isDomain hg hh] at hm + exact hm + let S := Finset.univ.filter (fun i : ι => eval (algebraMap k K ∘ z i) g = 0) + let T := Finset.univ.filter (fun i : ι => ¬ eval (algebraMap k K ∘ z i) g = 0) + have hs := plane_extension_section_card_le f hf g hg (by omega) + (fun i : S => z i.val) (hinj.comp Subtype.val_injective) + (fun i => hz i.val) (fun i => (Finset.mem_filter.mp i.property).2) + have ht := plane_extension_section_card_le f hf h hh (by omega) + (fun i : T => z i.val) (hinj.comp Subtype.val_injective) + (fun i => hz i.val) (by + intro i + have he := congrArg (algebraMap k K) (hz i.val) + rw [map_eval, hfac, eval_mul, map_zero] at he + exact (mul_eq_zero.mp he).resolve_left (Finset.mem_filter.mp i.property).2) + have hsum := Finset.card_filter_add_card_filter_not + (s := Finset.univ) (p := fun i : ι => eval (algebraMap k K ∘ z i) g = 0) + simp only [Fintype.card_coe] at hs ht + change S.card + T.card = Finset.univ.card at hsum + rw [Finset.card_univ] at hsum + calc + Fintype.card ι = S.card + T.card := hsum.symm + _ ≤ f.totalDegree * g.totalDegree + f.totalDegree * h.totalDegree := Nat.add_le_add hs ht + _ = f.totalDegree ^ 2 := by rw [← Nat.mul_add, hdegree, pow_two] + +/-- Splitting over any extension, Galois or not, gives the d² bound on +base-field points. -/ +theorem nonabsolute_plane_points_card_le {k K ι : Type*} + [Field k] [Field K] [Algebra k K] [Fintype ι] + (f : MvPolynomial (Fin 2) k) (hf : Irreducible f) (hd : 0 < f.totalDegree) + (hred : ¬ Irreducible (MvPolynomial.map (algebraMap k K) f)) + (z : ι → Fin 2 → k) (hinj : Function.Injective z) (hz : ∀ i, eval (z i) f = 0) : + Fintype.card ι ≤ f.totalDegree ^ 2 := by + classical + have hmap0 : MvPolynomial.map (algebraMap k K) f ≠ 0 := by + exact fun he => hf.ne_zero + ((map_injective (algebraMap k K) (algebraMap k K).injective) (by simpa using he)) + have hmapu : ¬ IsUnit (MvPolynomial.map (algebraMap k K) f) := by + intro hu + have he := (MvPolynomial.isUnit_iff_totalDegree_of_isReduced.mp hu).2 + rw [totalDegree_map_of_injective (algebraMap k K).injective] at he + exact (Nat.ne_of_gt hd) he + obtain ⟨g, h, hgu, hhu, hfac⟩ := (irreducible_or_factor hmapu).resolve_left hred + have hg : g ≠ 0 := fun he => hmap0 (by simp [hfac, he]) + have hh : h ≠ 0 := fun he => hmap0 (by simp [hfac, he]) + exact plane_points_card_le_of_extension_factorization f hf g h hg hh + (totalDegree_pos_of_ne_zero_of_not_isUnit hg hgu) + (totalDegree_pos_of_ne_zero_of_not_isUnit hh hhu) hfac z hinj hz + +end +end CCDN diff --git a/LeanPool/CCDN/Plane/ParameterIncidence.lean b/LeanPool/CCDN/Plane/ParameterIncidence.lean new file mode 100644 index 0000000000..89d1a3efb3 --- /dev/null +++ b/LeanPool/CCDN/Plane/ParameterIncidence.lean @@ -0,0 +1,98 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.Noether.UniversalPlaneSpecialization + +/-! Exact incidence averaging over every affine-plane parameter triple, +including degenerate triples. No divisibility correction for plane charts +is needed because points and directions are kept as coordinates. -/ + +@[expose] public section +namespace CCDN +noncomputable section +open MvPolynomial + +/-- A parameter triple and a coordinate pair mapping into the hypersurface. -/ +private def PlaneIncidence {K σ : Type*} [CommRing K] (f : MvPolynomial σ K) := + {vt : (PlaneParameters σ → K) × (Fin 2 → K) // + bivariateEval (vt.2 0) (vt.2 1) (universalPlaneSection f vt.1) = 0} + +/-- Once the image point, both directions and the two plane coordinates are +fixed, the base point is determined uniquely. -/ +private def planeIncidenceEquiv {K σ : Type*} [CommRing K] (f : MvPolynomial σ K) : + PlaneIncidence f ≃ + ({z : σ → K // eval z f = 0} × (σ → K) × (σ → K) × (Fin 2 → K)) where + toFun vt := + (⟨fun i => vt.val.1 (i,0) + vt.val.1 (i,1) * vt.val.2 0 + vt.val.1 (i,2) * vt.val.2 1, + by rw [← universalPlaneSection_eval]; exact vt.property⟩, + fun i => vt.val.1 (i,1), fun i => vt.val.1 (i,2), vt.val.2) + invFun z := + ⟨(fun i => ![z.1.val i.1 - z.2.1 i.1 * z.2.2.2 0 - z.2.2.1 i.1 * z.2.2.2 1, + z.2.1 i.1, z.2.2.1 i.1] i.2, z.2.2.2), by + rw [universalPlaneSection_eval] + convert z.1.property using 1 + congr 2 + funext i + simp + ring⟩ + left_inv vt := by + apply Subtype.ext + apply Prod.ext + · funext i + rcases i with ⟨i,j⟩ + fin_cases j <;> simp + ring + · rfl + right_inv z := by + apply Prod.ext + · apply Subtype.ext + funext i + simp + ring + · rfl + +/-- Exact incidence identity: q^(2*m+2) incidences per hypersurface point. -/ +private theorem planeIncidence_card {K σ : Type*} [CommRing K] [Fintype K] [Fintype σ] + (f : MvPolynomial σ K) : + Nat.card (PlaneIncidence f) = + Nat.card {z : σ → K // eval z f = 0} * Fintype.card K ^ (2 * Fintype.card σ + 2) := by + classical + rw [Nat.card_congr (planeIncidenceEquiv f)] + simp only [Nat.card_eq_fintype_card, Fintype.card_prod, Fintype.card_fun, Fintype.card_fin] + rw [← pow_add, ← pow_add] + congr 2 + omega + +/-- All plane coordinates whose image lies on the hypersurface. -/ +def PlaneSectionPoints {K σ : Type*} [CommRing K] + (f : MvPolynomial σ K) (v : PlaneParameters σ → K) := + {t : Fin 2 → K // bivariateEval (t 0) (t 1) (universalPlaneSection f v) = 0} + +private def planeIncidenceSigmaEquiv {K σ : Type*} [CommRing K] (f : MvPolynomial σ K) : + PlaneIncidence f ≃ Σ v, PlaneSectionPoints f v where + toFun vt := ⟨vt.val.1, ⟨vt.val.2, vt.property⟩⟩ + invFun vt := ⟨(vt.1, vt.2.val), vt.2.property⟩ + left_inv _ := rfl + right_inv _ := rfl + +/-- The section counts sum to the exact incidence count. -/ +theorem sum_planeSectionPoints_card {K σ : Type*} [CommRing K] [Fintype K] [Fintype σ] + [DecidableEq σ] (f : MvPolynomial σ K) : + ∑ v : PlaneParameters σ → K, Nat.card (PlaneSectionPoints f v) = + Nat.card {z : σ → K // eval z f = 0} * Fintype.card K ^ (2 * Fintype.card σ + 2) := by + classical + rw [← planeIncidence_card f, Nat.card_congr (planeIncidenceSigmaEquiv f)] + let (v : PlaneParameters σ → K) : Fintype (PlaneSectionPoints f v) := + inferInstanceAs (Fintype {t : Fin 2 → K // + bivariateEval (t 0) (t 1) (universalPlaneSection f v) = 0}) + simp only [Nat.card_eq_fintype_card, Fintype.card_sigma] + +end +end CCDN diff --git a/LeanPool/CCDN/Plane/ProjectivePlaneAbsoluteCount.lean b/LeanPool/CCDN/Plane/ProjectivePlaneAbsoluteCount.lean new file mode 100644 index 0000000000..17093a07e2 --- /dev/null +++ b/LeanPool/CCDN/Plane/ProjectivePlaneAbsoluteCount.lean @@ -0,0 +1,155 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.Height.ProjectiveZeroLocus +public import LeanPool.CCDN.Plane.ScaledPlaneCount +import LeanPool.CCDN.Algebra.PrimitiveNormalization +import LeanPool.CCDN.ForMathlib.Log +import LeanPool.CCDN.Plane.ProjectivePlanePositionedCount +import LeanPool.CCDN.Plane.RationalPlaneAssembly + +/-! An absolute d^4 B^(2/d) bound for primitive absolutely irreducible +homogeneous plane equations of degree at least two. -/ + +@[expose] public section +namespace CCDN +noncomputable section +open MvPolynomial + +/-- The absolute constant of `projective_plane_primitive_absolute_count`. Write +`E = heightDeterminantError projectivePlaneDeterminantConstant`. + +* Small coefficient height: the auxiliary degree is at most + `d + log R + 2 exp (E + 23) d³ (1 + log H / d²) R^(2/d) / |lc|^(1/d²) + 2` + (`projective_auxiliary_small_height_degree`), where `23 = 5 + 2 + 16` collects the + archimedean error of `plane_rank_archimedean_error` and the remainders of the box and + coefficient terms in `plane_auxiliary_log_degree_bound`. Positioning the leading coefficient + costs `log H ≤ log |lc| + 14 d²` (`exists_translated_coefficient_position`), hence the factor + `exp 14` (`scaled_positioned_height_ratio_le`). The translated radius `R = (d + 1) B` + satisfies `R^(2/d) ≤ e² B^(2/d)`, and `d + log R + 2 ≤ 5 d³ B^(2/d)`; this gives + `exp 2` and the leading `5` (`projective_positioned_bound_le`). +* Large coefficient height: the auxiliary degree is at least `32 d` and at most + `32 d + 8 exp (E + 6 + T) d³ + 2 ≤ (34 + 8 exp (E + 6 + T)) d³` + (`projective_auxiliary_large_height_degree`), where `6 = 5 + 1` and the `8 = 2 · 4` come from + `plane_auxiliary_large_height_log_bound`, and `T = 14` is the positioning loss; hence + `E + 20`. + +Bézout multiplies either degree bound by `d`. -/ +def projectivePlaneAbsoluteConstant : ℝ := + 5 + 2 * Real.exp (heightDeterminantError projectivePlaneDeterminantConstant + 23) * + Real.exp 14 * Real.exp 2 + + (34 + 8 * Real.exp (heightDeterminantError projectivePlaneDeterminantConstant + 20)) + +/-- The constant `projectivePlaneAbsoluteConstant` is positive. -/ +lemma projectivePlaneAbsoluteConstant_pos : 0 < projectivePlaneAbsoluteConstant := by + unfold projectivePlaneAbsoluteConstant + positivity + +/-- For `d ≥ 1` and `B ≥ 1`, the bound of `projective_plane_finite_count_positioned` is at most +`projectivePlaneAbsoluteConstant * d ^ 4 * B ^ (2 / d)`. -/ +lemma projective_positioned_bound_le {d : ℕ} (hd : 0 < d) {B : ℝ} (hB : 1 ≤ B) : + max ((d : ℝ) * + scaledPlaneAuxiliaryDegreeBound d projectivePlaneDeterminantConstant (((d : ℝ) + 1) * B) 1) + ((34 + 8 * Real.exp (heightDeterminantError projectivePlaneDeterminantConstant + 20)) * + (d : ℝ) ^ 4) ≤ + projectivePlaneAbsoluteConstant * (d : ℝ) ^ 4 * B ^ (2 / (d : ℝ)) := by + have hd1 : (1 : ℝ) ≤ d := by exact_mod_cast hd + have hd0 : (0 : ℝ) < d := by positivity + have hB0 : 0 < B := by linarith + let Q := B ^ (2 / (d : ℝ)) + let E := 2 * Real.exp (heightDeterminantError projectivePlaneDeterminantConstant + 23) * + Real.exp 14 + let A := 34 + 8 * Real.exp (heightDeterminantError projectivePlaneDeterminantConstant + 20) + have hQ : 1 ≤ Q := Real.one_le_rpow hB (by positivity) + have hE : 0 ≤ E := by dsimp [E]; positivity + have hA : 0 ≤ A := by dsimp [A]; positivity + have hlogplus : Real.log ((d : ℝ) + 1) ≤ d := Real.log_add_one_le_self d.cast_nonneg + have hlogR : Real.log (((d : ℝ) + 1) * B) ≤ d + Real.log B := by + rw [Real.log_mul (by positivity) hB0.ne'] + linarith + have hlogB : Real.log B ≤ (d : ℝ) / 2 * Q := by + have h := Real.log_le_rpow_div hB0.le (show (0 : ℝ) < 2 / d by positivity) + convert h using 1 + dsimp only [Q] + field_simp + have hpower : (((d : ℝ) + 1) * B) ^ (2 / (d : ℝ)) ≤ Real.exp 2 * Q := by + rw [Real.mul_rpow (by positivity) hB0.le] + apply mul_le_mul_of_nonneg_right ?_ (by positivity) + rw [Real.rpow_def_of_pos (by positivity)] + apply Real.exp_le_exp.mpr + rw [← mul_div_assoc] + apply (div_le_iff₀ hd0).mpr + linarith + have hd3 : (d : ℝ) ≤ (d : ℝ) ^ 3 := by nlinarith [sq_nonneg ((d : ℝ) - 1)] + have hdQ : (d : ℝ) ≤ d * Q := by nlinarith + have hd3Q : (d : ℝ) * Q ≤ (d : ℝ) ^ 3 * Q := + mul_le_mul_of_nonneg_right hd3 (by positivity) + have hrest : (d : ℝ) + Real.log (((d : ℝ) + 1) * B) + 2 ≤ + 5 * (d : ℝ) ^ 3 * Q := by nlinarith + have hterm := mul_le_mul_of_nonneg_left hpower + (show 0 ≤ E * (d : ℝ) ^ 3 by positivity) + have hdeg : + scaledPlaneAuxiliaryDegreeBound d projectivePlaneDeterminantConstant (((d : ℝ) + 1) * B) 1 ≤ + (5 + E * Real.exp 2) * (d : ℝ) ^ 3 * Q := by + unfold scaledPlaneAuxiliaryDegreeBound + simp only [Real.log_one, zero_div, add_zero, mul_one, Real.one_rpow, div_one] + dsimp only [E] at hterm ⊢ + nlinarith only [hrest, hterm] + have hsmall := mul_le_mul_of_nonneg_left hdeg hd0.le + have hlarge := mul_le_mul_of_nonneg_left hQ (show 0 ≤ A * (d : ℝ) ^ 4 by positivity) + apply max_le + · dsimp only [projectivePlaneAbsoluteConstant] + dsimp only [E, Q] at hsmall + nlinarith only [hsmall, mul_nonneg hA (mul_nonneg (pow_nonneg hd0.le 4) + (show 0 ≤ Q by positivity))] + · dsimp only [projectivePlaneAbsoluteConstant] + dsimp only [A, Q] at hlarge + have hpositive : 0 ≤ (5 + E * Real.exp 2) * (d : ℝ) ^ 4 * Q := by positivity + dsimp only [E, Q] at hpositive + nlinarith only [hlarge, hpositive] + +/-- The full real-radius bound for primitive absolutely irreducible plane +equations of degree >= 2, including their points at infinity. -/ +theorem projective_plane_primitive_absolute_count + {K : Type*} [Field K] [CharZero K] [IsAlgClosed K] {d : ℕ} + (f : MvPolynomial (Fin 3) ℤ) (hf : f.IsHomogeneous d) (hd : 2 ≤ d) + (hprim : PrimitiveIntegerPolynomial f) + (hir : Irreducible (map (Int.castRingHom K) f)) + {B : ℝ} (hB : 1 ≤ B) : + (projectiveCount (projectiveZeroLocus (map (Int.castRingHom ℚ) f)) B : ℝ) ≤ + projectivePlaneAbsoluteConstant * (d : ℝ) ^ 4 * B ^ (2 / (d : ℝ)) := by + apply projective_zero_count_le_of_family_bound f hf + intro t z hz0 hinj hzprim hz hbox + exact (projective_plane_finite_count_positioned f hf hd hprim hir z hz0 hinj + ⟨⟨hB, hz, hbox⟩, hzprim⟩).trans + (projective_positioned_bound_le (by omega) hB) + +/-- The bound for an arbitrary rational absolutely irreducible plane equation, +which need not be primitive or integral. The constant is independent of its +coefficients, degree, and real height radius. -/ +theorem projective_plane_rational_absolute_count + {K : Type*} [Field K] [CharZero K] [IsAlgClosed K] {d : ℕ} + (f : MvPolynomial (Fin 3) ℚ) (hf : f.IsHomogeneous d) (hd : 2 ≤ d) + (hir : Irreducible (map (algebraMap ℚ K) f)) + {B : ℝ} (hB : 1 ≤ B) : + (projectiveCount (projectiveZeroLocus f) B : ℝ) ≤ + projectivePlaneAbsoluteConstant * (d : ℝ) ^ 4 * B ^ (2 / (d : ℝ)) := by + have hf0 : f ≠ 0 := by intro h; exact hir.ne_zero (by simp [h]) + obtain ⟨g, a, hg, ha, he⟩ := exists_primitive_integer_model f hf0 + have hgh := rational_scalar_model_homogeneous he hf + have hgi := rational_scalar_model_irreducible_charZero ha he hir + have hzero : projectiveZeroLocus (map (Int.castRingHom ℚ) g) = projectiveZeroLocus f := by + ext x + exact rational_scalar_model_zero_iff ha he x.rep + have h := projective_plane_primitive_absolute_count g hgh hd hg hgi hB + rwa [hzero] at h + +end +end CCDN diff --git a/LeanPool/CCDN/Plane/ProjectivePlaneBezout.lean b/LeanPool/CCDN/Plane/ProjectivePlaneBezout.lean new file mode 100644 index 0000000000..256ca50adb --- /dev/null +++ b/LeanPool/CCDN/Plane/ProjectivePlaneBezout.lean @@ -0,0 +1,82 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.Algebra.ProjectiveSection +public import LeanPool.CCDN.Height.ProjectiveZeroLocus +import LeanPool.CCDN.Hilbert.PlaneHilbert + +/-! The homogeneous degree of a plane equation agrees with its +projective Hilbert degree, giving sharp projective plane intersection bounds. -/ + +@[expose] public section +namespace CCDN +noncomputable section +open MvPolynomial + +variable {K : Type*} [Field K] + +private theorem principal_projective_plane_hilbert_function + (f : MvPolynomial (Fin 3) K) {d M : ℕ} + (hf : f.IsHomogeneous d) (hf0 : f ≠ 0) (hM : d ≤ M) : + (projectiveHilbertFunction (Ideal.span {f}) M : ℤ) = + (d : ℤ) * M + ((d : ℤ) - d.choose 2) := by + have h := principal_homogeneous_hilbert_count f hf hf0 hM + rw [Nat.add_comm 2 (M - d), Nat.add_comm 2 M] at h + rw [Nat.choose_symm_add, Nat.choose_symm_add] at h + exact choose_two_difference_eq_linear hM h + +/-- The ideal generated by a nonzero ternary form of degree `d` defines a projective plane curve of +degree `d`, in the sense of its Hilbert polynomial. -/ +theorem principal_plane_hasProjectiveCurveDegree + (f : MvPolynomial (Fin 3) K) {d : ℕ} (hf : f.IsHomogeneous d) (hf0 : f ≠ 0) : + HasProjectiveCurveDegree (Ideal.span {f}) d := + ⟨d, (d : ℤ) - d.choose 2, fun _ hM => + principal_projective_plane_hilbert_function f hf hf0 hM⟩ + +/-- The projective zero locus of the ideal generated by a homogeneous polynomial `f` is the +projective zero locus of `f`. -/ +theorem projectiveIdealZeroLocus_span_singleton {n d : ℕ} + (f : MvPolynomial (Fin (n + 1)) K) (hf : f.IsHomogeneous d) : + projectiveIdealZeroLocus (Ideal.span {f}) = projectiveZeroLocus f := by + ext x + constructor + · intro hx + exact projectiveIdealZeroLocus_rep hx f (Ideal.subset_span (by simp)) + · intro hx v hv hvx g hg + have hfv : eval v f = 0 := by + apply (mem_projectiveZeroLocus_mk hf v hv).mp + rwa [hvx] + obtain ⟨q, rfl⟩ := Ideal.mem_span_singleton.mp hg + rw [eval_mul, hfv, zero_mul] + +/-- Sharp projective Bézout over any field for an irreducible plane equation and +a homogeneous polynomial not divisible by it, including points at infinity. -/ +theorem projective_plane_common_zeros_card_le {d m : ℕ} {ι : Type*} + [Fintype ι] + (f g : MvPolynomial (Fin 3) K) (hf : f.IsHomogeneous d) + (hir : Irreducible f) (hg : g.IsHomogeneous m) (hfg : ¬ f ∣ g) + (z : ι → Projectivization K (Fin 3 → K)) (hz : Function.Injective z) + (hzf : ∀ i, z i ∈ projectiveZeroLocus f) + (hzg : ∀ i, z i ∈ projectiveZeroLocus g) : + Fintype.card ι ≤ d * m := by + classical + let I : Ideal (MvPolynomial (Fin 3) K) := Ideal.span {f} + have : I.IsPrime := Ideal.isPrime_span_singleton_of_prime hir.prime + apply projective_section_card_le_degree I + (principal_plane_hasProjectiveCurveDegree f hf hir.ne_zero) g hg + (by simpa only [I, Ideal.mem_span_singleton] using hfg) z hz + · intro i + change z i ∈ projectiveIdealZeroLocus (Ideal.span {f}) + rw [projectiveIdealZeroLocus_span_singleton f hf] + exact hzf i + · exact hzg + +end +end CCDN diff --git a/LeanPool/CCDN/Plane/ProjectivePlaneLinearCount.lean b/LeanPool/CCDN/Plane/ProjectivePlaneLinearCount.lean new file mode 100644 index 0000000000..4817856092 --- /dev/null +++ b/LeanPool/CCDN/Plane/ProjectivePlaneLinearCount.lean @@ -0,0 +1,109 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.Plane.ProjectivePlaneAbsoluteCount +import LeanPool.CCDN.Algebra.PrimitiveNormalization +import LeanPool.CCDN.ForMathlib.SchwartzZippel +import LeanPool.CCDN.Plane.ProjectivePlaneNonabsoluteCount + +/-! The elementary linear case completes the uniform plane-equation +bound in every positive degree. -/ + +@[expose] public section +namespace CCDN +noncomputable section +open MvPolynomial + +private lemma integer_ternary_zero_box_card_le (f : MvPolynomial (Fin 3) ℤ) (hf : f ≠ 0) + {B : ℝ} (hB : 1 ≤ B) : + (((integerBox 3 B).filter (fun x => eval x f = 0)).card : ℝ) ≤ + f.totalDegree * (3 * B) ^ 2 := by + classical + let S := Finset.Icc (-⌊B⌋) ⌊B⌋ + have hfloor : 0 ≤ ⌊B⌋ := Int.floor_nonneg.mpr (by linarith) + have hSne : S.Nonempty := ⟨0, Finset.mem_Icc.mpr ⟨by omega, hfloor⟩⟩ + have hSpos : (0 : ℝ) < S.card := by exact_mod_cast Finset.card_pos.mpr hSne + have h : ((integerBox 3 B).filter (fun x => eval x f = 0)).card * S.card ≤ + f.totalDegree * S.card ^ 3 := by + simpa only [integerBox, S, Fintype.card_fin] using schwartz_zippel_totalDegree_card hf S + have hcard : (((integerBox 3 B).filter (fun x => eval x f = 0)).card : ℝ) ≤ + f.totalDegree * (S.card : ℝ) ^ 2 := by + refine le_of_mul_le_mul_right ?_ hSpos + rw [mul_assoc, ← pow_succ] + exact_mod_cast h + have hScard : (S.card : ℝ) ≤ 3 * B := by + dsimp only [S] + rw [Int.card_Icc] + rw [← Int.cast_natCast, Int.toNat_of_nonneg (by omega)] + push_cast + have hh := Int.floor_le B + linarith + exact hcard.trans (mul_le_mul_of_nonneg_left + (pow_le_pow_left₀ hSpos.le hScard 2) (by positivity)) + +private theorem projective_plane_linear_count + (f : MvPolynomial (Fin 3) ℚ) (hf : f.IsHomogeneous 1) (hf0 : f ≠ 0) + {B : ℝ} (hB : 1 ≤ B) : + (projectiveCount (projectiveZeroLocus f) B : ℝ) ≤ 9 * B ^ 2 := by + classical + obtain ⟨g, a, _, ha, he⟩ := exists_primitive_integer_model f hf0 + have hgh := rational_scalar_model_homogeneous he hf + have hg0 : g ≠ 0 := by + intro h + have hzero : C a * f = 0 := by simpa [h] using he.symm + exact mul_ne_zero (C_eq_zero.not.mpr ha) hf0 hzero + have hzero : projectiveZeroLocus (map (Int.castRingHom ℚ) g) = projectiveZeroLocus f := by + ext x + exact rational_scalar_model_zero_iff ha he x.rep + rw [← hzero] + apply projective_zero_count_le_of_family_bound g hgh + intro t z hz0 hinj _ hz hbox + let T := (integerBox 3 B).filter (fun x => eval x g = 0) + let ψ : Fin t → T := fun i => ⟨z i, Finset.mem_filter.mpr ⟨mem_integerBox.mpr (hbox i), hz i⟩⟩ + have hψ : Function.Injective ψ := by + intro i j heq + have hzi : z i = z j := congrArg Subtype.val heq + apply hinj + apply (Projectivization.mk_eq_mk_iff' ℚ _ _ _ _).mpr + exact ⟨1, by simp [hzi]⟩ + have hc := Fintype.card_le_of_injective ψ hψ + have hcR : (t : ℝ) ≤ T.card := by + exact_mod_cast (show t ≤ T.card by simpa only [Fintype.card_fin, Fintype.card_coe] using hc) + have hb := integer_ternary_zero_box_card_le g hg0 hB + rw [hgh.totalDegree hg0] at hb + have hh := hcR.trans hb + convert hh using 1 + norm_num + ring + +/-- Uniform CCDN Theorem 2 for rational projective plane equations, +in every positive degree and for every real B >= 1. -/ +theorem projective_plane_equation_bound {d : ℕ} + (f : MvPolynomial (Fin 3) ℚ) (hf : f.IsHomogeneous d) (hd : 0 < d) + (hir : Irreducible f) {B : ℝ} (hB : 1 ≤ B) : + (projectiveCount (projectiveZeroLocus f) B : ℝ) ≤ + projectivePlaneAbsoluteConstant * (d : ℝ) ^ 4 * B ^ (2 / (d : ℝ)) := by + by_cases hd1 : d = 1 + · subst d + have h := projective_plane_linear_count f hf hir.ne_zero hB + have hC : 9 ≤ projectivePlaneAbsoluteConstant := by + unfold projectivePlaneAbsoluteConstant + have hpos := Real.exp_pos + (heightDeterminantError projectivePlaneDeterminantConstant + 20) + have hpos' : 0 ≤ + 2 * Real.exp (heightDeterminantError projectivePlaneDeterminantConstant + 23) * + Real.exp 14 * Real.exp 2 := by positivity + linarith + norm_num only [Nat.cast_one, one_pow, mul_one, div_one, Real.rpow_two] + exact h.trans (mul_le_mul_of_nonneg_right hC (sq_nonneg B)) + · exact projective_plane_rational_count f hf (by omega) hir hB + +end +end CCDN diff --git a/LeanPool/CCDN/Plane/ProjectivePlaneNonabsoluteCount.lean b/LeanPool/CCDN/Plane/ProjectivePlaneNonabsoluteCount.lean new file mode 100644 index 0000000000..9efa01c7a7 --- /dev/null +++ b/LeanPool/CCDN/Plane/ProjectivePlaneNonabsoluteCount.lean @@ -0,0 +1,120 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.Algebra.PolynomialPredicates +public import LeanPool.CCDN.Plane.ProjectivePlaneAbsoluteCount +import LeanPool.CCDN.Algebra.HomogeneousChartIrreducible +import LeanPool.CCDN.Algebra.ProjectiveAffineChart +import LeanPool.CCDN.ForMathlib.MvPolynomial +import LeanPool.CCDN.Plane.NonabsolutePlaneCount +import LeanPool.CCDN.Plane.ProjectivePlaneBezout + +/-! Curves that are irreducible over the base field but split over an +extension have at most d^2+d projective base-field points. -/ + +@[expose] public section +namespace CCDN +noncomputable section +open MvPolynomial + +private theorem nonabsolute_projective_plane_points_card_le {k K ι : Type*} + [Field k] [Field K] [Algebra k K] [Fintype ι] {d : ℕ} + (f : MvPolynomial (Fin 3) k) (hf : f.IsHomogeneous d) (hd : 2 ≤ d) + (hir : Irreducible f) (hred : ¬ Irreducible (map (algebraMap k K) f)) + (z : ι → Projectivization k (Fin 3 → k)) (hinj : Function.Injective z) + (hz : ∀ i, z i ∈ projectiveZeroLocus f) : + Fintype.card ι ≤ d ^ 2 + d := by + classical + have hX := variable_not_dvd_irreducible_homogeneous f hf hir hd 0 + have hdeg := affineChart_degree_of_not_variable_dvd f hf hX + have hchart := affineChart_irreducible_of_not_variable_dvd f hf hir hX + have hchartred : ¬ Irreducible (map (algebraMap k K) (affineChart 2 f)) := by + intro hi + apply hred + apply irreducible_of_affineChart_irreducible + · rw [affineChart_map] + have hmapdegree (q : MvPolynomial (Fin 2) k) : + (map (algebraMap k K) q).totalDegree = q.totalDegree := + totalDegree_map_of_injective (algebraMap k K).injective _ + rw [hmapdegree, hdeg, (hf.map _).totalDegree] + exact fun h => hir.ne_zero + ((map_injective (algebraMap k K) (algebraMap k K).injective) (by simpa using h)) + · rwa [affineChart_map] + let S := Finset.univ.filter (fun i : ι => (z i).rep 0 ≠ 0) + let T := Finset.univ.filter (fun i : ι => ¬ (z i).rep 0 ≠ 0) + have hs := nonabsolute_plane_points_card_le (affineChart 2 f) hchart + (by omega : 0 < (affineChart 2 f).totalDegree) hchartred + (fun i : S => projectiveAffineCoordinates (z i.val)) ?_ ?_ + · have ht := projective_plane_common_zeros_card_le f (X 0) hf hir (isHomogeneous_X _ _) + (irreducible_homogeneous_not_dvd_variable f hf hir hd 0) + (fun i : T => z i.val) (hinj.comp Subtype.val_injective) (fun i => hz i.val) ?_ + · have hsum := Finset.card_filter_add_card_filter_not + (s := Finset.univ) (p := fun i : ι => (z i).rep 0 ≠ 0) + simp only [Fintype.card_coe, hdeg] at hs + simp only [Fintype.card_coe, mul_one] at ht + change S.card + T.card = Finset.univ.card at hsum + rw [Finset.card_univ] at hsum + omega + · intro i + change eval (z i.val).rep (X 0) = 0 + simpa using (Finset.mem_filter.mp i.property).2 + · intro i j he + apply Subtype.ext + apply hinj + exact projectiveAffineCoordinates_injective + (Finset.mem_filter.mp i.property).2 (Finset.mem_filter.mp j.property).2 he + · intro i + exact projectiveAffineCoordinates_zero f hf (z i.val) + (Finset.mem_filter.mp i.property).2 (hz i.val) + +private theorem projectiveCount_nonabsolute_plane_le + {K : Type*} [Field K] [Algebra ℚ K] {d : ℕ} + (f : MvPolynomial (Fin 3) ℚ) (hf : f.IsHomogeneous d) (hd : 2 ≤ d) + (hir : Irreducible f) (hred : ¬ Irreducible (map (algebraMap ℚ K) f)) (B : ℝ) : + projectiveCount (projectiveZeroLocus f) B ≤ d ^ 2 + d := by + classical + let S := projectivePoints (projectiveZeroLocus f) B + have h := nonabsolute_projective_plane_points_card_le f hf hd hir hred + (fun x : S => x.val) Subtype.val_injective (fun x => (mem_projectivePoints.mp x.property).2) + simpa only [Fintype.card_coe, S, projectiveCount] using h + +/-- The uniform plane bound for every rational irreducible homogeneous +equation of degree at least two, whether or not geometrically irreducible. -/ +theorem projective_plane_rational_count {d : ℕ} + (f : MvPolynomial (Fin 3) ℚ) (hf : f.IsHomogeneous d) (hd : 2 ≤ d) + (hir : Irreducible f) {B : ℝ} (hB : 1 ≤ B) : + (projectiveCount (projectiveZeroLocus f) B : ℝ) ≤ + projectivePlaneAbsoluteConstant * (d : ℝ) ^ 4 * B ^ (2 / (d : ℝ)) := by + by_cases habs : AbsolutelyIrreducibleRat f + · exact projective_plane_rational_absolute_count f hf hd habs hB + · have h := projectiveCount_nonabsolute_plane_le f hf hd hir habs B + have hR : (projectiveCount (projectiveZeroLocus f) B : ℝ) ≤ (d : ℝ) ^ 2 + d := by + exact_mod_cast h + have hd1 : (1 : ℝ) ≤ d := by exact_mod_cast (show 1 ≤ d by omega) + have hd4 : (d : ℝ) ^ 2 + d ≤ 2 * (d : ℝ) ^ 4 := by nlinarith [sq_nonneg ((d : ℝ) ^ 2 - 1)] + have hC : 2 ≤ projectivePlaneAbsoluteConstant := by + unfold projectivePlaneAbsoluteConstant + have hpos := Real.exp_pos + (heightDeterminantError projectivePlaneDeterminantConstant + 20) + have hpos' : 0 ≤ + 2 * Real.exp (heightDeterminantError projectivePlaneDeterminantConstant + 23) * + Real.exp 14 * Real.exp 2 := by positivity + linarith + have hQ : 1 ≤ B ^ (2 / (d : ℝ)) := Real.one_le_rpow hB (by positivity) + calc + _ ≤ (d : ℝ) ^ 2 + d := hR + _ ≤ 2 * (d : ℝ) ^ 4 := hd4 + _ ≤ projectivePlaneAbsoluteConstant * (d : ℝ) ^ 4 := + mul_le_mul_of_nonneg_right hC (by positivity) + _ ≤ _ := le_mul_of_one_le_right + (mul_nonneg projectivePlaneAbsoluteConstant_pos.le (by positivity)) hQ + +end +end CCDN diff --git a/LeanPool/CCDN/Plane/ProjectivePlanePositionedCount.lean b/LeanPool/CCDN/Plane/ProjectivePlanePositionedCount.lean new file mode 100644 index 0000000000..784419e5b4 --- /dev/null +++ b/LeanPool/CCDN/Plane/ProjectivePlanePositionedCount.lean @@ -0,0 +1,129 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.Algebra.HomogeneousChart +public import LeanPool.CCDN.Height.ProjectiveBox +public import LeanPool.CCDN.Plane.ScaledPlaneCount +import LeanPool.CCDN.Algebra.HomogeneousChartIrreducible +import LeanPool.CCDN.Algebra.ProjectiveTranslation +import LeanPool.CCDN.AuxiliaryPolynomial.PlaneAuxiliaryCount +import LeanPool.CCDN.AuxiliaryPolynomial.ProjectiveAuxiliaryCount +import LeanPool.CCDN.Plane.ScaledModelPosition + +/-! Coefficient-uniform projective plane counts after a bounded integral +coordinate change. The finite-field and positioning inputs are proved. -/ + +@[expose] public section +namespace CCDN +noncomputable section +open MvPolynomial + +private theorem homogeneous_absolute_chart_data + {K : Type*} [Field K] [CharZero K] {d : ℕ} + (f : MvPolynomial (Fin 3) ℤ) (hf : f.IsHomogeneous d) (hd : 2 ≤ d) + (hir : Irreducible (map (Int.castRingHom K) f)) : + (affineChart 2 f).totalDegree = d ∧ + Irreducible (map (Int.castRingHom K) (affineChart 2 f)) := by + have hirQ := rational_irreducible_of_charZero_map f hir + have hQ := affineChart_degree_of_not_variable_dvd _ (hf.map _) + (variable_not_dvd_irreducible_homogeneous _ (hf.map _) hirQ hd 0) + rw [affineChart_map, rational_map_totalDegree] at hQ + refine ⟨hQ, ?_⟩ + rw [← affineChart_map] + exact affineChart_irreducible_of_not_variable_dvd _ (hf.map _) hir + (variable_not_dvd_irreducible_homogeneous _ (hf.map _) hir hd 0) + +/-- Coefficient-uniform projective plane count: for a primitive ternary integer form `f` of degree +`d ≥ 2`, absolutely irreducible over an algebraically closed field of characteristic zero, the +number of distinct projective points with primitive integer coordinates in the box of size `B` on +`f = 0` is at most the maximum of the two degree bounds of the scaled plane count with residue +constant `projectivePlaneDeterminantConstant`. The proof first moves `f` by a bounded integral +coordinate change. -/ +theorem projective_plane_finite_count_positioned + {K : Type*} [Field K] [CharZero K] [IsAlgClosed K] {d t : ℕ} + (f : MvPolynomial (Fin 3) ℤ) (hf : f.IsHomogeneous d) (hd : 2 ≤ d) + (hprim : PrimitiveIntegerPolynomial f) + (hir : Irreducible (map (Int.castRingHom K) f)) + (z : Fin t → IntPoint 3) (hz0 : ∀ i, z i ≠ 0) + (hinj : Function.Injective (fun i => Projectivization.mk ℚ (rationalPoint (z i)) + (rationalPoint_ne_zero (hz0 i)))) + {B : ℝ} (hzB : IsPrimitiveBoxZeroFamily f B z) : + (t : ℝ) ≤ max + ((d : ℝ) * scaledPlaneAuxiliaryDegreeBound d projectivePlaneDeterminantConstant (((d : ℝ) + + 1) * B) 1) + ((34 + 8 * Real.exp (heightDeterminantError projectivePlaneDeterminantConstant + 20)) * + (d : ℝ) ^ 4) := by + obtain ⟨⟨hB, hz, hbox⟩, hzprim⟩ := hzB + have hdpos : 0 < d := by omega + have hf0 : f ≠ 0 := by intro h; exact hir.ne_zero (by simp [h]) + obtain ⟨a, H, ha, hH, hc, hlo, hhi⟩ := exists_translated_coefficient_position hdpos f hf hf0 + let model := homogeneousTranslation ℤ a f + have hmhom : model.IsHomogeneous d := homogeneousTranslation_homogeneous a hf + have hm0 : model ≠ 0 := by + exact (homogeneousTranslation ℤ a).injective.ne (by simpa using hf0) + have hmir : Irreducible (map (Int.castRingHom K) model) := by + dsimp only [model] + rw [homogeneousTranslation_map] + exact (MulEquiv.irreducible_iff + (f := homogeneousTranslation K ((Int.castRingHom K) ∘ a))).mpr hir + obtain ⟨hmdeg, hmchart⟩ := homogeneous_absolute_chart_data model hmhom hd hmir + let w := fun i => homogeneousPointTranslation (-a) (z i) + have hw0 : ∀ i, w i ≠ 0 := fun i => homogeneousPointTranslation_ne_zero (-a) (hz0 i) + have hwi : Function.Injective (fun i => Projectivization.mk ℚ (rationalPoint (w i)) + (rationalPoint_ne_zero (hw0 i))) := + homogeneousPointTranslation_projective_injective (-a) z hz0 hinj + have hwp : ∀ i, Finset.univ.gcd (w i) = 1 := by + intro i + exact (homogeneousPointTranslation_gcd (-a) (z i)).trans (hzprim i) + have hwz : ∀ i, eval (w i) model = 0 := by + intro i + rw [homogeneousTranslation_eval] + change eval (homogeneousPointTranslation a (homogeneousPointTranslation (-a) (z i))) f = 0 + have he := homogeneousPointTranslation_inverse (-a) (z i) + simp only [neg_neg] at he + rw [he, hz i] + let R := ((d : ℝ) + 1) * B + have hR : 1 ≤ R := one_le_mul_of_one_le_of_one_le + (by have := Nat.cast_nonneg (α := ℝ) d; linarith) hB + have hwbox : ∀ i, InBox (w i) R := + fun i j => homogeneousPointTranslation_box hB a ha (z i) (hbox i) j + have hmp := hprim.translate_homogeneous a + by_cases hsmall : Real.log H ≤ 4 * d * Real.log R + · have ht := projective_points_small_height_bound MonomialOrder.lex model hmhom hm0 hd hmp + ⟨hmdeg, hmchart, hH, hc⟩ w hw0 hwi ⟨⟨hR, hwz, hwbox⟩, hwp⟩ (hlo.trans hsmall) + have hL : 0 < |((MonomialOrder.lex.leadingCoeff model : ℤ) : ℝ)| := + abs_pos.mpr (Int.cast_ne_zero.mpr (MonomialOrder.lex.leadingCoeff_ne_zero_iff.mpr hm0)) + have hratio := scaled_positioned_height_ratio_le hdpos (h := 1) le_rfl + (by simpa using hH) hL hhi + simp only [Real.log_one, zero_div, add_zero, Real.one_rpow, div_one] at hratio + have hterm := mul_le_mul_of_nonneg_left hratio (show 0 ≤ + 2 * Real.exp (heightDeterminantError projectivePlaneDeterminantConstant + 23) * (d : ℝ) ^ 3 * + R ^ (2 / (d : ℝ)) + by positivity) + have hbound : (d : ℝ) + Real.log R + + 2 * Real.exp (heightDeterminantError projectivePlaneDeterminantConstant + 23) * + (d : ℝ) ^ 3 * + (1 + Real.log H / (d : ℝ) ^ 2) * R ^ (2 / (d : ℝ)) / + |((MonomialOrder.lex.leadingCoeff model : ℤ) : ℝ)| ^ (1 / (d : ℝ) ^ 2) + 2 ≤ + scaledPlaneAuxiliaryDegreeBound d projectivePlaneDeterminantConstant R 1 := by + unfold scaledPlaneAuxiliaryDegreeBound + simp only [Real.log_one, zero_div, add_zero, mul_one, Real.one_rpow, div_one] + ring_nf at hterm ⊢ + linarith only [hterm] + exact (ht.trans (mul_le_mul_of_nonneg_left hbound (by positivity))).trans (le_max_left _ _) + · have ht := projective_points_large_height_bound MonomialOrder.lex model hmhom hm0 hd hmp + ⟨hmdeg, hmchart, hH, hc⟩ w hw0 hwi ⟨⟨hR, hwz, hwbox⟩, hwp⟩ 14 (by norm_num) + (le_of_lt (lt_of_not_ge hsmall)) hhi + rw [show heightDeterminantError projectivePlaneDeterminantConstant + 6 + 14 = + heightDeterminantError projectivePlaneDeterminantConstant + 20 by ring] at ht + exact ht.trans (le_max_right _ _) + +end +end CCDN diff --git a/LeanPool/CCDN/Plane/RationalPlaneAssembly.lean b/LeanPool/CCDN/Plane/RationalPlaneAssembly.lean new file mode 100644 index 0000000000..eb1cd3d2e5 --- /dev/null +++ b/LeanPool/CCDN/Plane/RationalPlaneAssembly.lean @@ -0,0 +1,67 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.Plane.AffineNonzeroConstant +import LeanPool.CCDN.Algebra.PrimitiveNormalization +import LeanPool.CCDN.Plane.AffinePlaneAssembly + +/-! Rational equations and the coefficient-uniform affine plane bound. +The sharp finite-field estimate is stated as a proposition, not an axiom. -/ + +@[expose] public section +namespace CCDN +noncomputable section +open MvPolynomial Real + +/-- The finite-field counting obligation `PlaneResidueCountBound` for every plane equation +of positive degree, with one absolute constant. `planeResidueBound` discharges it with +`C = planeResidueConstant`. -/ +def PlaneResidueBound (C : ℝ) : Prop := + ∀ f : MvPolynomial (Fin 2) ℤ, 0 < f.totalDegree → PlaneResidueCountBound C f.totalDegree f + +/-- If an integer polynomial `g` is a nonzero rational multiple of a rational polynomial `f` that +is irreducible over a field `F` of characteristic zero, then `g` is irreducible over `F`. -/ +lemma rational_scalar_model_irreducible_charZero {F σ : Type*} [Field F] [CharZero F] + {f : MvPolynomial σ ℚ} {g : MvPolynomial σ ℤ} {a : ℚ} (ha : a ≠ 0) + (he : MvPolynomial.map (Int.castRingHom ℚ) g = C a * f) + (hf : Irreducible (MvPolynomial.map (algebraMap ℚ F) f)) : + Irreducible (MvPolynomial.map (Int.castRingHom F) g) := by + have hh := congrArg (MvPolynomial.map (algebraMap ℚ F)) he + rw [map_map, map_mul, map_C] at hh + have hc : (algebraMap ℚ F).comp (Int.castRingHom ℚ) = Int.castRingHom F := by ext; simp + rw [hc] at hh + rw [hh] + have hu : IsUnit (C ((algebraMap ℚ F) a) : MvPolynomial σ F) := + ((isUnit_iff_ne_zero.mpr ha).map (algebraMap ℚ F)).map C + exact (irreducible_isUnit_mul hu).mpr hf + +/-- A rational absolutely irreducible equation has the affine bound for every +real B>=1. Primitive normalization and every analytic reduction are proved; +the sharp finite-field bound is the one explicit counting hypothesis. -/ +theorem count_rational_plane_of_residue_bound + {F : Type*} [Field F] [CharZero F] [IsAlgClosed F] + (f : MvPolynomial (Fin 2) ℚ) + (hir : Irreducible (MvPolynomial.map (algebraMap ℚ F) f)) (hd : 0 < f.totalDegree) + {B C : ℝ} (hB : 1 ≤ B) (hC : 0 ≤ C) (hres : PlaneResidueBound C) : + (count (zeroLocus f) B : ℝ) ≤ + (2 * Real.exp 1 * affineNonzeroConstantBound C) * curveBound f.totalDegree B := by + have hf0 : f ≠ 0 := fun h => hir.ne_zero (by simp [h]) + obtain ⟨g, a, hg, ha, he⟩ := exists_primitive_integer_model f hf0 + have hdeg := rational_scalar_model_degree ha hf0 he + have hgi := rational_scalar_model_irreducible_charZero ha he hir + have hgd : 0 < g.totalDegree := by rwa [hdeg] + have hzero : zeroLocus (MvPolynomial.map (Int.castRingHom ℚ) g) = zeroLocus f := by + ext x + exact rational_scalar_model_zero_iff ha he x + have h := count_plane_of_residue_bound g hg hgi hgd hB hC (hres g hgd) + rwa [hzero, hdeg] at h + +end +end CCDN diff --git a/LeanPool/CCDN/Plane/ScaledBoxExponent.lean b/LeanPool/CCDN/Plane/ScaledBoxExponent.lean new file mode 100644 index 0000000000..08344878b4 --- /dev/null +++ b/LeanPool/CCDN/Plane/ScaledBoxExponent.lean @@ -0,0 +1,159 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.Plane.ScaledPlaneCount +import LeanPool.CCDN.ForMathlib.Log + +/-! The scaling-prime interval yields the affine box exponent 1/d. These +numerical bounds are independent of the coefficient height. -/ + +@[expose] public section +namespace CCDN +open Real + +private lemma scaled_box_power_ratio_le {d : ℕ} (hd : 0 < d) {A B p : ℝ} + (hA : 1 ≤ A) (hB : 1 ≤ B) (hp : 0 < p) (hBp : B ≤ A * p) : + (((d : ℝ) + 1) * B) ^ (2 / (d : ℝ)) / p ^ (1 / (d : ℝ)) ≤ + Real.exp 2 * A * B ^ (1 / (d : ℝ)) := by + have hd1 : (1 : ℝ) ≤ d := by exact_mod_cast hd + have hd0 : (0 : ℝ) < d := by linarith + have hB0 : 0 < B := by linarith + have hdplus : (0 : ℝ) < d + 1 := by linarith + have hlog : Real.log ((d : ℝ) + 1) ≤ d := Real.log_add_one_le_self hd0.le + have hdeg : ((d : ℝ) + 1) ^ (2 / (d : ℝ)) ≤ Real.exp 2 := by + rw [Real.rpow_def_of_pos hdplus] + apply Real.exp_le_exp.mpr + rw [← mul_div_assoc] + apply (div_le_iff₀ hd0).mpr + linarith + have hquot : (B / p) ^ (1 / (d : ℝ)) ≤ A := by + have hbase : B / p ≤ A := (div_le_iff₀ hp).mpr hBp + have h := Real.rpow_le_rpow (div_nonneg hB0.le hp.le) hbase + (show (0 : ℝ) ≤ 1 / d by positivity) + apply h.trans + simpa only [Real.rpow_one] using Real.rpow_le_rpow_of_exponent_le hA + (show (1 : ℝ) / d ≤ 1 by apply (div_le_iff₀ hd0).mpr; linarith) + have he : (((d : ℝ) + 1) * B) ^ (2 / (d : ℝ)) / p ^ (1 / (d : ℝ)) = + ((d : ℝ) + 1) ^ (2 / (d : ℝ)) * (B / p) ^ (1 / (d : ℝ)) * B ^ (1 / (d : ℝ)) := by + rw [Real.mul_rpow hdplus.le hB0.le, Real.div_rpow hB0.le hp.le] + rw [show (2 : ℝ) / d = 1 / d + 1 / d by ring, Real.rpow_add hB0] + ring + rw [he] + exact mul_le_mul_of_nonneg_right + (mul_le_mul hdeg hquot (Real.rpow_nonneg (div_nonneg hB0.le hp.le) _) (Real.exp_pos _).le) + (Real.rpow_nonneg hB0.le _) + +private lemma scaled_auxiliary_degree_bound_le {d : ℕ} (hd : 0 < d) {A B p C : ℝ} + (hA : 1 ≤ A) (hB : 1 ≤ B) (hp : 1 ≤ p) (hpB : p ≤ B) (hBp : B ≤ A * p) : + (d : ℝ) * scaledPlaneAuxiliaryDegreeBound d C (((d : ℝ) + 1) * B) p ≤ + (4 + 2 * Real.exp (heightDeterminantError C + 23) * Real.exp 14 * Real.exp 2 * A) * + (d : ℝ) ^ 3 * B ^ (1 / (d : ℝ)) * (Real.log B + d) := by + have hd1 : (1 : ℝ) ≤ d := by exact_mod_cast hd + have hd0 : (0 : ℝ) < d := by linarith + have hB0 : 0 < B := by linarith + have hp0 : 0 < p := by linarith + have hlogB := Real.log_nonneg hB + have hlogp := Real.log_nonneg hp + have hlogpB := Real.log_le_log hp0 hpB + have hlogR : Real.log (((d : ℝ) + 1) * B) ≤ d + Real.log B := by + rw [Real.log_mul (by positivity) hB0.ne'] + linarith [Real.log_add_one_le_self hd0.le] + let Q := B ^ (1 / (d : ℝ)) + let E := 2 * Real.exp (heightDeterminantError C + 23) * Real.exp 14 + have hQ : 1 ≤ Q := Real.one_le_rpow hB (by positivity) + have hE : 0 ≤ E := by dsimp [E]; positivity + have hratio := scaled_box_power_ratio_le hd hA hB hp0 hBp + have hnum : 0 ≤ 1 + Real.log p / d := by positivity + have hnumB : 1 + Real.log p / d ≤ 1 + Real.log B / d := by gcongr + have hterm : E * (d : ℝ) ^ 3 * (1 + Real.log p / d) * + ((((d : ℝ) + 1) * B) ^ (2 / (d : ℝ)) / p ^ (1 / (d : ℝ))) ≤ + E * Real.exp 2 * A * (d : ℝ) ^ 2 * Q * (Real.log B + d) := by + have hh := mul_le_mul hnumB hratio + (Real.rpow_nonneg (show 0 ≤ ((d : ℝ) + 1) * B by positivity) _ |> fun h => + div_nonneg h (Real.rpow_nonneg hp0.le _)) (by positivity : 0 ≤ 1 + Real.log B / d) + have ht := mul_le_mul_of_nonneg_left hh (mul_nonneg hE (pow_nonneg hd0.le 3)) + convert ht using 1 + · ring + · dsimp only [Q] + field_simp + ring + have hrest : (d : ℝ) + Real.log (((d : ℝ) + 1) * B) + 2 ≤ + 4 * (d : ℝ) ^ 2 * Q * (Real.log B + d) := by + have hd2 : (1 : ℝ) ≤ (d : ℝ) ^ 2 := by nlinarith + have hprod : 1 ≤ (d : ℝ) ^ 2 * Q := one_le_mul_of_one_le_of_one_le hd2 hQ + nlinarith [mul_le_mul_of_nonneg_right hprod + (show 0 ≤ Real.log B + d by positivity)] + have hb : scaledPlaneAuxiliaryDegreeBound d C (((d : ℝ) + 1) * B) p ≤ + (4 + E * Real.exp 2 * A) * (d : ℝ) ^ 2 * Q * (Real.log B + d) := by + unfold scaledPlaneAuxiliaryDegreeBound + calc + _ = ((d : ℝ) + Real.log (((d : ℝ) + 1) * B) + 2) + E * (d : ℝ) ^ 3 * + (1 + Real.log p / d) * + ((((d : ℝ) + 1) * B) ^ (2 / (d : ℝ)) / p ^ (1 / (d : ℝ))) := by dsimp [E]; ring + _ ≤ 4 * (d : ℝ) ^ 2 * Q * (Real.log B + d) + + E * Real.exp 2 * A * (d : ℝ) ^ 2 * Q * (Real.log B + d) := add_le_add hrest hterm + _ = _ := by ring + have ht := mul_le_mul_of_nonneg_left hb hd0.le + convert ht using 1 + dsimp [E, Q] + ring + +/-- A coefficient-uniform affine count with exponent 1/d, conditional on the +sharp finite-field estimate and a prime p in [B/A,B] not dividing f(0). -/ +theorem scaled_plane_finite_count_one_over_degree + {F : Type*} [Field F] [CharZero F] [IsAlgClosed F] {t : ℕ} + (f : MvPolynomial (Fin 2) ℤ) (hprim : PrimitiveIntegerPolynomial f) + (hir : Irreducible (MvPolynomial.map (Int.castRingHom F) f)) + (hd : 0 < f.totalDegree) {p : ℕ} (hp : p.Prime) (hp0 : ¬ (p : ℤ) ∣ f.coeff 0) + (z : Fin t → Fin 2 → ℤ) (hinj : Function.Injective z) + {A B C : ℝ} (hzB : IsBoxZeroFamily f B z) (hA : 1 ≤ A) (hpB : (p : ℝ) ≤ B) (hBp : B ≤ A * p) + (hC : 0 ≤ C) + (hcount : PlaneResidueCountBound C f.totalDegree f) : + (t : ℝ) ≤ + (38 + 8 * Real.exp (heightDeterminantError C + 20) + + 2 * Real.exp (heightDeterminantError C + 23) * Real.exp 14 * Real.exp 2 * A) * + (f.totalDegree : ℝ) ^ 3 * B ^ (1 / (f.totalDegree : ℝ)) * + (Real.log B + f.totalDegree) := by + have hB := hzB.one_le_radius + let d := f.totalDegree + let Q := B ^ (1 / (d : ℝ)) + let U := Real.log B + d + let K₁ := 4 + 2 * Real.exp (heightDeterminantError C + 23) * Real.exp 14 * Real.exp 2 * A + let K₂ := 34 + 8 * Real.exp (heightDeterminantError C + 20) + have hdR : (0 : ℝ) < d := by exact_mod_cast hd + have hlog := Real.log_nonneg hB + have hQ : 1 ≤ Q := Real.one_le_rpow hB (by positivity) + have hU : (d : ℝ) ≤ U := by dsimp [U]; linarith + have hK₁ : 0 ≤ K₁ := by dsimp [K₁]; positivity + have hK₂ : 0 ≤ K₂ := by dsimp [K₂]; positivity + have hrest : (0 : ℝ) ≤ (d : ℝ) ^ 3 * Q * U := by dsimp [U]; positivity + have hlarge : K₂ * (d : ℝ) ^ 4 ≤ K₂ * ((d : ℝ) ^ 3 * Q * U) := by + apply mul_le_mul_of_nonneg_left _ hK₂ + have hh : (d : ℝ) ≤ Q * U := by nlinarith + have ht := mul_le_mul_of_nonneg_left hh (pow_nonneg hdR.le 3) + nlinarith only [ht] + have hsmall := scaled_auxiliary_degree_bound_le hd hA hB + (show (1 : ℝ) ≤ p by exact_mod_cast hp.one_lt.le) hpB hBp (C := C) + have hcount' := scaled_plane_finite_count f hprim hir hd hp hp0 z hinj hzB hpB hC hcount + have hb : (t : ℝ) ≤ (K₁ + K₂) * ((d : ℝ) ^ 3 * Q * U) := by + apply hcount'.trans + apply max_le + · have ht : (d : ℝ) * scaledPlaneAuxiliaryDegreeBound d C (((d : ℝ) + 1) * B) p ≤ + K₁ * ((d : ℝ) ^ 3 * Q * U) := by + convert hsmall using 1 + simp only [K₁, Q, U] + ring + exact ht.trans (mul_le_mul_of_nonneg_right (by linarith) hrest) + · exact hlarge.trans (mul_le_mul_of_nonneg_right (by linarith) hrest) + convert hb using 1 + simp only [K₁, K₂, Q, U, d] + ring + +end CCDN diff --git a/LeanPool/CCDN/Plane/ScaledModelCoordinates.lean b/LeanPool/CCDN/Plane/ScaledModelCoordinates.lean new file mode 100644 index 0000000000..34c4dc7357 --- /dev/null +++ b/LeanPool/CCDN/Plane/ScaledModelCoordinates.lean @@ -0,0 +1,108 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.Algebra.HomogeneousTranslation +public import LeanPool.CCDN.Algebra.ScaledHomogenization +public import LeanPool.CCDN.AuxiliaryPolynomial.Nonmultiple +import LeanPool.CCDN.AuxiliaryPolynomial.ScaledAuxiliaryCount + +/-! Polynomial coordinates for a translated scaled model, with exact point +transport and transport of an auxiliary nonmultiple back to the original slice. -/ + +@[expose] public section +namespace CCDN +noncomputable section +open MvPolynomial + +/-- The integer polynomial coordinate change `(h, x₁ - a₁ h, …, xₙ - aₙ h)` from affine +coordinates to the coordinates of the translated scaled projective model. -/ +def scaledModelCoordinates {n : ℕ} (h : ℤ) (a : Fin n → ℤ) : + Fin (n + 1) → MvPolynomial (Fin n) ℤ := + Fin.cons (C h) (fun j => X j - C (a j * h)) + +private lemma scaledModelCoordinates_eval {n : ℕ} (h : ℤ) (a z : Fin n → ℤ) : + (fun j => eval z (scaledModelCoordinates h a j)) = + Fin.cons h (fun j => z j - a j * h) := by + funext i + refine Fin.cases ?_ (fun j => ?_) i <;> simp [scaledModelCoordinates] + +/-- The translated scaled homogenization of `f`, evaluated at the scaled model coordinates of `z`, +equals `h ^ deg f * f(z)`. -/ +lemma scaledModelCoordinates_model_eval {n : ℕ} (h : ℤ) (a z : Fin n → ℤ) + (f : MvPolynomial (Fin n) ℤ) : + eval (fun j => eval z (scaledModelCoordinates h a j)) + (homogeneousTranslation ℤ a (scaledHomogenize f.totalDegree h f)) = + h ^ f.totalDegree * eval z f := by + rw [scaledModelCoordinates_eval, homogeneousTranslation_eval] + simp only [Fin.cons_zero, Fin.cons_succ, sub_add_cancel] + exact scaledHomogenize_eval _ _ le_rfl _ + +/-- For `0 ≤ h ≤ B`, translation parameters `0 ≤ a j ≤ d` and `z` in the box of size `B ≥ 1`, all +scaled model coordinates of `z` have absolute value at most `(d + 1) * B`. -/ +lemma scaledModelCoordinates_box {n : ℕ} {B : ℝ} {d : ℕ} {h : ℤ} + (hB : 1 ≤ B) (hh : 0 ≤ h) (hhB : (h : ℝ) ≤ B) + (a z : Fin n → ℤ) (ha : ∀ j, 0 ≤ a j ∧ a j ≤ d) + (hz : InBox z B) (j : Fin (n + 1)) : + |(eval z (scaledModelCoordinates h a j) : ℝ)| ≤ ((d : ℝ) + 1) * B := by + have hhR : (0 : ℝ) ≤ h := by exact_mod_cast hh + have hdR : (0 : ℝ) ≤ d := by positivity + have he := congrFun (scaledModelCoordinates_eval h a z) j + rw [he] + refine Fin.cases ?_ (fun k => ?_) j + · simp only [Fin.cons_zero, abs_of_nonneg hhR] + nlinarith + · simp only [Fin.cons_succ, Int.cast_sub, Int.cast_mul] + have ha0 : (0 : ℝ) ≤ a k := by exact_mod_cast (ha k).1 + have had : (a k : ℝ) ≤ d := by exact_mod_cast (ha k).2 + have hab : (a k : ℝ) * h ≤ d * B := mul_le_mul had hhB hhR hdR + calc + |(z k : ℝ) - (a k : ℝ) * h| ≤ |(z k : ℝ)| + |(a k : ℝ) * h| := abs_sub _ _ + _ = |(z k : ℝ)| + (a k : ℝ) * h := by rw [abs_of_nonneg (mul_nonneg ha0 hhR)] + _ ≤ ((d : ℝ) + 1) * B := by nlinarith [hz k] + +/-- A homogeneous auxiliary polynomial `g` of degree `M` not divisible by the translated scaled +homogenization of `f` transports to a homogeneous `g'` of degree `M` not divisible by the scaled +homogenization of `f`, with `g'(h, z)` equal to `g` at the scaled model coordinates of `z`. -/ +lemma scaledModelCoordinates_auxiliary_transport {n M : ℕ} + (h : ℤ) (a : Fin n → ℤ) (f : MvPolynomial (Fin n) ℤ) + (g : MvPolynomial (Fin (n + 1)) ℤ) (hg : g.IsHomogeneous M) + (hnd : ¬ homogeneousTranslation ℤ a (scaledHomogenize f.totalDegree h f) ∣ g) : + ∃ g' : MvPolynomial (Fin (n + 1)) ℤ, g'.IsHomogeneous M ∧ + (∀ z, eval (Fin.cons h z) g' = eval (fun j => eval z (scaledModelCoordinates h a j)) g) ∧ + ¬ scaledHomogenize f.totalDegree h f ∣ g' := by + refine ⟨homogeneousTranslation ℤ (-a) g, homogeneousTranslation_homogeneous _ hg, ?_, ?_⟩ + · intro z + rw [homogeneousTranslation_eval, scaledModelCoordinates_eval] + simp only [Fin.cons_zero, Fin.cons_succ, Pi.neg_apply, neg_mul, sub_eq_add_neg] + · intro hdiv + apply hnd + have he := map_dvd (homogeneousTranslation ℤ a).toRingHom hdiv + change homogeneousTranslation ℤ a (scaledHomogenize f.totalDegree h f) ∣ + homogeneousTranslation ℤ a ((homogeneousTranslation ℤ a).symm g) at he + simpa only [AlgEquiv.apply_symm_apply] using he + +/-- A nonmultiple on the translated scaled model counts the original +integer points. The bound uses only the auxiliary degree and original degree. -/ +theorem scaled_positioned_points_card_le {M t : ℕ} + (f : MvPolynomial (Fin 2) ℤ) (hprim : PrimitiveIntegerPolynomial f) + (hir : Irreducible (MvPolynomial.map (Int.castRingHom ℚ) f)) + {p : ℕ} (hp : p.Prime) (hp0 : ¬ (p : ℤ) ∣ f.coeff 0) + (a : Fin 2 → ℤ) (z : Fin t → Fin 2 → ℤ) (hinj : Function.Injective z) + (hz : ∀ i, eval (z i) f = 0) (g : MvPolynomial (Fin 3) ℤ) + (hg : IsPointAuxiliary (homogeneousTranslation ℤ a (scaledHomogenize f.totalDegree (p : ℤ) f)) + g M (fun i j => eval (z i) (scaledModelCoordinates (p : ℤ) a j))) : + t ≤ f.totalDegree * M := by + obtain ⟨g', hg', he, hnd'⟩ := scaledModelCoordinates_auxiliary_transport (p : ℤ) a f g + hg.isHomogeneous hg.not_dvd + exact scaled_plane_points_card_le_auxiliary_degree f hprim hir hp hp0 g' hg' hnd' z hinj hz + (fun i => (he (z i)).trans (hg.eval_eq_zero i)) + +end +end CCDN diff --git a/LeanPool/CCDN/Plane/ScaledModelPosition.lean b/LeanPool/CCDN/Plane/ScaledModelPosition.lean new file mode 100644 index 0000000000..655298afc5 --- /dev/null +++ b/LeanPool/CCDN/Plane/ScaledModelPosition.lean @@ -0,0 +1,162 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.Algebra.ScaledHomogenization +public import LeanPool.CCDN.Plane.LeadingPositionHeight +public import Mathlib.Analysis.SpecialFunctions.Pow.Real +import LeanPool.CCDN.Plane.HomogeneousLeadingCoefficient + +/-! +Positioning a scaled homogeneous model while retaining a coefficient bound +for the original affine equation and the full scaling gain in its height. + +A monotone scaled logarithmic-height ratio. A lower bound for the scaled +model height yields the saving needed to change the box exponent 2/d to 1/d. +-/ + +@[expose] public section +namespace CCDN +open Real + +private lemma log_height_ratio_antitone {u v : ℝ} (hu : 0 ≤ u) (huv : u ≤ v) : + (1 + v) / Real.exp v ≤ (1 + u) / Real.exp u := by + have he : 1 + (v - u) ≤ Real.exp (v - u) := by + simpa only [add_comm] using Real.add_one_le_exp (v - u) + have hh : 1 + v ≤ (1 + u) * Real.exp (v - u) := by + have hmul := mul_le_mul_of_nonneg_left he (show 0 ≤ 1 + u by linarith) + nlinarith [mul_nonneg hu (sub_nonneg.mpr huv)] + apply (div_le_div_iff₀ (Real.exp_pos _) (Real.exp_pos _)).mpr + have hp := mul_le_mul_of_nonneg_right hh (Real.exp_pos u).le + rw [mul_assoc, ← Real.exp_add, sub_add_cancel] at hp + exact hp + +/-- Eliminate an upper coefficient height in favor of any lower model height, +retaining the exact exponent 1/d² and the position loss exp(T). -/ +private theorem positioned_height_ratio_le {d H L Y T : ℝ} + (hd : 0 < d) (hY : 1 ≤ Y) (hYH : Y ≤ H) (hL : 0 < L) + (hposition : Real.log H ≤ Real.log L + T * d ^ 2) : + (1 + Real.log H / d ^ 2) / L ^ (1 / d ^ 2) ≤ + Real.exp T * ((1 + Real.log Y / d ^ 2) / Y ^ (1 / d ^ 2)) := by + have hY0 : 0 < Y := by linarith + have hH0 : 0 < H := by linarith + have hds : 0 < d ^ 2 := by positivity + have hu : 0 ≤ Real.log Y / d ^ 2 := div_nonneg (Real.log_nonneg hY) hds.le + have huv : Real.log Y / d ^ 2 ≤ Real.log H / d ^ 2 := + div_le_div_of_nonneg_right (Real.log_le_log hY0 hYH) hds.le + have hmono := log_height_ratio_antitone hu huv + have hpos : Real.log H / d ^ 2 ≤ Real.log L / d ^ 2 + T := by + apply (div_le_iff₀ hds).mpr + field_simp + nlinarith only [hposition] + have hexp : Real.exp (Real.log H / d ^ 2) ≤ + Real.exp T * Real.exp (Real.log L / d ^ 2) := by + rw [← Real.exp_add] + apply Real.exp_le_exp.mpr + linarith + have hnum : 0 ≤ 1 + Real.log H / d ^ 2 := by linarith + have hstep : (1 + Real.log H / d ^ 2) / Real.exp (Real.log L / d ^ 2) ≤ + Real.exp T * ((1 + Real.log H / d ^ 2) / Real.exp (Real.log H / d ^ 2)) := by + apply (div_le_iff₀ (Real.exp_pos _)).mpr + rw [← mul_div_assoc, div_mul_eq_mul_div] + apply (le_div_iff₀ (Real.exp_pos _)).mpr + nlinarith [mul_le_mul_of_nonneg_left hexp hnum] + have hh := hstep.trans (mul_le_mul_of_nonneg_left hmono (Real.exp_pos T).le) + rw [Real.rpow_def_of_pos hL, Real.rpow_def_of_pos hY0] + simpa only [mul_one_div] using hh + +end CCDN + +namespace CCDN +noncomputable section +open MvPolynomial Real + +private lemma leadingPositionHeightFactor_interpolation_le (d : ℕ) : + ((d : ℝ) + 1) ^ (2 * (d + 1)) ≤ leadingPositionHeightFactor d := by + have hd0 : (0 : ℝ) ≤ d := by positivity + have hb : (1 : ℝ) ≤ d + 1 := by linarith + have h₁ : 1 ≤ ((d : ℝ) + 2) ^ 2 := one_le_pow₀ (by linarith) + have h₂ : 1 ≤ (2 * (d : ℝ) + 1) ^ d := one_le_pow₀ (by linarith) + have hp := pow_le_pow_right₀ hb (show 2 * (d + 1) ≤ 4 * (d + 1) by omega) + unfold leadingPositionHeightFactor + nlinarith [show 0 ≤ ((d : ℝ) + 1) ^ (4 * (d + 1)) by positivity, + mul_le_mul_of_nonneg_left h₁ (show 0 ≤ ((d : ℝ) + 1) ^ (4 * (d + 1)) by positivity)] + +/-- The chosen height bounds both the original homogeneous coefficients and +the translated chart coefficients, with the same absolute position loss. -/ +private theorem exists_translated_model_position {d : ℕ} (hd : 0 < d) + (f : MvPolynomial (Fin 3) ℤ) (hf : f.IsHomogeneous d) (hf0 : f ≠ 0) : + ∃ (a : Fin 2 → ℤ) (H : ℝ), (∀ j, 0 ≤ a j ∧ a j ≤ d) ∧ 1 ≤ H ∧ + CoefficientHeightLE f H ∧ + CoefficientHeightLE (affineChart 2 (homogeneousTranslation ℤ a f)) H ∧ + Real.log |((MonomialOrder.lex.leadingCoeff (homogeneousTranslation ℤ a f) : ℤ) : ℝ)| ≤ + Real.log H ∧ + Real.log H ≤ + Real.log |((MonomialOrder.lex.leadingCoeff (homogeneousTranslation ℤ a f) : ℤ) : ℝ)| + + 14 * (d : ℝ) ^ 2 := by + obtain ⟨a, ha, hne, hcoeff⟩ := exists_bounded_leading_coefficient_translation f hf hf0 + let L := |((MonomialOrder.lex.leadingCoeff (homogeneousTranslation ℤ a f) : ℤ) : ℝ)| + have hL : 1 ≤ L := by + dsimp only [L] + exact_mod_cast Int.one_le_abs hne + have hP := leadingPositionHeightFactor_one_le d + have hpos : 0 < leadingPositionHeightFactor d := by linarith + have hLpos : 0 < L := by linarith + refine ⟨a, leadingPositionHeightFactor d * L, ha, one_le_mul_of_one_le_of_one_le hP hL, + ?_, homogeneousTranslation_chart_coefficient_bound f hf a ha hLpos.le hcoeff, ?_, ?_⟩ + · intro m + exact (hcoeff m).trans (mul_le_mul_of_nonneg_right + (leadingPositionHeightFactor_interpolation_le d) hLpos.le) + · apply Real.log_le_log hLpos + nlinarith + · rw [Real.log_mul hpos.ne' hLpos.ne'] + have h := leadingPositionHeightFactor_log_le hd + linarith + +/-- The model height is at least h^d and also bounds every original affine +coefficient, even after leading-coefficient positioning of the scaled model. -/ +theorem exists_scaled_model_position (f : MvPolynomial (Fin 2) ℤ) + (hf0 : f ≠ 0) (hd : 0 < f.totalDegree) {h : ℤ} (hh : 1 ≤ h) : + ∃ (a : Fin 2 → ℤ) (H : ℝ), (∀ j, 0 ≤ a j ∧ a j ≤ f.totalDegree) ∧ + 1 ≤ H ∧ (h : ℝ) ^ f.totalDegree ≤ H ∧ + CoefficientHeightLE f H ∧ + Real.log |((MonomialOrder.lex.leadingCoeff + (homogeneousTranslation ℤ a (scaledHomogenize f.totalDegree h f)) : ℤ) : ℝ)| ≤ Real.log H ∧ + Real.log H ≤ Real.log |((MonomialOrder.lex.leadingCoeff + (homogeneousTranslation ℤ a (scaledHomogenize f.totalDegree h f)) : ℤ) : ℝ)| + + 14 * (f.totalDegree : ℝ) ^ 2 := by + have hh0 : h ≠ 0 := by omega + obtain ⟨a, H, ha, hH, hc, _, hlo, hhi⟩ := exists_translated_model_position hd + (scaledHomogenize f.totalDegree h f) (scaledHomogenize_homogeneous _ _ le_rfl) + (scaledHomogenize_ne_zero _ hh0 hf0) + obtain ⟨m, _, hm⟩ := exists_scaledHomogenize_top_coefficient f hf0 hh + exact ⟨a, H, ha, hH, hm.trans (hc _), + fun m => (scaledHomogenize_integer_coefficient_lower _ hh f m).trans (hc _), hlo, hhi⟩ + +/-- The exact height saving obtained from a scaling factor h. -/ +theorem scaled_positioned_height_ratio_le {d : ℕ} (hd : 0 < d) {h H L : ℝ} + (hh : 1 ≤ h) (hH : h ^ d ≤ H) (hL : 0 < L) + (hposition : Real.log H ≤ Real.log L + 14 * (d : ℝ) ^ 2) : + (1 + Real.log H / (d : ℝ) ^ 2) / L ^ (1 / (d : ℝ) ^ 2) ≤ + Real.exp 14 * ((1 + Real.log h / d) / h ^ (1 / (d : ℝ))) := by + have hdR : (0 : ℝ) < d := by exact_mod_cast hd + have hh0 : 0 < h := by linarith + have hp : (h ^ d) ^ (1 / (d : ℝ) ^ 2) = h ^ (1 / (d : ℝ)) := by + rw [← Real.rpow_natCast, ← Real.rpow_mul hh0.le] + congr 1 + field_simp + have he : Real.log (h ^ d) / (d : ℝ) ^ 2 = Real.log h / d := by + rw [Real.log_pow] + field_simp + have ht := positioned_height_ratio_le hdR (one_le_pow₀ hh) hH hL hposition + rw [hp, he] at ht + exact ht + +end +end CCDN diff --git a/LeanPool/CCDN/Plane/ScaledPlaneCount.lean b/LeanPool/CCDN/Plane/ScaledPlaneCount.lean new file mode 100644 index 0000000000..0ae2d5c1fc --- /dev/null +++ b/LeanPool/CCDN/Plane/ScaledPlaneCount.lean @@ -0,0 +1,146 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.Algebra.HomogeneousTranslation +public import LeanPool.CCDN.AuxiliaryPolynomial.Nonmultiple +public import LeanPool.CCDN.Determinant.HeightDeterminant +public import LeanPool.CCDN.Height.CountingHypotheses +public import LeanPool.CCDN.Height.PrimitiveIntegerPolynomial +public import LeanPool.CCDN.Plane.ScaledModelCoordinates +import LeanPool.CCDN.AuxiliaryPolynomial.PlaneAuxiliaryCount +import LeanPool.CCDN.AuxiliaryPolynomial.PolynomialMapAuxiliary +import LeanPool.CCDN.Plane.ScaledModelPosition + +/-! A prime-scaled plane point bound with the arbitrary coefficient +height eliminated. The sharp finite-field count and a suitable scaling prime +remain explicit hypotheses. -/ + +@[expose] public section +namespace CCDN +noncomputable section +open MvPolynomial Real + +/-- The auxiliary degree bound for a plane equation of degree `d` after translation into a box +of radius `R` and scaling by `p`, with residue constant `C`. The constants `23` and `exp 14` are +explained in `projectivePlaneAbsoluteConstant`. -/ +def scaledPlaneAuxiliaryDegreeBound (d : ℕ) (C R p : ℝ) : ℝ := + d + Real.log R + 2 * Real.exp (heightDeterminantError C + 23) * Real.exp 14 * + (d : ℝ) ^ 3 * (1 + Real.log p / d) * R ^ (2 / (d : ℝ)) / p ^ (1 / (d : ℝ)) + 2 + +/-- Coefficient positioning gives both count bounds for any positive integer scale. +The callback supplies the proper-section estimate for that scale. -/ +theorem scaled_model_plane_count_bounds + {F : Type*} [Field F] [CharZero F] [IsAlgClosed F] {t : ℕ} + (f : MvPolynomial (Fin 2) ℤ) + (hir : Irreducible (MvPolynomial.map (Int.castRingHom F) f)) + (hd : 0 < f.totalDegree) (scale : ℤ) (hscale : 1 ≤ scale) + (z : Fin t → Fin 2 → ℤ) {B C : ℝ} (hzB : IsBoxZeroFamily f B z) + (hscaleB : (scale : ℝ) ≤ B) (hC : 0 ≤ C) + (hcount : PlaneResidueCountBound C f.totalDegree f) + (hsection : ∀ (a : Fin 2 → ℤ) (M : ℕ) (g : MvPolynomial (Fin 3) ℤ), + IsPointAuxiliary (homogeneousTranslation ℤ a (scaledHomogenize f.totalDegree scale f)) + g M (fun i j => eval (z i) (scaledModelCoordinates scale a j)) → + t ≤ f.totalDegree * M) : + (t : ℝ) ≤ max + ((f.totalDegree : ℝ) * scaledPlaneAuxiliaryDegreeBound f.totalDegree C + (((f.totalDegree : ℝ) + 1) * B) scale) + ((34 + 8 * Real.exp (heightDeterminantError C + 20)) * (f.totalDegree : ℝ) ^ 4) ∧ + ∀ m, f.coeff m ≠ 0 → + 4 * (f.totalDegree : ℝ) * Real.log (((f.totalDegree : ℝ) + 1) * B) ≤ + Real.log |(f.coeff m : ℝ)| → + (t : ℝ) ≤ (34 + 8 * Real.exp (heightDeterminantError C + 20)) * + (f.totalDegree : ℝ) ^ 4 := by + obtain ⟨hB, hz, hbox⟩ := hzB + let d := f.totalDegree + let R := ((d : ℝ) + 1) * B + have hdR : (0 : ℝ) < d := by exact_mod_cast hd + have hR : 1 ≤ R := one_le_mul_of_one_le_of_one_le (by linarith) hB + have hpR : (1 : ℝ) ≤ scale := by exact_mod_cast hscale + have hf0 : f ≠ 0 := fun h => hir.ne_zero (by simp [h]) + obtain ⟨a, H, ha, hH, hpH, hc, hlo, hhi⟩ := exists_scaled_model_position f hf0 hd hscale + let model := homogeneousTranslation ℤ a (scaledHomogenize d scale f) + have hmhom : model.IsHomogeneous d := + homogeneousTranslation_homogeneous a (scaledHomogenize_homogeneous _ _ le_rfl) + have hm0 : model ≠ 0 := by + have he := (homogeneousTranslation ℤ a).injective.ne (scaledHomogenize_ne_zero d + (show scale ≠ 0 by omega) hf0) + simpa only [map_zero] using he + let τ := scaledModelCoordinates scale a + have hmodel : ∀ i, eval (fun j => eval (z i) (τ j)) model = 0 := by + intro i + change eval (fun j => eval (z i) (scaledModelCoordinates scale a j)) + (homogeneousTranslation ℤ a (scaledHomogenize f.totalDegree scale f)) = 0 + rw [scaledModelCoordinates_model_eval, hz i, mul_zero] + have himagebox : ∀ i j, |(eval (z i) (τ j) : ℝ)| ≤ R := by + intro i j + exact scaledModelCoordinates_box hB (by positivity) hscaleB a (z i) ha + (hbox i) j + have hlarge (hlarge : 4 * d * Real.log R ≤ Real.log H) : + (t : ℝ) ≤ (34 + 8 * Real.exp (heightDeterminantError C + 20)) * (d : ℝ) ^ 4 := by + obtain ⟨M, hM, g, hg⟩ := polynomial_map_auxiliary_large_height_degree + MonomialOrder.lex model hmhom hm0 hd ⟨rfl, hir, hH, hc⟩ τ z hz ⟨hR, hmodel, himagebox⟩ hC + 14 (by norm_num) hlarge hhi hcount + have ht := hsection a M g hg + have htR : (t : ℝ) ≤ d * (M : ℝ) := by exact_mod_cast ht + have ht' := htR.trans (mul_le_mul_of_nonneg_left hM hdR.le) + rw [show heightDeterminantError C + 6 + 14 = heightDeterminantError C + 20 by ring] at ht' + convert ht' using 1 + ring + constructor + · by_cases hsmall : Real.log H ≤ 4 * d * Real.log R + · obtain ⟨M, hM, g, hg⟩ := polynomial_map_auxiliary_small_height_degree + MonomialOrder.lex model hmhom hm0 hd ⟨rfl, hir, hH, hc⟩ τ z hz ⟨hR, hmodel, himagebox⟩ hC + (hlo.trans hsmall) hcount + have ht := hsection a M g hg + have htR : (t : ℝ) ≤ d * (M : ℝ) := by exact_mod_cast ht + have hL : 0 < |((MonomialOrder.lex.leadingCoeff model : ℤ) : ℝ)| := by + exact abs_pos.mpr (Int.cast_ne_zero.mpr + (MonomialOrder.lex.leadingCoeff_ne_zero_iff.mpr hm0)) + have hratio := scaled_positioned_height_ratio_le hd hpR hpH hL hhi + have hterm : 2 * Real.exp (heightDeterminantError C + 23) * (d : ℝ) ^ 3 * + (1 + Real.log H / (d : ℝ) ^ 2) * R ^ (2 / (d : ℝ)) / + |((MonomialOrder.lex.leadingCoeff model : ℤ) : ℝ)| ^ (1 / (d : ℝ) ^ 2) ≤ + 2 * Real.exp (heightDeterminantError C + 23) * Real.exp 14 * (d : ℝ) ^ 3 * + (1 + Real.log (scale : ℝ) / d) * R ^ (2 / (d : ℝ)) / (scale : ℝ) ^ (1 / (d : ℝ)) := by + let A := 2 * Real.exp (heightDeterminantError C + 23) * (d : ℝ) ^ 3 * R ^ (2 / (d : ℝ)) + have hA : 0 ≤ A := by dsimp [A]; positivity + convert mul_le_mul_of_nonneg_left hratio hA using 1 <;> dsimp [A] <;> ring + have hMb : (M : ℝ) ≤ scaledPlaneAuxiliaryDegreeBound d C R scale := by + unfold scaledPlaneAuxiliaryDegreeBound + linarith + exact (htR.trans (mul_le_mul_of_nonneg_left hMb hdR.le)).trans (le_max_left _ _) + · exact (hlarge (le_of_lt (lt_of_not_ge hsmall))).trans (le_max_right _ _) + · intro m hm hheight + exact hlarge (hheight.trans + (Real.log_le_log (abs_pos.mpr (Int.cast_ne_zero.mpr hm)) (hc m))) + +/-- Count original integer plane points using the translated scaled +model. The determinant estimate is applied to polynomial columns on the +original curve, so its sharp residue-count hypothesis concerns that curve. -/ +theorem scaled_plane_finite_count + {F : Type*} [Field F] [CharZero F] [IsAlgClosed F] {t : ℕ} + (f : MvPolynomial (Fin 2) ℤ) (hprim : PrimitiveIntegerPolynomial f) + (hir : Irreducible (MvPolynomial.map (Int.castRingHom F) f)) + (hd : 0 < f.totalDegree) {p : ℕ} (hp : p.Prime) (hp0 : ¬ (p : ℤ) ∣ f.coeff 0) + (z : Fin t → Fin 2 → ℤ) (hinj : Function.Injective z) + {B C : ℝ} (hzB : IsBoxZeroFamily f B z) (hpB : (p : ℝ) ≤ B) (hC : 0 ≤ C) + (hcount : PlaneResidueCountBound C f.totalDegree f) : + (t : ℝ) ≤ max + ((f.totalDegree : ℝ) * scaledPlaneAuxiliaryDegreeBound f.totalDegree C + (((f.totalDegree : ℝ) + 1) * B) p) + ((34 + 8 * Real.exp (heightDeterminantError C + 20)) * (f.totalDegree : ℝ) ^ 4) := by + simpa only [Int.cast_natCast] using + (scaled_model_plane_count_bounds f hir hd (p : ℤ) (by exact_mod_cast hp.one_lt.le) + z hzB (by exact_mod_cast hpB) hC hcount (fun a M g hg => + scaled_positioned_points_card_le f hprim (rational_irreducible_of_charZero_map f hir) + hp hp0 a z hinj hzB.eval_eq_zero g hg)).1 + +end +end CCDN diff --git a/LeanPool/CCDN/Plane/SectionBounds.lean b/LeanPool/CCDN/Plane/SectionBounds.lean new file mode 100644 index 0000000000..184f54c8e2 --- /dev/null +++ b/LeanPool/CCDN/Plane/SectionBounds.lean @@ -0,0 +1,120 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.Plane.FiniteFieldConstants +public import LeanPool.CCDN.Plane.ParameterIncidence +public import Mathlib.Algebra.Field.ZMod +public import Mathlib.Analysis.Real.Sqrt +import LeanPool.CCDN.AuxiliaryPolynomial.PlaneBadPrimes +import LeanPool.CCDN.Noether.ShearNormalization +import LeanPool.CCDN.Plane.CurveCount +import LeanPool.CCDN.Plane.FiniteFieldZeroPlanes + +/-! Section-point bounds in the good, nonzero and unrestricted cases. +The good-section bound uses the proved Stepanov theorem over the prime field. -/ + +@[expose] public section +namespace CCDN +noncomputable section +open MvPolynomial + +/-- Evaluating the nested-polynomial form of a bivariate polynomial `f` at `(t 0, t 1)` agrees +with evaluating `f` at `t`. -/ +lemma bivariateEval_equiv {K : Type*} [CommRing K] + (f : MvPolynomial (Fin 2) K) (t : Fin 2 → K) : + bivariateEval (t 0) (t 1) (bivariatePolynomialEquiv K f) = eval t f := by + have he : (bivariateEval (t 0) (t 1)).comp (bivariatePolynomialEquiv K).toRingHom = eval t := by + apply MvPolynomial.ringHom_ext + · intro r + simp [bivariateEval, bivariatePolynomialEquiv_C] + · intro i + fin_cases i <;> simp [bivariateEval, + bivariatePolynomialEquiv_X_zero, bivariatePolynomialEquiv_X_one] + exact RingHom.congr_fun he f + +/-- The plane section `universalPlaneSection f v` as a polynomial in two variables +(`MvPolynomial (Fin 2)`), the presentation used by point counting. -/ +def planeSectionPolynomial {K σ : Type*} [CommRing K] + (f : MvPolynomial σ K) (v : PlaneParameters σ → K) : MvPolynomial (Fin 2) K := + (bivariatePolynomialEquiv K).symm (universalPlaneSection f v) + +/-- A plane section of `f` has total degree at most that of `f`. -/ +lemma planeSectionPolynomial_totalDegree {K σ : Type*} [CommRing K] + (f : MvPolynomial σ K) (v : PlaneParameters σ → K) : + (planeSectionPolynomial f v).totalDegree ≤ f.totalDegree := + universalPlanePolynomial_specialization_totalDegree f (eval v) + +private lemma planeSectionPoints_card_eq {K σ : Type*} [CommRing K] + (f : MvPolynomial σ K) (v : PlaneParameters σ → K) : + Nat.card (PlaneSectionPoints f v) = + Nat.card {t : Fin 2 → K // eval t (planeSectionPolynomial f v) = 0} := by + have he (t : Fin 2 → K) : eval t (planeSectionPolynomial f v) = + bivariateEval (t 0) (t 1) (universalPlaneSection f v) := by + rw [← bivariateEval_equiv, planeSectionPolynomial, AlgEquiv.apply_symm_apply] + apply Nat.card_congr + exact { toFun := fun t => ⟨t.val, by rw [he]; exact t.property⟩ + invFun := fun t => ⟨t.val, by rw [← he]; exact t.property⟩ + left_inv := fun _ => rfl + right_inv := fun _ => rfl } + +/-- The number of points of a plane section over a finite ring `K` is at most `|K| ^ 2`. -/ +lemma planeSectionPoints_card_le_square {K σ : Type*} [CommRing K] [Fintype K] + (f : MvPolynomial σ K) (v : PlaneParameters σ → K) : + Nat.card (PlaneSectionPoints f v) ≤ Fintype.card K ^ 2 := by + have h := Nat.card_le_card_of_injective (fun t : PlaneSectionPoints f v => t.val) + Subtype.val_injective + simpa only [Nat.card_eq_fintype_card, Fintype.card_fun, Fintype.card_fin] using h + +/-- A nonzero plane section of `f` over a finite field `K` has at most `deg f * |K|` points. -/ +lemma planeSectionPoints_card_le_degree_mul_card {K σ : Type*} [Field K] [Fintype K] + (f : MvPolynomial σ K) (v : PlaneParameters σ → K) + (hv : universalPlaneSection f v ≠ 0) : + Nat.card (PlaneSectionPoints f v) ≤ f.totalDegree * Fintype.card K := by + have hg : planeSectionPolynomial f v ≠ 0 := by + intro hz + apply hv + have h := congrArg (bivariatePolynomialEquiv K) hz + simpa only [planeSectionPolynomial, AlgEquiv.apply_symm_apply, map_zero] using h + rw [planeSectionPoints_card_eq] + have h := finite_field_polynomial_zeros_card (planeSectionPolynomial f v) hg + simp only [Fintype.card_fin, Nat.reduceSub, pow_one] at h + exact h.trans (Nat.mul_le_mul_right _ (planeSectionPolynomial_totalDegree f v)) + +/-- A plane section that is absolutely irreducible over `𝔽_p`, for `p > 27 d⁴`, has at most +`p + 161 d² √p` points. -/ +lemma planeSectionPoints_card_le_add_mul_sqrt {σ : Type*} + (p : ℕ) [Fact p.Prime] (f : MvPolynomial σ (ZMod p)) + (v : PlaneParameters σ → ZMod p) + (hir : Irreducible (bivariateMap (algebraMap (ZMod p) (AlgebraicClosure (ZMod p))) + (universalPlaneSection f v))) + (hsize : stepanovThreshold (f.totalDegree : ℝ) < p) : + (Nat.card (PlaneSectionPoints f v) : ℝ) ≤ + p + planeResidueConstant * (f.totalDegree : ℝ) ^ 2 * Real.sqrt p := by + classical + let g := planeSectionPolynomial f v + have hg : Irreducible g := by + apply mvPolynomial_irreducible_of_field_map (algebraMap (ZMod p) (AlgebraicClosure (ZMod p))) + apply (MulEquiv.irreducible_iff (f := bivariatePolynomialEquiv (AlgebraicClosure (ZMod p)))).mp + rw [← bivariatePolynomialEquiv_map] + simpa only [g, planeSectionPolynomial, AlgEquiv.apply_symm_apply] using hir + have hd : g.totalDegree ≤ f.totalDegree := planeSectionPolynomial_totalDegree f v + have hs : stepanovThreshold (g.totalDegree : ℝ) < p := by + apply lt_of_le_of_lt _ hsize + gcongr + let S := {t : Fin 2 → ZMod p // eval t g = 0} + have h := plane_finite_field_card_bound p g hg hs (fun t : S => t.val) + Subtype.val_injective (fun t => t.property) + rw [planeSectionPoints_card_eq, Nat.card_eq_fintype_card] + apply h.trans + gcongr + exact planeResidueConstant_pos.le + +end +end CCDN diff --git a/LeanPool/CCDN/Plane/SectionSum.lean b/LeanPool/CCDN/Plane/SectionSum.lean new file mode 100644 index 0000000000..df3a5bf8f5 --- /dev/null +++ b/LeanPool/CCDN/Plane/SectionSum.lean @@ -0,0 +1,89 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.Plane.FiniteFieldConstants +public import LeanPool.CCDN.Plane.FiniteFieldZeroPlanes +public import Mathlib.Algebra.Field.ZMod +public import Mathlib.Analysis.Real.Sqrt +import LeanPool.CCDN.Plane.SectionBounds + +/-! Sum the sharp good-section bound and the separately controlled bad and +zero contributions over the full plane parameter space. -/ + +@[expose] public section +namespace CCDN +noncomputable section +open MvPolynomial + +/-- The exact incidence identity with each section bounded according to its +equation. -/ +theorem plane_sections_incidence_bound {σ : Type*} [Fintype σ] + (p : ℕ) [Fact p.Prime] (f : MvPolynomial σ (ZMod p)) + (hsize : stepanovThreshold (f.totalDegree : ℝ) < p) : + (Nat.card {z : σ → ZMod p // eval z f = 0} : ℝ) * + (p : ℝ) ^ (2 * Fintype.card σ + 2) ≤ + (p : ℝ) ^ (3 * Fintype.card σ) * + (p + planeResidueConstant * (f.totalDegree : ℝ) ^ 2 * Real.sqrt p) + + (Nat.card (BadPlaneParameters f) : ℝ) * (f.totalDegree * p) + + (Nat.card (ZeroPlaneParameters f) : ℝ) * (p : ℝ) ^ 2 := by + classical + let V := PlaneParameters σ → ZMod p + let B : V → Prop := fun v => + bivariateTotalDegree (universalPlaneSection f v) ≠ f.totalDegree ∨ + ¬Irreducible (bivariateMap (algebraMap (ZMod p) (AlgebraicClosure (ZMod p))) + (universalPlaneSection f v)) + let Z : V → Prop := fun v => universalPlaneSection f v = 0 + let A : ℝ := p + planeResidueConstant * (f.totalDegree : ℝ) ^ 2 * Real.sqrt p + have hA : 0 ≤ A := by + have := planeResidueConstant_pos + dsimp [A] + positivity + have hpoint (v : V) : (Nat.card (PlaneSectionPoints f v) : ℝ) ≤ + A + (if B v then (f.totalDegree : ℝ) * p else 0) + + (if Z v then (p : ℝ) ^ 2 else 0) := by + by_cases hz : Z v + · have hh : (Nat.card (PlaneSectionPoints f v) : ℝ) ≤ (p : ℝ) ^ 2 := by + exact_mod_cast (by simpa only [ZMod.card] using planeSectionPoints_card_le_square f v) + rw [ite_eq_left hz] + have hb : (0 : ℝ) ≤ if B v then (f.totalDegree : ℝ) * p else 0 := by split_ifs <;> positivity + linarith + · rw [ite_eq_right hz, add_zero] + by_cases hb : B v + · rw [ite_eq_left hb] + have hh : (Nat.card (PlaneSectionPoints f v) : ℝ) ≤ (f.totalDegree : ℝ) * p := by + exact_mod_cast (by simpa only [ZMod.card] using + planeSectionPoints_card_le_degree_mul_card f v hz) + linarith + · rw [ite_eq_right hb, add_zero] + apply planeSectionPoints_card_le_add_mul_sqrt p f v _ hsize + exact not_not.mp (fun hh => hb (Or.inr hh)) + have hsum := Finset.sum_le_sum (fun v (_ : v ∈ (Finset.univ : Finset V)) => hpoint v) + have hi : (Nat.card {z : σ → ZMod p // eval z f = 0} : ℝ) * + (p : ℝ) ^ (2 * Fintype.card σ + 2) = + ∑ v : V, (Nat.card (PlaneSectionPoints f v) : ℝ) := by + have hh := sum_planeSectionPoints_card f + rw [ZMod.card] at hh + exact_mod_cast hh.symm + have hB : (∑ v : V, if B v then (f.totalDegree : ℝ) * p else 0) = + (Nat.card (BadPlaneParameters f) : ℝ) * ((f.totalDegree : ℝ) * p) := by + rw [← Finset.sum_filter B (fun _ : V => (f.totalDegree : ℝ) * p)] + simp [V, B, BadPlaneParameters, Nat.card_eq_fintype_card, Fintype.card_subtype] + have hZ : (∑ v : V, if Z v then (p : ℝ) ^ 2 else 0) = + (Nat.card (ZeroPlaneParameters f) : ℝ) * (p : ℝ) ^ 2 := by + rw [← Finset.sum_filter Z (fun _ : V => (p : ℝ) ^ 2)] + simp [V, Z, ZeroPlaneParameters, Nat.card_eq_fintype_card, Fintype.card_subtype] + simp only [Finset.sum_add_distrib, hB, hZ, Finset.sum_const, Finset.card_univ, + nsmul_eq_mul] at hsum + rw [hi] + convert hsum using 1 + simp [A, V, PlaneParameters, Nat.mul_comm] + +end +end CCDN diff --git a/LeanPool/CCDN/Plane/ShearHeight.lean b/LeanPool/CCDN/Plane/ShearHeight.lean new file mode 100644 index 0000000000..6709ec1f69 --- /dev/null +++ b/LeanPool/CCDN/Plane/ShearHeight.lean @@ -0,0 +1,145 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.Noether.ShearNormalization + +/-! Explicit coefficient-height bounds for the bounded integral shear. -/ + +@[expose] public section +namespace CCDN +noncomputable section +open Polynomial + +private lemma bivariateShear_outerX {R : Type*} [CommRing R] (a : R) : + bivariateShear R a (X : R[X][X]) = X := by + rw [← bivariatePolynomialEquiv_X_zero, bivariateShear_equiv] + change bivariatePolynomialEquiv R (MvPolynomial.aeval _ (MvPolynomial.X 0)) = _ + simp only [MvPolynomial.aeval_X, Matrix.cons_val_zero, bivariatePolynomialEquiv_X_zero] + +private lemma bivariateShear_innerX {R : Type*} [CommRing R] (a : R) : + bivariateShear R a (C X : R[X][X]) = C X + C (C a) * X := by + rw [← bivariatePolynomialEquiv_X_one, bivariateShear_equiv] + change bivariatePolynomialEquiv R (MvPolynomial.aeval _ (MvPolynomial.X 1)) = _ + simp only [MvPolynomial.aeval_X, Matrix.cons_val_one, Matrix.cons_val_fin_one, map_add, map_mul, + bivariatePolynomialEquiv_X_one, bivariatePolynomialEquiv_X_zero, bivariatePolynomialEquiv_C] + +private lemma bivariateShear_constant {R : Type*} [CommRing R] (a c : R) : + bivariateShear R a (C (C c)) = C (C c) := + (bivariateShear R a).commutes c + +private lemma bivariateShear_monomial {R : Type*} [CommRing R] + (a c : R) (i j : ℕ) : + bivariateShear R a (monomial i (monomial j c)) = + monomial i (monomial 0 c) * (C X + C (C a) * X) ^ j := by + have he : monomial i (monomial j c) = C (C c) * (X : R[X][X]) ^ i * (C X) ^ j := by + rw [← C_mul_X_pow_eq_monomial, ← C_mul_X_pow_eq_monomial] + simp only [map_mul, map_pow] + ring + rw [he, map_mul, map_mul, map_pow, map_pow, bivariateShear_outerX, + bivariateShear_innerX, bivariateShear_constant] + simp only [← C_mul_X_pow_eq_monomial, pow_zero, mul_one] + +private lemma bivariateCoefficientBound_add {f g : ℤ[X][X]} {H K : ℝ} + (hf : BivariateCoefficientBound f H) (hg : BivariateCoefficientBound g K) : + BivariateCoefficientBound (f + g) (H + K) := by + intro i j + simp only [coeff_add, Int.cast_add] + exact (abs_add_le _ _).trans (add_le_add (hf i j) (hg i j)) + +private lemma bivariateCoefficientBound_shear_linear_mul (a : ℤ) + {f : ℤ[X][X]} {H : ℝ} (hH : 0 ≤ H) (hf : BivariateCoefficientBound f H) : + BivariateCoefficientBound ((C X + C (C a) * X) * f) ((1 + |(a : ℝ)|) * H) := by + have h₁ := bivariateCoefficientBound_monomial_mul hH hf 1 0 1 + have h₂ := bivariateCoefficientBound_monomial_mul hH hf a 1 0 + have hh := bivariateCoefficientBound_add h₁ h₂ + have he : monomial 0 (monomial 1 (1 : ℤ)) * f + monomial 1 (monomial 0 a) * f = + (C X + C (C a) * X) * f := by + simp only [← C_mul_X_pow_eq_monomial, pow_zero, pow_one, map_one, one_mul, mul_one] + ring + rw [he] at hh + convert hh using 1 + simp only [Int.cast_one, abs_one, one_mul] + ring + +private lemma bivariateCoefficientBound_shear_linear_pow (a : ℤ) (n : ℕ) : + BivariateCoefficientBound ((C X + C (C a) * X : ℤ[X][X]) ^ n) + ((1 + |(a : ℝ)|) ^ n) := by + induction n with + | zero => + simp only [pow_zero] + intro i j + by_cases hi : i = 0 <;> by_cases hj : j = 0 <;> simp [coeff_one, hi, hj] + | succ n ih => + rw [pow_succ', pow_succ'] + exact bivariateCoefficientBound_shear_linear_mul a (by positivity) ih + +/-- A sheared monomial has coefficient height at most |c|*(1+|a|)^j. -/ +private lemma bivariateCoefficientBound_shear_monomial (a c : ℤ) (i j : ℕ) : + BivariateCoefficientBound (bivariateShear ℤ a (monomial i (monomial j c))) + (|(c : ℝ)| * (1 + |(a : ℝ)|) ^ j) := by + rw [bivariateShear_monomial] + exact bivariateCoefficientBound_monomial_mul (by positivity) + (bivariateCoefficientBound_shear_linear_pow a j) c i 0 + +private lemma bivariateCoefficientBound_sum {ι : Type*} [Fintype ι] + (f : ι → ℤ[X][X]) (H : ℝ) (hf : ∀ i, BivariateCoefficientBound (f i) H) : + BivariateCoefficientBound (∑ i, f i) ((Fintype.card ι : ℝ) * H) := by + intro i j + simp only [finsetSum_coeff, Int.cast_sum] + calc + _ ≤ ∑ k : ι, |(((f k).coeff i).coeff j : ℝ)| := Finset.abs_sum_le_sum_abs _ _ + _ ≤ ∑ _k : ι, H := Finset.sum_le_sum (fun k _ => hf k i j) + _ = _ := by simp + +/-- A shear costs at most (d+1)²*(1+|a|)^d in coefficient height. The proof +uses the coefficient grid and the two-term multiplication recurrence. -/ +private theorem bivariateShear_coefficient_bound (a : ℤ) (d : ℕ) (f : ℤ[X][X]) + (hd : bivariateTotalDegree f ≤ d) (H : ℝ) (hH : 0 ≤ H) + (hf : BivariateCoefficientBound f H) : + BivariateCoefficientBound (bivariateShear ℤ a f) + (((d : ℝ) + 1) ^ 2 * H * (1 + |(a : ℝ)|) ^ d) := by + classical + have hg : InBivariateGrid (d + 1) f := inBivariateGrid_of_totalDegree_lt _ _ (by omega) + have he := bivariateGridPolynomial_reconstruct (d + 1) f hg + change (∑ k : BivariateGrid (d + 1), + monomial k.1.val (monomial k.2.val ((f.coeff k.1.val).coeff k.2.val))) = f at he + rw [← he, map_sum] + have hb : ∀ k : BivariateGrid (d + 1), + BivariateCoefficientBound + (bivariateShear ℤ a (monomial k.1.val (monomial k.2.val ((f.coeff k.1.val).coeff k.2.val)))) + (H * (1 + |(a : ℝ)|) ^ d) := by + intro k + apply bivariateCoefficientBound_mono + (bivariateCoefficientBound_shear_monomial a _ k.1.val k.2.val) + exact mul_le_mul (hf _ _) (pow_le_pow_right₀ (by linarith [abs_nonneg (a : ℝ)]) (by omega)) + (by positivity) hH + have hh := bivariateCoefficientBound_sum _ _ hb + convert hh using 1 + simp only [BivariateGrid, Fintype.card_prod, Fintype.card_fin, Nat.cast_mul, Nat.cast_add, + Nat.cast_one] + ring + +/-- For the selected parameter 0<=a<=d, the sheared height is at most +H*(d+1)^(d+2), hence its logarithm costs only O(d log d). -/ +theorem bivariateShear_bounded_coefficient_bound (a d : ℕ) (ha : a ≤ d) + (f : ℤ[X][X]) (hd : bivariateTotalDegree f ≤ d) + (H : ℝ) (hH : 0 ≤ H) (hf : BivariateCoefficientBound f H) : + BivariateCoefficientBound (bivariateShear ℤ (a : ℤ) f) (((d : ℝ) + 1) ^ (d + 2) * H) := by + apply bivariateCoefficientBound_mono (bivariateShear_coefficient_bound (a : ℤ) d f hd H hH hf) + simp only [Int.cast_natCast, abs_of_nonneg (show (0 : ℝ) ≤ a from Nat.cast_nonneg _)] + have haR : (a : ℝ) ≤ d := by exact_mod_cast ha + calc + _ ≤ ((d : ℝ) + 1) ^ 2 * H * ((d : ℝ) + 1) ^ d := + mul_le_mul_of_nonneg_left (pow_le_pow_left₀ (show (0 : ℝ) ≤ 1 + a by positivity) + (show (1 : ℝ) + a ≤ d + 1 by linarith) d) (by positivity) + _ = _ := by rw [pow_add]; ring + +end +end CCDN diff --git a/LeanPool/CCDN/Plane/StepanovBound.lean b/LeanPool/CCDN/Plane/StepanovBound.lean new file mode 100644 index 0000000000..4c064a5495 --- /dev/null +++ b/LeanPool/CCDN/Plane/StepanovBound.lean @@ -0,0 +1,88 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.Hilbert.FilteredIntersection +public import Mathlib.Algebra.Field.ZMod +public import Mathlib.Algebra.MvPolynomial.PDeriv +import LeanPool.CCDN.Algebra.CharZeroOrGT +import LeanPool.CCDN.AuxiliaryPolynomial.PlaneFrobeniusAuxiliary +import LeanPool.CCDN.Determinant.PlaneSingularities +import LeanPool.CCDN.ForMathlib.MvPolynomialDerivative +import LeanPool.CCDN.Plane.Bezout +import LeanPool.CCDN.Plane.FrobeniusSystem + +/-! The Stepanov point bound with an explicit finite-dimensional inequality. +The polynomial bases, auxiliary coefficients, formal branches and multiplicity +argument are explicit, given a numerical choice of degrees. -/ + +@[expose] public section +namespace CCDN +noncomputable section +open MvPolynomial + +/-- A strict dimension inequality gives the smooth prime-field point count. -/ +private theorem plane_stepanov_smooth_bound {ι : Type*} [Fintype ι] + (p : ℕ) [Fact p.Prime] (f : MvPolynomial (Fin 2) (ZMod p)) (hf : Irreducible f) + (z : ι → Fin 2 → ZMod p) (hz : Function.Injective z) + (hzero : ∀ i, eval (z i) f = 0) (j : Fin 2) + (hsmooth : ∀ i, eval (z i) (pderiv j.rev f) ≠ 0) + {D A N : ℕ} (hD : f.totalDegree * D < p) (hN : N ≤ p) + (hdim : (∑ r : Fin N, Module.finrank (ZMod p) + (filteredCoordinateSpace (Ideal.span {f}) (D + A + r.val * f.totalDegree))) < + Module.finrank (ZMod p) (filteredCoordinateSpace (Ideal.span {f}) D) * + Module.finrank (ZMod p) (filteredCoordinateSpace (Ideal.span {f}) A)) : + Fintype.card ι * N ≤ f.totalDegree * (D + p * A) := by + classical + obtain ⟨h, hd, hi⟩ := exists_filtered_polynomial_basis (Ideal.span {f}) D + obtain ⟨b, hb, hbi⟩ := exists_filtered_polynomial_basis (Ideal.span {f}) A + obtain ⟨a, ha, hne, hdiv⟩ := exists_planeFrobenius_coefficients f j h b hd hb hbi + (by simpa only [Fintype.card_fin] using hdim) + exact planeFrobeniusAuxiliary_point_bound p f hf z hz hzero j hsmooth h a hD hN hd ha hi hne hdiv + +/-- Include the points where the selected partial derivative vanishes. +Their number is bounded by d(d-1) using the proper derivative section. -/ +theorem plane_stepanov_bound {ι : Type*} [Fintype ι] + (p : ℕ) [Fact p.Prime] (f : MvPolynomial (Fin 2) (ZMod p)) (hf : Irreducible f) + (hpos : 0 < f.totalDegree) (hchar : f.totalDegree < p) + (z : ι → Fin 2 → ZMod p) (hz : Function.Injective z) + (hzero : ∀ i, eval (z i) f = 0) + {D A N : ℕ} (hD : f.totalDegree * D < p) (hN : N ≤ p) + (hdim : (∑ r : Fin N, Module.finrank (ZMod p) + (filteredCoordinateSpace (Ideal.span {f}) (D + A + r.val * f.totalDegree))) < + Module.finrank (ZMod p) (filteredCoordinateSpace (Ideal.span {f}) D) * + Module.finrank (ZMod p) (filteredCoordinateSpace (Ideal.span {f}) A)) : + Fintype.card ι * N ≤ f.totalDegree * (D + p * A) + + f.totalDegree * (f.totalDegree - 1) * N := by + classical + obtain ⟨k, hk⟩ := exists_pderiv_ne_zero hpos (CharZeroOrGT.of_charP hchar) + let P (i : ι) := eval (z i) (pderiv k f) = 0 + have hgood := plane_stepanov_smooth_bound p f hf + (fun i : {i // ¬ P i} => z i.val) (hz.comp Subtype.val_injective) + (fun i => hzero i.val) k.rev + (by simpa only [Fin.rev_rev] using fun i : {i // ¬ P i} => i.property) + hD hN hdim + have hnd : ¬ f ∣ pderiv k f := by + rintro ⟨u, hu⟩ + have hun : u ≠ 0 := by intro h; apply hk; simp [hu, h] + have he := totalDegree_mul_of_isDomain hf.ne_zero hun + rw [← hu] at he + have hd := pderiv_totalDegree_le f k + omega + have hbad := (plane_common_zeros_card_le f (pderiv k f) hf hnd + (fun i : {i // P i} => z i.val) (hz.comp Subtype.val_injective) + (fun i => hzero i.val) (fun i => i.property)).trans + (Nat.mul_le_mul_left _ (pderiv_totalDegree_le f k)) + have hpartition : Fintype.card {i // P i} + Fintype.card {i // ¬ P i} = Fintype.card ι := by + rw [Fintype.card_subtype_compl] + exact Nat.add_sub_of_le (Fintype.card_subtype_le P) + nlinarith + +end +end CCDN diff --git a/LeanPool/CCDN/Plane/StepanovDimension.lean b/LeanPool/CCDN/Plane/StepanovDimension.lean new file mode 100644 index 0000000000..25ca8b05a5 --- /dev/null +++ b/LeanPool/CCDN/Plane/StepanovDimension.lean @@ -0,0 +1,76 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.Hilbert.FilteredIntersection +public import LeanPool.CCDN.Plane.FiniteFieldConstants +import LeanPool.CCDN.Hilbert.PlaneHilbert +import LeanPool.CCDN.Plane.StepanovParameters +import Mathlib.Data.Nat.Choose.Bounds + +/-! The exact plane Hilbert function verifies the Stepanov dimension inequality +for the constructed integer parameters. -/ + +@[expose] public section +namespace CCDN +noncomputable section + +private lemma principal_plane_hilbert_balance {K : Type*} [Field K] + (f : MvPolynomial (Fin 2) K) (hf : f ≠ 0) {M : ℕ} (hM : f.totalDegree ≤ M) : + Module.finrank K (filteredCoordinateSpace (Ideal.span {f}) M) + f.totalDegree.choose 2 = + f.totalDegree * M + f.totalDegree := by + have hh := principal_plane_hilbert_function f hf hM + have he : (Module.finrank K (filteredCoordinateSpace (Ideal.span {f}) M) : ℤ) + + f.totalDegree.choose 2 = (f.totalDegree : ℤ) * M + f.totalDegree := by omega + exact_mod_cast he + +private lemma principal_plane_hilbert_bounds {K : Type*} [Field K] + (f : MvPolynomial (Fin 2) K) (hf : f ≠ 0) {M : ℕ} (hM : f.totalDegree ≤ M) : + f.totalDegree * M - f.totalDegree ^ 2 ≤ + Module.finrank K (filteredCoordinateSpace (Ideal.span {f}) M) ∧ + Module.finrank K (filteredCoordinateSpace (Ideal.span {f}) M) ≤ f.totalDegree * (M + 1) := by + have hh := principal_plane_hilbert_balance f hf hM + have hc := Nat.choose_le_pow f.totalDegree 2 + constructor + · omega + · nlinarith + +/-- The numerical inequality suffices for the exact filtered dimensions. -/ +theorem stepanov_hilbert_dimension_inequality {K : Type*} [Field K] + (f : MvPolynomial (Fin 2) K) (hf : f ≠ 0) (p A D N : ℕ) + (hd : 0 < f.totalDegree) (hp : stepanovThreshold f.totalDegree < p) + (hA : f.totalDegree ≤ A) (hD : f.totalDegree ≤ D) + (hx : (f.totalDegree * A) ^ 2 ≤ p) (hy : p ≤ f.totalDegree * D + f.totalDegree) + (hN : N + 4 * f.totalDegree ^ 2 = f.totalDegree * A) (hNpos : 0 < N) : + (∑ r : Fin N, Module.finrank K + (filteredCoordinateSpace (Ideal.span {f}) (D + A + r.val * f.totalDegree))) < + Module.finrank K (filteredCoordinateSpace (Ideal.span {f}) D) * + Module.finrank K (filteredCoordinateSpace (Ideal.span {f}) A) := by + have hsum : (∑ r : Fin N, Module.finrank K + (filteredCoordinateSpace (Ideal.span {f}) (D + A + r.val * f.totalDegree))) ≤ + N * (f.totalDegree * A + f.totalDegree * D + f.totalDegree + f.totalDegree ^ 2 * N) := by + calc + _ ≤ ∑ _r : Fin N, f.totalDegree * (D + A + N * f.totalDegree + 1) := by + apply Finset.sum_le_sum + intro r _ + apply (principal_plane_hilbert_bounds f hf + (by omega : f.totalDegree ≤ D + A + r.val * f.totalDegree)).2.trans + apply Nat.mul_le_mul_left + have hh := Nat.mul_le_mul_right f.totalDegree (Nat.le_of_lt r.isLt) + omega + _ = _ := by + simp only [Finset.sum_const, Finset.card_univ, Fintype.card_fin, smul_eq_mul]; ring + have hn := stepanov_numeric_dimension f.totalDegree p (f.totalDegree * A) + (f.totalDegree * D) N hd hp hx hy hN hNpos + have hprod := Nat.mul_le_mul (principal_plane_hilbert_bounds f hf hA).1 + (principal_plane_hilbert_bounds f hf hD).1 + exact hsum.trans_lt (hn.trans_le (by nlinarith only [hprod])) + +end +end CCDN diff --git a/LeanPool/CCDN/Plane/StepanovParameters.lean b/LeanPool/CCDN/Plane/StepanovParameters.lean new file mode 100644 index 0000000000..528a0f91cd --- /dev/null +++ b/LeanPool/CCDN/Plane/StepanovParameters.lean @@ -0,0 +1,114 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.Plane.FiniteFieldConstants +import Mathlib.Algebra.Order.Group.Unbundled.Int +import Mathlib.Tactic.Linarith + +/-! Integer parameters for the Stepanov construction above the cutoff +`stepanovThreshold d = 27 * d ^ 4`. -/ + +@[expose] public section +namespace CCDN +noncomputable section + +/-- The numerical dimension inequality of the Stepanov method: for `p > 27 d⁴`, `x ^ 2 ≤ p`, +`p ≤ y + d` and `N = x - 4 d² > 0`, one has `N (x + y + d + d² N) < (x - d²) (y - d²)`. -/ +lemma stepanov_numeric_dimension (d p x y N : ℕ) (hd : 0 < d) + (hp : stepanovThreshold d < p) (hx : x ^ 2 ≤ p) (hy : p ≤ y + d) + (hN : N + 4 * d ^ 2 = x) (hNpos : 0 < N) : + N * (x + y + d + d ^ 2 * N) < (x - d ^ 2) * (y - d ^ 2) := by + unfold stepanovThreshold at hp + let b := d ^ 2 + have hb : d ≤ b := by simpa only [b, pow_two] using Nat.le_mul_self d + have hbpos : 0 < b := by dsimp [b]; positivity + have hbb : b ≤ b ^ 2 := by simpa only [pow_two] using Nat.le_mul_self b + have hp' : 27 * b ^ 2 < p := by dsimp [b]; nlinarith [hp] + have hNx : N + 4 * b = x := hN + have hxb : b ≤ x := by omega + have hyb : b ≤ y := by nlinarith + have hxp : 5 * x ≤ p := by + have hh : 5 ≤ x := by omega + nlinarith [Nat.mul_le_mul_left x hh] + have hNN : N ^ 2 ≤ p := by + have hh : N ≤ x := by omega + nlinarith [Nat.mul_self_le_mul_self hh] + have hNx' : N * x ≤ p := by + have hh : N ≤ x := by omega + nlinarith [Nat.mul_le_mul_right x hh] + have hNb : b * N ≤ b * x := Nat.mul_le_mul_left b (by omega) + have hNd : d * N ≤ b * x := Nat.mul_le_mul hb (by omega) + have h3y := Nat.mul_le_mul_left (3 * b) hy + have h5bx := Nat.mul_le_mul_left (2 * b) hxp + have hNp := Nat.mul_le_mul_left b hNN + have hbd := Nat.mul_le_mul_left b hb + have hbp : p ≤ b * p := by nlinarith [Nat.mul_le_mul_right p hbpos] + have hsubx : x - b + b = x := Nat.sub_add_cancel hxb + have hsuby : y - b + b = y := Nat.sub_add_cancel hyb + have hc : 2 * b * x + 6 * b ^ 2 < b * p := by + nlinarith only [hp', hbp, h5bx] + have hv : N * (b + d + x + b * N) + 3 * b ^ 2 < 3 * b * y := by + nlinarith only [hc, hbp, h3y, hbd, hNb, hNd, hNx', hNp] + have hi : (x - b) * (y - b) + N * (b + d + x + b * N) + 3 * b ^ 2 = + N * (x + y + d + b * N) + 3 * b * y := by + have hx' : x - b = N + 3 * b := by omega + rw [hx'] + nlinarith only [hsuby, hNx] + change N * (x + y + d + b * N) < (x - b) * (y - b) + omega + + +/-- Concrete integer degrees: A=floor(sqrt(p)/d), D=floor((p-1)/d), +and N=d*A-4*d^2. The cutoff ensures a positive multiplicity. -/ +theorem exists_stepanov_integer_parameters (d p : ℕ) (hd : 0 < d) + (hp : stepanovThreshold d < p) : + ∃ A D N : ℕ, d ≤ A ∧ d ≤ D ∧ d * D < p ∧ N ≤ p ∧ + N + 4 * d ^ 2 = d * A ∧ 0 < N ∧ (d * A) ^ 2 ≤ p ∧ + p ≤ d * D + d ∧ p < (d * A + d) ^ 2 := by + unfold stepanovThreshold at hp + let A := Nat.sqrt p / d + let D := (p - 1) / d + let x := d * A + let y := d * D + have hb : d ≤ d ^ 2 := by simpa only [pow_two] using Nat.le_mul_self d + have hbpos : 0 < d ^ 2 := by positivity + have hbb : d ^ 2 ≤ (d ^ 2) ^ 2 := by nlinarith only [Nat.le_mul_self (d ^ 2)] + have hp' : 27 * (d ^ 2) ^ 2 < p := by nlinarith only [hp] + have hp0 : 0 < p := by omega + have hs := Nat.mod_add_div (Nat.sqrt p) d + have hsm := Nat.mod_lt (Nat.sqrt p) hd + have hxs : x ≤ Nat.sqrt p := by dsimp only [x, A]; omega + have hsx : Nat.sqrt p + 1 ≤ x + d := by dsimp only [x, A]; omega + have hx : x ^ 2 ≤ p := (Nat.pow_le_pow_left hxs 2).trans (Nat.sqrt_le' p) + have hpx : p < (x + d) ^ 2 := + (Nat.lt_succ_sqrt' p).trans_le (Nat.pow_le_pow_left hsx 2) + have hydiv := Nat.mod_add_div (p - 1) d + have hymod := Nat.mod_lt (p - 1) hd + have hyp : y < p := by dsimp only [y, D]; omega + have hpy : p ≤ y + d := by dsimp only [y, D]; omega + have hx4 : 4 * d ^ 2 < x := by + by_contra! hn + have he : x + d ≤ 5 * d ^ 2 := by omega + have he2 := Nat.pow_le_pow_left he 2 + nlinarith only [hp', hpx, he2] + have hA : d ≤ A := by dsimp only [x] at hx4; nlinarith only [hx4, hd] + have hD : d ≤ D := by + have hdy : d ^ 2 ≤ y := by nlinarith only [hp', hpy, hb, hbb] + dsimp only [y] at hdy + nlinarith only [hdy, hd] + let N := x - 4 * d ^ 2 + have hNx : N + 4 * d ^ 2 = x := Nat.sub_add_cancel (Nat.le_of_lt hx4) + have hNpos : 0 < N := Nat.sub_pos_of_lt hx4 + have hxx : x ≤ x ^ 2 := by simpa only [pow_two] using Nat.le_mul_self x + have hxp : x ≤ p := hxx.trans hx + exact ⟨A, D, N, hA, hD, hyp, (by omega), hNx, hNpos, hx, hpy, hpx⟩ + +end +end CCDN diff --git a/LeanPool/CCDN/Plane/UnscaledModelCount.lean b/LeanPool/CCDN/Plane/UnscaledModelCount.lean new file mode 100644 index 0000000000..c8de11b514 --- /dev/null +++ b/LeanPool/CCDN/Plane/UnscaledModelCount.lean @@ -0,0 +1,97 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.Plane.ScaledModelCoordinates +public import LeanPool.CCDN.Plane.ScaledPlaneCount +import LeanPool.CCDN.Algebra.HomogeneousSlice +import LeanPool.CCDN.AuxiliaryPolynomial.PlaneAuxiliaryCount +import LeanPool.CCDN.AuxiliaryPolynomial.PolynomialMapAuxiliary +import LeanPool.CCDN.Plane.ScaledModelPosition + +/-! The unit-scaled model gives a coefficient-uniform 2/d fallback and a +large-coefficient O(d^4) count, without requiring a scaling prime. -/ + +@[expose] public section +namespace CCDN +noncomputable section +open MvPolynomial Real + +/-- The homogenization (with scaling factor `1`) of a primitive integer polynomial is +primitive. -/ +lemma PrimitiveIntegerPolynomial.scaledHomogenize_one {n : ℕ} + {f : MvPolynomial (Fin n) ℤ} (hf : PrimitiveIntegerPolynomial f) (d : ℕ) : + PrimitiveIntegerPolynomial (CCDN.scaledHomogenize d 1 f) := by + intro c hc + apply hf c + intro m + have h := hc (homogenizedExponent d m) + simpa only [scaledHomogenize_coefficient, one_pow, one_mul] using h + +private lemma unscaled_positioned_points_card_le {M t : ℕ} + (f : MvPolynomial (Fin 2) ℤ) (hprim : PrimitiveIntegerPolynomial f) + (hir : Irreducible (MvPolynomial.map (Int.castRingHom ℚ) f)) + (a : Fin 2 → ℤ) (z : Fin t → Fin 2 → ℤ) (hinj : Function.Injective z) + (hz : ∀ i, eval (z i) f = 0) (g : MvPolynomial (Fin 3) ℤ) + (hg : IsPointAuxiliary (homogeneousTranslation ℤ a (scaledHomogenize f.totalDegree 1 f)) + g M (fun i j => eval (z i) (scaledModelCoordinates 1 a j))) : + t ≤ f.totalDegree * M := by + obtain ⟨g', hg', he, hnd'⟩ := scaledModelCoordinates_auxiliary_transport 1 a f g + hg.isHomogeneous hg.not_dvd + have hf0 : f ≠ 0 := fun h => hir.ne_zero (by simp [h]) + have hchart : affineChart 2 (scaledHomogenize f.totalDegree 1 f) = f := by + change homogeneousSlice 2 1 (scaledHomogenize f.totalDegree 1 f) = f + simp only [homogeneousSlice_scaledHomogenize 1 f le_rfl, one_pow, map_one, one_mul] + have hh := plane_chart_points_card_le_auxiliary_degree (scaledHomogenize f.totalDegree 1 f) g' + (scaledHomogenize_homogeneous _ _ le_rfl) (scaledHomogenize_ne_zero _ one_ne_zero hf0) + hg' (hprim.scaledHomogenize_one _) (by rw [hchart]) (by rwa [hchart]) hnd' z hinj + (by simpa only [hchart] using hz) (fun i => (he (z i)).trans (hg.eval_eq_zero i)) + exact hh + +/-- Coefficient positioning removes the coefficient height from the 2/d +fallback bound. This holds for any primitive absolutely irreducible equation. -/ +theorem unscaled_plane_finite_count + {F : Type*} [Field F] [CharZero F] [IsAlgClosed F] {t : ℕ} + (f : MvPolynomial (Fin 2) ℤ) (hprim : PrimitiveIntegerPolynomial f) + (hir : Irreducible (MvPolynomial.map (Int.castRingHom F) f)) (hd : 0 < f.totalDegree) + (z : Fin t → Fin 2 → ℤ) (hinj : Function.Injective z) {B C : ℝ} (hzB : IsBoxZeroFamily f B z) + (hC : 0 ≤ C) + (hcount : PlaneResidueCountBound C f.totalDegree f) : + (t : ℝ) ≤ max + ((f.totalDegree : ℝ) * scaledPlaneAuxiliaryDegreeBound f.totalDegree C + (((f.totalDegree : ℝ) + 1) * B) 1) + ((34 + 8 * Real.exp (heightDeterminantError C + 20)) * (f.totalDegree : ℝ) ^ 4) := by + simpa only [Int.cast_one] using + (scaled_model_plane_count_bounds f hir hd 1 le_rfl z hzB + (by simpa only [Int.cast_one] using hzB.one_le_radius) hC hcount (fun a M g hg => + unscaled_positioned_points_card_le f hprim + (rational_irreducible_of_charZero_map f hir) a z hinj hzB.eval_eq_zero g hg)).1 + +/-- A sufficiently large coefficient forces the large-height branch +and gives O(d^4). -/ +theorem unscaled_plane_finite_count_large_coefficient + {F : Type*} [Field F] [CharZero F] [IsAlgClosed F] {t : ℕ} + (f : MvPolynomial (Fin 2) ℤ) (hprim : PrimitiveIntegerPolynomial f) + (hir : Irreducible (MvPolynomial.map (Int.castRingHom F) f)) (hd : 0 < f.totalDegree) + (z : Fin t → Fin 2 → ℤ) (hinj : Function.Injective z) {B C : ℝ} (hzB : IsBoxZeroFamily f B z) + (hC : 0 ≤ C) + (m : Fin 2 →₀ ℕ) (hm : f.coeff m ≠ 0) + (hheight : 4 * (f.totalDegree : ℝ) * Real.log (((f.totalDegree : ℝ) + 1) * B) ≤ + Real.log |(f.coeff m : ℝ)|) + (hcount : PlaneResidueCountBound C f.totalDegree f) : + (t : ℝ) ≤ (34 + 8 * Real.exp (heightDeterminantError C + 20)) * (f.totalDegree : ℝ) ^ 4 := by + simpa only [Int.cast_one] using + (scaled_model_plane_count_bounds f hir hd 1 le_rfl z hzB + (by simpa only [Int.cast_one] using hzB.one_le_radius) hC hcount (fun a M g hg => + unscaled_positioned_points_card_le f hprim + (rational_irreducible_of_charZero_map f hir) a z hinj + hzB.eval_eq_zero g hg)).2 m hm hheight + +end +end CCDN diff --git a/LeanPool/CCDN/Projection/AffineProjectionCount.lean b/LeanPool/CCDN/Projection/AffineProjectionCount.lean new file mode 100644 index 0000000000..d783867d78 --- /dev/null +++ b/LeanPool/CCDN/Projection/AffineProjectionCount.lean @@ -0,0 +1,111 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.Algebra.IdealZeroLocus +public import LeanPool.CCDN.Hilbert.AffineCurveDegree +public import LeanPool.CCDN.Projection.DegreePreservingProjectionBox +public import Mathlib.Analysis.SpecialFunctions.Pow.Real +import LeanPool.CCDN.Determinant.PolynomialMapDeterminant +import LeanPool.CCDN.Projection.BoundedDegreePreservingProjection + +/-! Reduce the integral-point count of an arbitrary prime affine curve +to the proved plane count, using the constructed degree-preserving projection. -/ + +@[expose] public section +namespace CCDN +noncomputable section +open MvPolynomial + +/-- The inclusion of integer points into rational points is injective. -/ +lemma rationalPoint_injective (n : ℕ) : Function.Injective (@rationalPoint n) := by + intro z w he + funext j + have hh := congrFun he j + change (z j : ℚ) = (w j : ℚ) at hh + exact_mod_cast hh + +/-- Suppose the projection of the affine variety of `I` by the moment coordinates `k₀`, `k₁` has +kernel generated by the plane polynomial `f`, and that this projection loses at most `d * (d - 1)` +points on any finite set of rational points of the variety. Then the integral points of height at +most `B` on the variety number at most those of height `projectionBoxFactor n d * B` on the plane +curve `f = 0`, plus `d * (d - 1)`. -/ +theorem affine_curve_count_le_of_moment_projection {n d k₀ k₁ : ℕ} + (I : Ideal (CoordinatePolynomial n)) + (f : MvPolynomial (Fin 2) ℚ) + (hk₀ : k₀ ≤ n ^ 2 * d) (hk₁ : k₁ ≤ n * d ^ 2) + (hker : ∀ g, aeval ![momentCoordinate ℚ n k₀, momentCoordinate ℚ n k₁] g ∈ I ↔ f ∣ g) + (hcount : ∀ S : Finset (Fin n → ℚ), (∀ z ∈ S, z ∈ idealZeroLocus I) → + S.card ≤ (S.image (fun z i => + eval z (![momentCoordinate ℚ n k₀, momentCoordinate ℚ n k₁] i))).card + d * (d - 1)) + (B : ℝ) (hB : 1 ≤ B) : + count (idealZeroLocus I) B ≤ + count (zeroLocus f) (projectionBoxFactor n d * B) + d * (d - 1) := by + classical + let S := integralPoints (idealZeroLocus I) B + let R := S.image rationalPoint + let π : RatPoint n → RatPoint 2 := fun z => + ![eval z (momentCoordinate ℚ n k₀), eval z (momentCoordinate ℚ n k₁)] + let P := S.image (degreeProjection n k₀ k₁) + have hR : ∀ z ∈ R, z ∈ idealZeroLocus I := by + intro w hw + obtain ⟨z, hz, rfl⟩ := Finset.mem_image.mp hw + exact (mem_integralPoints.mp hz).2 + have hc := hcount R hR + have hRcard : R.card = S.card := Finset.card_image_of_injective S (rationalPoint_injective n) + have himg : R.image π = P.image rationalPoint := by + simp only [R, P, Finset.image_image] + congr 1 + funext z + exact (rationalPoint_degreeProjection z).symm + have hπ : (fun z i => eval z + (![momentCoordinate ℚ n k₀, momentCoordinate ℚ n k₁] i)) = π := by + funext z i + fin_cases i <;> rfl + rw [hπ, hRcard, himg, Finset.card_image_of_injective P (rationalPoint_injective 2)] at hc + have hP : P ⊆ integralPoints (zeroLocus f) (projectionBoxFactor n d * B) := by + rintro _ hy + obtain ⟨z, hz, rfl⟩ := Finset.mem_image.mp hy + have hzbox := mem_integerBox.mpr (mem_integralPoints.mp hz).1 + apply mem_integralPoints.mpr + refine ⟨mem_integerBox.mp (degreeProjection_box hk₀ hk₁ (by linarith) hzbox), ?_⟩ + change eval (rationalPoint (degreeProjection n k₀ k₁ z)) f = 0 + rw [rationalPoint_degreeProjection] + have hfI := (hker f).mpr (dvd_refl f) + have hh := (mem_integralPoints.mp hz).2 _ hfI + rw [eval_polynomial_coordinate_map] at hh + convert hh using 1 + congr 2 + funext j + fin_cases j <;> rfl + exact hc.trans (Nat.add_le_add_right (Finset.card_le_card hP) _) + +/-- An integral affine curve of degree `d` has an irreducible plane equation `f` of degree +`d` such that, for every radius `B ≥ 1`, its integral points of height at most `B` number at +most those of `f` in a box enlarged by `projectionBoxFactor n d`, plus `d(d-1)`. -/ +theorem exists_plane_count_reduction {n : ℕ} + (I : Ideal (CoordinatePolynomial n)) [I.IsPrime] + (d : ℕ) (hd : 0 < d) (hdegree : HasAffineCurveDegree I d) : + ∃ f : MvPolynomial (Fin 2) ℚ, Irreducible f ∧ f.totalDegree = d ∧ + ∀ B : ℝ, 1 ≤ B → count (idealZeroLocus I) B ≤ + count (zeroLocus f) (projectionBoxFactor n d * B) + d * (d - 1) := by + obtain ⟨k₀, k₁, f, hk₀, hk₁, hf, hfd, hker, hcount⟩ := + exists_bounded_projection_card_le I d hd hdegree + have hπ : (fun z i => eval z (![momentCoordinate ℚ n k₀, momentCoordinate ℚ n k₁] i)) = + fun z : Fin n → ℚ => + ![eval z (momentCoordinate ℚ n k₀), eval z (momentCoordinate ℚ n k₁)] := by + funext z i + fin_cases i <;> rfl + refine ⟨f, hf, hfd, affine_curve_count_le_of_moment_projection I f hk₀ hk₁ hker ?_⟩ + intro S hS + rw [hπ] + exact hcount S hS + +end +end CCDN diff --git a/LeanPool/CCDN/Projection/BoundedBirationalHypersurfaceProjection.lean b/LeanPool/CCDN/Projection/BoundedBirationalHypersurfaceProjection.lean new file mode 100644 index 0000000000..d09cbb9b39 --- /dev/null +++ b/LeanPool/CCDN/Projection/BoundedBirationalHypersurfaceProjection.lean @@ -0,0 +1,158 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.Algebra.PolynomialPredicates +public import LeanPool.CCDN.Projection.BoundedIntegerNormalization +public import LeanPool.CCDN.Projection.CurvePrimitivePair +public import LeanPool.CCDN.Projection.ImageFractionField +import LeanPool.CCDN.Projection.LeadingCoordinateExtension +import LeanPool.CCDN.Projection.LinearParameterPrimitive + +/-! A bounded integer linear finite birational projection of a +homogeneous prime quotient to a hypersurface of exactly the same degree. -/ + +@[expose] public section +namespace CCDN +noncomputable section +open MvPolynomial + +/-- The coefficient bound for the birational hypersurface projection in `n` variables and degree +`d`: the maximum of the normalization height and `(n * d ^ 2 + 1) ^ n`, which bounds the moment +row. -/ +def integerBirationalProjectionHeight (n d : ℕ) : ℕ := + max (integerNormalizationHeight n d) ((n * d ^ 2 + 1) ^ n) + +/-- The birational projection height is at least one. -/ +lemma integerBirationalProjectionHeight_one_le (n d : ℕ) : + 1 ≤ integerBirationalProjectionHeight n d := + (integerNormalizationHeight_pos n d).trans_le (le_max_left _ _) + +/-- Prepending the integer moment row `(t ^ j)_j` to integer rows realizes the moment +coordinate as the leading linear form. -/ +theorem integerLinearForm_moment_cons {m n : ℕ} (t : ℕ) (a : Fin (m + 1) → Fin n → ℤ) : + (fun i => integerLinearForm + ((Fin.cases (fun j => (t : ℤ) ^ j.val) a : Fin (m + 2) → Fin n → ℤ) i)) = + Fin.cases (momentCoordinate ℚ n t) (fun i => integerLinearForm (a i)) := by + funext i + refine Fin.cases ?_ (fun _ => rfl) i + simp only [Fin.cases_zero, integerLinearForm, linearForm, momentCoordinate, Int.cast_pow, + Int.cast_natCast] + +/-- Prepending the moment row with parameter `t ≤ n d²` to rows bounded by the normalization +height keeps every entry within the birational projection height. -/ +theorem moment_cons_abs_le_integerBirationalProjectionHeight {m n d t : ℕ} + (ht : t ≤ n * d ^ 2) (a : Fin (m + 1) → Fin n → ℤ) + (ha : ∀ i j, |a i j| ≤ integerNormalizationHeight n d) (i : Fin (m + 2)) (j : Fin n) : + |(Fin.cases (fun j => (t : ℤ) ^ j.val) a : Fin (m + 2) → Fin n → ℤ) i j| ≤ + integerBirationalProjectionHeight n d := by + refine Fin.cases ?_ (fun s => ?_) i + · change |(t : ℤ) ^ j.val| ≤ _ + rw [abs_pow, abs_of_nonneg (Nat.cast_nonneg t)] + have hp : t ^ j.val ≤ (n * d ^ 2 + 1) ^ n := by + calc + _ ≤ (n * d ^ 2 + 1) ^ j.val := + Nat.pow_le_pow_left (ht.trans (Nat.le_succ _)) _ + _ ≤ _ := Nat.pow_le_pow_right (by omega) (Nat.le_of_lt j.isLt) + exact_mod_cast hp.trans (le_max_right (integerNormalizationHeight n d) _) + · exact (ha s j).trans (by + exact_mod_cast le_max_left (integerNormalizationHeight n d) ((n * d ^ 2 + 1) ^ n)) + +/-- A bounded primitive augmentation of the constructed normalization +produces a homogeneous irreducible equation of exactly the original degree. -/ +private theorem exists_bounded_birational_hypersurface_projection + (m k : ℕ) (I : Ideal (MvPolynomial (Fin (m + 1 + k)) ℚ)) [I.IsPrime] + (hI : IsHomogeneousIdeal I) + (d : ℕ) (hdimdeg : HasProjectiveDimensionDegree I m d) : + ∃ (b : Fin (m + 2) → Fin (m + 1 + k) → ℤ) (f : MvPolynomial (Fin (m + 2)) ℚ), + (∀ i j, |b i j| ≤ integerBirationalProjectionHeight (m + 1 + k) d) ∧ + IsNoetherNormalization I (fun i : Fin (m + 1) => integerLinearForm (b i.succ)) ∧ + IsBirationalProjection I (fun i => integerLinearForm (b i)) f ∧ + f.IsHomogeneous d ∧ f.totalDegree = d := by + classical + let n := m + 1 + k + let F := FractionRing (MvPolynomial (Fin n) ℚ ⧸ I) + obtain ⟨a, ha, hinta, hinja⟩ := exists_bounded_integer_normalization m k I d hdimdeg + obtain ⟨t, ht, hfield⟩ := exists_bounded_linear_parameter_primitive (F := F) + I _ (fun i => linearForm_totalDegree_le _) hinja d hdimdeg + let b : Fin (m + 2) → Fin n → ℤ := Fin.cases (fun j => (t : ℤ) ^ j.val) a + let l : Fin (m + 2) → MvPolynomial (Fin n) ℚ := fun i => integerLinearForm (b i) + have hfield' : IntermediateField.adjoin ℚ (Set.range (fun i => + algebraMap _ F ((Ideal.Quotient.mk I) (l i)))) = ⊤ := by + have hl : l = Fin.cases (momentCoordinate ℚ n t) (fun i => integerLinearForm (a i)) := + integerLinearForm_moment_cons t a + rwa [hl] + obtain ⟨f, hf, hfhom, hfdeg, _, hker⟩ := exists_homogeneous_extended_projection_equation + (F := F) I hI l (fun i => linearForm_isHomogeneous _) hinta hinja hfield' d + hdimdeg + exact ⟨b, f, moment_cons_abs_le_integerBirationalProjectionHeight ht a ha, ⟨hinta, hinja⟩, + ⟨⟨hfield', hf, hker⟩, projectionCoordinateMap_isIntegral_of_tail I l hinta⟩, hfhom, hfdeg⟩ + +/-- A homogeneous prime ideal in `n ≥ m + 1` variables, of projective dimension `m` and +degree `d`, has a bounded integral linear projection to `m + 2` coordinates: the integer +coefficients are bounded by `integerBirationalProjectionHeight n d`, the projection is integral +and birational, and its kernel is generated by an irreducible homogeneous form of degree `d`. +The number of variables is arbitrary, not written as `m + 1 + k`. -/ +theorem exists_bounded_birational_hypersurface_projection_of_le + (m n : ℕ) (hmn : m + 1 ≤ n) + (I : Ideal (MvPolynomial (Fin n) ℚ)) [I.IsPrime] + (hI : IsHomogeneousIdeal I) + (d : ℕ) (hdimdeg : HasProjectiveDimensionDegree I m d) : + ∃ (b : Fin (m + 2) → Fin n → ℤ) (f : MvPolynomial (Fin (m + 2)) ℚ), + (∀ i j, |b i j| ≤ integerBirationalProjectionHeight n d) ∧ + IsNoetherNormalization I (fun i : Fin (m + 1) => integerLinearForm (b i.succ)) ∧ + IsBirationalProjection I (fun i => integerLinearForm (b i)) f ∧ + f.IsHomogeneous d ∧ f.totalDegree = d := by + obtain ⟨k, rfl⟩ := Nat.exists_eq_add_of_le hmn + exact exists_bounded_birational_hypersurface_projection m k I hI d hdimdeg + +section + +/-! The constructed projection has a coefficient bound polynomial in +the degree, with exponent and constant depending only on ambient +dimension. -/ + +/-- The constant `n! * 2 ^ n + (n + 1) ^ n` in the polynomial bound on the birational projection +height in `n` variables. -/ +def birationalProjectionHeightConstant (n : ℕ) : ℕ := + n.factorial * 2 ^ n + (n + 1) ^ n + +/-- The birational projection height constant is positive. -/ +lemma birationalProjectionHeightConstant_pos (n : ℕ) : + 0 < birationalProjectionHeightConstant n := by + dsimp [birationalProjectionHeightConstant] + positivity + +/-- For degree `d ≥ 1`, the birational projection height in `n` variables is at most +`birationalProjectionHeightConstant n * d ^ (2 * n)`. -/ +theorem integerBirationalProjectionHeight_le (n d : ℕ) (hd : 1 ≤ d) : + integerBirationalProjectionHeight n d ≤ + birationalProjectionHeightConstant n * d ^ (2 * n) := by + have hpow : 1 ≤ d ^ 2 := Nat.one_le_pow _ _ hd + have hfirst : integerNormalizationHeight n d ≤ + (n.factorial * 2 ^ n) * d ^ (2 * n) := by + dsimp only [integerNormalizationHeight] + calc + _ ≤ n.factorial * (2 * d) ^ n := + Nat.mul_le_mul_left _ (Nat.pow_le_pow_left (by omega) _) + _ = (n.factorial * 2 ^ n) * d ^ n := by rw [Nat.mul_pow, Nat.mul_assoc] + _ ≤ _ := Nat.mul_le_mul_left _ (Nat.pow_le_pow_right hd (by omega)) + have hsecond : (n * d ^ 2 + 1) ^ n ≤ (n + 1) ^ n * d ^ (2 * n) := by + calc + _ ≤ ((n + 1) * d ^ 2) ^ n := + Nat.pow_le_pow_left (by nlinarith) _ + _ = _ := by rw [Nat.mul_pow, ← pow_mul] + apply max_le + · exact hfirst.trans (Nat.mul_le_mul_right _ (Nat.le_add_right _ _)) + · exact hsecond.trans (Nat.mul_le_mul_right _ (Nat.le_add_left _ _)) + +end + +end +end CCDN diff --git a/LeanPool/CCDN/Projection/BoundedClosureElimination.lean b/LeanPool/CCDN/Projection/BoundedClosureElimination.lean new file mode 100644 index 0000000000..6b135481d6 --- /dev/null +++ b/LeanPool/CCDN/Projection/BoundedClosureElimination.lean @@ -0,0 +1,80 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.Algebra.IntegerLinearForms +public import LeanPool.CCDN.Hilbert.FilteredHilbertPolynomial +public import LeanPool.CCDN.Projection.ProjectiveClosureProjection +public import Mathlib.RingTheory.IntegralClosure.IsIntegral.Defs +import LeanPool.CCDN.Projection.FixedCoordinateElimination +import LeanPool.CCDN.Projection.HilbertLowDegreeEquation +import LeanPool.CCDN.Projection.IntegralProjectionHilbert +import LeanPool.CCDN.Projection.ProjectiveClosureSpecialization + +/-! A bounded elimination step whose affine projection and projective +closure projection are both integral. The latter map fixes X₀. -/ + +@[expose] public section +namespace CCDN +noncomputable section +open MvPolynomial + +/-- One step of bounded linear elimination: for a prime ideal `I` in `n + 1` variables of projective +dimension `m < n` and degree `d`, there are integer linear forms in `n` coordinates with +coefficients of absolute value at most `d + 1` whose projection is integral, with prime kernel of +projective dimension `m` and some degree `e` with `0 < e ≤ d`, and such that the associated +projection of the projective closure of `I` is integral as well. -/ +theorem exists_fin_integer_closure_elimination_step {n m : ℕ} + (I : Ideal (MvPolynomial (Fin (n + 1)) ℚ)) [I.IsPrime] (d : ℕ) + (hdimdeg : HasProjectiveDimensionDegree I m d) (hcodim : m < n) : + ∃ (c : Fin n → Fin (n + 1) → ℤ) (e : ℕ), + (∀ i j, |c i j| ≤ d + 1) ∧ 0 < e ∧ e ≤ d ∧ + let a := fun i => integerLinearForm (c i) + let ψ := projectionCoordinateMap I a + ψ.IsIntegral ∧ (RingHom.ker ψ).IsPrime ∧ + HasProjectiveDimensionDegree (RingHom.ker ψ) m e ∧ + (projectionCoordinateMap (projectiveClosureIdeal I) + (closureProjectionCoordinates a)).IsIntegral := by + obtain ⟨f, hf, hfi, hfd⟩ := exists_mem_ideal_degree_le_multiplicity I d hdimdeg + (by simpa only [Fintype.card_fin] using Nat.add_lt_add_right hcodim 1) + let F := renameEquiv ℚ (finSuccEquiv n) f + have hF : F ≠ 0 := by + intro h + apply hf + exact (renameEquiv ℚ (finSuccEquiv n)).injective (h.trans (map_zero _).symm) + have hFdeg : F.totalDegree = f.totalDegree := totalDegree_renameEquiv (finSuccEquiv n) f + obtain ⟨b, hbound, hb, hdir⟩ := exists_bounded_full_degree_direction F hF + have hbQ : (b none : ℚ) ≠ 0 := by exact_mod_cast hb + have hdir' : eval (Fin.cons (b none : ℚ) (fun i => (b (some i) : ℚ))) + (homogeneousComponent f.totalDegree f) ≠ 0 := by + rw [hFdeg] at hdir + change eval (fun i => (b i : ℚ)) + (homogeneousComponent f.totalDegree (rename (finSuccEquiv n) f)) ≠ 0 at hdir + rw [← rename_homogeneousComponent, eval_rename] at hdir + convert hdir using 1 + congr 1 + congr 1 + funext i + refine Fin.cases rfl (fun j => rfl) i + have hcone := fixedCoordinateElimination_isIntegral I f hf hfi b hbQ hdir' + let c := finEliminationMatrix b + have hc : ∀ i j, |c i j| ≤ d + 1 := by + apply finEliminationMatrix_height + intro i + refine ⟨(hbound i).1, ?_⟩ + rw [hFdeg] at hbound + exact (hbound i).2.trans (by exact_mod_cast Nat.add_le_add_right hfd 1) + have hint := affine_projection_integral_of_closure_integral I + (fun i => integerLinearForm (c i)) hcone + obtain ⟨e, he, hed, hker⟩ := integral_linear_projection_dimension_degree I + (fun i => integerLinearForm (c i)) (fun i => linearForm_totalDegree_le _) hint hdimdeg + exact ⟨c, e, hc, he, hed, hint, RingHom.ker_isPrime _, hker, hcone⟩ + +end +end CCDN diff --git a/LeanPool/CCDN/Projection/BoundedDegreeNormalizationFiber.lean b/LeanPool/CCDN/Projection/BoundedDegreeNormalizationFiber.lean new file mode 100644 index 0000000000..5102deeafb --- /dev/null +++ b/LeanPool/CCDN/Projection/BoundedDegreeNormalizationFiber.lean @@ -0,0 +1,125 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.Algebra.IdealZeroLocus +public import LeanPool.CCDN.Hilbert.FilteredHilbertPolynomial +public import LeanPool.CCDN.Hilbert.LinearProjectionFiltration +public import Mathlib.RingTheory.IntegralClosure.IsIntegral.Defs +import LeanPool.CCDN.Hilbert.BoundedDegreeFunctionFieldRank +import LeanPool.CCDN.Hilbert.LinearCoordinates +import LeanPool.CCDN.Projection.IntegralFiberRank +import LeanPool.CCDN.Projection.PrimitiveCoordinateField +import Mathlib.RingTheory.AlgebraicIndependent.Basic + +/-! Sharp fibers of integral polynomial parameters of degree at most q. +The Hilbert multiplicity bounds the field degree by d*q^dimension; +a separating integral element gives the same bound for every finite fiber. -/ + +@[expose] public section +namespace CCDN +noncomputable section +open MvPolynomial + +private theorem bounded_degree_normalization_fiber_card_le_of_fractionField + {K E : Type*} [Field K] [CharZero K] [Field E] [Algebra K E] + {n m : ℕ} (I : Ideal (MvPolynomial (Fin n) K)) [I.IsPrime] + [Algebra (MvPolynomial (Fin n) K ⧸ I) E] + [IsScalarTower K (MvPolynomial (Fin n) K ⧸ I) E] + [IsFractionRing (MvPolynomial (Fin n) K ⧸ I) E] + (q : ℕ) (hq : 0 < q) + (l : Fin (m + 1) → MvPolynomial (Fin n) K) + (hl : ∀ i, (l i).totalDegree ≤ q) + (hi : Function.Injective (projectionCoordinateMap I l)) + (hint : (projectionCoordinateMap I l).IsIntegral) + (d : ℕ) (hdimdeg : HasProjectiveDimensionDegree I m d) + (z : Fin (m + 1) → K) (s : Finset (Fin n → K)) + (hzero : ∀ x ∈ s, x ∈ idealZeroLocus I) + (hproj : ∀ x ∈ s, ∀ i, eval x (l i) = z i) : + s.card ≤ d * q ^ (m + 1) := by + classical + let A := MvPolynomial (Fin n) K ⧸ I + let η : A →ₐ[K] E := IsScalarTower.toAlgHom K A E + let v : Fin (m + 1) → E := fun i => η ((Ideal.Quotient.mk I) (l i)) + let R := IntermediateField.adjoin K (Set.range v) + let u : Fin (m + 1) → R := fun i => + ⟨v i, IntermediateField.subset_adjoin K _ ⟨i, rfl⟩⟩ + have hiA : AlgebraicIndependent K (fun i => (Ideal.Quotient.mk I) (l i)) := + injective_projectionCoordinateMap_iff.mp hi + have hiE : AlgebraicIndependent K v := hiA.map' (IsFractionRing.injective A E) + have hiR : AlgebraicIndependent K u := + AlgebraicIndependent.of_comp (x := u) R.val hiE + obtain ⟨hfinite, hrank⟩ := + bounded_degree_hilbert_functionField_finite_rank_le (R := R) (F := E) I d q hq + (by simpa only [Fintype.card_fin] using hdimdeg) l hl u hiR (fun _ => rfl) + let : Module.Finite R E := hfinite + let P := MvPolynomial (Fin (m + 1)) K + let ψ : P →ₐ[K] A := projectionCoordinateMap I l + let ρ : P →ₐ[K] R := aeval u + have hcomp : R.val.comp ρ = η.comp ψ := by + ext i + simp only [AlgHom.comp_apply, ρ, ψ, projectionCoordinateMap, aeval_X, + Ideal.Quotient.mkₐ_eq_mk] + rfl + let : Algebra P A := ψ.toRingHom.toAlgebra + let : Algebra P R := ρ.toRingHom.toAlgebra + let : Algebra P E := (η.comp ψ).toRingHom.toAlgebra + let : IsScalarTower P A E := IsScalarTower.of_algebraMap_eq (fun _ => rfl) + let : IsScalarTower P R E := IsScalarTower.of_algebraMap_eq + (fun p => congrArg (fun f : P →ₐ[K] E => f p) hcomp.symm) + have hgen : IntermediateField.adjoin K (Set.range u) = ⊤ := by + apply IntermediateField.lift_injective R + rw [IntermediateField.lift_adjoin, IntermediateField.lift_top, ← Set.range_comp] + rfl + let : IsFractionRing P R := isFractionRing_of_generating_tuple ρ hiR + (fun i => X i) (by + convert hgen using 2 + congr 1 + funext i + change (aeval u) (X i) = u i + rw [aeval_X]) + have hbound := integral_projection_fiber_card_le_finrank I l rfl R E hint z s hzero hproj + simpa only [Fintype.card_fin] using + hbound.trans hrank + +/-- The fiber bound for the given normalization, with the function field +and degree estimates obtained from the coordinate ring. -/ +theorem bounded_degree_normalization_fiber_card_le + {K : Type*} [Field K] [CharZero K] {n m : ℕ} (I : Ideal (MvPolynomial (Fin n) K)) [I.IsPrime] + (q : ℕ) (hq : 0 < q) + (l : Fin (m + 1) → MvPolynomial (Fin n) K) + (hl : ∀ i, (l i).totalDegree ≤ q) + (hi : Function.Injective (projectionCoordinateMap I l)) + (hint : (projectionCoordinateMap I l).IsIntegral) + (d : ℕ) (hdimdeg : HasProjectiveDimensionDegree I m d) + (z : Fin (m + 1) → K) (s : Finset (Fin n → K)) + (hzero : ∀ x ∈ s, x ∈ idealZeroLocus I) + (hproj : ∀ x ∈ s, ∀ i, eval x (l i) = z i) : + s.card ≤ d * q ^ (m + 1) := + bounded_degree_normalization_fiber_card_le_of_fractionField + (E := FractionRing (MvPolynomial (Fin n) K ⧸ I)) I q hq l hl hi hint d hdimdeg z s hzero hproj + +/-- The linear case `q = 1` of `bounded_degree_normalization_fiber_card_le`: +a fiber of a linear normalization has at most `d` points. -/ +theorem linear_normalization_fiber_card_le + {K : Type*} [Field K] [CharZero K] {n m : ℕ} (I : Ideal (MvPolynomial (Fin n) K)) [I.IsPrime] + (l : Fin (m + 1) → MvPolynomial (Fin n) K) + (hl : ∀ i, (l i).totalDegree ≤ 1) + (hi : Function.Injective (projectionCoordinateMap I l)) + (hint : (projectionCoordinateMap I l).IsIntegral) + (d : ℕ) (hdimdeg : HasProjectiveDimensionDegree I m d) + (z : Fin (m + 1) → K) (s : Finset (Fin n → K)) + (hzero : ∀ x ∈ s, x ∈ idealZeroLocus I) + (hproj : ∀ x ∈ s, ∀ i, eval x (l i) = z i) : + s.card ≤ d := by + simpa only [one_pow, mul_one] using bounded_degree_normalization_fiber_card_le I 1 one_pos l hl + hi hint d hdimdeg z s hzero hproj + +end +end CCDN diff --git a/LeanPool/CCDN/Projection/BoundedDegreePreservingProjection.lean b/LeanPool/CCDN/Projection/BoundedDegreePreservingProjection.lean new file mode 100644 index 0000000000..9866f1cc5b --- /dev/null +++ b/LeanPool/CCDN/Projection/BoundedDegreePreservingProjection.lean @@ -0,0 +1,141 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.Algebra.IdealZeroLocus +public import LeanPool.CCDN.Projection.CurvePrimitivePair +public import LeanPool.CCDN.Projection.InfinityParameterReduction +public import Mathlib.FieldTheory.RatFunc.AsPolynomial +import LeanPool.CCDN.Hilbert.RecurrenceParameterTranscendence +import LeanPool.CCDN.Projection.BoundedInfinityAvoidance +import LeanPool.CCDN.Projection.BoundedInfinityParameter +import LeanPool.CCDN.Projection.DegreePreservingProjectionCount +import LeanPool.CCDN.Projection.FullDegreePrimitiveProjection + +/-! +Every positive-degree prime affine curve in characteristic zero admits +an explicitly bounded integral linear plane projection preserving its degree. +The first coordinate is constructed by infinity avoidance and the second by +the quantitative primitive-element theorem. + +Construct the rational-function field embedding from a transcendental +coordinate, and prove the degree bound for the resulting field extension. +-/ + +@[expose] public section +namespace CCDN +noncomputable section + +/-- Send the rational-function variable to a transcendental element. -/ +private def transcendentalRatFuncMap {K F : Type*} [Field K] [Field F] [Algebra K F] + (u : F) (hu : Transcendental K u) : RatFunc K →ₐ[K] F := + RatFunc.liftAlgHom (Polynomial.aeval u) + (nonZeroDivisors_le_comap_nonZeroDivisors_of_injective _ (transcendental_iff_injective.mp hu)) + +private lemma transcendentalRatFuncMap_X {K F : Type*} [Field K] [Field F] [Algebra K F] + (u : F) (hu : Transcendental K u) : transcendentalRatFuncMap u hu RatFunc.X = u := by + change RatFunc.liftRingHom (Polynomial.aeval u).toRingHom _ RatFunc.X = u + exact (RatFunc.liftRingHom_X (Polynomial.aeval u).toRingHom _).trans (Polynomial.aeval_X u) + +end +end CCDN + +namespace CCDN +noncomputable section + +section + +/-! An infinity-avoiding linear form gives a degree-preserving plane +projection. The parameter field and the second coordinate are constructed. -/ + +open MvPolynomial + +/-- Avoiding infinity and the certified affine Hilbert degree suffice for a +degree-preserving plane projection. -/ +private theorem exists_degree_preserving_projection_of_infinity_avoidance {K L F : Type*} + [Field K] [CharZero K] [Field L] [Algebra K L] [IsAlgClosed L] + [Field F] [Algebra K F] {n : ℕ} + (I : Ideal (MvPolynomial (Fin n) K)) [I.IsPrime] + [Algebra (MvPolynomial (Fin n) K ⧸ I) F] + [IsScalarTower K (MvPolynomial (Fin n) K ⧸ I) F] + [IsFractionRing (MvPolynomial (Fin n) K ⧸ I) F] + (d : ℕ) (hd : 0 < d) (hdegree : HasFilteredCurveDegree I d) + (l : MvPolynomial (Fin n) K) (hl : l.IsHomogeneous 1) + (hzero : ∀ z ∈ MvPolynomial.zeroLocus L (leadingInfinityIdeal I ⊔ Ideal.span {l}), z = 0) : + ∃ (k : ℕ) (f : MvPolynomial (Fin 2) K), k ≤ n * d ^ 2 ∧ + Irreducible f ∧ f.totalDegree = d ∧ + ∀ g, aeval (![l, momentCoordinate K n k]) g ∈ I ↔ f ∣ g := by + obtain ⟨N, hrec⟩ := filtration_recurrence_of_infinity_avoidance I l hl hzero + have hu := recurrence_parameter_transcendental I d hd hdegree l N hrec + let u := algebraMap (MvPolynomial (Fin n) K ⧸ I) F ((Ideal.Quotient.mk I) l) + have huF : Transcendental K u := by + intro h + exact hu ((isAlgebraic_algHom_iff + (IsScalarTower.toAlgHom K (MvPolynomial (Fin n) K ⧸ I) F) + (IsFractionRing.injective _ _)).mp h) + let φ := transcendentalRatFuncMap u huF + let : Algebra (RatFunc K) F := φ.toRingHom.toAlgebra + let : IsScalarTower K (RatFunc K) F := IsScalarTower.of_algHom φ + apply exists_full_degree_primitive_projection (F := F) I d hdegree l hl.totalDegree_le + _ N hrec + exact (transcendentalRatFuncMap_X u huF).symm + +end + +open MvPolynomial + +/-- A bounded degree-preserving plane projection of a prime affine curve in +characteristic zero. The curve need not be geometrically integral. -/ +theorem exists_bounded_degree_preserving_projection {K : Type*} [Field K] [CharZero K] + {n : ℕ} (I : Ideal (MvPolynomial (Fin n) K)) [I.IsPrime] + (d : ℕ) (hd : 0 < d) (hdegree : HasFilteredCurveDegree I d) : + ∃ (k₀ k₁ : ℕ) (f : MvPolynomial (Fin 2) K), + k₀ ≤ n ^ 2 * d ∧ k₁ ≤ n * d ^ 2 ∧ Irreducible f ∧ f.totalDegree = d ∧ + Transcendental K ((Ideal.Quotient.mk I) (momentCoordinate K n k₀)) ∧ + ∀ g, aeval (![momentCoordinate K n k₀, momentCoordinate K n k₁]) g ∈ I ↔ f ∣ g := by + obtain ⟨k₀, hk₀, havoid⟩ := + exists_bounded_infinity_parameter (L := AlgebraicClosure K) I d hdegree + obtain ⟨N, hrec⟩ := filtration_recurrence_of_infinity_avoidance I + (momentCoordinate K n k₀) (momentCoordinate_homogeneous K n k₀) havoid + have htrans := recurrence_parameter_transcendental I d hd hdegree _ N hrec + obtain ⟨k₁, f, hk₁, hf, hfd, hker⟩ := + exists_degree_preserving_projection_of_infinity_avoidance + (F := FractionRing (MvPolynomial (Fin n) K ⧸ I)) I d hd hdegree + (momentCoordinate K n k₀) (momentCoordinate_homogeneous K n k₀) havoid + exact ⟨k₀, k₁, f, hk₀, hk₁, hf, hfd, htrans, hker⟩ + +/-- An integral affine curve of degree `d` has a projection to the plane by two moment +coordinates of bounded index whose image is cut out by an irreducible equation of degree `d`, +and which identifies at most `d(d-1)` points of any finite subset of the curve. -/ +theorem exists_bounded_projection_card_le {K : Type*} [Field K] [CharZero K] + [DecidableEq (Fin 2 → K)] {n : ℕ} + (I : Ideal (MvPolynomial (Fin n) K)) [I.IsPrime] + (d : ℕ) (hd : 0 < d) (hdegree : HasFilteredCurveDegree I d) : + ∃ (k₀ k₁ : ℕ) (f : MvPolynomial (Fin 2) K), + k₀ ≤ n ^ 2 * d ∧ k₁ ≤ n * d ^ 2 ∧ Irreducible f ∧ f.totalDegree = d ∧ + (∀ g, aeval (![momentCoordinate K n k₀, momentCoordinate K n k₁]) g ∈ I ↔ f ∣ g) ∧ + ∀ S : Finset (Fin n → K), (∀ z ∈ S, z ∈ idealZeroLocus I) → + S.card ≤ (S.image (fun z => + ![eval z (momentCoordinate K n k₀), eval z (momentCoordinate K n k₁)])).card + + d * (d - 1) := by + obtain ⟨k₀, k₁, f, hk₀, hk₁, hf, hfd, hu, hker⟩ := + exists_bounded_degree_preserving_projection I d hd hdegree + refine ⟨k₀, k₁, f, hk₀, hk₁, hf, hfd, hker, ?_⟩ + intro S hS + let a := ![momentCoordinate K n k₀, momentCoordinate K n k₁] + have ha (j : Fin 2) : (a j).totalDegree ≤ 1 := by + fin_cases j <;> exact momentCoordinate_degree _ _ _ + have hc := degree_preserving_projection_card_le I a ha d hdegree hu f hf.ne_zero hfd hker S hS + convert hc using 1 + congr 3 + funext z j + fin_cases j <;> rfl + +end +end CCDN diff --git a/LeanPool/CCDN/Projection/BoundedInfinityAvoidance.lean b/LeanPool/CCDN/Projection/BoundedInfinityAvoidance.lean new file mode 100644 index 0000000000..b980b393a4 --- /dev/null +++ b/LeanPool/CCDN/Projection/BoundedInfinityAvoidance.lean @@ -0,0 +1,47 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.Projection.CurvePrimitivePair +public import LeanPool.CCDN.Projection.InfinityParameterReduction +public import LeanPool.CCDN.Algebra.BoundedLinearSeparation +import LeanPool.CCDN.Algebra.CoordinatePowerGeneration +import Mathlib.Algebra.Polynomial.OfFn + +/-! An explicit bounded integer moment form avoids any prescribed finite set +of nonzero directions. No separation of pairs is needed for this step. -/ + +@[expose] public section +namespace CCDN +noncomputable section + +/-- The moment coordinate `∑ j, k ^ j * X j` is homogeneous of degree one. -/ +lemma momentCoordinate_homogeneous (K : Type*) [Field K] (n k : ℕ) : + (momentCoordinate K n k).IsHomogeneous 1 := by + apply MvPolynomial.IsHomogeneous.sum + intro j _ + exact MvPolynomial.isHomogeneous_C_mul_X _ _ + +/-- Avoiding infinity gives the eventual recurrence of the original +coordinate filtration. -/ +theorem filtration_recurrence_of_infinity_avoidance {K L σ : Type*} + [Field K] [Field L] [Algebra K L] [IsAlgClosed L] [Finite σ] + (I : Ideal (MvPolynomial σ K)) (l : MvPolynomial σ K) (hl : l.IsHomogeneous 1) + (hzero : ∀ z ∈ MvPolynomial.zeroLocus L (leadingInfinityIdeal I ⊔ Ideal.span {l}), z = 0) : + ∃ N : ℕ, ∀ m ≥ N, filteredCoordinateSpace I (m + 1) = + filteredCoordinateSpace I m ⊔ (filteredCoordinateSpace I m).map + (LinearMap.mulLeft K ((Ideal.Quotient.mk I) l)) := by + classical + let := Fintype.ofFinite σ + obtain ⟨N, hN, hred⟩ := exists_power_reductions_of_infinity_avoidance I l hl hzero + exact ⟨Fintype.card σ * N, + eventual_generation_of_power_reductions I l hl.totalDegree_le N hN hred⟩ + +end +end CCDN diff --git a/LeanPool/CCDN/Projection/BoundedInfinityParameter.lean b/LeanPool/CCDN/Projection/BoundedInfinityParameter.lean new file mode 100644 index 0000000000..cb871c442b --- /dev/null +++ b/LeanPool/CCDN/Projection/BoundedInfinityParameter.lean @@ -0,0 +1,81 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.Projection.CurvePrimitivePair +public import LeanPool.CCDN.Projection.InfinityParameterReduction +import LeanPool.CCDN.Projection.BoundedInfinityAvoidance +import LeanPool.CCDN.Projection.InfinityChartCardinality +import LeanPool.CCDN.Projection.InfinityScaling + +/-! Finite normalized charts give a bounded integral moment parameter +avoiding every nonzero infinity direction. -/ + +@[expose] public section +namespace CCDN +noncomputable section +open MvPolynomial + +private lemma momentCoordinate_aeval {K L : Type*} [Field K] [Field L] [Algebra K L] + {n : ℕ} (k : ℕ) (z : Fin n → L) : + aeval z (momentCoordinate K n k) = ∑ j : Fin n, z j * (k : L) ^ j.val := by + simp [momentCoordinate, mul_comm] + +/-- The first coordinate can be chosen with an explicit bound n²*d on its +integer moment parameter. The overlap between normalized charts is harmless. -/ +theorem exists_bounded_infinity_parameter {K L : Type*} [Field K] [Field L] + [Algebra K L] [CharZero L] {n : ℕ} (I : Ideal (MvPolynomial (Fin n) K)) (d : ℕ) + (hdegree : HasFilteredCurveDegree I d) : + ∃ k : ℕ, k ≤ n ^ 2 * d ∧ + ∀ z ∈ MvPolynomial.zeroLocus L + (leadingInfinityIdeal I ⊔ Ideal.span {momentCoordinate K n k}), z = 0 := by + classical + choose S hcard hS using (fun j : Fin n => exists_finite_infinity_chart (L := L) I d hdegree j) + let T := (j : Fin n) × {z : Fin n → L // z ∈ S j} + let v (t : T) := t.2.val + have hv (t : T) : v t ≠ 0 := by + intro he + have hj := ((hS t.1 t.2.val).mp t.2.property).2 + have hh := congrFun he t.1 + change t.2.val t.1 = 0 at hh + rw [hh] at hj + exact zero_ne_one hj + have hT : Fintype.card T ≤ n * d := by + rw [Fintype.card_sigma] + calc + _ ≤ ∑ _j : Fin n, d := Finset.sum_le_sum (fun j _ => by + simpa only [Fintype.card_coe] using hcard j) + _ = _ := by simp + obtain ⟨k, hk, hnon⟩ := exists_bounded_moment_nonvanishing v hv + refine ⟨k, hk.trans (by nlinarith), ?_⟩ + intro z hz + by_contra hzne + obtain ⟨j, hj⟩ : ∃ j : Fin n, z j ≠ 0 := by + by_contra! he + exact hzne (funext he) + have hzI : z ∈ MvPolynomial.zeroLocus L (leadingInfinityIdeal I) := + fun f hf => hz f ((le_sup_left : leadingInfinityIdeal I ≤ + leadingInfinityIdeal I ⊔ Ideal.span {momentCoordinate K n k}) hf) + let w : Fin n → L := fun i => (z j)⁻¹ * z i + have hw : w ∈ S j := (hS j w).mpr + ⟨infinity_zeroLocus_scale I z hzI (z j)⁻¹, by simp [w, hj]⟩ + let t : T := ⟨j, w, hw⟩ + have hwzero : aeval w (momentCoordinate K n k) = 0 := by + rw [homogeneous_aeval_scale _ (momentCoordinate_homogeneous K n k)] + have hz0 := hz (momentCoordinate K n k) + ((le_sup_right : Ideal.span {momentCoordinate K n k} ≤ + leadingInfinityIdeal I ⊔ Ideal.span {momentCoordinate K n k}) + (Ideal.subset_span (Set.mem_singleton _))) + rw [hz0, mul_zero] + apply hnon t + rw [momentCoordinate_aeval] at hwzero + exact hwzero + +end +end CCDN diff --git a/LeanPool/CCDN/Projection/BoundedIntegerNormalization.lean b/LeanPool/CCDN/Projection/BoundedIntegerNormalization.lean new file mode 100644 index 0000000000..d6ddc95130 --- /dev/null +++ b/LeanPool/CCDN/Projection/BoundedIntegerNormalization.lean @@ -0,0 +1,152 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.Algebra.IntegerLinearForms +public import LeanPool.CCDN.Hilbert.FilteredHilbertPolynomial +public import LeanPool.CCDN.Projection.ProjectiveClosureProjection +public import Mathlib.RingTheory.IntegralClosure.IsIntegral.Defs +import LeanPool.CCDN.Hilbert.FullDimension +import LeanPool.CCDN.Projection.BoundedClosureElimination +import LeanPool.CCDN.Projection.ClosureIntegralComposition +import Mathlib.RingTheory.IntegralClosure.IsIntegralClosure.Basic + +/-! Constructive bounded linear Noether normalization. The coefficients of +the integer linear forms are bounded by n! (d+1)^n, and the map of projective closures +fixing the homogenizing coordinate is integral as well. -/ + +@[expose] public section +namespace CCDN +noncomputable section +open MvPolynomial + +/-- The coefficient bound `n! * (d + 1) ^ n` for the bounded linear Noether normalization of a +degree-`d` variety in `n` variables. -/ +def integerNormalizationHeight (n d : ℕ) : ℕ := n.factorial * (d + 1) ^ n + +/-- The normalization height is positive. -/ +lemma integerNormalizationHeight_pos (n d : ℕ) : 0 < integerNormalizationHeight n d := by + dsimp [integerNormalizationHeight] + positivity + +private lemma integerNormalizationHeight_mono (n : ℕ) {d e : ℕ} (h : d ≤ e) : + integerNormalizationHeight n d ≤ integerNormalizationHeight n e := by + dsimp [integerNormalizationHeight] + gcongr + +private lemma integerNormalizationHeight_step (n d : ℕ) : + n * integerNormalizationHeight n d * (d + 1) ≤ + integerNormalizationHeight (n + 1) d := by + dsimp [integerNormalizationHeight] + rw [Nat.factorial_succ, pow_succ] + nlinarith [Nat.zero_le (n.factorial * (d + 1) ^ n * (d + 1))] + +/-- Integer linear normalization of a prime quotient of positive Hilbert dimension. The +resulting polynomial map is injective and integral, and so is the induced map of projective +closures fixing the homogenizing coordinate. -/ +theorem exists_bounded_integer_closure_normalization (m k : ℕ) + (I : Ideal (MvPolynomial (Fin (m + 1 + k)) ℚ)) [I.IsPrime] (d : ℕ) + (hdimdeg : HasProjectiveDimensionDegree I m d) : + ∃ a : Fin (m + 1) → Fin (m + 1 + k) → ℤ, + (∀ i j, |a i j| ≤ integerNormalizationHeight (m + 1 + k) d) ∧ + (projectionCoordinateMap I (fun i => integerLinearForm (a i))).IsIntegral ∧ + Function.Injective (projectionCoordinateMap I (fun i => integerLinearForm (a i))) ∧ + (projectionCoordinateMap (projectiveClosureIdeal I) + (closureProjectionCoordinates (fun i => integerLinearForm (a i)))).IsIntegral := by + induction k generalizing d with + | zero => + let a : Fin (m + 1) → Fin (m + 1) → ℤ := fun i j => if j = i then 1 else 0 + have heq : projectionCoordinateMap I (fun i => integerLinearForm (a i)) = + Ideal.Quotient.mkₐ ℚ I := by + ext i + simp [projectionCoordinateMap, a, integerLinearForm_identity] + have hI0 : I = ⊥ := ideal_eq_bot_of_full_hilbert_dimension I (Ideal.IsPrime.ne_top ‹I.IsPrime›) + (by simpa only [Fintype.card_fin] using hdimdeg.natDegree_eq) + refine ⟨a, ?_, ?_, ?_, ?_⟩ + · intro i j + have hH : (1 : ℤ) ≤ integerNormalizationHeight (m + 1) d := by + exact_mod_cast Nat.succ_le_of_lt (integerNormalizationHeight_pos (m + 1) d) + by_cases he : j = i + · simpa [a, he] using hH + · simp only [a, ite_eq_right he, abs_zero] + exact Nat.cast_nonneg _ + · rw [heq] + exact RingHom.isIntegral_of_surjective _ (Ideal.Quotient.mkₐ_surjective ℚ I) + · rw [heq] + apply (RingHom.injective_iff_ker_eq_bot _).mpr + change RingHom.ker ((Ideal.Quotient.mkₐ ℚ I) : + MvPolynomial (Fin (m + 1)) ℚ →+* MvPolynomial (Fin (m + 1)) ℚ ⧸ I) = ⊥ + rwa [Ideal.Quotient.mkₐ_ker] + · have heqcone : projectionCoordinateMap (projectiveClosureIdeal I) + (closureProjectionCoordinates (fun i => integerLinearForm (a i))) = + Ideal.Quotient.mkₐ ℚ (projectiveClosureIdeal I) := by + ext i + refine Fin.cases ?_ (fun j => ?_) i <;> + simp [projectionCoordinateMap, closureProjectionCoordinates, a, + integerLinearForm_identity] + rw [heqcone] + exact RingHom.isIntegral_of_surjective _ (Ideal.Quotient.mkₐ_surjective _ _) + | succ k ih => + obtain ⟨c, e, hc, he, hed, hintc, hprime, hdimdegJ, hconec⟩ := + exists_fin_integer_closure_elimination_step (n := m + 1 + k) (m := m) I d hdimdeg + (by omega) + let ψ := projectionCoordinateMap I (fun i => integerLinearForm (c i)) + let J := RingHom.ker ψ + have : J.IsPrime := hprime + obtain ⟨a, ha, hinta, hinja, hconea⟩ := ih J e hdimdegJ + let a' : Fin (m + 1) → Fin (m + 1 + (k + 1)) → ℤ := + fun i j => ∑ t, a i t * c t j + let ψa := projectionCoordinateMap J (fun i => integerLinearForm (a i)) + let φ := Ideal.kerLiftAlg ψ + have hφ : φ.IsIntegral := hintc.kerLift + have hcomp := RingHom.IsIntegral.trans ψa.toRingHom φ.toRingHom hinta hφ + have heq : φ.comp ψa = projectionCoordinateMap I (fun i => integerLinearForm (a' i)) := by + ext i + simp only [AlgHom.comp_apply, ψa, projectionCoordinateMap, aeval_X, + Ideal.Quotient.mkₐ_eq_mk] + change (Ideal.Quotient.mk I) + (aeval (fun t => integerLinearForm (c t)) (integerLinearForm (a i))) = + (Ideal.Quotient.mk I) (integerLinearForm (a' i)) + rw [integerLinearForm_comp] + rfl + refine ⟨a', ?_, ?_, ?_, ?_⟩ + · intro i j + have hbound := integerLinearForm_matrix_height (a i) c + (integerNormalizationHeight (m + 1 + k) e) (d + 1) (ha i) + (fun i j => by exact_mod_cast hc i j) j + simp only [Fintype.card_fin] at hbound + apply hbound.trans + have hmono := integerNormalizationHeight_mono (m + 1 + k) hed + have hstep := integerNormalizationHeight_step (m + 1 + k) d + exact_mod_cast (Nat.mul_le_mul_right (d + 1) + (Nat.mul_le_mul_left (m + 1 + k) hmono)).trans hstep + · change (φ.comp ψa).IsIntegral at hcomp + rwa [heq] at hcomp + · rw [← heq] + exact (Ideal.kerLiftAlg_injective ψ).comp hinja + · have hcone := closure_projection_integral_comp I + (fun i => integerLinearForm (c i)) + (fun i => linearForm_isHomogeneous _) + (fun i => integerLinearForm (a i)) hconec hconea + simpa only [integerLinearForm_comp] using hcone + +/-- Integer linear normalization of a prime quotient of positive +Hilbert dimension. The resulting polynomial map is both injective and integral. -/ +theorem exists_bounded_integer_normalization (m k : ℕ) + (I : Ideal (MvPolynomial (Fin (m + 1 + k)) ℚ)) [I.IsPrime] (d : ℕ) + (hdimdeg : HasProjectiveDimensionDegree I m d) : + ∃ a : Fin (m + 1) → Fin (m + 1 + k) → ℤ, + (∀ i j, |a i j| ≤ integerNormalizationHeight (m + 1 + k) d) ∧ + (projectionCoordinateMap I (fun i => integerLinearForm (a i))).IsIntegral ∧ + Function.Injective (projectionCoordinateMap I (fun i => integerLinearForm (a i))) := by + obtain ⟨a, ha, hint, hinj, -⟩ := exists_bounded_integer_closure_normalization m k I d hdimdeg + exact ⟨a, ha, hint, hinj⟩ + +end +end CCDN diff --git a/LeanPool/CCDN/Projection/BoundedLinearElimination.lean b/LeanPool/CCDN/Projection/BoundedLinearElimination.lean new file mode 100644 index 0000000000..1015b74613 --- /dev/null +++ b/LeanPool/CCDN/Projection/BoundedLinearElimination.lean @@ -0,0 +1,123 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.AuxiliaryPolynomial.HypersurfaceAffineCoordinates +public import Mathlib.RingTheory.Ideal.Quotient.Operations +public import Mathlib.RingTheory.IntegralClosure.IsIntegral.Defs +public import Mathlib.RingTheory.MvPolynomial.Homogeneous +import LeanPool.CCDN.Algebra.IntegerPolynomialAvoidance +import LeanPool.CCDN.ForMathlib.MvPolynomial +import Mathlib.RingTheory.IntegralClosure.IsIntegralClosure.Basic + +/-! A single bounded linear elimination step. A chosen nonzero ideal equation +of degree d becomes monic after a linear coordinate change with integer +direction bounded by d+1. The quotient is integral over the remaining linear +coordinates. -/ + +@[expose] public section +namespace CCDN +noncomputable section +open MvPolynomial + +/-- A bounded integral direction simultaneously avoids the top form and the +coordinate hyperplane needed for an invertible linear substitution. -/ +theorem exists_bounded_full_degree_direction {σ : Type*} [Finite σ] + (f : MvPolynomial (Option σ) ℚ) (hf : f ≠ 0) : + ∃ b : Option σ → ℤ, (∀ i, 0 ≤ b i ∧ b i ≤ f.totalDegree + 1) ∧ + b none ≠ 0 ∧ + eval (fun i => (b i : ℚ)) (homogeneousComponent f.totalDegree f) ≠ 0 := by + classical + let := Fintype.ofFinite σ + let P := X none * homogeneousComponent f.totalDegree f + have hp : P ≠ 0 := mul_ne_zero (X_ne_zero none) (homogeneousComponent_totalDegree_ne_zero hf) + have hd : P.totalDegree ≤ f.totalDegree + 1 := by + apply (totalDegree_mul _ _).trans + rw [totalDegree_X] + have hh := (homogeneousComponent_isHomogeneous f.totalDegree f).totalDegree_le + omega + obtain ⟨b, hb, hval⟩ := exists_integer_grid_eval_ne_zero_of_totalDegree P hp _ hd + change eval (fun i => (b i : ℚ)) + (X none * homogeneousComponent f.totalDegree f) ≠ 0 at hval + rw [eval_mul, eval_X] at hval + exact ⟨b, hb, by exact_mod_cast (mul_ne_zero_iff.mp hval).1, + (mul_ne_zero_iff.mp hval).2⟩ + +/-- The retained variables, transported back through the coordinate +automorphism, as elements of the original quotient. -/ +def coordinateEliminationMap {K σ : Type*} [Field K] + (I : Ideal (MvPolynomial (Option σ) K)) + (e : MvPolynomial (Option σ) K ≃ₐ[K] MvPolynomial (Option σ) K) : + MvPolynomial σ K →ₐ[K] MvPolynomial (Option σ) K ⧸ I := + (Ideal.Quotient.mkₐ K I).comp (e.symm.toAlgHom.comp (rename some)) + +/-- For the linear coordinate change attached to a direction `b` with `b none ≠ 0`, the elimination +map sends the variable `X i` to the class of `X (some i) - (b (some i) / b none) * X none`. -/ +lemma coordinateEliminationMap_direction_X {K σ : Type*} [Field K] + (I : Ideal (MvPolynomial (Option σ) K)) (b : Option σ → K) + (hb : b none ≠ 0) (i : σ) : + coordinateEliminationMap I (hypersurfaceAffineCoordinates 0 b hb) (X i) = + (Ideal.Quotient.mk I) (X (some i) - C (b (some i) / b none) * X none) := by + simp [coordinateEliminationMap, hypersurfaceAffineCoordinates, + directionCoordinates, directionCoordinatesInv, + div_eq_mul_inv, map_mul, mul_assoc] + +/-- A monic equation in the eliminated coordinate proves integrality over +the retained coordinates, for any ideal containing the equation. -/ +theorem coordinateEliminationMap_isIntegral {K σ : Type*} [Field K] + (I : Ideal (MvPolynomial (Option σ) K)) + (e : MvPolynomial (Option σ) K ≃ₐ[K] MvPolynomial (Option σ) K) + (q : MvPolynomial (Option σ) K) + (hq : q ∈ I.map e) + (hm : (optionEquivLeft K σ q).Monic) : + (coordinateEliminationMap I e).IsIntegral := by + let E := e.trans (optionEquivLeft K σ) + let J := I.map E + have hJ : (optionEquivLeft K σ q) ∈ J := by + change (optionEquivLeft K σ q) ∈ I.map + ((optionEquivLeft K σ).toRingEquiv.toRingHom.comp e.toRingEquiv.toRingHom) + rw [← Ideal.map_map] + exact Ideal.mem_map_of_mem _ hq + let φ := (Ideal.Quotient.mkₐ (MvPolynomial σ K) J).comp + (Algebra.ofId (MvPolynomial σ K) (Polynomial (MvPolynomial σ K))) + have hφ : φ.IsIntegral := hm.quotient_isIntegral hJ + have hmap : J.map E.symm = I := by + change Ideal.map E.symm.toRingEquiv.toRingHom + (Ideal.map E.toRingEquiv.toRingHom I) = I + rw [Ideal.map_map] + have he : E.symm.toRingEquiv.toRingHom.comp E.toRingEquiv.toRingHom = RingHom.id _ := + E.toRingEquiv.symm_toRingHom_comp_toRingHom + change I.map (E.symm.toRingEquiv.toRingHom.comp E.toRingEquiv.toRingHom) = I + rw [he, Ideal.map_id] + let ψ : (Polynomial (MvPolynomial σ K) ⧸ J) ≃ₐ[K] + (MvPolynomial (Option σ) K ⧸ I) := + Ideal.quotientEquivAlg J I E.symm hmap.symm + have hψ : RingHom.IsIntegral (R := Polynomial (MvPolynomial σ K) ⧸ J) + (A := MvPolynomial (Option σ) K ⧸ I) ψ.toRingEquiv.toRingHom := by + apply RingHom.isIntegral_of_surjective + exact ψ.surjective + have hint := RingHom.IsIntegral.trans (R := MvPolynomial σ K) + (S := Polynomial (MvPolynomial σ K) ⧸ J) + (T := MvPolynomial (Option σ) K ⧸ I) + φ.toRingHom ψ.toRingEquiv.toRingHom hφ hψ + have heq : ψ.toAlgHom.comp (φ.restrictScalars K) = coordinateEliminationMap I e := by + ext i + suffices h : ψ (Ideal.Quotient.mk J (Polynomial.C (X i))) = + (Ideal.Quotient.mk I) (e.symm (X (some i))) by + simpa [φ, coordinateEliminationMap, + IsScalarTower.algebraMap_eq (MvPolynomial σ K) + (Polynomial (MvPolynomial σ K)) (Polynomial (MvPolynomial σ K) ⧸ J)] using h + change (Ideal.Quotient.mk I) (E.symm (Polynomial.C (X i))) = + (Ideal.Quotient.mk I) (e.symm (X (some i))) + simp [E, optionEquivLeft_symm_C_X] + change (ψ.toAlgHom.comp (φ.restrictScalars K)).IsIntegral at hint + rwa [heq] at hint + +end +end CCDN diff --git a/LeanPool/CCDN/Projection/BoundedLinearProjection.lean b/LeanPool/CCDN/Projection/BoundedLinearProjection.lean new file mode 100644 index 0000000000..a6c6c821b1 --- /dev/null +++ b/LeanPool/CCDN/Projection/BoundedLinearProjection.lean @@ -0,0 +1,199 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.Height.ProjectiveBox +import LeanPool.CCDN.Algebra.BoundedRowSeparation +import LeanPool.CCDN.Height.ProjectiveIntegerHeight + +/-! An explicit integral linear projection away from a bounded rational +linear center. Its kernel is exactly the span of the given independent +rows, and its coefficient bound is uniform in their entries. -/ + +@[expose] public section +namespace CCDN +noncomputable section +open Matrix + +private lemma scalar_complement_projection_square {R ι κ : Type*} [CommRing R] + [Fintype ι] [Fintype κ] [DecidableEq ι] [DecidableEq κ] + (A L : Matrix ι κ R) (D : R) + (hLA : L * A.transpose = D • (1 : Matrix ι ι R)) : + (D • (1 : Matrix κ κ R) - A.transpose * L) * + (D • (1 : Matrix κ κ R) - A.transpose * L) = + D • (D • (1 : Matrix κ κ R) - A.transpose * L) := by + have hPA : (D • (1 : Matrix κ κ R) - A.transpose * L) * A.transpose = 0 := by + rw [Matrix.sub_mul, Matrix.smul_mul, Matrix.one_mul, Matrix.mul_assoc, + hLA, Matrix.mul_smul, Matrix.mul_one, sub_self] + rw [Matrix.mul_sub, Matrix.mul_smul, Matrix.mul_one, + ← Matrix.mul_assoc, hPA, Matrix.zero_mul, sub_zero] + +private lemma scalar_complement_projection_kernel {K ι κ : Type*} [Field K] + [Fintype ι] [Fintype κ] [DecidableEq ι] [DecidableEq κ] + (A L : Matrix ι κ K) (D : K) (hD : D ≠ 0) + (hLA : L * A.transpose = D • (1 : Matrix ι ι K)) (x : κ → K) : + (D • (1 : Matrix κ κ K) - A.transpose * L) *ᵥ x = 0 ↔ + x ∈ Submodule.span K (Set.range A.row) := by + have hrange : Submodule.span K (Set.range A.row) = + LinearMap.range A.transpose.mulVecLin := by + rw [Matrix.range_mulVecLin] + rfl + rw [hrange] + constructor + · intro hx + have he : D • x = A.transpose *ᵥ (L *ᵥ x) := by + apply sub_eq_zero.mp + simpa only [Matrix.sub_mulVec, Matrix.smul_mulVec, Matrix.one_mulVec, + Matrix.mulVec_mulVec] using hx + refine ⟨D⁻¹ • (L *ᵥ x), ?_⟩ + change A.transpose *ᵥ (D⁻¹ • (L *ᵥ x)) = x + rw [Matrix.mulVec_smul, ← he, smul_smul, inv_mul_cancel₀ hD, one_smul] + · rintro ⟨y, rfl⟩ + change (D • (1 : Matrix κ κ K) - A.transpose * L) *ᵥ (A.transpose *ᵥ y) = 0 + rw [Matrix.sub_mulVec, Matrix.smul_mulVec, Matrix.one_mulVec, + Matrix.mulVec_mulVec, Matrix.mul_assoc, hLA, Matrix.mul_smul, + Matrix.mul_one, Matrix.smul_mulVec, sub_self] + +/-- Constructing the matrix requires only independent bounded integer rows. +In particular it is nonvanishing off their span. -/ +theorem exists_bounded_integer_scaled_projection {s r : ℕ} + (A : Matrix (Fin (s + 1)) (Fin r) ℤ) + (hli : LinearIndependent ℚ (fun i => fun j => (A i j : ℚ))) + (T : ℕ) (hA : ∀ i j, |A i j| ≤ (T : ℤ)) : + ∃ D : ℤ, D ≠ 0 ∧ ∃ P : Matrix (Fin r) (Fin r) ℤ, + (∀ i j, |P i j| ≤ + 2 * ((s + 1).factorial : ℤ) * (T : ℤ) ^ (s + 1)) ∧ + P * P = D • P ∧ + ∀ x : Fin r → ℚ, + (P.map (Int.castRingHom ℚ)) *ᵥ x = 0 ↔ + x ∈ Submodule.span ℚ (Set.range (fun i => fun j => (A i j : ℚ))) := by + classical + obtain ⟨D, hD, hDb, b, hb, hbb⟩ := exists_bounded_integer_dual_rows A hli T hA + let L : Matrix (Fin (s + 1)) (Fin r) ℤ := Matrix.of b + let P : Matrix (Fin r) (Fin r) ℤ := D • 1 - A.transpose * L + let AQ := A.map (Int.castRingHom ℚ) + let LQ := L.map (Int.castRingHom ℚ) + have hLAZ : L * A.transpose = D • (1 : Matrix (Fin (s + 1)) (Fin (s + 1)) ℤ) := by + ext i ℓ + simpa only [L, Matrix.mul_apply, Matrix.transpose_apply, Matrix.of_apply, + Matrix.smul_apply, Matrix.one_apply, smul_eq_mul, mul_ite, mul_one, mul_zero, + mul_comm, eq_comm] using hb i ℓ + have hDQ : (D : ℚ) ≠ 0 := by exact_mod_cast hD + have hLA : LQ * AQ.transpose = (D : ℚ) • (1 : Matrix (Fin (s + 1)) (Fin (s + 1)) ℚ) := by + ext i ℓ + have hh : (∑ j, (A ℓ j : ℚ) * (b i j : ℚ)) = if ℓ = i then (D : ℚ) else 0 := by + exact_mod_cast hb i ℓ + simpa only [LQ, AQ, L, Matrix.mul_apply, Matrix.transpose_apply, Matrix.map_apply, + Int.coe_castRingHom, Matrix.of_apply, Matrix.smul_apply, Matrix.one_apply, + smul_eq_mul, mul_ite, mul_one, mul_zero, mul_comm, eq_comm] using hh + have hPmap : P.map (Int.castRingHom ℚ) = + (D : ℚ) • (1 : Matrix (Fin r) (Fin r) ℚ) - AQ.transpose * LQ := by + ext i j + simp [P, AQ, LQ, Matrix.mul_apply, Matrix.one_apply, Matrix.intCast_apply] + refine ⟨D, hD, P, ?_, scalar_complement_projection_square A L D hLAZ, ?_⟩ + · intro j k + have hfirst : |D * (1 : Matrix (Fin r) (Fin r) ℤ) j k| ≤ + ((s + 1).factorial : ℤ) * (T : ℤ) ^ (s + 1) := by + by_cases h : j = k + · simpa only [Matrix.one_apply, ite_eq_left h, mul_one] using hDb + · simp only [Matrix.one_apply, ite_eq_right h, mul_zero, abs_zero] + positivity + have hsum : |∑ i, A i j * b i k| ≤ + (s + 1 : ℕ) * ((T : ℤ) * ((s.factorial : ℤ) * (T : ℤ) ^ s)) := by + calc + _ ≤ ∑ i, |A i j * b i k| := Finset.abs_sum_le_sum_abs _ _ + _ ≤ ∑ _i : Fin (s + 1), (T : ℤ) * ((s.factorial : ℤ) * (T : ℤ) ^ s) := by + apply Finset.sum_le_sum + intro i _ + rw [abs_mul] + exact mul_le_mul (hA i j) (hbb i k) (abs_nonneg _) (Int.natCast_nonneg _) + _ = _ := by simp + change |D * (1 : Matrix (Fin r) (Fin r) ℤ) j k - ∑ i, A i j * b i k| ≤ _ + calc + _ ≤ |D * (1 : Matrix (Fin r) (Fin r) ℤ) j k| + |∑ i, A i j * b i k| := + abs_sub _ _ + _ ≤ ((s + 1).factorial : ℤ) * (T : ℤ) ^ (s + 1) + + (s + 1 : ℕ) * ((T : ℤ) * ((s.factorial : ℤ) * (T : ℤ) ^ s)) := + add_le_add hfirst hsum + _ = _ := by rw [Nat.factorial_succ, Nat.cast_mul, pow_succ]; ring + · intro x + rw [hPmap] + exact scalar_complement_projection_kernel AQ LQ D hDQ hLA x + +private theorem integer_linear_map_box_bound {m n : ℕ} + (P : Matrix (Fin m) (Fin n) ℤ) {H B : ℝ} (hH : 0 ≤ H) + (hP : ∀ i j, |(P i j : ℝ)| ≤ H) + (z : IntPoint n) (hz : InBox z B) : + ∀ i, |((P *ᵥ z) i : ℝ)| ≤ (n : ℝ) * H * B := by + intro i + simp only [Matrix.mulVec, dotProduct, Int.cast_sum, Int.cast_mul] + calc + _ ≤ ∑ j, |(P i j : ℝ) * (z j : ℝ)| := Finset.abs_sum_le_sum_abs _ _ + _ ≤ ∑ _j : Fin n, H * B := by + apply Finset.sum_le_sum + intro j _ + rw [abs_mul] + exact mul_le_mul (hP i j) (hz j) (abs_nonneg _) hH + _ = _ := by simp [mul_assoc] + +/-- An integer matrix with entries bounded by `H` maps an integer point of the box of radius +`B` to a projective point of multiplicative height at most `n H B` (when the image is nonzero), +since primitive reduction cannot enlarge an integral representative. -/ +private theorem projective_integer_linear_map_height {m n : ℕ} + (P : Matrix (Fin (m + 1)) (Fin n) ℤ) {H B : ℝ} (hH : 0 ≤ H) + (hP : ∀ i j, |(P i j : ℝ)| ≤ H) + (z : IntPoint n) (hz : InBox z B) (himage : P *ᵥ z ≠ 0) : + (Projectivization.mk ℚ (rationalPoint (P *ᵥ z)) + (rationalPoint_ne_zero himage)).mulHeight ≤ (n : ℝ) * H * B := + projective_integer_height_le _ himage (integer_linear_map_box_bound P hH hP z hz) + +private lemma rationalPoint_integer_mulVec {m n : ℕ} + (P : Matrix (Fin m) (Fin n) ℤ) (z : IntPoint n) : + rationalPoint (P *ᵥ z) = + (P.map (Int.castRingHom ℚ)) *ᵥ rationalPoint z := by + funext i + simp [rationalPoint, Matrix.mulVec, dotProduct] + +/-- The height estimate for the projective linear image of any +point outside the matrix kernel. The input height is its intrinsic +multiplicative projective height. -/ +theorem projective_linear_map_height {m n : ℕ} + (P : Matrix (Fin (m + 1)) (Fin (n + 1)) ℤ) + {H : ℝ} (hH : 0 ≤ H) (hP : ∀ i j, |(P i j : ℝ)| ≤ H) + (x : ProjectivePoint n) + (himage : (P.map (Int.castRingHom ℚ)) *ᵥ x.rep ≠ 0) : + (Projectivization.mk ℚ ((P.map (Int.castRingHom ℚ)) *ᵥ x.rep) himage).mulHeight ≤ + (n + 1 : ℕ) * H * x.mulHeight := by + let z := primitiveProjectiveRep x + let w := P *ᵥ z + have hrep := (primitiveProjectiveRep_mk x).trans x.mk_rep.symm + obtain ⟨c, hc⟩ := (Projectivization.mk_eq_mk_iff ℚ _ _ _ _).mp hrep + have hw : rationalPoint w = + (c : ℚ) • ((P.map (Int.castRingHom ℚ)) *ᵥ x.rep) := by + rw [rationalPoint_integer_mulVec] + change (P.map (Int.castRingHom ℚ)) *ᵥ rationalPoint (primitiveProjectiveRep x) = _ + rw [← hc, Matrix.mulVec_smul] + rfl + have hw0 : w ≠ 0 := by + intro hz + have hzero : (c : ℚ) • ((P.map (Int.castRingHom ℚ)) *ᵥ x.rep) = 0 := by + rw [← hw, hz] + rfl + exact himage ((smul_eq_zero.mp hzero).resolve_left c.ne_zero) + have hproj : + Projectivization.mk ℚ (rationalPoint w) (rationalPoint_ne_zero hw0) = + Projectivization.mk ℚ ((P.map (Int.castRingHom ℚ)) *ᵥ x.rep) himage := by + apply (Projectivization.mk_eq_mk_iff' ℚ _ _ _ _).mpr + exact ⟨c, hw.symm⟩ + rw [← hproj] + exact projective_integer_linear_map_height P hH hP z + (primitiveProjectiveRep_bound x) hw0 + +end +end CCDN diff --git a/LeanPool/CCDN/Projection/ClosureIntegralComposition.lean b/LeanPool/CCDN/Projection/ClosureIntegralComposition.lean new file mode 100644 index 0000000000..d36ed4991c --- /dev/null +++ b/LeanPool/CCDN/Projection/ClosureIntegralComposition.lean @@ -0,0 +1,75 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.Projection.ProjectiveClosureProjection +public import Mathlib.RingTheory.IntegralClosure.IsIntegral.Defs +import Mathlib.RingTheory.IntegralClosure.IsIntegralClosure.Basic + +/-! Closure-preserving integral affine projections compose. -/ + +@[expose] public section +namespace CCDN +noncomputable section +open MvPolynomial + +private lemma aeval_closureProjectionCoordinates_rename {K : Type*} [Field K] {m n : ℕ} + (a : Fin m → MvPolynomial (Fin n) K) (f : MvPolynomial (Fin m) K) : + aeval (closureProjectionCoordinates a) (rename Fin.succ f) = + rename Fin.succ (aeval a f) := by + have h : (aeval (R := K) (closureProjectionCoordinates a)).comp (rename Fin.succ) = + (rename Fin.succ).comp (aeval a) := by + ext i + simp [closureProjectionCoordinates] + exact AlgHom.congr_fun h f + +private lemma closureProjectionCoordinates_comp {K : Type*} [Field K] {l m n : ℕ} + (a : Fin l → MvPolynomial (Fin m) K) + (b : Fin m → MvPolynomial (Fin n) K) (i : Fin (l + 1)) : + aeval (closureProjectionCoordinates b) (closureProjectionCoordinates a i) = + closureProjectionCoordinates (fun j => aeval b (a j)) i := by + refine Fin.cases ?_ (fun j => ?_) i + · simp [closureProjectionCoordinates] + · exact aeval_closureProjectionCoordinates_rename b (a j) + +/-- Integrality of projective closure projections is stable under composition: if the closure +projection of `I` by homogeneous linear forms `b` is integral, and the closure projection of the +kernel of `b` by `a` is integral, then the closure projection of `I` by the composite forms +`aeval b (a j)` is integral. -/ +theorem closure_projection_integral_comp + {K : Type*} [Field K] {l m n : ℕ} + (I : Ideal (MvPolynomial (Fin n) K)) + (b : Fin m → MvPolynomial (Fin n) K) (hb : ∀ i, (b i).IsHomogeneous 1) + (a : Fin l → MvPolynomial (Fin m) K) + (hbintegral : (projectionCoordinateMap (projectiveClosureIdeal I) + (closureProjectionCoordinates b)).IsIntegral) + (haintegral : (projectionCoordinateMap + (projectiveClosureIdeal (RingHom.ker (projectionCoordinateMap I b))) + (closureProjectionCoordinates a)).IsIntegral) : + (projectionCoordinateMap (projectiveClosureIdeal I) + (closureProjectionCoordinates (fun j => aeval b (a j)))).IsIntegral := by + rw [projectiveClosureIdeal_projection_kernel I b hb] at haintegral + let ψ := projectionCoordinateMap (projectiveClosureIdeal I) (closureProjectionCoordinates b) + let φ := Ideal.kerLiftAlg ψ + have hφ : φ.IsIntegral := hbintegral.kerLift + let χ := projectionCoordinateMap (RingHom.ker ψ) (closureProjectionCoordinates a) + have hcomp := RingHom.IsIntegral.trans χ.toRingHom φ.toRingHom haintegral hφ + have heq : φ.comp χ = projectionCoordinateMap (projectiveClosureIdeal I) + (closureProjectionCoordinates (fun j => aeval b (a j))) := by + ext i + simp only [AlgHom.comp_apply, χ, projectionCoordinateMap, aeval_X, + Ideal.Quotient.mkₐ_eq_mk] + change (Ideal.Quotient.mk (projectiveClosureIdeal I)) + (aeval (closureProjectionCoordinates b) (closureProjectionCoordinates a i)) = _ + rw [closureProjectionCoordinates_comp] + change (φ.comp χ).IsIntegral at hcomp + rwa [heq] at hcomp + +end +end CCDN diff --git a/LeanPool/CCDN/Projection/CurveDerivativeReconstruction.lean b/LeanPool/CCDN/Projection/CurveDerivativeReconstruction.lean new file mode 100644 index 0000000000..fb2e768512 --- /dev/null +++ b/LeanPool/CCDN/Projection/CurveDerivativeReconstruction.lean @@ -0,0 +1,117 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.Hilbert.FilteredIntersection +public import LeanPool.CCDN.Plane.LinearDeformation +public import Mathlib.RingTheory.Algebraic.Defs +import LeanPool.CCDN.Projection.CurveProjectionDeformation +import LeanPool.CCDN.Projection.PlaneRelationSpace + +/-! +A degree-preserving linear plane projection admits reconstruction using +the partial derivative of its image equation. This bounds the common +denominator's degree by d-1 without normalization or a genus formula. + +The constant and first-order equations of a polynomial family of plane +relations, expressed as equations in the target algebra. +-/ + +@[expose] public section +namespace CCDN +noncomputable section +open MvPolynomial + +private lemma plane_deformation_relation_coefficients {K A ι : Type*} + [Field K] [CommRing A] [Algebra K A] [Fintype ι] + (a : Fin 2 → A) (k : Fin 2) (w : A) + (r : ι → MvPolynomial (Fin 2) K) (v : ι → Polynomial K) + (hv : ∑ j, (v j).map (algebraMap K A) * planeLinearDeformation a k w (r j) = 0) : + aeval a (∑ j, (v j).coeff 0 • r j) = 0 ∧ + aeval a (∑ j, (v j).coeff 1 • r j) + + aeval a (pderiv k (∑ j, (v j).coeff 0 • r j)) * w = 0 := by + have h0 := congrArg (fun p : Polynomial A => p.coeff 0) hv + have h1 := congrArg (fun p : Polynomial A => p.coeff 1) hv + simp only [Polynomial.finsetSum_coeff, Polynomial.mul_coeff_zero, Polynomial.coeff_map, + planeLinearDeformation_coeff_zero, Polynomial.coeff_zero] at h0 + simp only [Polynomial.finsetSum_coeff, Polynomial.mul_coeff_one, Polynomial.coeff_map, + planeLinearDeformation_coeff_zero, planeLinearDeformation_coeff_one, + Polynomial.coeff_zero, Finset.sum_add_distrib] at h1 + constructor + · simp only [map_sum, map_smul] + simpa only [Algebra.smul_def] using h0 + · simp only [map_sum, Derivation.map_smul, map_smul] + simp only [Algebra.smul_def, Finset.sum_mul] + simpa only [mul_assoc, add_comm] using h1 + +end +end CCDN + +namespace CCDN +noncomputable section +open MvPolynomial + +/-- Differentiate a family of degree-d plane equations to reconstruct +any linear source function using the same derivative denominator. -/ +theorem exists_derivative_reconstruction {K τ : Type*} + [Field K] [CharZero K] [Finite τ] + (I : Ideal (MvPolynomial τ K)) [I.IsPrime] + (a : Fin 2 → MvPolynomial τ K) (ha : ∀ i, (a i).totalDegree ≤ 1) + (d : ℕ) (hdegree : HasFilteredCurveDegree I d) + (hu : Transcendental K ((Ideal.Quotient.mk I) (a 0))) + (f : MvPolynomial (Fin 2) K) (hf : f ≠ 0) (hfd : f.totalDegree = d) + (hker : ∀ g, aeval a g ∈ I ↔ f ∣ g) + (l : MvPolynomial τ K) (hl : l.totalDegree ≤ 1) : + ∃ P : MvPolynomial (Fin 2) K, P.totalDegree ≤ d ∧ + aeval a (pderiv 1 f) * l - aeval a P ∈ I := by + classical + let := Fintype.ofFinite τ + let V := restrictTotalDegree (Fin 2) K d + let b := Module.finBasis K V + obtain ⟨v, hv, j, hj⟩ := exists_curve_projection_relation_family I a ha d hdegree hu l hl + let r := fun i => (b i).val + let f₀ := ∑ i, (v i).coeff 0 • r i + let f₁ := ∑ i, (v i).coeff 1 • r i + have hmem (m : ℕ) : (∑ i, (v i).coeff m • r i) ∈ V := by + apply V.sum_mem + intro i _ + exact V.smul_mem _ (b i).property + have hd₀ : f₀.totalDegree ≤ d := (mem_restrictTotalDegree _ _ _).mp (hmem 0) + have hd₁ : f₁.totalDegree ≤ d := (mem_restrictTotalDegree _ _ _).mp (hmem 1) + have hn₀ : f₀ ≠ 0 := by + intro hz + have hi : LinearIndependent K r := b.linearIndependent.map' V.subtype + (LinearMap.ker_eq_bot.mpr Subtype.val_injective) + have hh := Fintype.linearIndependent_iff.mp hi (fun i => (v i).coeff 0) hz j + exact hj (by rwa [← Polynomial.coeff_zero_eq_eval_zero]) + let aq := fun i => (Ideal.Quotient.mk I) (a i) + let ψ := aeval (R := K) aq + have hψ (p : MvPolynomial (Fin 2) K) : (Ideal.Quotient.mk I) (aeval a p) = ψ p := + comp_aeval_apply a (Ideal.Quotient.mkₐ K I) p + obtain ⟨h₀, h₁⟩ := plane_deformation_relation_coefficients aq 1 + ((Ideal.Quotient.mk I) l) r v hv + change ψ f₀ = 0 at h₀ + change ψ f₁ + ψ (pderiv 1 f₀) * (Ideal.Quotient.mk I) l = 0 at h₁ + have hz₀ : aeval a f₀ ∈ I := Ideal.Quotient.eq_zero_iff_mem.mp ((hψ f₀).trans h₀) + obtain ⟨c, hc⟩ := + plane_projection_relation_space I a f hf hker f₀ (by simpa only [hfd] using hd₀) hz₀ + have hcn : c ≠ 0 := by intro hz; apply hn₀; rw [hc, hz, zero_smul] + rw [hc, Derivation.map_smul, map_smul, smul_mul_assoc] at h₁ + have hs := congrArg (fun x : MvPolynomial τ K ⧸ I => c⁻¹ • x) h₁ + simp only [smul_add, smul_smul, inv_mul_cancel₀ hcn, one_smul, smul_zero] at hs + let P := -(c⁻¹ • f₁) + refine ⟨P, ?_, ?_⟩ + · exact (totalDegree_neg _).le.trans ((totalDegree_smul_le _ _).trans hd₁) + · apply Ideal.Quotient.eq_zero_iff_mem.mp + rw [map_sub, map_mul, hψ, hψ] + simp only [P, map_neg, map_smul, sub_neg_eq_add] + exact (add_comm _ _).trans hs + +end +end CCDN diff --git a/LeanPool/CCDN/Projection/CurvePrimitivePair.lean b/LeanPool/CCDN/Projection/CurvePrimitivePair.lean new file mode 100644 index 0000000000..27deb49684 --- /dev/null +++ b/LeanPool/CCDN/Projection/CurvePrimitivePair.lean @@ -0,0 +1,50 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import Mathlib.Algebra.MvPolynomial.Degrees +public import Mathlib.RingTheory.Ideal.Quotient.Defs + +/-! Bounded linear coordinates generating an affine curve's function +field. Degree preservation and control of exceptional fibers are separate +projection obligations; this module proves the quantified birational input. -/ + +@[expose] public section +namespace CCDN +noncomputable section +open MvPolynomial + +/-- The bounded integer moment-curve linear form. -/ +def momentCoordinate (K : Type*) [CommRing K] (n k : ℕ) : MvPolynomial (Fin n) K := + ∑ j : Fin n, C ((k : K) ^ j.val) * X j + +/-- The moment coordinate has total degree at most one. -/ +lemma momentCoordinate_degree (K : Type*) [CommRing K] [Nontrivial K] (n k : ℕ) : + (momentCoordinate K n k).totalDegree ≤ 1 := by + apply totalDegree_finsetSum_le + intro j _ + exact (totalDegree_mul _ _).trans (by rw [totalDegree_C, totalDegree_X]) + +/-- In any field `F` receiving the quotient by `I` compatibly with `K`, the image of the moment +coordinate with parameter `k` is `∑ j, x j * k ^ j`, where `x j` is the image of `X j`. -/ +lemma momentCoordinate_fraction {K F : Type*} [Field K] [Field F] + {n : ℕ} (I : Ideal (MvPolynomial (Fin n) K)) + [Algebra (MvPolynomial (Fin n) K ⧸ I) F] + (k : ℕ) : + algebraMap (MvPolynomial (Fin n) K ⧸ I) F + ((Ideal.Quotient.mk I) (momentCoordinate K n k)) = + ∑ j : Fin n, algebraMap (MvPolynomial (Fin n) K ⧸ I) F + ((Ideal.Quotient.mk I) (X j)) * (k : F) ^ j.val := by + simp only [momentCoordinate, map_sum, map_mul, map_pow, map_natCast] + apply Finset.sum_congr rfl + intro j _ + exact mul_comm _ _ + +end +end CCDN diff --git a/LeanPool/CCDN/Projection/CurveProjectionDeformation.lean b/LeanPool/CCDN/Projection/CurveProjectionDeformation.lean new file mode 100644 index 0000000000..13bd3234dd --- /dev/null +++ b/LeanPool/CCDN/Projection/CurveProjectionDeformation.lean @@ -0,0 +1,113 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.Hilbert.LinearProjectionFiltration +public import LeanPool.CCDN.Plane.LinearDeformation +public import Mathlib.Algebra.NoZeroSMulDivisors.Basic +public import Mathlib.LinearAlgebra.Dimension.Free +public import Mathlib.LinearAlgebra.FreeModule.PID +public import Mathlib.LinearAlgebra.FreeModule.StrongRankCondition +public import Mathlib.RingTheory.Algebraic.Defs +public import Mathlib.RingTheory.LocalRing.Basic +public import Mathlib.RingTheory.SimpleRing.Principal +import LeanPool.CCDN.Algebra.PolynomialFamilyRelations +import LeanPool.CCDN.Projection.PlaneProjectionDegree +import Mathlib.Algebra.MvPolynomial.Monad + +/-! Construct a polynomial family of plane relations for linear +projections of a prime affine curve. The relation has degree at most the +curve's Hilbert degree and remains nonzero at parameter zero. -/ + +@[expose] public section +namespace CCDN +noncomputable section +open MvPolynomial + +private lemma planeLinearDeformation_eval_quotient {K τ : Type*} [Field K] + (I : Ideal (MvPolynomial τ K)) (a : Fin 2 → MvPolynomial τ K) + (l : MvPolynomial τ K) (t : K) (f : MvPolynomial (Fin 2) K) : + (planeLinearDeformation (fun i => (Ideal.Quotient.mk I) (a i)) 1 + ((Ideal.Quotient.mk I) l) f).eval (algebraMap K (MvPolynomial τ K ⧸ I) t) = + projectionCoordinateMap I (![a 0, a 1 + t • l]) f := by + let E : Polynomial (MvPolynomial τ K ⧸ I) →ₐ[K] (MvPolynomial τ K ⧸ I) := + (Polynomial.aeval (algebraMap K (MvPolynomial τ K ⧸ I) t)).restrictScalars K + have he : E.comp (planeLinearDeformation (fun i => (Ideal.Quotient.mk I) (a i)) 1 + ((Ideal.Quotient.mk I) l)) = projectionCoordinateMap I (![a 0, a 1 + t • l]) := by + apply MvPolynomial.algHom_ext + intro i + fin_cases i + · simp [E, planeLinearDeformation, projectionCoordinateMap, Algebra.smul_def] + · have hC : (Ideal.Quotient.mk I) (C t) = + algebraMap K (MvPolynomial τ K ⧸ I) t := (Ideal.Quotient.mkₐ K I).commutes t + simpa [E, planeLinearDeformation, projectionCoordinateMap, Algebra.smul_def, hC] using + mul_comm ((Ideal.Quotient.mk I) l) (algebraMap K (MvPolynomial τ K ⧸ I) t) + exact congrArg (fun ψ => ψ f) he + +/-- Every bounded-degree linear displacement has a nonzero plane relation. +The relations can be chosen polynomially in the displacement parameter and +with a nonzero specialization at zero. -/ +theorem exists_curve_projection_relation_family {K τ : Type*} + [Field K] [CharZero K] [Finite τ] + (I : Ideal (MvPolynomial τ K)) [I.IsPrime] + (a : Fin 2 → MvPolynomial τ K) (ha : ∀ i, (a i).totalDegree ≤ 1) + (d : ℕ) (hdegree : HasFilteredCurveDegree I d) + (hu : Transcendental K ((Ideal.Quotient.mk I) (a 0))) + (l : MvPolynomial τ K) (hl : l.totalDegree ≤ 1) : + let V := restrictTotalDegree (Fin 2) K d + let b := Module.finBasis K V + ∃ v : Fin (Module.finrank K V) → Polynomial K, + (∑ j, (v j).map (algebraMap K (MvPolynomial τ K ⧸ I)) * + planeLinearDeformation (fun i => (Ideal.Quotient.mk I) (a i)) 1 + ((Ideal.Quotient.mk I) l) (b j).val) = 0 ∧ + ∃ j, (v j).eval 0 ≠ 0 := by + classical + let := Fintype.ofFinite τ + let V := restrictTotalDegree (Fin 2) K d + let b := Module.finBasis K V + let ψ := planeLinearDeformation (K := K) (fun i => (Ideal.Quotient.mk I) (a i)) 1 + ((Ideal.Quotient.mk I) l) + let g := fun j => ψ (b j).val + apply polynomial_family_relation_nonzero_at_zero g + intro k + let aT : Fin 2 → MvPolynomial τ K := ![a 0, a 1 + (k : K) • l] + have hat (i : Fin 2) : (aT i).totalDegree ≤ 1 := by + fin_cases i + · exact ha 0 + · change (a 1 + (k : K) • l).totalDegree ≤ 1 + apply (totalDegree_add _ _).trans + apply max_le (ha 1) + exact (totalDegree_smul_le _ _).trans hl + obtain ⟨p, hp, _, hpd, hkernel⟩ := exists_plane_projection_equation_degree_le + I aT hat d hdegree 0 hu + have hpzero : projectionCoordinateMap I aT p = 0 := by + change (Ideal.Quotient.mk I) (aeval aT p) = 0 + exact Ideal.Quotient.eq_zero_iff_mem.mpr ((hkernel p).mpr (dvd_refl p)) + let pV : V := ⟨p, (mem_restrictTotalDegree _ _ _).mpr hpd⟩ + let c := fun j => b.repr pV j + have hc : c ≠ 0 := by + intro hzero + have hpV : pV = 0 := by + apply b.repr.injective + ext j + simpa only [map_zero, Finsupp.zero_apply, Pi.zero_apply] using congrFun hzero j + exact hp.ne_zero (congrArg Subtype.val hpV) + have hs : ∑ j, c j • (b j).val = p := by + have hh := congrArg Subtype.val (b.sum_repr pV) + simpa only [Submodule.coe_sum, Submodule.coe_smul] using hh + refine ⟨c, hc, ?_⟩ + have hev (j : Fin (Module.finrank K V)) : (g j).eval (k : MvPolynomial τ K ⧸ I) = + projectionCoordinateMap I aT (b j).val := by + simpa only [map_natCast] using planeLinearDeformation_eval_quotient I a l (k : K) (b j).val + simp only [hev] + simp_rw [← map_smul] + rw [← map_sum, hs, hpzero] + +end +end CCDN diff --git a/LeanPool/CCDN/Projection/CurveRationalInverse.lean b/LeanPool/CCDN/Projection/CurveRationalInverse.lean new file mode 100644 index 0000000000..870c06852b --- /dev/null +++ b/LeanPool/CCDN/Projection/CurveRationalInverse.lean @@ -0,0 +1,80 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.Algebra.IdealZeroLocus +public import Mathlib.FieldTheory.IntermediateField.Adjoin.Defs +public import Mathlib.RingTheory.Ideal.Quotient.Operations +import LeanPool.CCDN.Height.PrimitivePairReconstruction +import Mathlib.Algebra.MvPolynomial.CommRing + +/-! Birational projection coordinates admit a rational inverse in the original +coordinate ring. Its denominator cuts out all possible noninjective fibers. +This module gives no bound on the degree of that denominator; +`ExceptionalCount` converts a denominator of degree `D` into at most `d * D` +exceptional points. -/ + +@[expose] public section +namespace CCDN +noncomputable section +open MvPolynomial + +/-- Generation of the field gives reconstruction identities modulo the +original ideal, with a denominator that does not vanish identically. -/ +theorem exists_curve_reconstruction_identities {K F σ τ : Type*} + [Field K] [Field F] [Algebra K F] [Finite τ] + (I : Ideal (MvPolynomial τ K)) (φ : (MvPolynomial τ K ⧸ I) →ₐ[K] F) + (hφ : Function.Injective φ) (a : σ → MvPolynomial τ K) + (hgen : IntermediateField.adjoin K + (Set.range (fun j => φ ((Ideal.Quotient.mk I) (a j)))) = ⊤) : + ∃ Q : MvPolynomial σ K, ∃ P : τ → MvPolynomial σ K, + aeval a Q ∉ I ∧ ∀ i, aeval a Q * X i - aeval a (P i) ∈ I := by + classical + let := Fintype.ofFinite τ + let θ := φ.comp (Ideal.Quotient.mkₐ K I) + have hθ (p : MvPolynomial τ K) : θ p = φ ((Ideal.Quotient.mk I) p) := rfl + obtain ⟨Q, P, hQ, hP⟩ := exists_common_polynomial_reconstruction + (fun j => θ (a j)) hgen (fun i => θ (X i)) + have he (p : MvPolynomial σ K) : θ (aeval a p) = aeval (fun j => θ (a j)) p := + comp_aeval_apply a θ p + refine ⟨Q, P, ?_, ?_⟩ + · intro hbad + apply hQ + rw [← he, hθ, Ideal.Quotient.eq_zero_iff_mem.mpr hbad, map_zero] + · intro i + apply Ideal.Quotient.eq_zero_iff_mem.mp + apply hφ + rw [map_zero] + change θ (aeval a Q * X i - aeval a (P i)) = 0 + rw [map_sub, map_mul, he, he, hP, sub_self] + +/-- Polynomial reconstruction gives injectivity at every source point where +the denominator is nonzero. -/ +theorem projection_injective_off_reconstruction {K σ τ : Type*} [Field K] + (I : Ideal (MvPolynomial τ K)) (a : σ → MvPolynomial τ K) + (Q : MvPolynomial σ K) (P : τ → MvPolynomial σ K) + (hP : ∀ i, aeval a Q * X i - aeval a (P i) ∈ I) + (z w : τ → K) (hz : z ∈ idealZeroLocus I) (hw : w ∈ idealZeroLocus I) + (hQ : eval z (aeval a Q) ≠ 0) + (he : (fun j => eval z (a j)) = (fun j => eval w (a j))) : z = w := by + have hev (p : MvPolynomial σ K) : eval z (aeval a p) = eval w (aeval a p) := by + change eval z (eval₂ C a p) = eval w (eval₂ C a p) + rw [← eval_assoc, ← eval_assoc] + change eval (fun j => eval z (a j)) p = eval (fun j => eval w (a j)) p + rw [he] + funext i + apply mul_left_cancel₀ hQ + have hz' := hz _ (hP i) + have hw' := hw _ (hP i) + simp only [eval_sub, eval_mul, eval_X, sub_eq_zero] at hz' hw' + rw [hz', hev (P i), hev Q] + exact hw'.symm + +end +end CCDN diff --git a/LeanPool/CCDN/Projection/DegreePreservingProjectionBox.lean b/LeanPool/CCDN/Projection/DegreePreservingProjectionBox.lean new file mode 100644 index 0000000000..a23608c226 --- /dev/null +++ b/LeanPool/CCDN/Projection/DegreePreservingProjectionBox.lean @@ -0,0 +1,137 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.Height.Box +public import LeanPool.CCDN.Projection.CurvePrimitivePair +public import Mathlib.Data.Fin.VecNotation + +/-! +Integral evaluation and coefficient-uniform height control for the two +moment coordinates in the degree-preserving projection. + +Evaluation of the moment coordinates used by the bounded primitive pair. +-/ + +@[expose] public section +namespace CCDN +noncomputable section +open MvPolynomial + +/-- Evaluating the moment coordinate with parameter `k` at `z` gives `∑ j, k ^ j * z j`. -/ +lemma momentCoordinate_eval {K : Type*} [CommRing K] {n : ℕ} + (k : ℕ) (z : Fin n → K) : + eval z (momentCoordinate K n k) = ∑ j : Fin n, (k : K) ^ j.val * z j := by + simp only [momentCoordinate, map_sum, map_mul, eval_C, eval_X] + +end +end CCDN + +namespace CCDN +noncomputable section +open MvPolynomial + +/-- The integer projection of `ℤⁿ` to `ℤ²` given by the two moment coordinates with parameters `k₀` +and `k₁`. -/ +def degreeProjection (n k₀ k₁ : ℕ) (z : IntPoint n) : IntPoint 2 := + ![eval z (momentCoordinate ℤ n k₀), eval z (momentCoordinate ℤ n k₁)] + +/-- The box scaling factor `(n + 1) * ((n + 1) * (d + 1)) ^ (2 * n)` for the moment projection in +`n` variables and degree `d`. -/ +def projectionBoxFactor (n d : ℕ) : ℝ := + ((n : ℝ) + 1) * ((((n : ℝ) + 1) * ((d : ℝ) + 1)) ^ 2) ^ n + +/-- The box scaling factor of the moment projection is at least one. -/ +lemma projectionBoxFactor_one_le (n d : ℕ) : 1 ≤ projectionBoxFactor n d := by + unfold projectionBoxFactor + have hn : (1 : ℝ) ≤ n + 1 := by linarith [Nat.cast_nonneg (α := ℝ) n] + have hd : (1 : ℝ) ≤ d + 1 := by linarith [Nat.cast_nonneg (α := ℝ) d] + have hmul : (1 : ℝ) ≤ (n + 1) * (d + 1) := by nlinarith + have hp : (1 : ℝ) ≤ ((((n : ℝ) + 1) * ((d : ℝ) + 1)) ^ 2) ^ n := + one_le_pow₀ (one_le_pow₀ hmul) + nlinarith + +private lemma momentCoordinate_height_of_le {n k : ℕ} {M B : ℝ} + (hM : 1 ≤ M) (hk : (k : ℝ) ≤ M) + (z : IntPoint n) (hz : InBox z B) : + |(eval z (momentCoordinate ℤ n k) : ℝ)| ≤ n * M ^ n * B := by + rw [momentCoordinate_eval, Int.cast_sum] + simp only [Int.cast_mul, Int.cast_pow, Int.cast_natCast] + calc + _ ≤ ∑ j : Fin n, |(k : ℝ) ^ j.val * (z j : ℝ)| := Finset.abs_sum_le_sum_abs _ _ + _ ≤ ∑ _j : Fin n, M ^ n * B := by + apply Finset.sum_le_sum + intro j _ + have hp : (k : ℝ) ^ j.val ≤ M ^ n := by + calc + _ ≤ M ^ j.val := by gcongr + _ ≤ _ := pow_le_pow_right₀ hM (Nat.le_of_lt j.isLt) + simp only [abs_mul, abs_pow, abs_of_nonneg (Nat.cast_nonneg k : (0 : ℝ) ≤ k)] + exact mul_le_mul hp (hz j) (abs_nonneg _) (by positivity) + _ = _ := by simp only [Finset.sum_const, Finset.card_univ, Fintype.card_fin, nsmul_eq_mul]; ring + +/-- For parameters `k₀ ≤ n ^ 2 * d` and `k₁ ≤ n * d ^ 2`, the moment projection maps the integer box +of radius `B` into the integer box of radius `projectionBoxFactor n d * B`. -/ +lemma degreeProjection_box {n d k₀ k₁ : ℕ} (hk₀ : k₀ ≤ n ^ 2 * d) + (hk₁ : k₁ ≤ n * d ^ 2) {B : ℝ} (hB : 0 ≤ B) + {z : IntPoint n} (hz : z ∈ integerBox n B) : + degreeProjection n k₀ k₁ z ∈ integerBox 2 (projectionBoxFactor n d * B) := by + let M : ℝ := (((n : ℝ) + 1) * ((d : ℝ) + 1)) ^ 2 + have hn : (0 : ℝ) ≤ n := by positivity + have hd : (0 : ℝ) ≤ d := by positivity + have hM : 1 ≤ M := by + apply one_le_pow₀ + nlinarith [mul_nonneg hn hd] + have hk₀M : (k₀ : ℝ) ≤ M := by + have hh : (k₀ : ℝ) ≤ (n : ℝ) ^ 2 * d := by exact_mod_cast hk₀ + calc + _ ≤ (n : ℝ) ^ 2 * d := hh + _ ≤ ((n : ℝ) + 1) ^ 2 * ((d : ℝ) + 1) ^ 2 := by + gcongr + · linarith + · nlinarith [sq_nonneg (d : ℝ)] + _ = M := by dsimp [M]; ring + have hk₁M : (k₁ : ℝ) ≤ M := by + have hh : (k₁ : ℝ) ≤ (n : ℝ) * d ^ 2 := by exact_mod_cast hk₁ + calc + _ ≤ (n : ℝ) * d ^ 2 := hh + _ ≤ ((n : ℝ) + 1) ^ 2 * ((d : ℝ) + 1) ^ 2 := by + gcongr + · nlinarith [sq_nonneg (n : ℝ)] + · linarith + _ = M := by dsimp [M]; ring + have hb := mem_integerBox.mp hz + have hc : (n : ℝ) * M ^ n * B ≤ projectionBoxFactor n d * B := by + unfold projectionBoxFactor + gcongr + norm_num + apply mem_integerBox.mpr + intro j + fin_cases j + · exact (momentCoordinate_height_of_le hM hk₀M z hb).trans hc + · exact (momentCoordinate_height_of_le hM hk₁M z hb).trans hc + +/-- Evaluating the integer moment coordinate at an integer point and casting to `ℚ` agrees with +evaluating the rational moment coordinate at the corresponding rational point. -/ +lemma rationalPoint_moment_eval {n : ℕ} (k : ℕ) (z : IntPoint n) : + (eval z (momentCoordinate ℤ n k) : ℚ) = + eval (rationalPoint z) (momentCoordinate ℚ n k) := by + simp [momentCoordinate_eval, rationalPoint] + +/-- The rational point of the integer moment projection of `z` is the pair of rational moment +coordinates evaluated at `z`. -/ +lemma rationalPoint_degreeProjection {n k₀ k₁ : ℕ} (z : IntPoint n) : + rationalPoint (degreeProjection n k₀ k₁ z) = + ![eval (rationalPoint z) (momentCoordinate ℚ n k₀), + eval (rationalPoint z) (momentCoordinate ℚ n k₁)] := by + funext j + fin_cases j <;> exact rationalPoint_moment_eval _ _ + +end +end CCDN diff --git a/LeanPool/CCDN/Projection/DegreePreservingProjectionCount.lean b/LeanPool/CCDN/Projection/DegreePreservingProjectionCount.lean new file mode 100644 index 0000000000..85550a8a45 --- /dev/null +++ b/LeanPool/CCDN/Projection/DegreePreservingProjectionCount.lean @@ -0,0 +1,72 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.Algebra.IdealZeroLocus +public import LeanPool.CCDN.Hilbert.FilteredIntersection +public import Mathlib.Algebra.MvPolynomial.PDeriv +public import Mathlib.RingTheory.Algebraic.Defs +import LeanPool.CCDN.Determinant.PlaneSingularities +import LeanPool.CCDN.Projection.CurveDerivativeReconstruction +import LeanPool.CCDN.Projection.ExceptionalCount +import LeanPool.CCDN.Projection.PlaneProjectionDerivative + +/-! A degree-preserving linear plane projection of a prime affine curve in +characteristic zero loses at most d(d-1) points from any finite source set. +The proof constructs a common derivative denominator and all reconstruction +identities, with no normalization or exceptional-fiber hypothesis. -/ + +@[expose] public section +namespace CCDN +noncomputable section +open MvPolynomial + +/-- All source coordinates have the same proper reconstruction denominator, +namely the partial derivative of the plane image equation. -/ +theorem degree_preserving_projection_reconstruction {K τ : Type*} + [Field K] [CharZero K] [Finite τ] + (I : Ideal (MvPolynomial τ K)) [I.IsPrime] + (a : Fin 2 → MvPolynomial τ K) (ha : ∀ i, (a i).totalDegree ≤ 1) + (d : ℕ) (hdegree : HasFilteredCurveDegree I d) + (hu : Transcendental K ((Ideal.Quotient.mk I) (a 0))) + (f : MvPolynomial (Fin 2) K) (hf : f ≠ 0) (hfd : f.totalDegree = d) + (hker : ∀ g, aeval a g ∈ I ↔ f ∣ g) : + aeval a (pderiv 1 f) ∉ I ∧ (pderiv 1 f).totalDegree ≤ d - 1 ∧ + ∃ P : τ → MvPolynomial (Fin 2) K, ∀ i, + (P i).totalDegree ≤ d ∧ aeval a (pderiv 1 f) * X i - aeval a (P i) ∈ I := by + classical + let := Fintype.ofFinite τ + have hp := projection_derivative_proper I a ha f hf hker 0 1 (by decide) hu + refine ⟨hp.1, by simpa only [hfd] using pderiv_totalDegree_le f 1, ?_⟩ + choose P hP using fun i : τ => exists_derivative_reconstruction I a ha d hdegree hu + f hf hfd hker (X i) (by simp) + exact ⟨P, hP⟩ + +/-- Uniform exceptional-fiber control for any degree-preserving linear plane +projection, including curves that are not geometrically integral. -/ +theorem degree_preserving_projection_card_le {K τ : Type*} + [Field K] [CharZero K] [Finite τ] [DecidableEq (Fin 2 → K)] + (I : Ideal (MvPolynomial τ K)) [I.IsPrime] + (a : Fin 2 → MvPolynomial τ K) (ha : ∀ i, (a i).totalDegree ≤ 1) + (d : ℕ) (hdegree : HasFilteredCurveDegree I d) + (hu : Transcendental K ((Ideal.Quotient.mk I) (a 0))) + (f : MvPolynomial (Fin 2) K) (hf : f ≠ 0) (hfd : f.totalDegree = d) + (hker : ∀ g, aeval a g ∈ I ↔ f ∣ g) + (S : Finset (τ → K)) (hS : ∀ z ∈ S, z ∈ idealZeroLocus I) : + S.card ≤ (S.image (fun z j => eval z (a j))).card + d * (d - 1) := by + classical + let := Fintype.ofFinite τ + obtain ⟨hQ, hQd, P, hP⟩ := degree_preserving_projection_reconstruction I a ha d hdegree hu + f hf hfd hker + exact (projection_card_le_image_add I d hdegree a ha (pderiv 1 f) P hQ + (fun i => (hP i).2) S hS).trans + (Nat.add_le_add_left (Nat.mul_le_mul_left d hQd) _) + +end +end CCDN diff --git a/LeanPool/CCDN/Projection/ExceptionalCount.lean b/LeanPool/CCDN/Projection/ExceptionalCount.lean new file mode 100644 index 0000000000..f4c6ab72fe --- /dev/null +++ b/LeanPool/CCDN/Projection/ExceptionalCount.lean @@ -0,0 +1,82 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.Algebra.IdealZeroLocus +public import LeanPool.CCDN.Hilbert.FilteredIntersection +import LeanPool.CCDN.Algebra.PolynomialSection +import LeanPool.CCDN.Hilbert.LinearProjectionFiltration +import LeanPool.CCDN.Projection.CurveRationalInverse + +/-! Polynomial reconstruction controls exceptional points and finite +point counts. The error is d times the reconstruction denominator's degree; +a uniform O(d^2) bound requires a low-degree denominator. -/ + +@[expose] public section +namespace CCDN +noncomputable section +open MvPolynomial + +/-- The proper denominator of a linear projection vanishes at at most d*deg(Q) +distinct points of the source curve. -/ +private theorem reconstruction_exceptional_card_le {K σ τ ι : Type*} [Field K] + [Finite σ] [Finite τ] [Fintype ι] + (I : Ideal (MvPolynomial τ K)) [I.IsPrime] (d : ℕ) + (hdegree : HasFilteredCurveDegree I d) + (a : σ → MvPolynomial τ K) (ha : ∀ j, (a j).totalDegree ≤ 1) + (Q : MvPolynomial σ K) (hQ : aeval a Q ∉ I) + (z : ι → τ → K) (hz : Function.Injective z) + (hzi : ∀ i, z i ∈ idealZeroLocus I) + (hzQ : ∀ i, eval (z i) (aeval a Q) = 0) : + Fintype.card ι ≤ d * Q.totalDegree := by + classical + let := Fintype.ofFinite σ + let := Fintype.ofFinite τ + apply polynomial_section_card_le_degree I d hdegree (aeval a Q) hQ Q.totalDegree + (by simpa only [one_mul] using aeval_totalDegree_le a ha Q) z hz hzi hzQ + +/-- For every finite set of source points, projection loses at most + d*deg(Q) points, where Q is any proper reconstruction denominator. -/ +theorem projection_card_le_image_add {K σ τ : Type*} [Field K] [Finite σ] [Finite τ] + [DecidableEq (σ → K)] + (I : Ideal (MvPolynomial τ K)) [I.IsPrime] (d : ℕ) + (hdegree : HasFilteredCurveDegree I d) + (a : σ → MvPolynomial τ K) (ha : ∀ j, (a j).totalDegree ≤ 1) + (Q : MvPolynomial σ K) (P : τ → MvPolynomial σ K) + (hQ : aeval a Q ∉ I) (hP : ∀ i, aeval a Q * X i - aeval a (P i) ∈ I) + (S : Finset (τ → K)) (hS : ∀ z ∈ S, z ∈ idealZeroLocus I) : + let π : (τ → K) → (σ → K) := fun z j => eval z (a j) + S.card ≤ (S.image π).card + d * Q.totalDegree := by + classical + let := Fintype.ofFinite σ + let := Fintype.ofFinite τ + let π : (τ → K) → (σ → K) := fun z j => eval z (a j) + let T := S.filter (fun z => eval z (aeval a Q) = 0) + let G := S.filter (fun z => eval z (aeval a Q) ≠ 0) + have hT : T.card ≤ d * Q.totalDegree := by + have hh := reconstruction_exceptional_card_le I d hdegree a ha Q hQ + (fun z : T => z.val) Subtype.val_injective + (fun z => hS z.val (Finset.mem_filter.mp z.property).1) + (fun z => (Finset.mem_filter.mp z.property).2) + simpa only [Fintype.card_coe] using hh + have hG : G.card ≤ (S.image π).card := by + apply Finset.card_le_card_of_injOn π + · intro z hz + exact Finset.mem_image.mpr ⟨z, (Finset.mem_filter.mp hz).1, rfl⟩ + · intro z hz w hw he + obtain ⟨hzS, hzQ⟩ := Finset.mem_filter.mp hz + obtain ⟨hwS, _⟩ := Finset.mem_filter.mp hw + exact projection_injective_off_reconstruction I a Q P hP z w + (hS z hzS) (hS w hwS) hzQ he + have hcard : T.card + G.card = S.card := Finset.card_filter_add_card_filter_not _ + change S.card ≤ (S.image π).card + _ + omega + +end +end CCDN diff --git a/LeanPool/CCDN/Projection/FieldDegree.lean b/LeanPool/CCDN/Projection/FieldDegree.lean new file mode 100644 index 0000000000..2815b34efe --- /dev/null +++ b/LeanPool/CCDN/Projection/FieldDegree.lean @@ -0,0 +1,102 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.Hilbert.LinearProjectionFiltration +public import Mathlib.FieldTheory.IntermediateField.Adjoin.Defs +public import Mathlib.FieldTheory.Minpoly.Basic +public import Mathlib.RingTheory.Algebraic.Defs +import LeanPool.CCDN.Hilbert.FunctionFieldRank +import LeanPool.CCDN.Hilbert.PlaneHilbert +import LeanPool.CCDN.Projection.PlaneProjectionDegree +import Mathlib.Algebra.MvPolynomial.Monad +import Mathlib.FieldTheory.PrimitiveElement + +/-! A primitive plane coordinate gives a lower bound for the image equation's + degree. Combined with the filtered upper bound, a parameter of full field + degree forces a linear projection to preserve the affine Hilbert degree. -/ + +@[expose] public section +namespace CCDN +noncomputable section +open MvPolynomial + +/-- A plane relation bounds the algebraic degree of either coordinate over a +field containing a transcendental linear parameter. -/ +private theorem minpoly_degree_le_plane_relation {K R F : Type*} + [Field K] [Field R] [Field F] [Algebra K R] [Algebra R F] [Algebra K F] + [IsScalarTower K R F] (ψ : MvPolynomial (Fin 2) K →ₐ[K] F) + (f : MvPolynomial (Fin 2) K) (hf : f ≠ 0) (hzero : ψ f = 0) + (j k : Fin 2) (u : R) (hu : Transcendental K u) + (he : ψ (X j) = algebraMap R F u) : + (minpoly R (ψ (X k))).natDegree ≤ f.totalDegree := by + let I : Ideal (MvPolynomial (Fin 2) K) := Ideal.span {f} + have hI : ∀ g ∈ I, ψ g = 0 := by + intro g hg + obtain ⟨q, rfl⟩ := Ideal.mem_span_singleton.mp hg + rw [map_mul, hzero, zero_mul] + let φ : (MvPolynomial (Fin 2) K ⧸ I) →ₐ[K] F := Ideal.Quotient.liftₐ I ψ hI + have hφ (g : MvPolynomial (Fin 2) K) : φ ((Ideal.Quotient.mk I) g) = ψ g := rfl + have hdegree : HasFilteredCurveDegree I f.totalDegree := + ⟨f.totalDegree, (f.totalDegree : ℤ) - f.totalDegree.choose 2, + fun n hn => principal_plane_hilbert_function f hf hn⟩ + have hi : LinearIndependent R (fun i : Fin (minpoly R (ψ (X k))).natDegree => + φ ((Ideal.Quotient.mk I) ((X k) ^ i.val))) := by + simpa only [hφ, map_pow] using (linearIndependent_pow (K := R) (ψ (X k))) + have h := polynomial_parameter_rank_le (K := K) (R := R) (F := F) + (ι := Fin (minpoly R (ψ (X k))).natDegree) I f.totalDegree hdegree φ (X j) + (by simp) u hu (by rw [hφ]; exact he) + (fun i : Fin (minpoly R (ψ (X k))).natDegree => (X k) ^ i.val) hi + simpa only [Fintype.card_fin] using h + +/-- The degree of a plane equation is at least the degree of a function-field +extension when its second coordinate is primitive over the parameter field. -/ +private theorem primitive_field_degree_le_plane_relation {K R F : Type*} + [Field K] [Field R] [Field F] [Algebra K R] [Algebra R F] [Algebra K F] + [IsScalarTower K R F] [Module.Finite R F] + (ψ : MvPolynomial (Fin 2) K →ₐ[K] F) + (f : MvPolynomial (Fin 2) K) (hf : f ≠ 0) (hzero : ψ f = 0) + (j k : Fin 2) (u : R) (hu : Transcendental K u) + (he : ψ (X j) = algebraMap R F u) + (hprim : IntermediateField.adjoin R {ψ (X k)} = ⊤) : + Module.finrank R F ≤ f.totalDegree := by + rw [← (Field.primitive_element_iff_minpoly_natDegree_eq R (ψ (X k))).mp hprim] + exact minpoly_degree_le_plane_relation ψ f hf hzero j k u hu he + +/-- Once the linear parameter has full field degree and the other coordinate +is primitive, the irreducible image equation preserves degree. -/ +theorem plane_projection_degree_eq_of_full_parameter {K R F τ : Type*} + [Field K] [Field R] [Field F] [Algebra K R] [Algebra R F] [Algebra K F] + [IsScalarTower K R F] [Module.Finite R F] [Finite τ] + (I : Ideal (MvPolynomial τ K)) (a : Fin 2 → MvPolynomial τ K) + (ha : ∀ i, (a i).totalDegree ≤ 1) (d : ℕ) + (hdegree : HasFilteredCurveDegree I d) + (φ : (MvPolynomial τ K ⧸ I) →ₐ[K] F) + (f : MvPolynomial (Fin 2) K) (hf : f ≠ 0) + (hker : RingHom.ker (projectionCoordinateMap I a) = Ideal.span {f}) + (j k : Fin 2) (u : R) (hu : Transcendental K u) + (he : φ ((Ideal.Quotient.mk I) (a j)) = algebraMap R F u) + (hprim : IntermediateField.adjoin R {φ ((Ideal.Quotient.mk I) (a k))} = ⊤) + (hrank : Module.finrank R F = d) : f.totalDegree = d := by + classical + let := Fintype.ofFinite τ + apply Nat.le_antisymm (plane_projection_equation_degree_le I a ha d hdegree f hf hker) + rw [← hrank] + let ψ := φ.comp (projectionCoordinateMap I a) + have hψ (i : Fin 2) : ψ (X i) = φ ((Ideal.Quotient.mk I) (a i)) := by + simp [ψ, projectionCoordinateMap] + have hz : projectionCoordinateMap I a f = 0 := by + apply RingHom.mem_ker.mp + rw [hker] + exact Ideal.subset_span (Set.mem_singleton f) + exact primitive_field_degree_le_plane_relation ψ f hf (by simp [ψ, hz]) + j k u hu (by rw [hψ]; exact he) (by rw [hψ]; exact hprim) + +end +end CCDN diff --git a/LeanPool/CCDN/Projection/FixedCoordinateElimination.lean b/LeanPool/CCDN/Projection/FixedCoordinateElimination.lean new file mode 100644 index 0000000000..6d8c780b0e --- /dev/null +++ b/LeanPool/CCDN/Projection/FixedCoordinateElimination.lean @@ -0,0 +1,241 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.Algebra.IntegerLinearForms +public import LeanPool.CCDN.Projection.IntegerHilbertElimination +public import LeanPool.CCDN.Projection.ProjectiveClosureProjection +import LeanPool.CCDN.Algebra.ScaledHomogenization +import LeanPool.CCDN.AuxiliaryPolynomial.HypersurfacePositioning + +/-! +Full-degree affine elimination also gives an integral projection of the +projective closure, fixing its homogenizing coordinate. + +One bounded integer-matrix elimination step on the standard Fin-indexed +polynomial rings, ready for iteration. + +The restriction of a bounded homogenization to the hyperplane at infinity +is exactly its highest indicated homogeneous component. +-/ + +@[expose] public section +namespace CCDN +noncomputable section +open MvPolynomial + +/-- For `f` of total degree at most `D`, the degree-`D` homogenization of `f`, restricted to the +hyperplane at infinity `X₀ = 0`, is the degree-`D` homogeneous component of `f`. -/ +theorem scaledHomogenize_one_eval_at_infinity {K : Type*} [Field K] {n D : ℕ} + (f : MvPolynomial (Fin n) K) (hf : f.totalDegree ≤ D) (b : Fin n → K) : + eval (Fin.cons 0 b) (scaledHomogenize D 1 f) = + eval b (homogeneousComponent D f) := by + classical + rw [scaledHomogenize, map_sum, homogeneousComponent_apply, map_sum, + Finset.sum_filter] + apply Finset.sum_congr rfl + intro e he + rw [eval_monomial, homogenizedExponent_value] + have hle : e.sum (fun _ k => k) ≤ D := (le_totalDegree he).trans hf + by_cases h : e.degree = D + · have heq : e.sum (fun _ k => k) = D := h + simp [h, heq, eval_monomial] + · have hpos : 0 < D - e.sum (fun _ k => k) := by + change e.sum (fun _ k => k) ≠ D at h + omega + simp [h, zero_pow (Nat.ne_of_gt hpos)] + +end +end CCDN + +namespace CCDN +noncomputable section +open MvPolynomial + +/-- The integer matrix whose `i`-th row is the linear form `b none * X (i + 1) - b (some i) * X 0`, +eliminating the coordinate `X 0` along the direction `b`. -/ +def finEliminationMatrix {n : ℕ} (b : Option (Fin n) → ℤ) : + Fin n → Fin (n + 1) → ℤ := + fun i => Fin.cases (-b (some i)) (fun j => if j = i then b none else 0) + +private lemma integerLinearForm_finEliminationMatrix {n : ℕ} + (b : Option (Fin n) → ℤ) (i : Fin n) : + integerLinearForm (finEliminationMatrix b i) = + C (b none : ℚ) * X i.succ - C (b (some i) : ℚ) * X 0 := by + rw [integerLinearForm, linearForm, Fin.sum_univ_succ] + simp [finEliminationMatrix, apply_ite, ite_mul, sub_eq_add_neg, add_comm] + +/-- If every entry of the direction `b` lies in `[0, d + 1]`, then every entry of the elimination +matrix has absolute value at most `d + 1`. -/ +lemma finEliminationMatrix_height {n d : ℕ} (b : Option (Fin n) → ℤ) + (hb : ∀ i, 0 ≤ b i ∧ b i ≤ d + 1) (i : Fin n) (j : Fin (n + 1)) : + |finEliminationMatrix b i j| ≤ d + 1 := by + refine Fin.cases ?_ (fun j => ?_) j + · simpa only [finEliminationMatrix, Fin.cases_zero, abs_neg, + abs_of_nonneg (hb (some i)).1] using (hb (some i)).2 + · by_cases he : j = i + · simpa only [finEliminationMatrix, Fin.cases_succ, he, ite_true, + abs_of_nonneg (hb none).1] using (hb none).2 + · simp only [finEliminationMatrix, Fin.cases_succ, ite_eq_right he, abs_zero] + positivity + +end +end CCDN + +namespace CCDN +noncomputable section +open MvPolynomial + +/-- The reindexing of `Fin (n + 2)` that sends coordinate `1` to `none` and the remaining +coordinates, in order, to `some 0, some 1, …`; it makes the homogenizing coordinate `0` the first +retained coordinate. -/ +def closurePivotEquiv (n : ℕ) : Fin (n + 2) ≃ Option (Fin (n + 1)) := + (Equiv.swap 0 (Fin.succ 0)).trans (finSuccEquiv (n + 1)) + +/-- The pivot reindexing sends coordinate `0` to `some 0`. -/ +@[simp] lemma closurePivotEquiv_zero (n : ℕ) : closurePivotEquiv n 0 = some 0 := by + simp [closurePivotEquiv] + +/-- The pivot reindexing sends coordinate `1` to `none`. -/ +@[simp] lemma closurePivotEquiv_one (n : ℕ) : closurePivotEquiv n 1 = none := by + simp [closurePivotEquiv] + +/-- The pivot reindexing sends coordinate `i + 2` to `some (i + 1)`. -/ +@[simp] lemma closurePivotEquiv_succ_succ {n : ℕ} (i : Fin n) : + closurePivotEquiv n i.succ.succ = some i.succ := by + have h0 : i.succ.succ ≠ (0 : Fin (n + 2)) := Fin.succ_ne_zero _ + have h1 : i.succ.succ ≠ (Fin.succ 0 : Fin (n + 2)) := by + intro h + exact Fin.succ_ne_zero i (Fin.succ_injective _ h) + simp only [closurePivotEquiv, Equiv.trans_apply, Equiv.swap_apply_of_ne_of_ne h0 h1] + rfl + +/-- The inverse pivot reindexing sends `none` to coordinate `1`. -/ +@[simp] lemma closurePivotEquiv_symm_none (n : ℕ) : + (closurePivotEquiv n).symm none = Fin.succ 0 := by + exact (closurePivotEquiv n).symm_apply_eq.mpr (closurePivotEquiv_one n).symm + +/-- The inverse pivot reindexing sends `some 0` to coordinate `0`. -/ +@[simp] lemma closurePivotEquiv_symm_some_zero (n : ℕ) : + (closurePivotEquiv n).symm (some 0) = 0 := by + exact (closurePivotEquiv n).symm_apply_eq.mpr (closurePivotEquiv_zero n).symm + +/-- The inverse pivot reindexing sends `some (i + 1)` to coordinate `i + 2`. -/ +@[simp] lemma closurePivotEquiv_symm_some_succ {n : ℕ} (i : Fin n) : + (closurePivotEquiv n).symm (some i.succ) = i.succ.succ := by + exact (closurePivotEquiv n).symm_apply_eq.mpr (closurePivotEquiv_succ_succ i).symm + +private def closureEliminationDirection {n : ℕ} (b : Option (Fin n) → ℤ) : + Option (Fin (n + 1)) → ℤ := + Option.elim' (b none) (Fin.cons 0 (fun i => b (some i))) + +private lemma closureEliminationDirection_comp {n : ℕ} (b : Option (Fin n) → ℤ) : + (fun i => (closureEliminationDirection b (closurePivotEquiv n i) : ℚ)) = + Fin.cons 0 (Fin.cons (b none : ℚ) (fun i => (b (some i) : ℚ))) := by + funext i + refine Fin.cases ?_ (fun j => ?_) i + · simp [closureEliminationDirection] + · refine Fin.cases ?_ (fun j => ?_) j + · have hpivot : closurePivotEquiv n (Fin.succ 0) = none := by + simpa only [Fin.succ_zero_eq_one] using closurePivotEquiv_one n + rw [hpivot] + rfl + · rw [closurePivotEquiv_succ_succ] + rfl + +private lemma closureEliminationDirection_eval {n : ℕ} (b : Option (Fin n) → ℤ) + (f : MvPolynomial (Fin (n + 1)) ℚ) : + eval (fun i => (closureEliminationDirection b i : ℚ)) + (rename (closurePivotEquiv n) (scaledHomogenize f.totalDegree 1 f)) = + eval (Fin.cons (b none : ℚ) (fun i => (b (some i) : ℚ))) + (homogeneousComponent f.totalDegree f) := by + rw [eval_rename] + change eval (fun i => (closureEliminationDirection b (closurePivotEquiv n i) : ℚ)) + (scaledHomogenize f.totalDegree 1 f) = _ + rw [closureEliminationDirection_comp, scaledHomogenize_one_eval_at_infinity f le_rfl] + +/-- A full-degree direction for an affine ideal equation gives an +integral map of projective closures whose first coordinate remains X₀. -/ +theorem fixedCoordinateElimination_isIntegral {n : ℕ} + (I : Ideal (MvPolynomial (Fin (n + 1)) ℚ)) + (f : MvPolynomial (Fin (n + 1)) ℚ) (hf : f ≠ 0) (hfi : f ∈ I) + (b : Option (Fin n) → ℤ) (hb : (b none : ℚ) ≠ 0) + (hdir : eval (Fin.cons (b none : ℚ) (fun i => (b (some i) : ℚ))) + (homogeneousComponent f.totalDegree f) ≠ 0) : + (projectionCoordinateMap (projectiveClosureIdeal I) + (closureProjectionCoordinates + (fun i => integerLinearForm (finEliminationMatrix b i)))).IsIntegral := by + let H := projectiveClosureIdeal I + let e := renameEquiv ℚ (closurePivotEquiv n) + let J := H.map e + let F := e (scaledHomogenize f.totalDegree 1 f) + let c := closureEliminationDirection b + have hc : (c none : ℚ) ≠ 0 := hb + have hFhom : F.IsHomogeneous f.totalDegree := + (scaledHomogenize_homogeneous 1 f le_rfl).rename_isHomogeneous + have hF0 : F ≠ 0 := by + intro h + have hh : scaledHomogenize f.totalDegree 1 f = 0 := e.injective (by + change e (scaledHomogenize f.totalDegree 1 f) = e 0 + rw [map_zero] + exact h) + apply hf + simpa only [affineChart_scaledHomogenize_one, map_zero] using + congrArg (affineChart (n + 1)) hh + have hFdeg := hFhom.totalDegree hF0 + have hFdir : eval (fun i => (c i : ℚ)) + (homogeneousComponent F.totalDegree F) ≠ 0 := by + rw [hFdeg, homogeneousComponent_eq_self hFhom] + exact (closureEliminationDirection_eval b f).trans_ne hdir + have hFmem : F ∈ J := by + apply Ideal.mem_map_of_mem + rw [homogeneous_mem_projectiveClosureIdeal_iff I _ + (scaledHomogenize_homogeneous 1 f le_rfl), affineChart_scaledHomogenize_one] + exact hfi + have hraw : (coordinateEliminationMap J + (hypersurfaceAffineCoordinates 0 (fun i => (c i : ℚ)) hc)).IsIntegral := by + apply coordinateEliminationMap_isIntegral J _ + (normalizedHypersurfaceCoordinates 0 (fun i => (c i : ℚ)) hc F) + · exact Ideal.mul_mem_left _ _ (Ideal.mem_map_of_mem _ hFmem) + · exact normalizedHypersurfaceCoordinates_monic 0 _ hc F hFdir + rw [coordinateEliminationMap_eq_projection] at hraw + have hint := integerEliminationMap_isIntegral J c hc hraw + have hJ : J = H.map + (e : MvPolynomial (Fin (n + 2)) ℚ →+* + MvPolynomial (Option (Fin (n + 1))) ℚ) := rfl + let φ := (Ideal.quotientEquivAlg H J e hJ).symm + have hφ : φ.toRingHom.IsIntegral := RingHom.isIntegral_of_surjective _ φ.surjective + have hcomp := RingHom.IsIntegral.trans + (projectionCoordinateMap J (integerEliminationCoordinates c)).toRingHom φ.toRingHom + hint hφ + let a := fun i => integerLinearForm (finEliminationMatrix b i) + let ψ := projectionCoordinateMap H (closureProjectionCoordinates a) + let χ : MvPolynomial (Fin (n + 1)) ℚ →ₐ[ℚ] MvPolynomial (Fin (n + 1)) ℚ := + aeval (Fin.cons (C (b none : ℚ) * X 0) (fun i => X i.succ)) + have heq : φ.toAlgHom.comp (projectionCoordinateMap J (integerEliminationCoordinates c)) = + ψ.comp χ := by + ext i + simp only [AlgHom.comp_apply, projectionCoordinateMap, aeval_X, + Ideal.Quotient.mkₐ_eq_mk] + change φ ((Ideal.Quotient.mk J) (integerEliminationCoordinates c i)) = ψ (χ (X i)) + dsimp only [φ] + rw [Ideal.quotientEquivAlg_symm, Ideal.quotientEquivAlg_mk] + refine Fin.cases ?_ (fun j => ?_) i + · simp [integerEliminationCoordinates, c, closureEliminationDirection, e, + ψ, χ, projectionCoordinateMap, closureProjectionCoordinates] + · simp [integerEliminationCoordinates, c, closureEliminationDirection, e, + ψ, χ, projectionCoordinateMap, closureProjectionCoordinates, a, + integerLinearForm_finEliminationMatrix] + change (φ.toAlgHom.comp + (projectionCoordinateMap J (integerEliminationCoordinates c))).IsIntegral at hcomp + rw [heq] at hcomp + exact RingHom.IsIntegral.tower_top χ.toRingHom ψ.toRingHom hcomp + +end +end CCDN diff --git a/LeanPool/CCDN/Projection/FullDegreePrimitiveProjection.lean b/LeanPool/CCDN/Projection/FullDegreePrimitiveProjection.lean new file mode 100644 index 0000000000..fae3ddb155 --- /dev/null +++ b/LeanPool/CCDN/Projection/FullDegreePrimitiveProjection.lean @@ -0,0 +1,125 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.Hilbert.FilteredIntersection +public import LeanPool.CCDN.Projection.CurvePrimitivePair +public import Mathlib.FieldTheory.RatFunc.AsPolynomial +public import Mathlib.RingTheory.SimpleRing.Principal +import LeanPool.CCDN.Algebra.ParameterRecurrenceGrowth +import LeanPool.CCDN.Height.BoundedPrimitiveElement +import LeanPool.CCDN.Hilbert.CurveFunctionField +import LeanPool.CCDN.Projection.FieldDegree +import LeanPool.CCDN.Projection.PlaneProjectionKernel + +/-! +Once a linear parameter generates the degree filtration, a bounded moment +coordinate produces a degree-preserving plane equation. This combines +full function-field degree with the quantitative primitive-element theorem. + +Eventual generation of the affine degree filtration by a linear parameter +forces the function-field extension to have the full Hilbert degree. +-/ + +@[expose] public section +namespace CCDN +noncomputable section + +/-- A linear parameter generating the filtration in large degrees gives a +function-field extension of degree exactly d. -/ +private theorem curve_parameter_full_degree_of_recurrence {K F σ : Type*} + [Field K] [Field F] [Finite σ] [Algebra K F] + [Algebra (RatFunc K) F] [IsScalarTower K (RatFunc K) F] + (I : Ideal (MvPolynomial σ K)) [I.IsPrime] + [Algebra (MvPolynomial σ K ⧸ I) F] [IsScalarTower K (MvPolynomial σ K ⧸ I) F] + [IsFractionRing (MvPolynomial σ K ⧸ I) F] + (d : ℕ) (hdegree : HasFilteredCurveDegree I d) + (l : MvPolynomial σ K) (hl : l.totalDegree ≤ 1) + (he : algebraMap (MvPolynomial σ K ⧸ I) F ((Ideal.Quotient.mk I) l) = + algebraMap (RatFunc K) F RatFunc.X) + (N : ℕ) (hrec : ∀ n ≥ N, filteredCoordinateSpace I (n + 1) = + filteredCoordinateSpace I n ⊔ (filteredCoordinateSpace I n).map + (LinearMap.mulLeft K ((Ideal.Quotient.mk I) l))) : + Module.Finite (RatFunc K) F ∧ Module.finrank (RatFunc K) F = d := by + classical + let := Fintype.ofFinite σ + obtain ⟨hfinite, hupper⟩ := curve_functionField_finite_rank_le I d hdegree l hl + RatFunc.X RatFunc.transcendental_X he + let : Module.Finite (RatFunc K) F := hfinite + let φ := IsScalarTower.toAlgHom K (MvPolynomial σ K ⧸ I) F + have hcomm (x : MvPolynomial σ K ⧸ I) : + φ.toLinearMap ((LinearMap.mulLeft K ((Ideal.Quotient.mk I) l)) x) = + algebraMap (RatFunc K) F RatFunc.X * φ.toLinearMap x := by + change φ (((Ideal.Quotient.mk I) l) * x) = _ + rw [map_mul] + exact congrArg (fun y => y * φ x) he + have hlower := parameter_recurrence_slope_le (filteredCoordinateSpace I) + (LinearMap.mulLeft K ((Ideal.Quotient.mk I) l)) φ.toLinearMap + (IsFractionRing.injective _ _) hcomm N hrec d hdegree + exact ⟨hfinite, Nat.le_antisymm hupper hlower⟩ + +end +end CCDN + +namespace CCDN +noncomputable section +open MvPolynomial + +/-- Build the bounded second projection coordinate and a plane equation of +exactly degree d from a parameter satisfying eventual filtered generation. -/ +theorem exists_full_degree_primitive_projection {K F : Type*} + [Field K] [CharZero K] [Field F] [Algebra K F] + [Algebra (RatFunc K) F] [IsScalarTower K (RatFunc K) F] + {n : ℕ} (I : Ideal (MvPolynomial (Fin n) K)) [I.IsPrime] + [Algebra (MvPolynomial (Fin n) K ⧸ I) F] + [IsScalarTower K (MvPolynomial (Fin n) K ⧸ I) F] + [IsFractionRing (MvPolynomial (Fin n) K ⧸ I) F] + (d : ℕ) (hdegree : HasFilteredCurveDegree I d) + (l : MvPolynomial (Fin n) K) (hl : l.totalDegree ≤ 1) + (he : algebraMap (MvPolynomial (Fin n) K ⧸ I) F ((Ideal.Quotient.mk I) l) = + algebraMap (RatFunc K) F RatFunc.X) + (N : ℕ) (hrec : ∀ m ≥ N, filteredCoordinateSpace I (m + 1) = + filteredCoordinateSpace I m ⊔ (filteredCoordinateSpace I m).map + (LinearMap.mulLeft K ((Ideal.Quotient.mk I) l))) : + ∃ (k : ℕ) (f : MvPolynomial (Fin 2) K), k ≤ n * d ^ 2 ∧ + Irreducible f ∧ f.totalDegree = d ∧ + ∀ g, aeval (![l, momentCoordinate K n k]) g ∈ I ↔ f ∣ g := by + obtain ⟨hfinite, hfull⟩ := curve_parameter_full_degree_of_recurrence I d hdegree l hl he N hrec + let : Module.Finite (RatFunc K) F := hfinite + let φ := IsScalarTower.toAlgHom K (MvPolynomial (Fin n) K ⧸ I) F + have hu : Transcendental K ((Ideal.Quotient.mk I) l) := by + intro h + have hh := (isAlgebraic_algHom_iff φ (IsFractionRing.injective _ _)).mpr h + change IsAlgebraic K (algebraMap (MvPolynomial (Fin n) K ⧸ I) F ((Ideal.Quotient.mk I) l)) at hh + rw [he] at hh + exact RatFunc.transcendental_X ((isAlgebraic_algHom_iff + (IsScalarTower.toAlgHom K (RatFunc K) F) (algebraMap (RatFunc K) F).injective).mp hh) + obtain ⟨k, hk, hprim⟩ := exists_bounded_curve_primitive_element (R := RatFunc K) (F := F) I + let a : Fin 2 → MvPolynomial (Fin n) K := ![l, momentCoordinate K n k] + have ha (i : Fin 2) : (a i).totalDegree ≤ 1 := by + fin_cases i + · exact hl + · exact momentCoordinate_degree K n k + obtain ⟨f, hf, hker⟩ := exists_plane_projection_equation I a ha d hdegree 0 hu + have hp : IntermediateField.adjoin (RatFunc K) {φ ((Ideal.Quotient.mk I) (a 1))} = ⊤ := by + change IntermediateField.adjoin (RatFunc K) + {algebraMap (MvPolynomial (Fin n) K ⧸ I) F + ((Ideal.Quotient.mk I) (momentCoordinate K n k))} = ⊤ + rwa [momentCoordinate_fraction] + have hfd := plane_projection_degree_eq_of_full_parameter I a ha d hdegree φ f hf.ne_zero hker + 0 1 RatFunc.X RatFunc.transcendental_X he hp hfull + refine ⟨k, f, by simpa only [hfull] using hk, hf, hfd, ?_⟩ + intro g + have hg : aeval a g ∈ I ↔ g ∈ RingHom.ker (projectionCoordinateMap I a) := by + change _ ↔ (Ideal.Quotient.mk I) (aeval a g) = 0 + exact Ideal.Quotient.eq_zero_iff_mem.symm + rw [hg, hker, Ideal.mem_span_singleton] + +end +end CCDN diff --git a/LeanPool/CCDN/Projection/HilbertLowDegreeEquation.lean b/LeanPool/CCDN/Projection/HilbertLowDegreeEquation.lean new file mode 100644 index 0000000000..3497ebf9ba --- /dev/null +++ b/LeanPool/CCDN/Projection/HilbertLowDegreeEquation.lean @@ -0,0 +1,83 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.Hilbert.FilteredHilbertPolynomial +import LeanPool.CCDN.Hilbert.CoordinateIndependence +import LeanPool.CCDN.Projection.HilbertProjectionDegree + +/-! A proper positive-codimension prime ideal of positive Hilbert dimension +contains a nonzero equation of degree at most its Hilbert multiplicity. +The independent coordinate subtuple and the equation are both constructed. -/ + +@[expose] public section +namespace CCDN +noncomputable section +open MvPolynomial + +/-- A prime ideal of projective dimension `n` and degree `d` in more than `n + 1` variables contains +a nonzero polynomial of total degree at most `d`. -/ +theorem exists_mem_ideal_degree_le_multiplicity + {K σ : Type*} [Field K] [Fintype σ] [LinearOrder σ] {n : ℕ} + (I : Ideal (MvPolynomial σ K)) [I.IsPrime] (d : ℕ) + (hdimdeg : HasProjectiveDimensionDegree I n d) (hcodim : n + 1 < Fintype.card σ) : + ∃ f : MvPolynomial σ K, f ≠ 0 ∧ f ∈ I ∧ f.totalDegree ≤ d := by + classical + obtain ⟨U, hU, hAI⟩ := exists_hilbert_dimension_independent_coordinates I + (Ideal.IsPrime.ne_top ‹I.IsPrime›) + rw [Nat.card_eq_fintype_card, hdimdeg.natDegree_eq] at hU + let e : Fin (n + 1) ≃ U := Fintype.equivOfCardEq (by simpa only [Fintype.card_fin] using hU.symm) + obtain ⟨v, hv⟩ : ∃ v : σ, v ∉ U := by + by_contra! hn + have hUniv : U = Set.univ := Set.eq_univ_iff_forall.mpr hn + have hcard : Fintype.card U = Fintype.card σ := by simp [hUniv] + omega + let j : Fin (n + 2) → σ := Fin.lastCases v (fun i => (e i).val) + have hj : Function.Injective j := by + intro i + refine Fin.lastCases ?_ (fun i => ?_) i + · intro k + refine Fin.lastCases ?_ (fun k => ?_) k + · intro _ + rfl + · intro h + have he : v = (e k).val := by simpa only [j, Fin.lastCases_last, + Fin.lastCases_castSucc] using h + exact (hv (he ▸ (e k).property)).elim + · intro k + refine Fin.lastCases ?_ (fun k => ?_) k + · intro h + have he : (e i).val = v := by simpa only [j, Fin.lastCases_last, + Fin.lastCases_castSucc] using h + exact (hv (he ▸ (e i).property)).elim + · intro h + have he : (e i).val = (e k).val := by simpa only [j, Fin.lastCases_castSucc] using h + exact congrArg Fin.castSucc (e.injective (Subtype.ext he)) + let a : Fin (n + 2) → MvPolynomial σ K := fun i => X (j i) + let b : Fin (n + 1) → MvPolynomial (Fin (n + 2)) K := fun i => X i.castSucc + have ha : ∀ i, (a i).totalDegree ≤ 1 := fun i => by simp [a] + have hb : ∀ i, (b i).totalDegree ≤ 1 := fun i => by simp [b] + have hi : AlgebraicIndependent K (fun i => projectionCoordinateMap I a (b i)) := by + simpa only [projectionCoordinateMap, AlgHom.comp_apply, b, aeval_X, a, j, + Fin.lastCases_castSucc, Function.comp_def, Ideal.Quotient.mkₐ_eq_mk] using + hAI.comp e e.injective + obtain ⟨f, hf, _, hfd, hker⟩ := exists_hypersurface_projection_equation_degree_le + I a ha d hdimdeg b hb hi + have he : aeval a = rename j := (rename_eq_aeval j).symm + refine ⟨aeval a f, ?_, ?_, ?_⟩ + · rw [he] + exact fun hz => hf.ne_zero ((rename_injective j hj) (by simpa only [map_zero] using hz)) + · have hmem : f ∈ RingHom.ker (projectionCoordinateMap I a) := by + rw [hker] + exact Ideal.subset_span (Set.mem_singleton f) + exact Ideal.Quotient.eq_zero_iff_mem.mp hmem + · exact (aeval_totalDegree_le a ha f).trans (by simpa using hfd) + +end +end CCDN diff --git a/LeanPool/CCDN/Projection/HilbertProjectionDegree.lean b/LeanPool/CCDN/Projection/HilbertProjectionDegree.lean new file mode 100644 index 0000000000..ea52e668a7 --- /dev/null +++ b/LeanPool/CCDN/Projection/HilbertProjectionDegree.lean @@ -0,0 +1,69 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.Hilbert.FilteredHilbertPolynomial +public import LeanPool.CCDN.Hilbert.LinearProjectionFiltration +public import Mathlib.RingTheory.AlgebraicIndependent.Defs +import LeanPool.CCDN.ForMathlib.MvPolynomial +import LeanPool.CCDN.Hilbert.PolynomialComparison +import LeanPool.CCDN.Hilbert.PrincipalHilbertPolynomial +import LeanPool.CCDN.Projection.HilbertProjectionKernel + +/-! Degree bounds for hypersurface equations of linear projections. +The degree is bounded by the factorial-normalized leading coefficient of +the original quotient's filtered Hilbert polynomial. -/ + +@[expose] public section +namespace CCDN +noncomputable section +open MvPolynomial + +private theorem hypersurface_projection_equation_degree_le + {K τ : Type*} [Field K] [Fintype τ] [LinearOrder τ] {n : ℕ} + (I : Ideal (MvPolynomial τ K)) (hI : I ≠ ⊤) + (a : Fin (n + 1) → MvPolynomial τ K) (ha : ∀ i, (a i).totalDegree ≤ 1) + (d : ℕ) (hdimdeg : HasAffineDimensionDegree I n d) + (f : MvPolynomial (Fin (n + 1)) K) (hf : f ≠ 0) (hfdeg : 0 < f.totalDegree) + (hker : RingHom.ker (projectionCoordinateMap I a) = Ideal.span {f}) : + f.totalDegree ≤ d := by + have hp : Ideal.span {f} ≠ ⊤ := by + rw [← hker] + let : Nontrivial (MvPolynomial τ K ⧸ I) := Ideal.Quotient.nontrivial_iff.mpr hI + exact RingHom.ker_ne_top _ + have hh := filteredHilbertPolynomial_leadingCoeff_le (Ideal.span {f}) I hp hI + ((principal_filteredHilbertPolynomial_natDegree f hf hfdeg).trans hdimdeg.natDegree_eq.symm) + (fun N => by + rw [← hker] + exact linear_projection_filtered_finrank_le I a ha N) + rw [principal_filteredHilbertPolynomial_leadingCoeff f hf hfdeg, hdimdeg.leadingCoeff_eq] at hh + have hfac : (0 : ℚ) < n.factorial := by positivity + exact_mod_cast (div_le_div_iff_of_pos_right hfac).mp hh + +/-- The equation and its degree bound are constructed from the projection. -/ +theorem exists_hypersurface_projection_equation_degree_le + {K τ : Type*} [Field K] [Fintype τ] [LinearOrder τ] {n : ℕ} + (I : Ideal (MvPolynomial τ K)) [I.IsPrime] + (a : Fin (n + 2) → MvPolynomial τ K) (ha : ∀ i, (a i).totalDegree ≤ 1) + (d : ℕ) (hdimdeg : HasProjectiveDimensionDegree I n d) + (b : Fin (n + 1) → MvPolynomial (Fin (n + 2)) K) + (hb : ∀ i, (b i).totalDegree ≤ 1) + (hi : AlgebraicIndependent K (fun i => projectionCoordinateMap I a (b i))) : + ∃ f : MvPolynomial (Fin (n + 2)) K, Irreducible f ∧ + 0 < f.totalDegree ∧ f.totalDegree ≤ d ∧ + RingHom.ker (projectionCoordinateMap I a) = Ideal.span {f} := by + obtain ⟨f, hf, hker⟩ := exists_hypersurface_projection_equation I a ha + (by rw [hdimdeg.natDegree_eq]; omega) b hb hi + have hfdeg : 0 < f.totalDegree := + totalDegree_pos_of_ne_zero_of_not_isUnit hf.ne_zero hf.not_isUnit + exact ⟨f, hf, hfdeg, hypersurface_projection_equation_degree_le I + (Ideal.IsPrime.ne_top ‹I.IsPrime›) a ha d hdimdeg f hf.ne_zero hfdeg hker, hker⟩ + +end +end CCDN diff --git a/LeanPool/CCDN/Projection/HilbertProjectionKernel.lean b/LeanPool/CCDN/Projection/HilbertProjectionKernel.lean new file mode 100644 index 0000000000..7224bee974 --- /dev/null +++ b/LeanPool/CCDN/Projection/HilbertProjectionKernel.lean @@ -0,0 +1,105 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.Hilbert.FilteredHilbertPolynomial +public import LeanPool.CCDN.Hilbert.LinearProjectionFiltration +public import Mathlib.RingTheory.AlgebraicIndependent.Defs +import LeanPool.CCDN.Algebra.BinomialSectionBound +import LeanPool.CCDN.Hilbert.LinearCoordinates +import LeanPool.CCDN.Hilbert.PolynomialComparison +import Mathlib.RingTheory.Polynomial.HilbertPoly +import Mathlib.RingTheory.Polynomial.UniqueFactorization +import Mathlib.RingTheory.SimpleModule.Basic + +/-! Hypersurface projection kernels in arbitrary dimension. +The proper-intersection estimate excludes a second independent equation. -/ + +@[expose] public section +namespace CCDN +noncomputable section +open MvPolynomial + +private theorem proper_intersection_hilbert_dimension_le + {K : Type*} [Field K] {n : ℕ} + {f g : MvPolynomial (Fin (n + 2)) K} (hf : Prime f) (hfg : ¬ f ∣ g) + (I : Ideal (MvPolynomial (Fin (n + 2)) K)) (hI : I ≠ ⊤) + (hIf : f ∈ I) (hIg : g ∈ I) : + (filteredHilbertPolynomial I).natDegree ≤ n := by + let Q := Polynomial.C ((f.totalDegree * g.totalDegree : ℕ) : ℚ) * + Polynomial.preHilbertPoly ℚ n 0 + have hQ : Q.natDegree ≤ n := by + apply Polynomial.natDegree_mul_le.trans + simp only [Polynomial.natDegree_C, Polynomial.natDegree_preHilbertPoly, zero_add] + exact le_rfl + apply (polynomial_natDegree_le_of_eventual_nat_eval_le + (filteredHilbertPolynomial I) Q (filteredHilbertPolynomial_leadingCoeff_pos I hI) ?_).trans hQ + obtain ⟨M, hM⟩ := filteredHilbertPolynomial_spec I + refine ⟨max M (f.totalDegree + g.totalDegree), fun N hN => ?_⟩ + rw [hM N ((le_max_left _ _).trans hN)] + dsimp [Q] + rw [Polynomial.eval_mul, Polynomial.eval_C, + Polynomial.preHilbertPoly_eq_choose_add_sub ℚ n (by omega : 0 ≤ N + n)] + simp only [Nat.sub_zero, Nat.add_comm N n] + exact_mod_cast filteredCoordinateSpace_codim_two_bound hf hfg I hIf hIg + ((le_max_right _ _).trans hN) + +/-- An irreducible equation generates the whole kernel whenever the image +contains the expected number of independent linear coordinates. -/ +theorem hypersurface_map_eq_zero_iff_dvd + {K A : Type*} [Field K] [CommRing A] [Nontrivial A] [Algebra K A] {n : ℕ} + (f : MvPolynomial (Fin (n + 2)) K) (hf : Irreducible f) + (ψ : MvPolynomial (Fin (n + 2)) K →ₐ[K] A) (hψ : ψ f = 0) + (b : Fin (n + 1) → MvPolynomial (Fin (n + 2)) K) + (hb : ∀ i, (b i).totalDegree ≤ 1) + (hi : AlgebraicIndependent K (fun i => ψ (b i))) + (g : MvPolynomial (Fin (n + 2)) K) : ψ g = 0 ↔ f ∣ g := by + constructor + · intro hg + by_contra hnd + let I := RingHom.ker ψ + let φ := Ideal.kerLiftAlg ψ + have hAI : AlgebraicIndependent K (fun i => (Ideal.Quotient.mk I) (b i)) := by + apply AlgebraicIndependent.of_comp φ + exact hi + have hlo := independent_linear_card_le_hilbert_dimension I b hb hAI + have hhi := proper_intersection_hilbert_dimension_le hf.prime hnd I + (RingHom.ker_ne_top ψ) hψ hg + simp only [Fintype.card_fin] at hlo + omega + · rintro ⟨u, rfl⟩ + rw [map_mul, hψ, zero_mul] + +/-- A linear projection with one more coordinate than Hilbert dimension +and an independent subtuple has a principal prime equation. -/ +theorem exists_hypersurface_projection_equation + {K τ : Type*} [Field K] [Fintype τ] [LinearOrder τ] {n : ℕ} + (I : Ideal (MvPolynomial τ K)) [I.IsPrime] + (a : Fin (n + 2) → MvPolynomial τ K) (ha : ∀ i, (a i).totalDegree ≤ 1) + (hd : (filteredHilbertPolynomial I).natDegree < n + 2) + (b : Fin (n + 1) → MvPolynomial (Fin (n + 2)) K) + (hb : ∀ i, (b i).totalDegree ≤ 1) + (hi : AlgebraicIndependent K (fun i => projectionCoordinateMap I a (b i))) : + ∃ f : MvPolynomial (Fin (n + 2)) K, Irreducible f ∧ + RingHom.ker (projectionCoordinateMap I a) = Ideal.span {f} := by + let ψ := projectionCoordinateMap I a + have hker : RingHom.ker ψ ≠ ⊥ := by + intro he + exact linear_projection_not_injective_of_hilbert_dimension_lt I a ha + (by simpa only [Fintype.card_fin] using hd) + ((RingHom.injective_iff_ker_eq_bot ψ.toRingHom).mpr he) + have hprime : (RingHom.ker ψ).IsPrime := RingHom.ker_isPrime ψ.toRingHom + obtain ⟨f, hfmem, hf⟩ := hprime.exists_mem_prime_of_ne_bot hker + refine ⟨f, hf.irreducible, ?_⟩ + ext g + exact (hypersurface_map_eq_zero_iff_dvd f hf.irreducible ψ hfmem b hb hi g).trans + Ideal.mem_span_singleton.symm + +end +end CCDN diff --git a/LeanPool/CCDN/Projection/HomogeneousBirationalDegree.lean b/LeanPool/CCDN/Projection/HomogeneousBirationalDegree.lean new file mode 100644 index 0000000000..6b8f2ffac0 --- /dev/null +++ b/LeanPool/CCDN/Projection/HomogeneousBirationalDegree.lean @@ -0,0 +1,138 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.Algebra.PolynomialPredicates +public import LeanPool.CCDN.Hilbert.FilteredHilbertPolynomial +public import LeanPool.CCDN.Hilbert.LinearProjectionFiltration +import LeanPool.CCDN.Algebra.CoordinatePowerGeneration +import LeanPool.CCDN.Hilbert.PolynomialComparison +import LeanPool.CCDN.Projection.HomogeneousFilteredProjection +import Mathlib.LinearAlgebra.FreeModule.PID + +/-! A homogeneous linear projection with a nonzero conductor preserves +Hilbert multiplicity. The conductor shifts the filtration by a fixed amount, +which leaves the leading coefficient of the Hilbert polynomial unchanged. -/ + +@[expose] public section +namespace CCDN +noncomputable section +open MvPolynomial + +private theorem conductor_filtered_finrank_le + {K σ τ : Type*} [Field K] [Finite σ] [Finite τ] + (I : Ideal (MvPolynomial τ K)) [I.IsPrime] + (hI : IsHomogeneousIdeal I) + (a : σ → MvPolynomial τ K) (ha : ∀ i, (a i).IsHomogeneous 1) + (c : MvPolynomial τ K) (hc : (Ideal.Quotient.mk I) c ≠ 0) + (hconductor : ∀ x : MvPolynomial τ K ⧸ I, ∃ p : MvPolynomial σ K, + projectionCoordinateMap I a p = (Ideal.Quotient.mk I) c * x) + (N : ℕ) : + Module.finrank K (filteredCoordinateSpace I N) ≤ + Module.finrank K (filteredCoordinateSpace + (RingHom.ker (projectionCoordinateMap I a)) (c.totalDegree + N)) := by + classical + let := Fintype.ofFinite σ + let := Fintype.ofFinite τ + let ψ := projectionCoordinateMap I a + let J := RingHom.ker ψ + let V := filteredCoordinateSpace I N + let W := filteredCoordinateSpace J (c.totalDegree + N) + let b := Module.finBasis K V + have hcl : (Ideal.Quotient.mk I) c ∈ filteredCoordinateSpace I c.totalDegree := + Submodule.mem_map.mpr ⟨c, (mem_restrictTotalDegree _ _ _).mpr le_rfl, rfl⟩ + have hpre (i : Fin (Module.finrank K V)) : + ∃ p : MvPolynomial σ K, p.totalDegree ≤ c.totalDegree + N ∧ + ψ p = (Ideal.Quotient.mk I) c * (b i).val := by + obtain ⟨p, hp⟩ := hconductor (b i).val + have hm : ψ p ∈ filteredCoordinateSpace I (c.totalDegree + N) := by + rw [hp] + exact filteredCoordinateSpace_mul_mem I hcl (b i).property + obtain ⟨q, hq, he⟩ := + homogeneous_projection_filtered_lift I hI a ha p (c.totalDegree + N) hm + exact ⟨q, hq, he.trans hp⟩ + choose p hp he using hpre + let w : Fin (Module.finrank K V) → W := fun i => + ⟨(Ideal.Quotient.mk J) (p i), + Submodule.mem_map.mpr ⟨p i, (mem_restrictTotalDegree _ _ _).mpr (hp i), rfl⟩⟩ + let T : (MvPolynomial τ K ⧸ I) →ₗ[K] (MvPolynomial τ K ⧸ I) := + { toFun := fun x => (Ideal.Quotient.mk I) c * x + map_add' := fun x y => mul_add _ x y + map_smul' := fun r x => by simp only [mul_smul_comm, RingHom.id_apply] } + have hT : Function.Injective (T.comp V.subtype) := by + intro x y heq + apply Subtype.ext + exact mul_left_cancel₀ hc heq + have hli := b.linearIndependent.map' (T.comp V.subtype) (LinearMap.ker_eq_bot.mpr hT) + have hwi : LinearIndependent K w := by + apply LinearIndependent.of_comp ((Ideal.kerLiftAlg ψ).toLinearMap.comp W.subtype) + convert hli using 1 + funext i + exact he i + simpa only [Fintype.card_fin] using hwi.fintype_card_le_finrank + +private theorem filteredHilbertPolynomial_leadingCoeff_le_of_shift + {K L σ τ : Type*} [Field K] [Field L] [Fintype σ] [Fintype τ] + [LinearOrder σ] [LinearOrder τ] + (I : Ideal (MvPolynomial σ K)) (J : Ideal (MvPolynomial τ L)) + (hI : I ≠ ⊤) (hJ : J ≠ ⊤) + (hd : (filteredHilbertPolynomial I).natDegree = (filteredHilbertPolynomial J).natDegree) + (D : ℕ) + (hle : ∀ N, Module.finrank K (filteredCoordinateSpace I N) ≤ + Module.finrank L (filteredCoordinateSpace J (D + N))) : + (filteredHilbertPolynomial I).leadingCoeff ≤ + (filteredHilbertPolynomial J).leadingCoeff := by + let Q := (filteredHilbertPolynomial J).comp (Polynomial.X + Polynomial.C (D : ℚ)) + have hQdeg : Q.natDegree = (filteredHilbertPolynomial J).natDegree := by + simp only [Q, Polynomial.natDegree_comp, Polynomial.natDegree_X_add_C, mul_one] + have hQlc : Q.leadingCoeff = (filteredHilbertPolynomial J).leadingCoeff := by + dsimp only [Q] + rw [Polynomial.leadingCoeff_comp (by rw [Polynomial.natDegree_X_add_C]; exact one_ne_zero), + Polynomial.leadingCoeff_X_add_C, one_pow, mul_one] + have hh := polynomial_leadingCoeff_le_of_eventual_nat_eval_le + (filteredHilbertPolynomial I) Q (filteredHilbertPolynomial_leadingCoeff_pos I hI) + (by rw [hQlc]; exact filteredHilbertPolynomial_leadingCoeff_pos J hJ) + (hd.trans hQdeg.symm) (by + obtain ⟨M, hM⟩ := filteredHilbertPolynomial_spec I + obtain ⟨N, hN⟩ := filteredHilbertPolynomial_spec J + refine ⟨max M N, fun n hn => ?_⟩ + rw [hM n ((le_max_left _ _).trans hn)] + simp only [Q, Polynomial.eval_comp, Polynomial.eval_add, Polynomial.eval_X, + Polynomial.eval_C] + rw [show (n : ℚ) + D = ((D + n : ℕ) : ℚ) by push_cast; ring, + hN (D + n) (by omega)] + exact_mod_cast hle n) + simpa only [hQlc] using hh + +/-- A nonzero conductor yields equality of Hilbert multiplicities +for a homogeneous linear birational projection. -/ +theorem homogeneous_projection_leadingCoeff_eq + {K σ τ : Type*} [Field K] [Fintype σ] [Fintype τ] + [LinearOrder σ] [LinearOrder τ] + (I : Ideal (MvPolynomial τ K)) [I.IsPrime] + (hI : IsHomogeneousIdeal I) + (a : σ → MvPolynomial τ K) (ha : ∀ i, (a i).IsHomogeneous 1) + (hd : (filteredHilbertPolynomial (RingHom.ker (projectionCoordinateMap I a))).natDegree = + (filteredHilbertPolynomial I).natDegree) + (c : MvPolynomial τ K) (hc : (Ideal.Quotient.mk I) c ≠ 0) + (hconductor : ∀ x : MvPolynomial τ K ⧸ I, ∃ p : MvPolynomial σ K, + projectionCoordinateMap I a p = (Ideal.Quotient.mk I) c * x) : + (filteredHilbertPolynomial (RingHom.ker (projectionCoordinateMap I a))).leadingCoeff = + (filteredHilbertPolynomial I).leadingCoeff := by + have hIproper : I ≠ ⊤ := Ideal.IsPrime.ne_top ‹I.IsPrime› + have hJproper : RingHom.ker (projectionCoordinateMap I a) ≠ ⊤ := + RingHom.ker_ne_top _ + apply le_antisymm + · exact filteredHilbertPolynomial_leadingCoeff_le _ _ hJproper hIproper hd + (linear_projection_filtered_finrank_le I a (fun i => (ha i).totalDegree_le)) + · exact filteredHilbertPolynomial_leadingCoeff_le_of_shift _ _ hIproper hJproper hd.symm + c.totalDegree (conductor_filtered_finrank_le I hI a ha c hc hconductor) + +end +end CCDN diff --git a/LeanPool/CCDN/Projection/HomogeneousFilteredProjection.lean b/LeanPool/CCDN/Projection/HomogeneousFilteredProjection.lean new file mode 100644 index 0000000000..7a503e437d --- /dev/null +++ b/LeanPool/CCDN/Projection/HomogeneousFilteredProjection.lean @@ -0,0 +1,107 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.Algebra.PolynomialPredicates +public import LeanPool.CCDN.Hilbert.LinearProjectionFiltration +import Mathlib.Algebra.MvPolynomial.Monad + +/-! A linear homogeneous projection of a homogeneous quotient is strict +for the total-degree filtration: any image element of bounded degree has a +preimage of the same bounded degree. -/ + +@[expose] public section +namespace CCDN +noncomputable section +open MvPolynomial + +private def degreeTruncation {K σ : Type*} [Field K] (N : ℕ) : + MvPolynomial σ K →ₗ[K] MvPolynomial σ K := + ∑ i ∈ Finset.range (N + 1), homogeneousComponent i + +private lemma degreeTruncation_apply {K σ : Type*} [Field K] (N : ℕ) + (p : MvPolynomial σ K) : + degreeTruncation N p = ∑ i ∈ Finset.range (N + 1), homogeneousComponent i p := by + simp [degreeTruncation] + +private lemma degreeTruncation_degree {K σ : Type*} [Field K] (N : ℕ) + (p : MvPolynomial σ K) : (degreeTruncation N p).totalDegree ≤ N := by + rw [degreeTruncation_apply] + apply totalDegree_finsetSum_le + intro i hi + exact (homogeneousComponent_isHomogeneous i p).totalDegree_le.trans + (Nat.le_of_lt_succ (Finset.mem_range.mp hi)) + +private lemma degreeTruncation_eq_self {K σ : Type*} [Field K] (N : ℕ) + (p : MvPolynomial σ K) (hp : p.totalDegree ≤ N) : degreeTruncation N p = p := by + rw [degreeTruncation_apply] + conv_rhs => rw [← sum_homogeneousComponent (φ := p)] + symm + apply Finset.sum_subset (Finset.range_mono (Nat.add_le_add_right hp 1)) + intro i hi hnot + apply homogeneousComponent_eq_zero + have hh : ¬ i < p.totalDegree + 1 := by simpa only [Finset.mem_range] using hnot + omega + +/-- Substituting homogeneous linear forms commutes with taking homogeneous components: the +degree-`N` component of `aeval a p` is `aeval a` of the degree-`N` component of `p`. -/ +lemma homogeneousComponent_aeval_linear {K σ τ : Type*} [Field K] + (a : σ → MvPolynomial τ K) (ha : ∀ i, (a i).IsHomogeneous 1) + (N : ℕ) (p : MvPolynomial σ K) : + homogeneousComponent N (aeval a p) = aeval a (homogeneousComponent N p) := by + classical + induction p using MvPolynomial.induction_on' with + | add p q hp hq => simp only [map_add, hp, hq] + | monomial e c => + have hm := isHomogeneous_monomial c (rfl : e.degree = e.degree) + have hm' : (aeval a (monomial e c)).IsHomogeneous e.degree := by + simpa only [one_mul] using hm.aeval a ha + rw [homogeneousComponent_of_mem hm, homogeneousComponent_of_mem hm'] + split_ifs <;> simp + +private lemma degreeTruncation_aeval_linear {K σ τ : Type*} [Field K] + (a : σ → MvPolynomial τ K) (ha : ∀ i, (a i).IsHomogeneous 1) + (N : ℕ) (p : MvPolynomial σ K) : + degreeTruncation N (aeval a p) = aeval a (degreeTruncation N p) := by + simp only [degreeTruncation_apply, map_sum, homogeneousComponent_aeval_linear a ha] + +/-- The projection of a homogeneous quotient by homogeneous linear forms `a` is strict for the +total-degree filtration: if the image of `p` has degree at most `N` in the quotient by `I`, then it +is the image of some `q` of total degree at most `N`. -/ +theorem homogeneous_projection_filtered_lift + {K σ τ : Type*} [Field K] [Finite σ] + (I : Ideal (MvPolynomial τ K)) + (hI : IsHomogeneousIdeal I) + (a : σ → MvPolynomial τ K) (ha : ∀ i, (a i).IsHomogeneous 1) + (p : MvPolynomial σ K) (N : ℕ) + (hp : projectionCoordinateMap I a p ∈ filteredCoordinateSpace I N) : + ∃ q : MvPolynomial σ K, q.totalDegree ≤ N ∧ + projectionCoordinateMap I a q = projectionCoordinateMap I a p := by + classical + let := Fintype.ofFinite σ + obtain ⟨f, hf, he⟩ := Submodule.mem_map.mp hp + have hfd : f.totalDegree ≤ N := (mem_restrictTotalDegree _ _ _).mp hf + have hmem : aeval a p - f ∈ I := by + apply Ideal.Quotient.eq_zero_iff_mem.mp + rw [map_sub] + change projectionCoordinateMap I a p - (Ideal.Quotient.mk I) f = 0 + change projectionCoordinateMap I a p - + (Ideal.Quotient.mkₐ K I).toLinearMap f = 0 + rw [he, sub_self] + have htmem : degreeTruncation N (aeval a p - f) ∈ I := by + rw [degreeTruncation_apply] + exact I.sum_mem (fun i _ => hI _ hmem i) + rw [map_sub, degreeTruncation_aeval_linear a ha, degreeTruncation_eq_self N f hfd] at htmem + refine ⟨degreeTruncation N p, degreeTruncation_degree N p, ?_⟩ + have ht := Ideal.Quotient.eq_zero_iff_mem.mpr htmem + rw [map_sub, sub_eq_zero] at ht + exact ht.trans he + +end +end CCDN diff --git a/LeanPool/CCDN/Projection/HomogeneousFiniteProjectionDegree.lean b/LeanPool/CCDN/Projection/HomogeneousFiniteProjectionDegree.lean new file mode 100644 index 0000000000..a243900045 --- /dev/null +++ b/LeanPool/CCDN/Projection/HomogeneousFiniteProjectionDegree.lean @@ -0,0 +1,92 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.Algebra.PolynomialPredicates +public import LeanPool.CCDN.Hilbert.FilteredHilbertPolynomial +public import LeanPool.CCDN.Hilbert.LinearProjectionFiltration +import LeanPool.CCDN.ForMathlib.FractionRing +import LeanPool.CCDN.Projection.HomogeneousBirationalDegree +import LeanPool.CCDN.Projection.IntegralProjectionHilbert +import LeanPool.CCDN.Projection.PrimitiveCoordinateField + +/-! A homogeneous finite birational linear projection preserves the +Hilbert multiplicity. Both the conductor and the dimension equality +are consequences of the algebraic hypotheses. -/ + +@[expose] public section +namespace CCDN +noncomputable section +open MvPolynomial + +/-- A homogeneous linear projection of a homogeneous prime quotient that is integral and birational +(its coordinates generate the function field) preserves the leading coefficient of the filtered +Hilbert polynomial, and hence the degree. -/ +theorem homogeneous_finite_birational_projection_leadingCoeff_eq + {K F σ τ : Type*} [Field K] [Field F] [Algebra K F] + [Fintype σ] [Fintype τ] [LinearOrder σ] [LinearOrder τ] + (I : Ideal (MvPolynomial τ K)) [I.IsPrime] + [Algebra (MvPolynomial τ K ⧸ I) F] + [IsScalarTower K (MvPolynomial τ K ⧸ I) F] + [IsFractionRing (MvPolynomial τ K ⧸ I) F] + (hI : IsHomogeneousIdeal I) + (a : σ → MvPolynomial τ K) (ha : ∀ i, (a i).IsHomogeneous 1) + (hint : (projectionCoordinateMap I a).IsIntegral) + (hfield : IntermediateField.adjoin K + (Set.range (fun i => algebraMap (MvPolynomial τ K ⧸ I) F + ((Ideal.Quotient.mk I) (a i)))) = ⊤) : + (filteredHilbertPolynomial (RingHom.ker (projectionCoordinateMap I a))).leadingCoeff = + (filteredHilbertPolynomial I).leadingCoeff := by + let A := MvPolynomial τ K ⧸ I + let ψ := projectionCoordinateMap I a + let J := RingHom.ker ψ + have : J.IsPrime := RingHom.ker_isPrime ψ.toRingHom + let R := MvPolynomial σ K ⧸ J + let φ : R →ₐ[K] A := Ideal.kerLiftAlg ψ + let η : A →ₐ[K] F := IsScalarTower.toAlgHom K A F + let : Algebra R A := φ.toRingHom.toAlgebra + let : IsScalarTower K R A := IsScalarTower.of_algHom φ + let : Algebra R F := (η.comp φ).toRingHom.toAlgebra + let : IsScalarTower R A F := IsScalarTower.of_algebraMap_eq (fun _ => rfl) + have hinj : Function.Injective (η.comp φ) := + (IsFractionRing.injective A F).comp (Ideal.kerLiftAlg_injective ψ) + let : IsFractionRing R F := isFractionRing_of_generating_tuple + (η.comp φ) hinj (fun i => (Ideal.Quotient.mk J) (X i)) (by + convert hfield using 2 + congr 1 + funext i + change algebraMap A F ((Ideal.Quotient.mk I) (aeval a (X i))) = + algebraMap A F ((Ideal.Quotient.mk I) (a i)) + rw [aeval_X]) + let : Algebra.IsIntegral R A := ⟨hint.kerLift⟩ + let : Algebra.FiniteType R A := + Algebra.FiniteType.of_restrictScalars_finiteType K R A + let : Module.Finite R A := Algebra.IsIntegral.finite + obtain ⟨c, hc, hconductor⟩ := IsFractionRing.exists_ne_zero_mul_mem_range_of_finite R A F + obtain ⟨p, hp⟩ := Ideal.Quotient.mk_surjective c + let q := aeval a p + have hq : (Ideal.Quotient.mk I) q = φ c := by + rw [← hp] + rfl + have hqne : (Ideal.Quotient.mk I) q ≠ 0 := by + rw [hq] + exact (map_ne_zero_iff φ (Ideal.kerLiftAlg_injective ψ)).mpr hc + apply homogeneous_projection_leadingCoeff_eq I hI a ha + (integral_projection_hilbert_dimension_eq I ψ hint) q hqne + intro x + obtain ⟨r, hr⟩ := hconductor x + obtain ⟨p', hp'⟩ := Ideal.Quotient.mk_surjective r + refine ⟨p', ?_⟩ + rw [hq] + change φ ((Ideal.Quotient.mk J) p') = φ c * x + rw [hp'] + exact hr + +end +end CCDN diff --git a/LeanPool/CCDN/Projection/HomogeneousProjectionEquation.lean b/LeanPool/CCDN/Projection/HomogeneousProjectionEquation.lean new file mode 100644 index 0000000000..65478ecc2b --- /dev/null +++ b/LeanPool/CCDN/Projection/HomogeneousProjectionEquation.lean @@ -0,0 +1,78 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.Algebra.PolynomialPredicates +public import LeanPool.CCDN.Hilbert.LinearProjectionFiltration +import LeanPool.CCDN.ForMathlib.MvPolynomial +import LeanPool.CCDN.Projection.HomogeneousFilteredProjection +import LeanPool.CCDN.Projection.PlaneRelationSpace + +/-! The principal equation of the image of a homogeneous linear +projection is homogeneous. -/ + +@[expose] public section +namespace CCDN +noncomputable section +open MvPolynomial + +/-- For a homogeneous ideal `I` and homogeneous linear forms `a`, the kernel of the projection by +`a` is closed under taking homogeneous components. -/ +theorem projection_kernel_homogeneousComponent_mem + {K σ τ : Type*} [Field K] + (I : Ideal (MvPolynomial τ K)) + (hI : IsHomogeneousIdeal I) + (a : σ → MvPolynomial τ K) (ha : ∀ i, (a i).IsHomogeneous 1) + (p : MvPolynomial σ K) (hp : p ∈ RingHom.ker (projectionCoordinateMap I a)) (N : ℕ) : + homogeneousComponent N p ∈ RingHom.ker (projectionCoordinateMap I a) := by + change (Ideal.Quotient.mk I) (aeval a (homogeneousComponent N p)) = 0 + apply Ideal.Quotient.eq_zero_iff_mem.mpr + rw [← homogeneousComponent_aeval_linear a ha] + apply hI + apply Ideal.Quotient.eq_zero_iff_mem.mp + exact hp + +/-- A nonzero generator of a principal ideal that is closed under homogeneous components is +homogeneous of its total degree. -/ +theorem principal_generator_isHomogeneous + {K σ : Type*} [Field K] + (I : Ideal (MvPolynomial σ K)) + (hI : IsHomogeneousIdeal I) + (f : MvPolynomial σ K) (hf : f ≠ 0) (hgen : I = Ideal.span {f}) : + f.IsHomogeneous f.totalDegree := by + have hmem : f ∈ I := by rw [hgen]; exact Ideal.subset_span (Set.mem_singleton f) + have ht := hI f hmem f.totalDegree + rw [hgen, Ideal.mem_span_singleton] at ht + obtain ⟨c, hc⟩ := eq_scalar_mul_of_dvd_of_totalDegree_le f + (homogeneousComponent f.totalDegree f) hf ht + (homogeneousComponent_isHomogeneous f.totalDegree f).totalDegree_le + have hcne : c ≠ 0 := by + intro hz + apply homogeneousComponent_totalDegree_ne_zero hf + rw [hc, hz, zero_smul] + have hh := (homogeneousComponent_isHomogeneous f.totalDegree f).C_mul c⁻¹ + rw [hc] at hh + rw [← smul_eq_C_mul] at hh + simpa only [smul_smul, inv_mul_cancel₀ hcne, one_smul] using hh + +/-- If the kernel of the projection of a homogeneous ideal by homogeneous linear forms is generated +by a nonzero polynomial `f`, then `f` is homogeneous of its total degree. -/ +theorem homogeneous_projection_equation + {K σ τ : Type*} [Field K] + (I : Ideal (MvPolynomial τ K)) + (hI : IsHomogeneousIdeal I) + (a : σ → MvPolynomial τ K) (ha : ∀ i, (a i).IsHomogeneous 1) + (f : MvPolynomial σ K) (hf : f ≠ 0) + (hgen : RingHom.ker (projectionCoordinateMap I a) = Ideal.span {f}) : + f.IsHomogeneous f.totalDegree := + principal_generator_isHomogeneous _ (projection_kernel_homogeneousComponent_mem I hI a ha) + f hf hgen + +end +end CCDN diff --git a/LeanPool/CCDN/Projection/ImageFractionField.lean b/LeanPool/CCDN/Projection/ImageFractionField.lean new file mode 100644 index 0000000000..7224accad9 --- /dev/null +++ b/LeanPool/CCDN/Projection/ImageFractionField.lean @@ -0,0 +1,164 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.Hilbert.LinearProjectionFiltration +public import Mathlib.FieldTheory.IntermediateField.Adjoin.Defs +public import Mathlib.RingTheory.IntegralClosure.IsIntegral.Defs +import LeanPool.CCDN.Projection.PrimitiveCoordinateField + +/-! The field generated by projected coordinates is the fraction field +of the image coordinate ring. Its inclusion in the source function +field is constructed and identified on every coordinate-ring element. -/ + +@[expose] public section +namespace CCDN +noncomputable section +open MvPolynomial + +variable {K F σ τ : Type*} [Field K] [Field F] [Algebra K F] +variable (I : Ideal (MvPolynomial τ K)) +variable [Algebra (MvPolynomial τ K ⧸ I) F] [IsScalarTower K (MvPolynomial τ K ⧸ I) F] +variable (a : σ → MvPolynomial τ K) + +/-- The subfield of `F` generated over `K` by the images of the forms `a` in the quotient by `I`; +for `F` the function field of `I`, it is the function field of the image of the projection by `a`. +-/ +def projectionImageField : IntermediateField K F := + IntermediateField.adjoin K (Set.range (fun i => + algebraMap (MvPolynomial τ K ⧸ I) F ((Ideal.Quotient.mk I) (a i)))) + +/-- The forms `a` give a Noether normalization of the quotient by `I`: the coordinate map +`K[y] → K[x]/I` is integral and injective, that is, the forms are algebraically independent +modulo `I` and the quotient is finite over the polynomial ring they generate. -/ +structure IsNoetherNormalization : Prop where + /-- The quotient by `I` is integral over the subring generated by the forms. -/ + integral : (projectionCoordinateMap I a).IsIntegral + /-- The forms are algebraically independent modulo `I`. -/ + injective : Function.Injective (projectionCoordinateMap I a) + +/-- The forms `a` map the variety of the prime `I` birationally onto the hypersurface `f = 0`: +the images of the forms generate the function field of `I`, and the kernel of the coordinate map +`K[y] → K[x]/I` is generated by the irreducible polynomial `f`. -/ +structure IsBirationalImage [I.IsPrime] (f : MvPolynomial σ K) : Prop where + /-- The images of the forms generate the function field of `I`. -/ + birational : projectionImageField (F := FractionRing (MvPolynomial τ K ⧸ I)) I a = ⊤ + /-- The image equation `f` is irreducible. -/ + irreducible : Irreducible f + /-- The kernel of the coordinate map is the principal ideal generated by `f`. -/ + ker_eq : RingHom.ker (projectionCoordinateMap I a) = Ideal.span {f} + +/-- The forms `a` define a finite birational projection of the variety of the prime `I` onto +the hypersurface `f = 0`: a birational image whose coordinate map `K[y] → K[x]/I` is moreover +integral. -/ +structure IsBirationalProjection [I.IsPrime] (f : MvPolynomial σ K) : Prop + extends IsBirationalImage I a f where + /-- The quotient by `I` is integral over the subring generated by the forms. -/ + integral : (projectionCoordinateMap I a).IsIntegral + +variable {I} in +/-- For a birational image onto `f = 0`, a polynomial in the forms lies in the ideal exactly when +it is a multiple of `f`. -/ +lemma IsBirationalImage.aeval_mem_iff [I.IsPrime] {a : σ → MvPolynomial τ K} + {f : MvPolynomial σ K} (h : IsBirationalImage I a f) (g : MvPolynomial σ K) : + aeval a g ∈ I ↔ f ∣ g := by + rw [← Ideal.mem_span_singleton, ← h.ker_eq, RingHom.mem_ker] + exact Ideal.Quotient.eq_zero_iff_mem.symm + +variable {I} in +/-- The image equation of a birational image vanishes on the variety: substituting the forms +into it gives an element of the ideal. -/ +lemma IsBirationalImage.aeval_mem [I.IsPrime] {a : σ → MvPolynomial τ K} + {f : MvPolynomial σ K} (h : IsBirationalImage I a f) : aeval a f ∈ I := + (h.aeval_mem_iff f).mpr dvd_rfl + +/-- The image of the form `a i` in `F`, as an element of the field generated by the projected +coordinates. -/ +def projectionImageCoordinate (i : σ) : projectionImageField (F := F) I a := + ⟨algebraMap (MvPolynomial τ K ⧸ I) F ((Ideal.Quotient.mk I) (a i)), + IntermediateField.subset_adjoin K _ ⟨i, rfl⟩⟩ + +/-- Evaluating a polynomial at the projected coordinates and including the result in `F` agrees with +applying the projection map to the quotient by `I` and then mapping to `F`. -/ +lemma projectionImageCoordinate_eval_comp : + (projectionImageField (F := F) I a).val.comp (aeval (projectionImageCoordinate (F := F) I a)) = + (IsScalarTower.toAlgHom K (MvPolynomial τ K ⧸ I) F).comp (projectionCoordinateMap I a) := by + ext i + simp only [AlgHom.comp_apply, aeval_X, projectionCoordinateMap, Ideal.Quotient.mkₐ_eq_mk] + rfl + +/-- The image coordinate ring embeds into the generated image field. -/ +def projectionImageFieldMap : + (MvPolynomial σ K ⧸ RingHom.ker (projectionCoordinateMap I a)) →ₐ[K] + projectionImageField (F := F) I a := + Ideal.Quotient.liftₐ _ (aeval (projectionImageCoordinate (F := F) I a)) (by + intro p hp + apply (projectionImageField (F := F) I a).val.injective + have he := DFunLike.congr_fun (projectionImageCoordinate_eval_comp (F := F) I a) p + change ((aeval (projectionImageCoordinate (F := F) I a)) p : F) = + algebraMap (MvPolynomial τ K ⧸ I) F ((projectionCoordinateMap I a) p) at he + change ((aeval (projectionImageCoordinate (F := F) I a)) p : F) = 0 + exact he.trans (by rw [hp, map_zero])) + + +/-- The embedding of the image coordinate ring into the image field, followed by the inclusion into +`F`, is the induced map from the image coordinate ring to the quotient by `I`, followed by the map +to `F`. -/ +lemma projectionImageFieldMap_comp_val : + (projectionImageField (F := F) I a).val.comp (projectionImageFieldMap (F := F) I a) = + (IsScalarTower.toAlgHom K (MvPolynomial τ K ⧸ I) F).comp + (Ideal.kerLiftAlg (projectionCoordinateMap I a)) := by + apply AlgHom.ext + intro x + obtain ⟨p, rfl⟩ := Ideal.Quotient.mk_surjective x + exact DFunLike.congr_fun (projectionImageCoordinate_eval_comp (F := F) I a) p + +private lemma projectionImageFieldMap_X (i : σ) : + projectionImageFieldMap (F := F) I a + ((Ideal.Quotient.mk (RingHom.ker (projectionCoordinateMap I a))) (X i)) = + projectionImageCoordinate (F := F) I a i := by + change (aeval (projectionImageCoordinate (F := F) I a)) (X i) = _ + rw [aeval_X] + +/-- When `F` is the fraction field of the quotient by the prime `I`, the embedding of the image +coordinate ring into the image field is injective. -/ +lemma projectionImageFieldMap_injective [IsFractionRing (MvPolynomial τ K ⧸ I) F] : + Function.Injective (projectionImageFieldMap (F := F) I a) := by + intro x y h + apply Ideal.kerLiftAlg_injective (projectionCoordinateMap I a) + apply IsFractionRing.injective (MvPolynomial τ K ⧸ I) F + have hx := DFunLike.congr_fun (projectionImageFieldMap_comp_val (F := F) I a) x + have hy := DFunLike.congr_fun (projectionImageFieldMap_comp_val (F := F) I a) y + exact hx.symm.trans ((congrArg (projectionImageField (F := F) I a).val h).trans hy) + + +private lemma projectionImageFieldMap_generates : + IntermediateField.adjoin K (Set.range (fun i => projectionImageFieldMap (F := F) I a + ((Ideal.Quotient.mk (RingHom.ker (projectionCoordinateMap I a))) (X i)))) = ⊤ := by + let L := projectionImageField (F := F) I a + apply IntermediateField.lift_injective L + rw [IntermediateField.lift_adjoin, IntermediateField.lift_top, ← Set.range_comp] + simp only [Function.comp_def, projectionImageFieldMap_X] + rfl + +/-- When `F` is the fraction field of the quotient by the prime `I`, the field generated by the +projected coordinates is a fraction field of the image coordinate ring. -/ +theorem projectionImageFieldMap_isFractionRing + [IsFractionRing (MvPolynomial τ K ⧸ I) F] : + let : Algebra (MvPolynomial σ K ⧸ RingHom.ker (projectionCoordinateMap I a)) + (projectionImageField (F := F) I a) := + (projectionImageFieldMap (F := F) I a).toRingHom.toAlgebra + IsFractionRing (MvPolynomial σ K ⧸ RingHom.ker (projectionCoordinateMap I a)) + (projectionImageField (F := F) I a) := + isFractionRing_of_generating_tuple (projectionImageFieldMap (F := F) I a) + (projectionImageFieldMap_injective I a) (fun i => (Ideal.Quotient.mk _) (X i)) + (projectionImageFieldMap_generates I a) + +end +end CCDN diff --git a/LeanPool/CCDN/Projection/InfinityChartCardinality.lean b/LeanPool/CCDN/Projection/InfinityChartCardinality.lean new file mode 100644 index 0000000000..b184949dcb --- /dev/null +++ b/LeanPool/CCDN/Projection/InfinityChartCardinality.lean @@ -0,0 +1,239 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.Projection.InfinityParameterReduction +import LeanPool.CCDN.ForMathlib.LinearMap + +/-! +The certified affine Hilbert degree bounds the number of geometric +infinity points in each normalized projective chart. + +Homogeneous polynomials with extension-field coefficients contribute no +new directions beyond the extension-field span of ground-field monomials. + +Finite interpolation in a projective affine chart can be performed by +homogeneous polynomials of any sufficiently large common degree. + +Evaluating homogeneous polynomials at geometric infinity points has +L-linear rank at most the corresponding infinity Hilbert piece. The +source curve and its Hilbert function are defined over the original field K. +-/ + +@[expose] public section +namespace CCDN +noncomputable section +open MvPolynomial + +private def homogeneousPointEvaluation {K L σ ι : Type*} [Field K] [Field L] + [Algebra K L] (z : ι → σ → L) (m : ℕ) : + homogeneousSubmodule σ K m →ₗ[K] (ι → L) := + LinearMap.pi (fun i => (aeval (z i)).toLinearMap.comp (homogeneousSubmodule σ K m).subtype) + +/-- The L-span of evaluations of K-polynomials is controlled by the +infinity quotient. -/ +private theorem infinity_evaluation_span_finrank_le {K L σ ι : Type*} [Field K] [Field L] + [Algebra K L] [Finite σ] (I : Ideal (MvPolynomial σ K)) + (z : ι → σ → L) (hz : ∀ i, z i ∈ MvPolynomial.zeroLocus L (leadingInfinityIdeal I)) + (m : ℕ) : + Module.finrank L + (Submodule.span L (Set.range (homogeneousPointEvaluation (K := K) z (m + 1)))) ≤ + Module.finrank K (infinityGradedSpace I m) := by + classical + let := Fintype.ofFinite σ + let V := homogeneousSubmodule σ K (m + 1) + let : Module.Finite K V := Module.Finite.of_fg (homogeneousSubmodule_fg σ K (m + 1)) + let f := (infinityCoordinateMap I).toLinearMap.comp V.subtype + let g := homogeneousPointEvaluation (K := K) z (m + 1) + have hker : f.ker ≤ g.ker := by + intro p hp + have hpI : (p : MvPolynomial σ K) ∈ leadingInfinityIdeal I := hp + apply LinearMap.mem_ker.mpr + funext i + exact hz i _ hpI + have hrange : f.range ≤ infinityGradedSpace I m := by + rintro _ ⟨p, rfl⟩ + refine ⟨⟨(Ideal.Quotient.mk I) p, + ⟨p, (mem_restrictTotalDegree _ _ _).mpr p.property.totalDegree_le, rfl⟩⟩, ?_⟩ + change Ideal.Quotient.mk (projectiveInfinityIdeal I) (projectiveMonomialMap I (m + 1) _) = + infinityCoordinateMap I p + change _ = Ideal.Quotient.mk (projectiveInfinityIdeal I) (quotientRadialMap I p) + rw [quotientRadialMap_homogeneous I p (m + 1) p.property] + exact (LinearMap.finrank_span_range_le_of_ker_le f g hker).trans (Submodule.finrank_mono hrange) + +/-- In stable degrees `m ≥ N`, where the filtered coordinate spaces have dimension `d * m + b`, +the `L`-span of the value vectors `(h (z i))_i` of the homogeneous polynomials `h` of degree +`m + 1` at points `z i` of the cone at infinity has dimension at most `d`. -/ +private theorem infinity_evaluation_span_finrank_le_degree {K L σ ι : Type*} [Field K] [Field L] + [Algebra K L] [Finite σ] (I : Ideal (MvPolynomial σ K)) + (z : ι → σ → L) (hz : ∀ i, z i ∈ MvPolynomial.zeroLocus L (leadingInfinityIdeal I)) + {d N : ℕ} {b : ℤ} (hdegree : ∀ m ≥ N, + (Module.finrank K (filteredCoordinateSpace I m) : ℤ) = (d : ℤ) * m + b) + (m : ℕ) (hm : N ≤ m) : + Module.finrank L + (Submodule.span L (Set.range (homogeneousPointEvaluation (K := K) z (m + 1)))) ≤ d := by + classical + let := Fintype.ofFinite σ + exact (infinity_evaluation_span_finrank_le I z hz m).trans_eq + (infinityGradedSpace_eventually_degree I hdegree m hm) + +end +end CCDN + +namespace CCDN +noncomputable section +open MvPolynomial + +private lemma exists_homogeneous_chart_delta {L σ ι : Type*} [Field L] [Fintype ι] [DecidableEq ι] + (z : ι → σ → L) (hz : Function.Injective z) (j : σ) (hj : ∀ i, z i j = 1) + (m : ℕ) (hm : Fintype.card ι ≤ m) (i : ι) : + ∃ p : MvPolynomial σ L, p.IsHomogeneous m ∧ + ∀ k, MvPolynomial.eval (z k) p = if i = k then 1 else 0 := by + classical + have hdiff (k : ι) (hik : i ≠ k) : ∃ q : σ, z i q ≠ z k q := by + by_contra! he + exact hik (hz (funext he)) + let q (k : ι) (hik : i ≠ k) := (hdiff k hik).choose + have hq (k : ι) (hik : i ≠ k) : z i (q k hik) - z k (q k hik) ≠ 0 := + sub_ne_zero.mpr (hdiff k hik).choose_spec + let l (k : ι) : MvPolynomial σ L := if hik : i = k then X j else + C ((z i (q k hik) - z k (q k hik))⁻¹) * + (X (q k hik) - C (z k (q k hik)) * X j) + have hl (k : ι) : (l k).IsHomogeneous 1 := by + dsimp [l] + split_ifs + · exact isHomogeneous_X L j + · exact ((isHomogeneous_X L _).sub (isHomogeneous_C_mul_X _ _)).C_mul _ + have hli (k : ι) : MvPolynomial.eval (z i) (l k) = 1 := by + dsimp [l] + split_ifs with hik + · simp [hj] + · simp [hj, hq k hik] + have hlk (k : ι) (hik : i ≠ k) : MvPolynomial.eval (z k) (l k) = 0 := by + simp [l, hik, hj] + let p := (∏ k, l k) * X j ^ (m - Fintype.card ι) + refine ⟨p, ?_, ?_⟩ + · have hh : (∏ k, l k).IsHomogeneous (Fintype.card ι) := by + simpa only [Finset.sum_const, Finset.card_univ, smul_eq_mul, mul_one] using + IsHomogeneous.prod Finset.univ l (fun _ => 1) (fun k _ => hl k) + have ht := hh.mul (isHomogeneous_X_pow j (m - Fintype.card ι)) + simpa only [Nat.add_sub_of_le hm] using ht + · intro k + by_cases hik : i = k + · subst k + simp [p, hli, hj] + · have hh : ∏ t, MvPolynomial.eval (z k) (l t) = 0 := + Finset.prod_eq_zero (Finset.mem_univ k) (hlk k hik) + simp [p, hik, hh] + +end +end CCDN + +namespace CCDN +noncomputable section +open MvPolynomial + +private lemma homogeneous_extension_evaluation_mem {K L σ ι : Type*} [Field K] [Field L] + [Algebra K L] (z : ι → σ → L) (m : ℕ) (p : MvPolynomial σ L) + (hp : p.IsHomogeneous m) : + (fun i => MvPolynomial.eval (z i) p) ∈ + Submodule.span L (Set.range (homogeneousPointEvaluation (K := K) z m)) := by + classical + let W := Submodule.span L (Set.range (homogeneousPointEvaluation (K := K) z m)) + have hdeg (e : σ →₀ ℕ) (he : e ∈ p.support) : e.degree = m := + by + rw [Finsupp.degree_eq_weight_one, ← Pi.one_def] + exact hp (mem_support_iff.mp he) + have hmono (e : σ →₀ ℕ) (he : e ∈ p.support) : + (fun i => MvPolynomial.eval (z i) (monomial e (p.coeff e))) ∈ W := by + let q : homogeneousSubmodule σ K m := ⟨monomial e 1, isHomogeneous_monomial 1 (hdeg e he)⟩ + have hq : homogeneousPointEvaluation (K := K) z m q ∈ W := Submodule.subset_span ⟨q, rfl⟩ + have hval : (fun i => MvPolynomial.eval (z i) (monomial e (p.coeff e))) = + p.coeff e • homogeneousPointEvaluation (K := K) z m q := by + funext i + simp [homogeneousPointEvaluation, q, MvPolynomial.eval_monomial, aeval_monomial] + rw [hval] + exact W.smul_mem _ hq + have hsum : (fun i => MvPolynomial.eval (z i) p) = + ∑ e ∈ p.support, (fun i => MvPolynomial.eval (z i) (monomial e (p.coeff e))) := by + funext i + simpa only [Finset.sum_apply, map_sum] using congrArg (MvPolynomial.eval (z i)) p.as_sum + rw [hsum] + exact W.sum_mem hmono + +end +end CCDN + +namespace CCDN +noncomputable section +open MvPolynomial + +private lemma homogeneous_chart_evaluation_span_eq_top {K L σ ι : Type*} [Field K] [Field L] + [Algebra K L] [Fintype ι] (z : ι → σ → L) (hz : Function.Injective z) + (j : σ) (hj : ∀ i, z i j = 1) (m : ℕ) (hm : Fintype.card ι ≤ m) : + Submodule.span L (Set.range (homogeneousPointEvaluation (K := K) z m)) = ⊤ := by + classical + let W := Submodule.span L (Set.range (homogeneousPointEvaluation (K := K) z m)) + let b := Pi.basisFun L ι + have hb (i : ι) : b i ∈ W := by + obtain ⟨p, hp, he⟩ := exists_homogeneous_chart_delta z hz j hj m hm i + have hv := homogeneous_extension_evaluation_mem (K := K) z m p hp + have heq : (fun k => MvPolynomial.eval (z k) p) = b i := by + funext k + simp [he, b, Pi.basisFun_apply, Pi.single_apply, eq_comm] + rwa [heq] at hv + apply top_unique + intro v _ + rw [← b.sum_repr v] + exact W.sum_mem (fun i _ => W.smul_mem _ (hb i)) + +/-- Every finite family of distinct normalized infinity points has size at +most the original affine Hilbert degree, over any extension field. -/ +private theorem infinity_chart_card_le_degree {K L σ ι : Type*} [Field K] [Field L] + [Algebra K L] [Finite σ] [Fintype ι] + (I : Ideal (MvPolynomial σ K)) (d : ℕ) + (hdegree : HasFilteredCurveDegree I d) + (z : ι → σ → L) (hz : ∀ i, z i ∈ MvPolynomial.zeroLocus L (leadingInfinityIdeal I)) + (hinj : Function.Injective z) (j : σ) (hj : ∀ i, z i j = 1) : + Fintype.card ι ≤ d := by + classical + let := Fintype.ofFinite σ + obtain ⟨N, c, hN⟩ := hdegree + let m := N + Fintype.card ι + have hb := infinity_evaluation_span_finrank_le_degree I z hz hN m (by dsimp [m]; omega) + rw [homogeneous_chart_evaluation_span_eq_top z hinj j hj (m + 1) (by dsimp [m]; omega)] at hb + simpa only [finrank_top, Module.finrank_pi, Module.finrank_self, Finset.sum_const, + Finset.card_univ, smul_eq_mul, mul_one] using hb + +/-- Each normalized infinity chart is a finite set with at most d +points, not merely a bound for already finite subsets. -/ +theorem exists_finite_infinity_chart {K L σ : Type*} [Field K] [Field L] + [Algebra K L] [Finite σ] (I : Ideal (MvPolynomial σ K)) (d : ℕ) + (hdegree : HasFilteredCurveDegree I d) + (j : σ) : + ∃ S : Finset (σ → L), S.card ≤ d ∧ ∀ z, + z ∈ S ↔ z ∈ MvPolynomial.zeroLocus L (leadingInfinityIdeal I) ∧ z j = 1 := by + classical + let := Fintype.ofFinite σ + let T := {z : σ → L | z ∈ MvPolynomial.zeroLocus L (leadingInfinityIdeal I) ∧ z j = 1} + have hT : T.Finite := by + by_contra hn + obtain ⟨S, hS, hc⟩ := Set.Infinite.exists_subset_card_eq hn (d + 1) + have hb := infinity_chart_card_le_degree I d hdegree (fun z : S => z.val) + (fun z => (hS z.property).1) Subtype.val_injective j (fun z => (hS z.property).2) + rw [Fintype.card_coe, hc] at hb + omega + refine ⟨hT.toFinset, ?_, fun z => hT.mem_toFinset⟩ + have hb := infinity_chart_card_le_degree I d hdegree (fun z : hT.toFinset => z.val) + (fun z => (hT.mem_toFinset.mp z.property).1) Subtype.val_injective j + (fun z => (hT.mem_toFinset.mp z.property).2) + simpa only [Fintype.card_coe] using hb + +end +end CCDN diff --git a/LeanPool/CCDN/Projection/InfinityParameterReduction.lean b/LeanPool/CCDN/Projection/InfinityParameterReduction.lean new file mode 100644 index 0000000000..2f57d2dbc7 --- /dev/null +++ b/LeanPool/CCDN/Projection/InfinityParameterReduction.lean @@ -0,0 +1,117 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.Algebra.RadialHomogeneousMap +public import Mathlib.RingTheory.Nullstellensatz +import LeanPool.CCDN.ForMathlib.MvPolynomial + +/-! Vanishing only at the origin on the infinity section supplies balanced +coordinate-power reductions. The Nullstellensatz is used on the kernel +of the map to the infinity quotient, over any algebraically closed extension. -/ + +@[expose] public section +namespace CCDN +noncomputable section +open MvPolynomial + +/-- The map from the polynomial ring to the coordinate ring of the section at infinity of the +projective closure of `I`: a polynomial `f` goes to the class of its radial scaling `∑ n, f_n t ^ n` +in the homogeneous coordinate algebra modulo the infinity ideal. -/ +def infinityCoordinateMap {K σ : Type*} [Field K] (I : Ideal (MvPolynomial σ K)) : + MvPolynomial σ K →ₐ[K] projectiveCoordinateAlgebra I ⧸ projectiveInfinityIdeal I := + (Ideal.Quotient.mkₐ K (projectiveInfinityIdeal I)).comp (quotientRadialMap I) + +/-- The homogeneous ideal of the section at infinity of the projective closure of `I`, defined as +the kernel of `infinityCoordinateMap I`. -/ +def leadingInfinityIdeal {K σ : Type*} [Field K] (I : Ideal (MvPolynomial σ K)) : + Ideal (MvPolynomial σ K) := RingHom.ker (infinityCoordinateMap I) + +/-- If `f` lies in the ideal at infinity of `I`, then modulo `I` each homogeneous component of `f` +of degree `n + 1` lies in the filtration piece of degree at most `n`. -/ +lemma leadingInfinityIdeal_component_mem {K σ : Type*} [Field K] + (I : Ideal (MvPolynomial σ K)) (f : MvPolynomial σ K) (hf : f ∈ leadingInfinityIdeal I) + (n : ℕ) : + (Ideal.Quotient.mk I) (homogeneousComponent (n + 1) f) ∈ filteredCoordinateSpace I n := by + have hm : quotientRadialMap I f ∈ projectiveInfinityIdeal I := by + apply Ideal.Quotient.eq_zero_iff_mem.mp + exact hf + rw [projectiveInfinityIdeal, + Ideal.mem_span_singleton (α := projectiveCoordinateAlgebra I)] at hm + obtain ⟨g, hg⟩ := hm + have he := congrArg (fun h : projectiveCoordinateAlgebra I => + (h : Polynomial (MvPolynomial σ K ⧸ I)).coeff (n + 1)) hg + change ((quotientRadialMap I f : projectiveCoordinateAlgebra I) : + Polynomial (MvPolynomial σ K ⧸ I)).coeff (n + 1) = + (Polynomial.X * (g : Polynomial (MvPolynomial σ K ⧸ I))).coeff (n + 1) at he + rw [quotientRadialMap_coeff, Polynomial.coeff_X_mul] at he + rw [he] + exact g.property n + +/-- A coordinate-power certificate at infinity gives a balanced reduction in +the original affine quotient. -/ +private theorem power_reduction_of_infinity_certificate {K σ : Type*} [Field K] + (I : Ideal (MvPolynomial σ K)) (l : MvPolynomial σ K) (hl : l.IsHomogeneous 1) + (j : σ) (N : ℕ) (hN : 0 < N) + (hpow : X j ^ N ∈ leadingInfinityIdeal I ⊔ Ideal.span {l}) : + ∃ r s : MvPolynomial σ K, r.totalDegree ≤ N - 1 ∧ s.totalDegree ≤ N - 1 ∧ + (Ideal.Quotient.mk I) (X j ^ N) = (Ideal.Quotient.mk I) r + + (Ideal.Quotient.mk I) l * (Ideal.Quotient.mk I) s := by + cases N with + | zero => omega + | succ n => + obtain ⟨g, hg, t, ht, he⟩ := Submodule.mem_sup.mp hpow + obtain ⟨s, hs⟩ := Ideal.mem_span_singleton.mp ht + have hrel : X j ^ (n + 1) - l * s ∈ leadingInfinityIdeal I := by + rw [← he, hs] + simpa only [add_sub_cancel_right] using hg + have hm := leadingInfinityIdeal_component_mem I _ hrel n + rw [map_sub, homogeneousComponent_eq_self (isHomogeneous_X_pow j (n + 1)), + show n + 1 = 1 + n by omega, homogeneousComponent_mul_of_isHomogeneous_left hl] at hm + obtain ⟨r, hr, heq⟩ := Submodule.mem_map.mp hm + refine ⟨r, homogeneousComponent n s, (mem_restrictTotalDegree _ _ _).mp hr, + (homogeneousComponent_isHomogeneous n s).totalDegree_le, ?_⟩ + change (Ideal.Quotient.mk I) r = + (Ideal.Quotient.mk I) (X j ^ (1 + n) - l * homogeneousComponent n s) at heq + rw [map_sub, map_mul] at heq + rw [show n + 1 = 1 + n by omega] + exact eq_add_of_sub_eq (heq.symm) + +/-- Avoiding every nonzero infinity direction supplies all coordinate-power +reductions with a single positive exponent. -/ +theorem exists_power_reductions_of_infinity_avoidance {K L σ : Type*} + [Field K] [Field L] [Algebra K L] [IsAlgClosed L] [Finite σ] + (I : Ideal (MvPolynomial σ K)) (l : MvPolynomial σ K) (hl : l.IsHomogeneous 1) + (hzero : ∀ z ∈ MvPolynomial.zeroLocus L (leadingInfinityIdeal I ⊔ Ideal.span {l}), z = 0) : + ∃ N : ℕ, 0 < N ∧ ∀ j : σ, ∃ r s : MvPolynomial σ K, + r.totalDegree ≤ N - 1 ∧ s.totalDegree ≤ N - 1 ∧ + (Ideal.Quotient.mk I) (X j ^ N) = (Ideal.Quotient.mk I) r + + (Ideal.Quotient.mk I) l * (Ideal.Quotient.mk I) s := by + classical + let := Fintype.ofFinite σ + let J := leadingInfinityIdeal I ⊔ Ideal.span {l} + have hp (j : σ) : ∃ m : ℕ, X j ^ m ∈ J := by + have hm : X j ∈ vanishingIdeal K (MvPolynomial.zeroLocus L J) := by + intro z hz + rw [hzero z hz] + simp + rw [vanishingIdeal_zeroLocus_eq_radical] at hm + exact hm + choose m hm using hp + let N := Finset.univ.sup m + 1 + have hN : 0 < N := by dsimp [N]; omega + refine ⟨N, hN, ?_⟩ + intro j + apply power_reduction_of_infinity_certificate I l hl j N hN + have hj : m j ≤ N := (Finset.le_sup (f := m) (Finset.mem_univ j)).trans (Nat.le_succ _) + have hh := J.mul_mem_left (X j ^ (N - m j)) (hm j) + simpa only [← pow_add, Nat.sub_add_cancel hj] using hh + +end +end CCDN diff --git a/LeanPool/CCDN/Projection/InfinityScaling.lean b/LeanPool/CCDN/Projection/InfinityScaling.lean new file mode 100644 index 0000000000..93dbdaa2a4 --- /dev/null +++ b/LeanPool/CCDN/Projection/InfinityScaling.lean @@ -0,0 +1,90 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.Projection.InfinityParameterReduction + +/-! The infinity ideal is homogeneous, so its geometric zero locus is closed +under scalar multiplication. This allows normalization into finite charts. -/ + +@[expose] public section +namespace CCDN +noncomputable section +open MvPolynomial + +private lemma leadingInfinityIdeal_homogeneousComponent_mem {K σ : Type*} [Field K] + (I : Ideal (MvPolynomial σ K)) (f : MvPolynomial σ K) + (hf : f ∈ leadingInfinityIdeal I) (m : ℕ) : + homogeneousComponent m f ∈ leadingInfinityIdeal I := by + have hhom := homogeneousComponent_isHomogeneous m f + change Ideal.Quotient.mk (projectiveInfinityIdeal I) + (quotientRadialMap I (homogeneousComponent m f)) = 0 + rw [quotientRadialMap_homogeneous I _ m hhom, Ideal.Quotient.eq_zero_iff_mem] + cases m with + | succ n => + exact (projectiveMonomial_mem_infinityIdeal I n _).mpr + (leadingInfinityIdeal_component_mem I f hf n) + | zero => + have hm : quotientRadialMap I f ∈ projectiveInfinityIdeal I := + Ideal.Quotient.eq_zero_iff_mem.mp hf + rw [projectiveInfinityIdeal, + Ideal.mem_span_singleton (α := projectiveCoordinateAlgebra I)] at hm + obtain ⟨g, hg⟩ := hm + have he := congrArg (fun h : projectiveCoordinateAlgebra I => + (h : Polynomial (MvPolynomial σ K ⧸ I)).coeff 0) hg + change ((quotientRadialMap I f : projectiveCoordinateAlgebra I) : + Polynomial (MvPolynomial σ K ⧸ I)).coeff 0 = + (Polynomial.X * (g : Polynomial (MvPolynomial σ K ⧸ I))).coeff 0 at he + rw [quotientRadialMap_coeff, Polynomial.coeff_X_mul_zero] at he + have hz : projectiveMonomialMap I 0 + ⟨(Ideal.Quotient.mk I) (homogeneousComponent 0 f), + ⟨_, (mem_restrictTotalDegree _ _ _).mpr hhom.totalDegree_le, rfl⟩⟩ = 0 := by + apply Subtype.ext + simp only [projectiveMonomialMap_val, he, map_zero, ZeroMemClass.coe_zero] + rw [hz] + exact (projectiveInfinityIdeal I).zero_mem + +/-- A homogeneous polynomial of degree `m` satisfies `f (c • z) = c ^ m * f z`. -/ +lemma homogeneous_aeval_scale {K L σ : Type*} [Field K] [Field L] [Algebra K L] + [Finite σ] (f : MvPolynomial σ K) {m : ℕ} (hf : f.IsHomogeneous m) + (z : σ → L) (c : L) : aeval (fun j => c * z j) f = c ^ m * aeval z f := by + classical + let := Fintype.ofFinite σ + conv_lhs => rw [f.as_sum, map_sum] + conv_rhs => rw [f.as_sum, map_sum, Finset.mul_sum] + apply Finset.sum_congr rfl + intro e he + have hedeg : e.degree = m := by + rw [Finsupp.degree_eq_weight_one, ← Pi.one_def] + exact hf (mem_support_iff.mp he) + simp only [aeval_monomial] + rw [Finsupp.prod_fintype _ _ (by simp), Finsupp.prod_fintype _ _ (by simp)] + simp only [mul_pow, Finset.prod_mul_distrib, Finset.prod_pow_eq_pow_sum] + have hesum : ∑ j : σ, e j = m := by + have hh := Finsupp.sum_fintype e (fun _ k => k) (by simp) + exact hh.symm.trans hedeg + rw [hesum] + ring + +/-- The zero locus of the ideal at infinity is a cone: it is stable under scaling by any `c`. -/ +lemma infinity_zeroLocus_scale {K L σ : Type*} [Field K] [Field L] + [Algebra K L] [Finite σ] (I : Ideal (MvPolynomial σ K)) (z : σ → L) + (hz : z ∈ MvPolynomial.zeroLocus L (leadingInfinityIdeal I)) (c : L) : + (fun j => c * z j) ∈ MvPolynomial.zeroLocus L (leadingInfinityIdeal I) := by + classical + let := Fintype.ofFinite σ + intro f hf + rw [← sum_homogeneousComponent (φ := f), map_sum] + apply Finset.sum_eq_zero + intro m _ + rw [homogeneous_aeval_scale _ (homogeneousComponent_isHomogeneous m f)] + rw [hz _ (leadingInfinityIdeal_homogeneousComponent_mem I f hf m), mul_zero] + +end +end CCDN diff --git a/LeanPool/CCDN/Projection/IntegerHilbertElimination.lean b/LeanPool/CCDN/Projection/IntegerHilbertElimination.lean new file mode 100644 index 0000000000..c9397cc805 --- /dev/null +++ b/LeanPool/CCDN/Projection/IntegerHilbertElimination.lean @@ -0,0 +1,94 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.Hilbert.LinearProjectionFiltration +public import LeanPool.CCDN.Projection.BoundedLinearElimination +import LeanPool.CCDN.Noether.GenericPlaneStretch +import Mathlib.RingTheory.IntegralClosure.IsIntegralClosure.Basic + +/-! +Bounded integral linear elimination with integer coefficients. +Scaling the retained coordinates by the nonzero pivot preserves integrality. + +A complete bounded elimination step: the contraction stays prime, +retains dimension, and has no larger Hilbert multiplicity. +-/ + +@[expose] public section +namespace CCDN +noncomputable section +open MvPolynomial + +/-- The linear forms `X (some i) - (b (some i) / b none) * X none`, projecting away the coordinate +`X none` along the direction `b`. -/ +def eliminationLinearCoordinates {σ : Type*} (b : Option σ → ℚ) : + σ → MvPolynomial (Option σ) ℚ := + fun i => X (some i) - C (b (some i) / b none) * X none + +/-- For a direction `b` with `b none ≠ 0`, the elimination map of the associated linear coordinate +change is the projection by the elimination linear forms of `b`. -/ +lemma coordinateEliminationMap_eq_projection {σ : Type*} + (I : Ideal (MvPolynomial (Option σ) ℚ)) (b : Option σ → ℚ) (hb : b none ≠ 0) : + coordinateEliminationMap I (hypersurfaceAffineCoordinates 0 b hb) = + projectionCoordinateMap I (eliminationLinearCoordinates b) := by + ext i + rw [coordinateEliminationMap_direction_X] + simp [projectionCoordinateMap, eliminationLinearCoordinates] + +end +end CCDN + +namespace CCDN +noncomputable section +open MvPolynomial + +/-- The linear forms `b none * X (some i) - b (some i) * X none` with integer coefficients, +projecting away the coordinate `X none` along the integer direction `b`. -/ +def integerEliminationCoordinates {σ : Type*} (b : Option σ → ℤ) : + σ → MvPolynomial (Option σ) ℚ := + fun i => C (b none : ℚ) * X (some i) - C (b (some i) : ℚ) * X none + +private lemma integerEliminationCoordinates_eq_scale {σ : Type*} + (b : Option σ → ℤ) (hb : (b none : ℚ) ≠ 0) (i : σ) : + integerEliminationCoordinates b i = + C (b none : ℚ) * eliminationLinearCoordinates (fun i => (b i : ℚ)) i := by + have hh : (b none : ℚ) * ((b (some i) : ℚ) / b none) = b (some i) := by + field_simp + dsimp only [integerEliminationCoordinates, eliminationLinearCoordinates] + rw [mul_sub, ← mul_assoc, ← map_mul, hh] + +/-- If the projection by the rational elimination forms of an integer direction `b` with +`b none ≠ 0` is integral, then so is the projection by the integer elimination forms, which differ +by the scalar `b none`. -/ +theorem integerEliminationMap_isIntegral {σ : Type*} + (I : Ideal (MvPolynomial (Option σ) ℚ)) (b : Option σ → ℤ) + (hb : (b none : ℚ) ≠ 0) + (hint : (projectionCoordinateMap I + (eliminationLinearCoordinates (fun i => (b i : ℚ)))).IsIntegral) : + (projectionCoordinateMap I (integerEliminationCoordinates b)).IsIntegral := by + let e := variableScaleEquiv ℚ σ (b none : ℚ) hb + let ψ := projectionCoordinateMap I (eliminationLinearCoordinates (fun i => (b i : ℚ))) + have he : e.toRingHom.IsIntegral := RingHom.isIntegral_of_surjective _ e.surjective + have hcomp := RingHom.IsIntegral.trans e.toRingHom ψ.toRingHom he hint + have heq : ψ.comp e.toAlgHom = projectionCoordinateMap I (integerEliminationCoordinates b) := by + ext i + change ψ (e (X i)) = + projectionCoordinateMap I (integerEliminationCoordinates b) (X i) + rw [show e (X i) = C (b none : ℚ) * X i from variableScaleEquiv_X _ hb i] + change (Ideal.Quotient.mkₐ ℚ I) (aeval + (eliminationLinearCoordinates (fun i => (b i : ℚ))) (C (b none : ℚ) * X i)) = + (Ideal.Quotient.mkₐ ℚ I) (aeval (integerEliminationCoordinates b) (X i)) + rw [map_mul, aeval_C, aeval_X, aeval_X, integerEliminationCoordinates_eq_scale b hb, + algebraMap_eq] + change (ψ.comp e.toAlgHom).IsIntegral at hcomp + rwa [heq] at hcomp + +end +end CCDN diff --git a/LeanPool/CCDN/Projection/IntegralFiberRank.lean b/LeanPool/CCDN/Projection/IntegralFiberRank.lean new file mode 100644 index 0000000000..e6c8dbdb03 --- /dev/null +++ b/LeanPool/CCDN/Projection/IntegralFiberRank.lean @@ -0,0 +1,126 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.Algebra.IdealZeroLocus +public import LeanPool.CCDN.Hilbert.LinearProjectionFiltration +public import Mathlib.RingTheory.IntegralClosure.IntegrallyClosed +import LeanPool.CCDN.Algebra.BoundedLinearSeparation +import LeanPool.CCDN.Algebra.IntegralMinpolyDegree +import LeanPool.CCDN.Projection.IntegralProjectionFiberCount +import Mathlib.FieldTheory.PurelyInseparable.Basic +import Mathlib.RingTheory.Polynomial.RationalRoot + +/-! Sharp specialization-fiber bounds over a normal base. A single integral +linear combination separates a finite set of characteristic-zero points. +Its specialized monic minimal polynomial bounds the entire fiber by the +fraction-field rank, independently of the number of coordinates. -/ + +@[expose] public section +namespace CCDN +noncomputable section + +/-- Distinct specialization tuples are roots of one separating integral +element's minimal polynomial. Normality bounds its degree by the field rank. -/ +private theorem integral_coordinate_tuple_count_le_finrank + (R A F E : Type*) [CommRing R] [IsDomain R] [IsIntegrallyClosed R] + [CommRing A] [IsDomain A] [Field F] [Field E] + [Algebra R A] [Algebra R F] [IsFractionRing R F] + [Algebra R E] [Algebra A E] [Algebra F E] + [IsScalarTower R A E] [IsScalarTower R F E] + [FiniteDimensional F E] + {k ι : Type*} [Field k] [CharZero k] [Algebra R k] [Finite ι] + (a : ι → A) (ha : ∀ i, IsIntegral R (a i)) + (s : Finset (ι → k)) + (hs : ∀ x ∈ s, ∃ φ : A →ₐ[R] k, ∀ i, φ (a i) = x i) : + s.card ≤ Module.finrank F E := by + classical + let := Fintype.ofFinite ι + let e := (Fintype.equivFin ι).symm + let v : s → Fin (Fintype.card ι) → k := fun x j => x.val (e j) + have hv : Function.Injective v := by + intro x y h + apply Subtype.ext + funext i + obtain ⟨j, rfl⟩ := e.surjective i + exact congrFun h j + obtain ⟨t, _, ht⟩ := exists_bounded_moment_separation v hv + let b : A := ∑ j : Fin (Fintype.card ι), a (e j) * (t : A) ^ j.val + let L : (ι → k) → k := fun x => + ∑ j : Fin (Fintype.card ι), x (e j) * (t : k) ^ j.val + have hb : IsIntegral R b := + IsIntegral.sum _ (fun j _ => (ha (e j)).mul ((isIntegral_natCast t).pow j.val)) + have hL : Set.InjOn L s := by + intro x hx y hy hxy + exact congrArg Subtype.val (ht (a₁ := ⟨x, hx⟩) (a₂ := ⟨y, hy⟩) hxy) + let P := (minpoly R b).map (algebraMap R k) + have hP : P.Monic := (minpoly.monic hb).map _ + have hroot : ∀ x ∈ s, L x ∈ P.roots := by + intro x hx + obtain ⟨φ, hφ⟩ := hs x hx + have hφb : φ b = L x := by + simp only [b, L, map_sum, map_mul, map_pow, map_natCast, hφ] + apply Polynomial.mem_roots hP.ne_zero |>.mpr + change P.eval (L x) = 0 + rw [← hφb] + dsimp only [P] + rw [Polynomial.eval_map_algebraMap, Polynomial.aeval_algHom_apply, + minpoly.aeval, map_zero] + have hsub : s.image L ⊆ P.roots.toFinset := by + intro y hy + obtain ⟨x, hx, rfl⟩ := Finset.mem_image.mp hy + exact Multiset.mem_toFinset.mpr (hroot x hx) + calc + s.card = (s.image L).card := (Finset.card_image_of_injOn hL).symm + _ ≤ P.roots.toFinset.card := Finset.card_le_card hsub + _ ≤ P.roots.card := Multiset.toFinset_card_le _ + _ ≤ P.natDegree := Polynomial.card_roots' _ + _ ≤ (minpoly R b).natDegree := Polynomial.natDegree_map_le + _ ≤ Module.finrank F E := + integral_minpoly_natDegree_le_fraction_finrank R A F E b hb + +open MvPolynomial + +/-- In characteristic zero, every fiber of an integral projection of the variety of a prime `I` +by polynomials `l` has at most `[E : F]` points, where `F` is the fraction field of the polynomial +ring of the target and `E` is a finite extension of `F` receiving the coordinate ring of `I`. -/ +theorem integral_projection_fiber_card_le_finrank + {K σ τ : Type*} [Field K] [CharZero K] [Finite σ] + (I : Ideal (MvPolynomial σ K)) [I.IsPrime] + (l : τ → MvPolynomial σ K) + [Algebra (MvPolynomial τ K) (MvPolynomial σ K ⧸ I)] + (hmap : algebraMap (MvPolynomial τ K) (MvPolynomial σ K ⧸ I) = + (projectionCoordinateMap I l).toRingHom) + (F E : Type*) [Field F] [Field E] + [Algebra (MvPolynomial τ K) F] [IsFractionRing (MvPolynomial τ K) F] + [Algebra (MvPolynomial τ K) E] [Algebra (MvPolynomial σ K ⧸ I) E] + [Algebra F E] + [IsScalarTower (MvPolynomial τ K) (MvPolynomial σ K ⧸ I) E] + [IsScalarTower (MvPolynomial τ K) F E] [FiniteDimensional F E] + (hint : (projectionCoordinateMap I l).IsIntegral) + (z : τ → K) (s : Finset (σ → K)) + (hzero : ∀ x ∈ s, x ∈ idealZeroLocus I) + (hproj : ∀ x ∈ s, ∀ i, eval x (l i) = z i) : + s.card ≤ Module.finrank F E := by + classical + let := Fintype.ofFinite σ + let R := MvPolynomial τ K + let A := MvPolynomial σ K ⧸ I + let b : σ → A := fun i => Ideal.Quotient.mk I (X i) + let : Algebra R K := (aeval z).toRingHom.toAlgebra + have hb (i) : IsIntegral R (b i) := by + change (algebraMap R A).IsIntegralElem (b i) + rw [hmap] + exact hint (b i) + apply integral_coordinate_tuple_count_le_finrank R A F E b hb s + intro x hx + exact exists_projection_fiber_specialization I l hmap z x (hzero x hx) (hproj x hx) + +end +end CCDN diff --git a/LeanPool/CCDN/Projection/IntegralProjectionFiberCount.lean b/LeanPool/CCDN/Projection/IntegralProjectionFiberCount.lean new file mode 100644 index 0000000000..72bae9ef0f --- /dev/null +++ b/LeanPool/CCDN/Projection/IntegralProjectionFiberCount.lean @@ -0,0 +1,58 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.Algebra.IdealZeroLocus +public import LeanPool.CCDN.Hilbert.LinearProjectionFiltration + +/-! Rational-point fibers of an integral polynomial projection. +Evaluation on the quotient constructs the specialization homomorphisms. -/ + +@[expose] public section +namespace CCDN +noncomputable section +open MvPolynomial + +/-- A rational point of the fiber over `z` gives a specialization of the quotient algebra over +the parameter ring, sending each coordinate function to the corresponding coordinate. -/ +theorem exists_projection_fiber_specialization {K σ τ : Type*} [Field K] + (I : Ideal (MvPolynomial σ K)) (l : τ → MvPolynomial σ K) + [Algebra (MvPolynomial τ K) (MvPolynomial σ K ⧸ I)] + (hmap : algebraMap (MvPolynomial τ K) (MvPolynomial σ K ⧸ I) = + (projectionCoordinateMap I l).toRingHom) + (z : τ → K) (x : σ → K) (hzero : x ∈ idealZeroLocus I) + (hproj : ∀ i, eval x (l i) = z i) : + letI : Algebra (MvPolynomial τ K) K := (aeval z).toRingHom.toAlgebra + ∃ ψ : (MvPolynomial σ K ⧸ I) →ₐ[MvPolynomial τ K] K, + ∀ i, ψ (Ideal.Quotient.mk I (X i)) = x i := by + let : Algebra (MvPolynomial τ K) K := (aeval z).toRingHom.toAlgebra + let R := MvPolynomial τ K + let A := MvPolynomial σ K ⧸ I + let φ : A →ₐ[K] K := Ideal.Quotient.liftₐ I (aeval x) (fun f hf => hzero f hf) + have hφ (f : MvPolynomial σ K) : + φ (Ideal.Quotient.mk I f) = eval x f := by + exact congrArg (fun g : MvPolynomial σ K →ₐ[K] K => g f) + (Ideal.Quotient.liftₐ_comp I (aeval x) (fun f hf => hzero f hf)) + have hcomp : φ.comp (projectionCoordinateMap I l) = aeval z := by + ext i + simpa only [projectionCoordinateMap, AlgHom.comp_apply, aeval_X, + Ideal.Quotient.mkₐ_eq_mk] using (hφ (l i)).trans (hproj i) + let ψ : A →ₐ[R] K := + { φ.toRingHom with + commutes' := fun r => by + change φ (algebraMap R A r) = (aeval z) r + rw [hmap] + exact congrArg (fun f : R →ₐ[K] K => f r) hcomp } + refine ⟨ψ, ?_⟩ + intro i + change φ (Ideal.Quotient.mk I (X i)) = x i + simpa only [eval_X] using hφ (X i) + +end +end CCDN diff --git a/LeanPool/CCDN/Projection/IntegralProjectionHilbert.lean b/LeanPool/CCDN/Projection/IntegralProjectionHilbert.lean new file mode 100644 index 0000000000..91e258a8cb --- /dev/null +++ b/LeanPool/CCDN/Projection/IntegralProjectionHilbert.lean @@ -0,0 +1,83 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.Hilbert.FilteredHilbertPolynomial +public import LeanPool.CCDN.Hilbert.LinearProjectionFiltration +public import Mathlib.RingTheory.IntegralClosure.IsIntegral.Defs +import LeanPool.CCDN.Hilbert.PolynomialComparison +import LeanPool.CCDN.Hilbert.TranscendenceBasis +import Mathlib.RingTheory.IntegralClosure.IsIntegralClosure.Basic + +/-! Integral linear projections preserve Hilbert dimension and +do not increase its multiplicity. -/ + +@[expose] public section +namespace CCDN +noncomputable section +open MvPolynomial + +/-- An integral map `ψ` from a polynomial ring to the quotient by a prime `I` preserves Hilbert +dimension: the filtered Hilbert polynomials of `ker ψ` and of `I` have the same degree. -/ +theorem integral_projection_hilbert_dimension_eq + {K σ τ : Type*} [Field K] [Fintype σ] [Fintype τ] + [LinearOrder σ] [LinearOrder τ] + (I : Ideal (MvPolynomial τ K)) [I.IsPrime] + (ψ : MvPolynomial σ K →ₐ[K] (MvPolynomial τ K ⧸ I)) + (hint : ψ.IsIntegral) : + (filteredHilbertPolynomial (RingHom.ker ψ)).natDegree = + (filteredHilbertPolynomial I).natDegree := by + have : (RingHom.ker ψ).IsPrime := RingHom.ker_isPrime ψ.toRingHom + exact integral_embedding_hilbert_dimension_eq (RingHom.ker ψ) I + (Ideal.kerLiftAlg ψ) (Ideal.kerLiftAlg_injective ψ) hint.kerLift + +private theorem integral_linear_projection_multiplicity + {K σ τ : Type*} [Field K] [Fintype σ] [Fintype τ] + [LinearOrder σ] [LinearOrder τ] + (I : Ideal (MvPolynomial τ K)) [I.IsPrime] + (a : σ → MvPolynomial τ K) (ha : ∀ i, (a i).totalDegree ≤ 1) + (hint : (projectionCoordinateMap I a).IsIntegral) (d : ℕ) + (hdegree : (filteredHilbertPolynomial I).leadingCoeff = + (d : ℚ) / (filteredHilbertPolynomial I).natDegree.factorial) : + (filteredHilbertPolynomial (RingHom.ker (projectionCoordinateMap I a))).natDegree = + (filteredHilbertPolynomial I).natDegree ∧ + ∃ e : ℕ, 0 < e ∧ e ≤ d ∧ + (filteredHilbertPolynomial (RingHom.ker (projectionCoordinateMap I a))).leadingCoeff = + (e : ℚ) / (filteredHilbertPolynomial I).natDegree.factorial := by + let J := RingHom.ker (projectionCoordinateMap I a) + have hJ : J ≠ ⊤ := RingHom.ker_ne_top _ + have hI : I ≠ ⊤ := Ideal.IsPrime.ne_top ‹I.IsPrime› + have hdim := integral_projection_hilbert_dimension_eq I _ hint + obtain ⟨e, he, _, hlc⟩ := filteredHilbertPolynomial_positive_multiplicity J hJ + have hle := filteredHilbertPolynomial_leadingCoeff_le J I hJ hI hdim + (fun N => linear_projection_filtered_finrank_le I a ha N) + rw [hdim] at hlc + refine ⟨hdim, e, he, ?_, hlc⟩ + rw [hlc, hdegree] at hle + exact_mod_cast (div_le_div_iff_of_pos_right + (by positivity : (0 : ℚ) < (filteredHilbertPolynomial I).natDegree.factorial)).mp hle + +/-- An integral linear projection of a prime of dimension `m` and degree `d` has dimension `m` +and a positive degree at most `d`. -/ +theorem integral_linear_projection_dimension_degree + {K σ τ : Type*} [Field K] [Fintype σ] [Fintype τ] + [LinearOrder σ] [LinearOrder τ] + (I : Ideal (MvPolynomial τ K)) [I.IsPrime] + (a : σ → MvPolynomial τ K) (ha : ∀ i, (a i).totalDegree ≤ 1) + (hint : (projectionCoordinateMap I a).IsIntegral) {m d : ℕ} + (hdimdeg : HasAffineDimensionDegree I m d) : + ∃ e : ℕ, 0 < e ∧ e ≤ d ∧ + HasAffineDimensionDegree (RingHom.ker (projectionCoordinateMap I a)) m e := by + obtain ⟨hdim, e, he, hed, hlc⟩ := integral_linear_projection_multiplicity I a ha hint d + (by rw [hdimdeg.natDegree_eq]; exact hdimdeg.leadingCoeff_eq) + rw [hdimdeg.natDegree_eq] at hdim hlc + exact ⟨e, he, hed, hdim, hlc⟩ + +end +end CCDN diff --git a/LeanPool/CCDN/Projection/LeadingCoordinateExtension.lean b/LeanPool/CCDN/Projection/LeadingCoordinateExtension.lean new file mode 100644 index 0000000000..2a490b3bff --- /dev/null +++ b/LeanPool/CCDN/Projection/LeadingCoordinateExtension.lean @@ -0,0 +1,113 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.Algebra.PolynomialPredicates +public import LeanPool.CCDN.Hilbert.FilteredHilbertPolynomial +public import LeanPool.CCDN.Hilbert.LinearProjectionFiltration +public import Mathlib.RingTheory.AlgebraicIndependent.Defs +import LeanPool.CCDN.Hilbert.LinearCoordinates +import LeanPool.CCDN.Hilbert.PrincipalHilbertPolynomial +import LeanPool.CCDN.Projection.HilbertProjectionDegree +import LeanPool.CCDN.Projection.HomogeneousFiniteProjectionDegree +import LeanPool.CCDN.Projection.HomogeneousProjectionEquation + +/-! Extending a linear projection by one leading coordinate. Integrality and +algebraic independence of the tail coordinates pass to the extended projection; +for a homogeneous prime whose function field is generated by the extended +coordinates, the image is a hypersurface cut out by a form of exactly the +original degree. -/ + +@[expose] public section +namespace CCDN +noncomputable section +open MvPolynomial + +/-- Restricting the projection along `l` to the tail variables gives the projection along the +tail of `l`. -/ +private theorem projectionCoordinateMap_comp_rename_succ {K τ : Type*} [Field K] {m : ℕ} + (I : Ideal (MvPolynomial τ K)) (l : Fin (m + 1) → MvPolynomial τ K) : + (projectionCoordinateMap I l).comp (rename Fin.succ) = + projectionCoordinateMap I (fun i : Fin m => l i.succ) := by + ext i + simp [projectionCoordinateMap] + +/-- A projection is integral as soon as the projection along its tail coordinates is. -/ +theorem projectionCoordinateMap_isIntegral_of_tail {K τ : Type*} [Field K] {m : ℕ} + (I : Ideal (MvPolynomial τ K)) (l : Fin (m + 1) → MvPolynomial τ K) + (h : (projectionCoordinateMap I (fun i : Fin m => l i.succ)).IsIntegral) : + (projectionCoordinateMap I l).IsIntegral := by + apply RingHom.IsIntegral.tower_top (rename Fin.succ).toRingHom + (projectionCoordinateMap I l).toRingHom + change ((projectionCoordinateMap I l).comp (rename Fin.succ)).IsIntegral + rwa [projectionCoordinateMap_comp_rename_succ] + +/-- If the projection along the tail coordinates is injective, the images of the tail +variables under the extended projection are algebraically independent. -/ +private theorem algebraicIndependent_projectionCoordinateMap_X_succ {K τ : Type*} [Field K] {m : ℕ} + (I : Ideal (MvPolynomial τ K)) (l : Fin (m + 1) → MvPolynomial τ K) + (h : Function.Injective (projectionCoordinateMap I (fun i : Fin m => l i.succ))) : + AlgebraicIndependent K (fun i : Fin m => projectionCoordinateMap I l (X i.succ)) := by + have hl : (fun i : Fin m => projectionCoordinateMap I l (X i.succ)) = + fun i => Ideal.Quotient.mk I (l i.succ) := by + funext i + simp [projectionCoordinateMap] + rw [hl] + exact injective_projectionCoordinateMap_iff.mp h + +/-- Extending a finite injective projection of a prime of projective dimension `m` and degree +`d` by one linear coordinate gives a hypersurface equation: the kernel is generated by an +irreducible polynomial of positive total degree at most `d`. -/ +theorem exists_extended_projection_equation_degree_le + {K τ : Type*} [Field K] [Fintype τ] [LinearOrder τ] {m : ℕ} + (I : Ideal (MvPolynomial τ K)) [I.IsPrime] + (l : Fin (m + 2) → MvPolynomial τ K) (hl : ∀ i, (l i).totalDegree ≤ 1) + (hinj : Function.Injective (projectionCoordinateMap I (fun i : Fin (m + 1) => l i.succ))) + (d : ℕ) (hdimdeg : HasProjectiveDimensionDegree I m d) : + ∃ f : MvPolynomial (Fin (m + 2)) K, Irreducible f ∧ + 0 < f.totalDegree ∧ f.totalDegree ≤ d ∧ + RingHom.ker (projectionCoordinateMap I l) = Ideal.span {f} := + exists_hypersurface_projection_equation_degree_le I l hl d hdimdeg (fun i => X i.succ) + (fun _ => by simp) (algebraicIndependent_projectionCoordinateMap_X_succ I l hinj) + +/-- A finite birational projection of a homogeneous prime of projective dimension `m` and +degree `d`, obtained by extending a finite injective linear projection by one linear form and +generating the function field, has image a hypersurface cut out by an irreducible form of +degree exactly `d`. -/ +theorem exists_homogeneous_extended_projection_equation + {K F τ : Type*} [Field K] [Field F] [Algebra K F] [Fintype τ] [LinearOrder τ] {m : ℕ} + (I : Ideal (MvPolynomial τ K)) [I.IsPrime] + [Algebra (MvPolynomial τ K ⧸ I) F] [IsScalarTower K (MvPolynomial τ K ⧸ I) F] + [IsFractionRing (MvPolynomial τ K ⧸ I) F] + (hI : IsHomogeneousIdeal I) + (l : Fin (m + 2) → MvPolynomial τ K) (hl : ∀ i, (l i).IsHomogeneous 1) + (hint : (projectionCoordinateMap I (fun i : Fin (m + 1) => l i.succ)).IsIntegral) + (hinj : Function.Injective (projectionCoordinateMap I (fun i : Fin (m + 1) => l i.succ))) + (hfield : IntermediateField.adjoin K + (Set.range (fun i => algebraMap (MvPolynomial τ K ⧸ I) F + ((Ideal.Quotient.mk I) (l i)))) = ⊤) + (d : ℕ) (hdimdeg : HasProjectiveDimensionDegree I m d) : + ∃ f : MvPolynomial (Fin (m + 2)) K, + Irreducible f ∧ f.IsHomogeneous d ∧ f.totalDegree = d ∧ 0 < d ∧ + RingHom.ker (projectionCoordinateMap I l) = Ideal.span {f} := by + obtain ⟨f, hf, hfpos, _, hker⟩ := exists_extended_projection_equation_degree_le I l + (fun i => (hl i).totalDegree_le) hinj d hdimdeg + have hlead := homogeneous_finite_birational_projection_leadingCoeff_eq (F := F) I hI l hl + (projectionCoordinateMap_isIntegral_of_tail I l hint) hfield + rw [hker, principal_filteredHilbertPolynomial_leadingCoeff f hf.ne_zero hfpos, + hdimdeg.leadingCoeff_eq] at hlead + have hfdeg : f.totalDegree = d := by + have hh := (div_left_inj' (by positivity : ((m + 1).factorial : ℚ) ≠ 0)).mp hlead + exact_mod_cast hh + refine ⟨f, hf, ?_, hfdeg, hfdeg ▸ hfpos, hker⟩ + rw [← hfdeg] + exact homogeneous_projection_equation I hI l hl f hf.ne_zero hker + +end +end CCDN diff --git a/LeanPool/CCDN/Projection/LinearParameterPrimitive.lean b/LeanPool/CCDN/Projection/LinearParameterPrimitive.lean new file mode 100644 index 0000000000..925b7173c0 --- /dev/null +++ b/LeanPool/CCDN/Projection/LinearParameterPrimitive.lean @@ -0,0 +1,76 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.Hilbert.FilteredHilbertPolynomial +public import LeanPool.CCDN.Hilbert.LinearProjectionFiltration +public import LeanPool.CCDN.Projection.CurvePrimitivePair +public import Mathlib.FieldTheory.IntermediateField.Adjoin.Defs +import LeanPool.CCDN.Height.BoundedPrimitiveElement +import LeanPool.CCDN.Hilbert.BoundedDegreeFunctionFieldRank +import LeanPool.CCDN.Hilbert.LinearCoordinates +import LeanPool.CCDN.Projection.PrimitiveCoordinateField +import Mathlib.Algebra.CharP.IntermediateField +import Mathlib.RingTheory.AlgebraicIndependent.Basic + +/-! Bounded primitive augmentation of any independent linear parameter +tuple of the Hilbert dimension. -/ + +@[expose] public section +namespace CCDN +noncomputable section +open MvPolynomial + +/-- Given `m + 1` linear forms algebraically independent modulo a prime `I` of projective dimension +`m` and degree `d` (in characteristic zero), some moment coordinate with parameter `k ≤ n * d ^ 2` +together with these forms generates the function field of `I`. -/ +theorem exists_bounded_linear_parameter_primitive + {K F : Type*} [Field K] [CharZero K] [Field F] [Algebra K F] {n m : ℕ} + (I : Ideal (MvPolynomial (Fin n) K)) [I.IsPrime] + [Algebra (MvPolynomial (Fin n) K ⧸ I) F] + [IsScalarTower K (MvPolynomial (Fin n) K ⧸ I) F] + [IsFractionRing (MvPolynomial (Fin n) K ⧸ I) F] + (l : Fin (m + 1) → MvPolynomial (Fin n) K) + (hl : ∀ i, (l i).totalDegree ≤ 1) + (hi : Function.Injective (projectionCoordinateMap I l)) + (d : ℕ) (hdimdeg : HasProjectiveDimensionDegree I m d) : + ∃ k : ℕ, k ≤ n * d ^ 2 ∧ + IntermediateField.adjoin K + (Set.range (fun i : Fin (m + 2) => + algebraMap (MvPolynomial (Fin n) K ⧸ I) F + ((Ideal.Quotient.mk I) (Fin.cases (momentCoordinate K n k) l i)))) = ⊤ := by + let A := MvPolynomial (Fin n) K ⧸ I + let φ := IsScalarTower.toAlgHom K A F + let u : Fin (m + 1) → F := fun i => φ ((Ideal.Quotient.mk I) (l i)) + let R := IntermediateField.adjoin K (Set.range u) + let uR : Fin (m + 1) → R := fun i => + ⟨u i, IntermediateField.subset_adjoin K _ ⟨i, rfl⟩⟩ + have hiA : AlgebraicIndependent K (fun i => (Ideal.Quotient.mk I) (l i)) := + injective_projectionCoordinateMap_iff.mp hi + have hiF : AlgebraicIndependent K u := hiA.map' (IsFractionRing.injective A F) + have hiR : AlgebraicIndependent K uR := AlgebraicIndependent.of_comp R.val hiF + obtain ⟨hfinite, hrank⟩ := hilbert_functionField_finite_rank_le (R := R) (F := F) I d + (by simpa only [Fintype.card_fin] using hdimdeg) l hl uR hiR (fun _ => rfl) + let : Module.Finite R F := hfinite + obtain ⟨k, hk, hprim⟩ := exists_bounded_curve_primitive_element (R := R) (F := F) I + refine ⟨k, hk.trans (Nat.mul_le_mul_left n (Nat.pow_le_pow_left hrank 2)), ?_⟩ + have hprim' : IntermediateField.adjoin R + {φ ((Ideal.Quotient.mk I) (momentCoordinate K n k))} = ⊤ := by + change IntermediateField.adjoin R + {algebraMap A F ((Ideal.Quotient.mk I) (momentCoordinate K n k))} = ⊤ + rwa [momentCoordinate_fraction] + have hh := primitive_parameter_tuple_adjoin_top u + (φ ((Ideal.Quotient.mk I) (momentCoordinate K n k))) hprim' + convert hh using 2 + congr 1 + funext i + refine Fin.cases ?_ (fun j => ?_) i <;> rfl + +end +end CCDN diff --git a/LeanPool/CCDN/Projection/PlaneProjectionDegree.lean b/LeanPool/CCDN/Projection/PlaneProjectionDegree.lean new file mode 100644 index 0000000000..cbae1fd280 --- /dev/null +++ b/LeanPool/CCDN/Projection/PlaneProjectionDegree.lean @@ -0,0 +1,68 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.Hilbert.LinearProjectionFiltration +public import Mathlib.RingTheory.Algebraic.Defs +import LeanPool.CCDN.ForMathlib.MvPolynomial +import LeanPool.CCDN.Hilbert.AffineCurveDegree +import LeanPool.CCDN.Hilbert.PlaneHilbert +import LeanPool.CCDN.Plane.NonabsolutePlaneCount +import LeanPool.CCDN.Projection.PlaneProjectionKernel + +/-! The irreducible image equation has degree at most the original affine +Hilbert degree. This is an upper bound; the degree need not be preserved. -/ + +@[expose] public section +namespace CCDN +noncomputable section +open MvPolynomial + +/-- A principal image equation of a linear projection has degree at most the +source's affine Hilbert degree. -/ +theorem plane_projection_equation_degree_le {K τ : Type*} [Field K] [Finite τ] + (I : Ideal (MvPolynomial τ K)) (a : Fin 2 → MvPolynomial τ K) + (ha : ∀ i, (a i).totalDegree ≤ 1) (d : ℕ) + (hdegree : HasFilteredCurveDegree I d) + (f : MvPolynomial (Fin 2) K) (hf : f ≠ 0) + (hker : RingHom.ker (projectionCoordinateMap I a) = Ideal.span {f}) : + f.totalDegree ≤ d := by + classical + let := Fintype.ofFinite τ + apply eventual_linear_slope_le + (fun M => Module.finrank K (filteredCoordinateSpace (Ideal.span {f}) M)) + (fun M => Module.finrank K (filteredCoordinateSpace I M)) f.totalDegree d + ⟨f.totalDegree, (f.totalDegree : ℤ) - f.totalDegree.choose 2, + fun M hM => principal_plane_hilbert_function f hf hM⟩ hdegree + intro M + rw [← hker] + exact linear_projection_filtered_finrank_le I a ha M + +/-- A linear plane projection with a transcendental coordinate has an +irreducible equation of positive degree at most d and no extra kernel relations. -/ +theorem exists_plane_projection_equation_degree_le {K τ : Type*} [Field K] [Finite τ] + (I : Ideal (MvPolynomial τ K)) [I.IsPrime] + (a : Fin 2 → MvPolynomial τ K) (ha : ∀ i, (a i).totalDegree ≤ 1) + (d : ℕ) (hdegree : HasFilteredCurveDegree I d) + (j : Fin 2) (hj : Transcendental K ((Ideal.Quotient.mk I) (a j))) : + ∃ f : MvPolynomial (Fin 2) K, Irreducible f ∧ 0 < f.totalDegree ∧ f.totalDegree ≤ d ∧ + ∀ g, aeval a g ∈ I ↔ f ∣ g := by + classical + let := Fintype.ofFinite τ + obtain ⟨f, hf, hker⟩ := exists_plane_projection_equation I a ha d hdegree j hj + refine ⟨f, hf, totalDegree_pos_of_ne_zero_of_not_isUnit hf.ne_zero hf.not_isUnit, + plane_projection_equation_degree_le I a ha d hdegree f hf.ne_zero hker, ?_⟩ + intro g + have hh : aeval a g ∈ I ↔ g ∈ RingHom.ker (projectionCoordinateMap I a) := by + change _ ↔ (Ideal.Quotient.mk I) (aeval a g) = 0 + exact Ideal.Quotient.eq_zero_iff_mem.symm + rw [hh, hker, Ideal.mem_span_singleton] + +end +end CCDN diff --git a/LeanPool/CCDN/Projection/PlaneProjectionDerivative.lean b/LeanPool/CCDN/Projection/PlaneProjectionDerivative.lean new file mode 100644 index 0000000000..b9bf64fcd1 --- /dev/null +++ b/LeanPool/CCDN/Projection/PlaneProjectionDerivative.lean @@ -0,0 +1,84 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import Mathlib.Algebra.MvPolynomial.PDeriv +public import Mathlib.RingTheory.Algebraic.Defs +public import Mathlib.RingTheory.Ideal.Quotient.Operations +import LeanPool.CCDN.Determinant.PlaneSingularities +import LeanPool.CCDN.ForMathlib.MvPolynomialDerivative +import LeanPool.CCDN.Hilbert.LinearProjectionFiltration +import Mathlib.RingTheory.AlgebraicIndependent.Transcendental + +/-! In characteristic zero, a plane relation with a transcendental first +coordinate has a nonzero derivative in the second coordinate. That derivative +is a proper section of degree at most d-1 of the source curve. -/ + +@[expose] public section +namespace CCDN +noncomputable section +open MvPolynomial + +/-- A nonzero relation cannot omit the coordinate complementary to a +transcendental coordinate. -/ +private theorem plane_relation_pderiv_ne_zero {K A : Type*} [Field K] [CharZero K] + [CommRing A] [Algebra K A] (ψ : MvPolynomial (Fin 2) K →ₐ[K] A) + (f : MvPolynomial (Fin 2) K) (hf : f ≠ 0) (hzero : ψ f = 0) + (j k : Fin 2) (hjk : j ≠ k) (hj : Transcendental K (ψ (X j))) : + pderiv k f ≠ 0 := by + classical + intro hz + have hk := notMem_vars_of_pderiv_eq_zero hz fun n hn _ ↦ Nat.cast_ne_zero.mpr hn.ne' + have hvars : (f.vars : Set (Fin 2)) ⊆ Set.range (fun _ : Unit => j) := by + intro t ht + have htk : t ≠ k := fun h => hk (h ▸ ht) + have htj : t = j := by omega + exact ⟨(), htj.symm⟩ + obtain ⟨g, hg⟩ := exists_rename_eq_of_vars_subset_range f (fun _ : Unit => j) + (Function.injective_of_subsingleton _) hvars + have he : ψ.comp (rename (fun _ : Unit => j)) = aeval (fun _ : Unit => ψ (X j)) := by + apply MvPolynomial.algHom_ext + intro t + simp + have hi : Function.Injective (aeval (R := K) (fun _ : Unit => ψ (X j))) := + (algebraicIndependent_unique_type_iff (R := K) (x := fun _ : Unit => ψ (X j))).mpr hj + have hz : aeval (fun _ : Unit => ψ (X j)) g = 0 := by + rw [← he] + change ψ (rename (fun _ : Unit => j) g) = 0 + rwa [hg] + have hg0 : g = 0 := hi (by simpa only [map_zero] using hz) + exact hf (by rw [← hg, hg0, map_zero]) + +/-- The derivative is a proper degree-(d-1) section when f generates the +projection kernel. This is the proposed common reconstruction cut. -/ +theorem projection_derivative_proper {K τ : Type*} [Field K] [CharZero K] [Finite τ] + (I : Ideal (MvPolynomial τ K)) (a : Fin 2 → MvPolynomial τ K) + (ha : ∀ i, (a i).totalDegree ≤ 1) + (f : MvPolynomial (Fin 2) K) (hf : f ≠ 0) + (hker : ∀ g, aeval a g ∈ I ↔ f ∣ g) + (j k : Fin 2) (hjk : j ≠ k) + (hj : Transcendental K ((Ideal.Quotient.mk I) (a j))) : + aeval a (pderiv k f) ∉ I ∧ + (aeval a (pderiv k f)).totalDegree ≤ f.totalDegree - 1 := by + classical + let := Fintype.ofFinite τ + let ψ := projectionCoordinateMap I a + have hz : ψ f = 0 := by + change (Ideal.Quotient.mk I) (aeval a f) = 0 + exact Ideal.Quotient.eq_zero_iff_mem.mpr ((hker f).mpr (dvd_refl f)) + have ht : Transcendental K (ψ (X j)) := by + change Transcendental K ((Ideal.Quotient.mk I) (aeval a (X j))) + rwa [aeval_X] + have hp := plane_relation_pderiv_ne_zero ψ f hf hz j k hjk ht + refine ⟨fun hbad => not_dvd_pderiv_of_pderiv_ne_zero hf hp ((hker _).mp hbad), + (aeval_totalDegree_le a ha _).trans ?_⟩ + simpa only [one_mul] using pderiv_totalDegree_le f k + +end +end CCDN diff --git a/LeanPool/CCDN/Projection/PlaneProjectionKernel.lean b/LeanPool/CCDN/Projection/PlaneProjectionKernel.lean new file mode 100644 index 0000000000..6dcd0e7f01 --- /dev/null +++ b/LeanPool/CCDN/Projection/PlaneProjectionKernel.lean @@ -0,0 +1,130 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.Hilbert.LinearProjectionFiltration +public import Mathlib.RingTheory.Algebraic.Defs +import LeanPool.CCDN.Algebra.SectionImageRank +import LeanPool.CCDN.Hilbert.FunctionFieldRank +import LeanPool.CCDN.Hilbert.PlaneHilbert +import Mathlib.LinearAlgebra.Dimension.StrongRankCondition +import Mathlib.LinearAlgebra.FreeModule.StrongRankCondition +import Mathlib.RingTheory.Polynomial.UniqueFactorization +import Mathlib.RingTheory.SimpleModule.Basic + +/-! A plane projection of a curve has a nonzero kernel. If one coordinate is +transcendental, that kernel is generated by a single irreducible equation. -/ + +@[expose] public section +namespace CCDN +noncomputable section +open MvPolynomial + +/-- A two-variable polynomial ring cannot inject, through linear coordinates, +into a coordinate ring with eventually linear Hilbert growth. -/ +private theorem plane_projection_not_injective {K τ : Type*} [Field K] [Finite τ] + (I : Ideal (MvPolynomial τ K)) (a : Fin 2 → MvPolynomial τ K) + (ha : ∀ i, (a i).totalDegree ≤ 1) (d : ℕ) + (hdegree : HasFilteredCurveDegree I d) : + ¬ Function.Injective (projectionCoordinateMap I a) := by + classical + let := Fintype.ofFinite τ + intro hinj + have hlow (M : ℕ) : (M + 2).choose 2 ≤ Module.finrank K (filteredCoordinateSpace I M) := by + let V := restrictTotalDegree (Fin 2) K M + let ψ := projectionCoordinateMap I a + let T : V →ₗ[K] filteredCoordinateSpace I M := + (ψ.toLinearMap.comp V.subtype).codRestrict _ (fun v => + projectionCoordinateMap_filtered I a ha v.val + ((mem_restrictTotalDegree _ _ _).mp v.property)) + have hi : Function.Injective T := by + intro x y h + apply Subtype.ext + apply hinj + exact congrArg Subtype.val h + have hh := LinearMap.finrank_le_finrank_of_injective hi + rwa [finrank_restrictTotalDegree_two] at hh + obtain ⟨N₀, c, hN₀⟩ := hdegree + let M := 4 * (d + c.natAbs + N₀ + 1) + have hM : N₀ ≤ M := by dsimp [M]; omega + have he := hN₀ M hM + have hlo : (((M + 2).choose 2 : ℕ) : ℤ) ≤ (d : ℤ) * M + c := by + rw [← he] + exact_mod_cast hlow M + have hchoose : (2 : ℤ) * ((M + 2).choose 2 : ℕ) = ((M : ℤ) + 2) * (M + 1) := by + exact_mod_cast two_mul_choose_add_two M + have hm4d : (4 : ℤ) * d ≤ M := by + simp only [M, Nat.cast_mul, Nat.cast_add, Nat.cast_one, Nat.cast_ofNat]; omega + have hm4c : (4 : ℤ) * c.natAbs + 4 ≤ M := by + simp only [M, Nat.cast_mul, Nat.cast_add, Nat.cast_one, Nat.cast_ofNat]; omega + have hprod := mul_le_mul_of_nonneg_right hm4d (Nat.cast_nonneg M : (0 : ℤ) ≤ M) + have hc : c ≤ (c.natAbs : ℤ) := Int.le_natAbs + nlinarith + +/-- An irreducible equation in a plane map with a transcendental coordinate +generates its entire kernel. The proof uses the filtered section-rank bound. -/ +private theorem plane_map_eq_zero_iff_dvd {K A : Type*} [Field K] [CommRing A] [Algebra K A] + (f : MvPolynomial (Fin 2) K) (hf : Irreducible f) + (ψ : MvPolynomial (Fin 2) K →ₐ[K] A) (hψ : ψ f = 0) + (j : Fin 2) (hj : Transcendental K (ψ (X j))) + (g : MvPolynomial (Fin 2) K) : ψ g = 0 ↔ f ∣ g := by + constructor + · intro hg + by_contra hnd + let p : Ideal (MvPolynomial (Fin 2) K) := Ideal.span {f} + have : p.IsPrime := Ideal.isPrime_span_singleton_of_prime hf.prime + let φ : (MvPolynomial (Fin 2) K ⧸ p) →ₐ[K] A := + Ideal.Quotient.liftₐ p ψ (by + intro l hl + obtain ⟨u, rfl⟩ := Ideal.mem_span_singleton.mp hl + rw [map_mul, hψ, zero_mul]) + have hφ (l : MvPolynomial (Fin 2) K) : φ ((Ideal.Quotient.mk p) l) = ψ l := rfl + let N := f.totalDegree * g.totalDegree + 1 + let h : Fin N → MvPolynomial (Fin 2) K := fun i => X j ^ i.val + have hi : LinearIndependent K (fun i => φ ((Ideal.Quotient.mk p) (h i))) := by + simpa only [hφ, h, map_pow] using transcendental_powers_independent (ψ (X j)) hj N + have hh := section_image_rank_le p f.totalDegree + ⟨f.totalDegree, (f.totalDegree : ℤ) - f.totalDegree.choose 2, + fun M hM => principal_plane_hilbert_function f hf.ne_zero hM⟩ + φ h hi g (by simpa only [p, Ideal.mem_span_singleton] using hnd) g.totalDegree le_rfl + (by rw [hφ]; exact hg) + simp only [Fintype.card_fin, N] at hh + omega + · rintro ⟨u, rfl⟩ + rw [map_mul, hψ, zero_mul] + +/-- A linear plane projection with a transcendental coordinate has a +principal prime kernel with an irreducible generator. -/ +theorem exists_plane_projection_equation {K τ : Type*} [Field K] [Finite τ] + (I : Ideal (MvPolynomial τ K)) [I.IsPrime] + (a : Fin 2 → MvPolynomial τ K) (ha : ∀ i, (a i).totalDegree ≤ 1) + (d : ℕ) (hdegree : HasFilteredCurveDegree I d) + (j : Fin 2) (hj : Transcendental K ((Ideal.Quotient.mk I) (a j))) : + ∃ f : MvPolynomial (Fin 2) K, Irreducible f ∧ + RingHom.ker (projectionCoordinateMap I a) = Ideal.span {f} := by + classical + let := Fintype.ofFinite τ + let ψ := projectionCoordinateMap I a + have hker : RingHom.ker ψ ≠ ⊥ := by + intro he + exact plane_projection_not_injective I a ha d hdegree + ((RingHom.injective_iff_ker_eq_bot ψ.toRingHom).mpr he) + have hprime : (RingHom.ker ψ).IsPrime := RingHom.ker_isPrime ψ.toRingHom + obtain ⟨f, hfmem, hf⟩ := hprime.exists_mem_prime_of_ne_bot hker + refine ⟨f, hf.irreducible, ?_⟩ + ext g + exact (plane_map_eq_zero_iff_dvd f hf.irreducible ψ hfmem j + (by + change Transcendental K (projectionCoordinateMap I a (X j)) + rw [projectionCoordinateMap, AlgHom.comp_apply, aeval_X] + exact hj) g).trans + Ideal.mem_span_singleton.symm + +end +end CCDN diff --git a/LeanPool/CCDN/Projection/PlaneRelationSpace.lean b/LeanPool/CCDN/Projection/PlaneRelationSpace.lean new file mode 100644 index 0000000000..01da8d05ed --- /dev/null +++ b/LeanPool/CCDN/Projection/PlaneRelationSpace.lean @@ -0,0 +1,51 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import Mathlib.Algebra.MvPolynomial.Degrees +public import Mathlib.RingTheory.Ideal.Defs +import Mathlib.RingTheory.MvPolynomial.IrreducibleQuadratic + +/-! At the degree of the irreducible image equation, the plane relation +space is one-dimensional. This supplies the specialized-kernel condition +for first-order deformation of a degree-preserving projection. -/ + +@[expose] public section +namespace CCDN +noncomputable section +open MvPolynomial + +/-- A multiple `g` of a nonzero polynomial `f` with total degree at most that of `f` is a scalar +multiple of `f`. -/ +lemma eq_scalar_mul_of_dvd_of_totalDegree_le {K σ : Type*} [Field K] + (f g : MvPolynomial σ K) (hf : f ≠ 0) (hdiv : f ∣ g) + (hdeg : g.totalDegree ≤ f.totalDegree) : ∃ c : K, g = c • f := by + by_cases hg : g = 0 + · exact ⟨0, by simp [hg]⟩ + obtain ⟨q, hq⟩ := hdiv + have hq0 : q ≠ 0 := by intro hz; apply hg; rw [hq, hz, mul_zero] + have hd := totalDegree_mul_of_isDomain hf hq0 + rw [← hq] at hd + have hqd : q.totalDegree = 0 := by omega + have hqC := totalDegree_eq_zero_iff_eq_C.mp hqd + refine ⟨q.coeff 0, ?_⟩ + rw [hq, hqC, Algebra.smul_def] + simpa using (mul_comm f (C (q.coeff 0))) + +/-- Every relation no larger than the image equation is its scalar multiple. -/ +theorem plane_projection_relation_space {K τ : Type*} [Field K] + (I : Ideal (MvPolynomial τ K)) (a : Fin 2 → MvPolynomial τ K) + (f : MvPolynomial (Fin 2) K) (hf : f ≠ 0) + (hker : ∀ g, aeval a g ∈ I ↔ f ∣ g) + (g : MvPolynomial (Fin 2) K) (hg : g.totalDegree ≤ f.totalDegree) + (hzero : aeval a g ∈ I) : ∃ c : K, g = c • f := + eq_scalar_mul_of_dvd_of_totalDegree_le f g hf ((hker g).mp hzero) hg + +end +end CCDN diff --git a/LeanPool/CCDN/Projection/PrimitiveCoordinateField.lean b/LeanPool/CCDN/Projection/PrimitiveCoordinateField.lean new file mode 100644 index 0000000000..af2e9aa09f --- /dev/null +++ b/LeanPool/CCDN/Projection/PrimitiveCoordinateField.lean @@ -0,0 +1,69 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import Mathlib.FieldTheory.IntermediateField.Adjoin.Defs +public import Mathlib.RingTheory.Localization.FractionRing +import LeanPool.CCDN.Height.PrimitivePairReconstruction + +/-! Field generation by a parameter tuple and one primitive coordinate, +and the resulting fraction-field structure on the image ring. -/ + +@[expose] public section +namespace CCDN +noncomputable section +open MvPolynomial + +/-- If `v` generates `F` over the subfield generated by the tuple `u`, then the extended tuple +`(v, u 0, u 1, …)` generates `F` over `K`. -/ +theorem primitive_parameter_tuple_adjoin_top + {K F : Type*} [Field K] [Field F] [Algebra K F] {m : ℕ} + (u : Fin m → F) (v : F) + (hprim : IntermediateField.adjoin (IntermediateField.adjoin K (Set.range u)) {v} = ⊤) : + IntermediateField.adjoin K (Set.range (Fin.cases v u : Fin (m + 1) → F)) = ⊤ := by + let R := IntermediateField.adjoin K (Set.range u) + let L := IntermediateField.adjoin K (Set.range (Fin.cases v u : Fin (m + 1) → F)) + have hu (i : Fin m) : u i ∈ L := + IntermediateField.subset_adjoin K _ ⟨i.succ, rfl⟩ + have hv : v ∈ L := IntermediateField.subset_adjoin K _ ⟨0, rfl⟩ + have hRL : R ≤ L := IntermediateField.adjoin_le_iff.mpr (by + rintro _ ⟨i, rfl⟩ + exact hu i) + have hsub : (IntermediateField.adjoin R {v} : Set F) ⊆ L := by + apply (IntermediateField.adjoin_subset_adjoin_iff R (F' := K)).mpr + refine ⟨?_, ?_⟩ + · rintro _ ⟨r, rfl⟩ + exact hRL r.property + · intro x hx + obtain rfl := Set.mem_singleton_iff.mp hx + exact hv + apply top_unique + intro x _ + exact hsub (by rw [hprim]; trivial) + +/-- An injective algebra map into a field realizes that field as the fraction +field whenever a tuple of source elements generates it as a field. -/ +theorem isFractionRing_of_generating_tuple + {K A F σ : Type*} [Field K] [CommRing A] [Field F] + [Algebra K A] [Algebra K F] + (φ : A →ₐ[K] F) (hφ : Function.Injective φ) (a : σ → A) + (ha : IntermediateField.adjoin K (Set.range (fun i => φ (a i))) = ⊤) : + let : Algebra A F := φ.toRingHom.toAlgebra + IsFractionRing A F := by + let : Algebra A F := φ.toRingHom.toAlgebra + let : FaithfulSMul A F := (faithfulSMul_iff_algebraMap_injective A F).mpr hφ + apply IsFractionRing.of_field + intro x + obtain ⟨p, q, _, he⟩ := adjoin_top_polynomial_fraction (fun i => φ (a i)) ha x + refine ⟨aeval a p, aeval a q, ?_⟩ + change x = φ (aeval a p) / φ (aeval a q) + simpa only [comp_aeval_apply] using he + +end +end CCDN diff --git a/LeanPool/CCDN/Projection/ProjectiveClosureProjection.lean b/LeanPool/CCDN/Projection/ProjectiveClosureProjection.lean new file mode 100644 index 0000000000..a2f02931b6 --- /dev/null +++ b/LeanPool/CCDN/Projection/ProjectiveClosureProjection.lean @@ -0,0 +1,96 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.Algebra.ProjectiveClosureIdeal +public import LeanPool.CCDN.Hilbert.LinearProjectionFiltration +import LeanPool.CCDN.Algebra.ProjectionScalarExtension +import LeanPool.CCDN.Projection.HomogeneousFilteredProjection + +/-! Homogenizing a linear affine projection while retaining its homogenizing +coordinate gives the projective closure of its affine image ideal. -/ + +@[expose] public section +namespace CCDN +noncomputable section +open MvPolynomial + +/-- The homogenization of a projection by forms `a`: the forms `X 0, a 0, a 1, …` on the projective +coordinates, which keep the homogenizing coordinate `X 0` and shift the variables of each `a i`. -/ +def closureProjectionCoordinates {K : Type*} [Field K] {m n : ℕ} + (a : Fin m → MvPolynomial (Fin n) K) : + Fin (m + 1) → MvPolynomial (Fin (n + 1)) K := + Fin.cons (X 0) (fun i => rename Fin.succ (a i)) + +/-- If all the forms `a i` are homogeneous linear, then so are their closure projection coordinates. +-/ +lemma closureProjectionCoordinates_homogeneous {K : Type*} [Field K] {m n : ℕ} + (a : Fin m → MvPolynomial (Fin n) K) + (ha : ∀ i, (a i).IsHomogeneous 1) (i : Fin (m + 1)) : + (closureProjectionCoordinates a i).IsHomogeneous 1 := by + refine Fin.cases (isHomogeneous_X K _) (fun j => ?_) i + exact (ha j).rename_isHomogeneous + +/-- Dehomogenizing at `X 0 = 1` a polynomial not involving `X 0` recovers the original polynomial. +-/ +lemma affineChart_rename_succ {K : Type*} [Field K] {n : ℕ} + (f : MvPolynomial (Fin n) K) : + affineChart n (rename Fin.succ f) = f := by + have h : (affineChart (K := K) n).comp + (rename (R := K) Fin.succ).toRingHom = RingHom.id _ := by + apply MvPolynomial.ringHom_ext + · intro c + simp [affineChart] + · intro i + simp [affineChart] + exact RingHom.congr_fun h f + +/-- Dehomogenization at `X 0 = 1` intertwines substitution of the closure projection coordinates +with substitution of the original forms `a`. -/ +lemma affineChart_aeval_closureProjectionCoordinates + {K : Type*} [Field K] {m n : ℕ} + (a : Fin m → MvPolynomial (Fin n) K) + (f : MvPolynomial (Fin (m + 1)) K) : + affineChart n (aeval (closureProjectionCoordinates a) f) = + aeval a (affineChart m f) := by + have h : (affineChart (K := K) n).comp (aeval (closureProjectionCoordinates a)).toRingHom = + (aeval (R := K) a).toRingHom.comp (affineChart (K := K) m) := by + apply MvPolynomial.ringHom_ext + · intro c + simp [affineChart] + · intro i + refine Fin.cases ?_ (fun j => ?_) i + · simp [closureProjectionCoordinates, affineChart] + · simp only [RingHom.comp_apply, AlgHom.toRingHom_eq_coe, RingHom.coe_coe, + aeval_X, closureProjectionCoordinates, Fin.cons_succ, + affineChart_rename_succ] + simp [affineChart] + exact RingHom.congr_fun h f + +/-- For homogeneous linear forms `a`, the projective closure of the kernel of the projection of +`I` by `a` is the kernel of the projection of the projective closure of `I` by the closure +coordinates `X 0, a 0, a 1, …`; this is an equality of ideals, not merely of zero sets. -/ +theorem projectiveClosureIdeal_projection_kernel + {K : Type*} [Field K] {m n : ℕ} + (I : Ideal (MvPolynomial (Fin n) K)) + (a : Fin m → MvPolynomial (Fin n) K) + (ha : ∀ i, (a i).IsHomogeneous 1) : + projectiveClosureIdeal (RingHom.ker (projectionCoordinateMap I a)) = + RingHom.ker (projectionCoordinateMap (projectiveClosureIdeal I) + (closureProjectionCoordinates a)) := by + ext f + rw [mem_projectionCoordinateMap_ker, mem_projectiveClosureIdeal_iff, + mem_projectiveClosureIdeal_iff] + simp only [homogeneousComponent_aeval_linear _ + (closureProjectionCoordinates_homogeneous a ha), + affineChart_aeval_closureProjectionCoordinates, + mem_projectionCoordinateMap_ker] + +end +end CCDN diff --git a/LeanPool/CCDN/Projection/ProjectiveClosureSpecialization.lean b/LeanPool/CCDN/Projection/ProjectiveClosureSpecialization.lean new file mode 100644 index 0000000000..8f599f1910 --- /dev/null +++ b/LeanPool/CCDN/Projection/ProjectiveClosureSpecialization.lean @@ -0,0 +1,97 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.Projection.ProjectiveClosureProjection +public import Mathlib.RingTheory.IntegralClosure.IsIntegral.Defs +import Mathlib.RingTheory.IntegralClosure.IsIntegralClosure.Basic + +/-! Integral projections of the projective closure that fix X₀ specialize +to integral affine projections on the chart X₀ = 1. -/ + +@[expose] public section +namespace CCDN +noncomputable section +open MvPolynomial + +/-- Dehomogenization at `X 0 = 1`, as a `K`-algebra homomorphism from polynomials in `n + 1` +variables to polynomials in `n` variables. -/ +def affineChartAlgHom (K : Type*) [Field K] (n : ℕ) : + MvPolynomial (Fin (n + 1)) K →ₐ[K] MvPolynomial (Fin n) K where + toRingHom := affineChart n + commutes' c := by simp [affineChart] + +/-- Dehomogenizing at `X 0 = 1` an element of the projective closure of `I` gives an element of `I`. +-/ +lemma projectiveClosureIdeal_affineChart_mem {K : Type*} [Field K] {n : ℕ} + (I : Ideal (MvPolynomial (Fin n) K)) (f : MvPolynomial (Fin (n + 1)) K) + (hf : f ∈ projectiveClosureIdeal I) : affineChart n f ∈ I := by + have h : affineChart n + (∑ M ∈ Finset.range (f.totalDegree + 1), homogeneousComponent M f) ∈ I := by + rw [map_sum] + exact I.sum_mem (fun M _ => (mem_projectiveClosureIdeal_iff I f).mp hf M) + rwa [sum_homogeneousComponent] at h + +/-- The specialization `X 0 = 1` from the homogeneous coordinate ring of the projective closure of +`I` to the affine coordinate ring of `I`. -/ +def projectiveClosureSpecialization {K : Type*} [Field K] {n : ℕ} + (I : Ideal (MvPolynomial (Fin n) K)) : + (MvPolynomial (Fin (n + 1)) K ⧸ projectiveClosureIdeal I) →ₐ[K] + (MvPolynomial (Fin n) K ⧸ I) := + Ideal.Quotient.liftₐ (projectiveClosureIdeal I) + ((Ideal.Quotient.mkₐ K I).comp (affineChartAlgHom K n)) + (fun f hf => Ideal.Quotient.eq_zero_iff_mem.mpr + (projectiveClosureIdeal_affineChart_mem I f hf)) + +/-- The specialization `X 0 = 1` sends the class of `f` to the class of its dehomogenization. -/ +@[simp] lemma projectiveClosureSpecialization_mk {K : Type*} [Field K] {n : ℕ} + (I : Ideal (MvPolynomial (Fin n) K)) (f : MvPolynomial (Fin (n + 1)) K) : + projectiveClosureSpecialization I (Ideal.Quotient.mk (projectiveClosureIdeal I) f) = + Ideal.Quotient.mk I (affineChart n f) := rfl + +private theorem projectiveClosureSpecialization_surjective {K : Type*} [Field K] {n : ℕ} + (I : Ideal (MvPolynomial (Fin n) K)) : + Function.Surjective (projectiveClosureSpecialization I) := by + intro x + obtain ⟨f, rfl⟩ := Ideal.Quotient.mk_surjective x + refine ⟨Ideal.Quotient.mk (projectiveClosureIdeal I) (rename Fin.succ f), ?_⟩ + rw [projectiveClosureSpecialization_mk, affineChart_rename_succ] + +/-- If the projection of the projective closure of `I` by the closure coordinates `X 0, a 0, a 1, …` +is integral, then the affine projection of `I` by the forms `a` is integral. -/ +theorem affine_projection_integral_of_closure_integral + {K : Type*} [Field K] {m n : ℕ} + (I : Ideal (MvPolynomial (Fin n) K)) (a : Fin m → MvPolynomial (Fin n) K) + (hint : (projectionCoordinateMap (projectiveClosureIdeal I) + (closureProjectionCoordinates a)).IsIntegral) : + (projectionCoordinateMap I a).IsIntegral := by + have hspecial : (projectiveClosureSpecialization I).toRingHom.IsIntegral := + RingHom.isIntegral_of_surjective _ (projectiveClosureSpecialization_surjective I) + have hcomp := RingHom.IsIntegral.trans + (projectionCoordinateMap (projectiveClosureIdeal I) + (closureProjectionCoordinates a)).toRingHom + (projectiveClosureSpecialization I).toRingHom hint hspecial + have heq : (projectiveClosureSpecialization I).comp + (projectionCoordinateMap (projectiveClosureIdeal I) (closureProjectionCoordinates a)) = + (projectionCoordinateMap I a).comp (affineChartAlgHom K m) := by + apply AlgHom.ext + intro f + change Ideal.Quotient.mk I + (affineChart n (aeval (closureProjectionCoordinates a) f)) = + Ideal.Quotient.mk I (aeval a (affineChart m f)) + rw [affineChart_aeval_closureProjectionCoordinates] + change ((projectiveClosureSpecialization I).comp + (projectionCoordinateMap (projectiveClosureIdeal I) + (closureProjectionCoordinates a))).IsIntegral at hcomp + rw [heq] at hcomp + exact RingHom.IsIntegral.tower_top (affineChartAlgHom K m).toRingHom + (projectionCoordinateMap I a).toRingHom hcomp + +end +end CCDN diff --git a/LeanPool/CCDN/Projection/ProjectiveLinearCountTransfer.lean b/LeanPool/CCDN/Projection/ProjectiveLinearCountTransfer.lean new file mode 100644 index 0000000000..439ec58c4d --- /dev/null +++ b/LeanPool/CCDN/Projection/ProjectiveLinearCountTransfer.lean @@ -0,0 +1,109 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.Algebra.IntegerLinearForms +public import LeanPool.CCDN.Algebra.ProjectiveSection +public import LeanPool.CCDN.Height.ProjectiveZeroLocus +import LeanPool.CCDN.Algebra.ProjectiveFiniteFibers +import LeanPool.CCDN.Determinant.PolynomialMapDeterminant +import LeanPool.CCDN.Projection.BoundedLinearProjection + +/-! Height and cardinality transport through an integer linear +projection with uniformly bounded affine fibers. -/ + +@[expose] public section +namespace CCDN +noncomputable section +open MvPolynomial Matrix + +private def integerProjectiveLinearImage {m n : ℕ} + (P : Matrix (Fin (m + 1)) (Fin (n + 1)) ℤ) (x : ProjectivePoint n) : + ProjectivePoint m := + if h : (P.map (Int.castRingHom ℚ)) *ᵥ x.rep ≠ 0 then + Projectivization.mk ℚ ((P.map (Int.castRingHom ℚ)) *ᵥ x.rep) h + else + Projectivization.mk ℚ (fun _ : Fin (m + 1) => 1) (by + intro hz + have hh := congrFun hz 0 + exact one_ne_zero hh) + +/-- Let `P` be an integer matrix with entries of absolute value at most `H` whose linear forms +satisfy the homogeneous relation `f` modulo `I`. If every affine fiber of the induced map on the +zero locus of `I` has at most `M` points, then the projective rational points of height at most `B` +on the zero locus of `I` number at most `M` times those of height at most `(n + 1) * H * B` on the +projective hypersurface `f = 0`. -/ +theorem projective_integer_linear_count_transfer + {m n : ℕ} (I : Ideal (MvPolynomial (Fin (n + 1)) ℚ)) + (P : Matrix (Fin (m + 1)) (Fin (n + 1)) ℤ) + (f : MvPolynomial (Fin (m + 1)) ℚ) (hf : f.IsHomogeneous f.totalDegree) + (hrelation : aeval (fun i => integerLinearForm (P i)) f ∈ I) + (M : ℕ) + (hfinite : ∀ z : Fin (m + 1) → ℚ, ∀ s : Finset (Fin (n + 1) → ℚ), + (∀ x ∈ s, x ∈ idealZeroLocus I) → + (∀ x ∈ s, ∀ i, eval x (integerLinearForm (P i)) = z i) → s.card ≤ M) + (H B : ℝ) (hH : 0 ≤ H) + (hP : ∀ i j, |(P i j : ℝ)| ≤ H) : + projectiveCount (projectiveIdealZeroLocus I) B ≤ + M * projectiveCount (projectiveZeroLocus f) ((n + 1 : ℕ) * H * B) := by + classical + let L := (P.map (Int.castRingHom ℚ)).mulVecLin + let π := integerProjectiveLinearImage P + let s := projectivePoints (projectiveIdealZeroLocus I) B + let t := projectivePoints (projectiveZeroLocus f) ((n + 1 : ℕ) * H * B) + have hLapply (x : Fin (n + 1) → ℚ) (i) : + L x i = eval x (integerLinearForm (P i)) := by + simp only [L, Matrix.mulVecLin_apply, Matrix.mulVec, dotProduct, Matrix.map_apply, + Int.coe_castRingHom, eval_linearForm] + have hfiniteL (z : Fin (m + 1) → ℚ) (u : Finset (Fin (n + 1) → ℚ)) + (hu : ∀ x ∈ u, x ∈ idealZeroLocus I) (huz : ∀ x ∈ u, L x = z) : + u.card ≤ M := by + apply hfinite z u hu + intro x hx i + rw [← hLapply, huz x hx] + have hnon (x : ProjectivePoint n) (hx : x ∈ projectiveIdealZeroLocus I) : + L x.rep ≠ 0 := + projective_linear_image_ne_zero_of_finite_zero_fiber I L M (hfiniteL 0) x hx + have hπ (x : ProjectivePoint n) (hx : x ∈ projectiveIdealZeroLocus I) : + π x = Projectivization.mk ℚ (L x.rep) (hnon x hx) := by + exact dite_eq_left (hnon x hx) + have himage : s.image π ⊆ t := by + intro y hy + obtain ⟨x, hx, rfl⟩ := Finset.mem_image.mp hy + obtain ⟨hxB, hxI⟩ := mem_projectivePoints.mp hx + rw [hπ x hxI] + apply mem_projectivePoints.mpr + constructor + · exact (projective_linear_map_height P hH hP x (hnon x hxI)).trans + (mul_le_mul_of_nonneg_left hxB (by positivity)) + · rw [mem_projectiveZeroLocus_mk hf] + have hz := projectiveIdealZeroLocus_rep hxI _ hrelation + rw [eval_polynomial_coordinate_map] at hz + have hev : L x.rep = fun i => eval x.rep (integerLinearForm (P i)) := + funext (hLapply x.rep) + rwa [hev] + have hfib (y : ProjectivePoint m) : + (s.filter (fun x => π x = y)).card ≤ M := by + let u := s.filter (fun x => π x = y) + have hu (x) (hx : x ∈ u) : x ∈ projectiveIdealZeroLocus I := + (mem_projectivePoints.mp (Finset.mem_filter.mp hx).1).2 + apply projective_linear_fiber_card_le I L u hu (fun x hx => hnon x (hu x hx)) + y _ M (hfiniteL y.rep) + intro x hx + rw [← hπ x (hu x hx)] + exact (Finset.mem_filter.mp hx).2 + change s.card ≤ M * t.card + rw [Finset.card_eq_sum_card_image π s] + calc + _ ≤ ∑ _y ∈ s.image π, M := Finset.sum_le_sum (fun y _ => hfib y) + _ = M * (s.image π).card := by simp [Nat.mul_comm] + _ ≤ M * t.card := Nat.mul_le_mul_left M (Finset.card_le_card himage) + +end +end CCDN diff --git a/LeanPool/CCDN/Projection/ProjectivePlaneProjection.lean b/LeanPool/CCDN/Projection/ProjectivePlaneProjection.lean new file mode 100644 index 0000000000..de108e9e15 --- /dev/null +++ b/LeanPool/CCDN/Projection/ProjectivePlaneProjection.lean @@ -0,0 +1,108 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.Algebra.ProjectiveChartIdeal +public import LeanPool.CCDN.Projection.DegreePreservingProjectionBox +import LeanPool.CCDN.Determinant.PolynomialMapDeterminant +import LeanPool.CCDN.Height.ProjectiveIntegerHeight +import LeanPool.CCDN.Projection.DegreePreservingProjectionBox + +/-! The bounded integral affine projection lifts to a projective plane +map on the standard chart, with the same polynomial height factor. -/ + +@[expose] public section +namespace CCDN +noncomputable section +open MvPolynomial + +/-- For an integer vector `z` with `z 0 ≠ 0`, the projective point `[1 : z 1 / z 0 : …]` is the +point with homogeneous coordinates `z`. -/ +lemma affineProjectivePoint_eq_integer {n : ℕ} (z : IntPoint (n + 1)) (hz0 : z 0 ≠ 0) : + affineProjectivePoint (fun j => (z j.succ : ℚ) / (z 0 : ℚ)) = + Projectivization.mk ℚ (rationalPoint z) + (rationalPoint_ne_zero (fun h => hz0 (congrFun h 0))) := by + have h0 : (z 0 : ℚ) ≠ 0 := by exact_mod_cast hz0 + apply (Projectivization.mk_eq_mk_iff' ℚ _ _ _ _).mpr + refine ⟨(z 0 : ℚ)⁻¹, ?_⟩ + funext j + refine Fin.cases ?_ (fun k => ?_) j + · simp [rationalPoint, h0] + · simp [rationalPoint, div_eq_mul_inv, mul_comm] + +/-- The map from `ℙⁿ` to the projective plane on the chart `X₀ ≠ 0`, sending a point with affine +coordinates `x` to `[1 : m₀(x) : m₁(x)]`, where `m₀`, `m₁` are the moment coordinates with +parameters `k₀` and `k₁`. -/ +def projectivePlaneChartProjection {n : ℕ} (k₀ k₁ : ℕ) (x : ProjectivePoint n) : + ProjectivePoint 2 := + affineProjectivePoint + ![eval (projectiveAffineCoordinates x) (momentCoordinate ℚ n k₀), + eval (projectiveAffineCoordinates x) (momentCoordinate ℚ n k₁)] + +/-- For parameters `k₀ ≤ n ^ 2 * d` and `k₁ ≤ n * d ^ 2`, the plane chart projection of a point on +the chart `X₀ ≠ 0` of height at most `B ≥ 1` has height at most `projectionBoxFactor n d * B`. -/ +theorem projectivePlaneChartProjection_height {n d k₀ k₁ : ℕ} + (hk₀ : k₀ ≤ n ^ 2 * d) (hk₁ : k₁ ≤ n * d ^ 2) + (x : ProjectivePoint n) (hx : x.rep 0 ≠ 0) {B : ℝ} (hB : 1 ≤ B) + (hxB : x.mulHeight ≤ B) : + (projectivePlaneChartProjection k₀ k₁ x).mulHeight ≤ projectionBoxFactor n d * B := by + let z := primitiveProjectiveRep x + obtain ⟨hz0, hchart⟩ := primitiveProjectiveRep_chart x hx + let w : IntPoint 3 := Fin.cons (z 0) (degreeProjection n k₀ k₁ (fun j => z j.succ)) + have hw0 : w 0 ≠ 0 := hz0 + have hcoord (k : ℕ) : + eval (projectiveAffineCoordinates x) (momentCoordinate ℚ n k) = + (eval (fun j => z j.succ) (momentCoordinate ℤ n k) : ℚ) / (z 0 : ℚ) := by + rw [hchart, rationalPoint_moment_eval, momentCoordinate_eval, momentCoordinate_eval, + Finset.sum_div] + apply Finset.sum_congr rfl + intro j _ + simp only [rationalPoint, z] + ring + have himage : projectivePlaneChartProjection k₀ k₁ x = + affineProjectivePoint (fun j => (w j.succ : ℚ) / (w 0 : ℚ)) := by + unfold projectivePlaneChartProjection + congr 1 + funext j + fin_cases j <;> exact hcoord _ + rw [himage, affineProjectivePoint_eq_integer w hw0] + apply projective_integer_height_le w (fun h => hw0 (congrFun h 0)) + intro j + refine Fin.cases ?_ (fun k => ?_) j + · change |(z 0 : ℝ)| ≤ _ + exact ((primitiveProjectiveRep_bound x 0).trans hxB).trans + (le_mul_of_one_le_left (by linarith) (projectionBoxFactor_one_le n d)) + · have hzbox : (fun j => z j.succ) ∈ integerBox n B := + mem_integerBox.mpr (fun j => (primitiveProjectiveRep_bound x j.succ).trans hxB) + exact mem_integerBox.mp (degreeProjection_box hk₀ hk₁ (by linarith) hzbox) k + +/-- If the moment coordinates satisfy the plane relation `f` of degree `d` on the affine chart of +the prime `I` (with `X 0 ∉ I`), then the plane chart projection maps the points of the projective +variety of `I` on the chart `X₀ ≠ 0` into the projective plane curve defined by the homogenization +of `f`. -/ +theorem projectivePlaneChartProjection_zero {n d k₀ k₁ : ℕ} + (I : Ideal (MvPolynomial (Fin (n + 1)) ℚ)) [I.IsPrime] (hX : X 0 ∉ I) + (f : MvPolynomial (Fin 2) ℚ) (hfd : f.totalDegree = d) + (hfI : aeval ![momentCoordinate ℚ n k₀, momentCoordinate ℚ n k₁] f ∈ projectiveChartIdeal I) + (x : ProjectivePoint n) (hx : x ∈ projectiveIdealZeroLocus I) (hx0 : x.rep 0 ≠ 0) : + projectivePlaneChartProjection k₀ k₁ x ∈ + projectiveZeroLocus (scaledHomogenize d 1 f) := by + have hh := projectiveChartIdeal_point I hX x hx hx0 _ hfI + rw [eval_polynomial_coordinate_map] at hh + change affineProjectivePoint _ ∈ _ + unfold affineProjectivePoint + rw [mem_projectiveZeroLocus_mk (scaledHomogenize_homogeneous 1 f hfd.le), + ← affineChart_eval, affineChart_scaledHomogenize_one] + convert hh using 1 + congr 2 + funext j + fin_cases j <;> rfl + +end +end CCDN diff --git a/LeanPool/CCDN/Projection/ProjectiveProjectionCount.lean b/LeanPool/CCDN/Projection/ProjectiveProjectionCount.lean new file mode 100644 index 0000000000..bad0d8a2c8 --- /dev/null +++ b/LeanPool/CCDN/Projection/ProjectiveProjectionCount.lean @@ -0,0 +1,126 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.Algebra.ProjectiveChartIdeal +public import LeanPool.CCDN.Projection.DegreePreservingProjectionBox +import LeanPool.CCDN.Algebra.ScaledHypersurfaceModel +import LeanPool.CCDN.Projection.BoundedDegreePreservingProjection +import LeanPool.CCDN.Projection.ProjectivePlaneProjection + +/-! A projective curve with a proper standard chart reduces to a plane +equation of the same degree. Projection loses at most d(d-1) affine points +and at most d further points lie on the omitted hyperplane. -/ + +@[expose] public section +namespace CCDN +noncomputable section +open MvPolynomial + +/-- Let `I` be a prime ideal defining a projective curve of degree `d` not contained in the +hyperplane `X₀ = 0`, and suppose the moment coordinates with parameters `k₀ ≤ n ^ 2 * d` and +`k₁ ≤ n * d ^ 2` satisfy the plane relation `f` of degree `d` on its affine chart, losing at most +`d * (d - 1)` points on any finite set of chart points. Then the curve has at most `d ^ 2` more +rational points of height at most `B` than the projective plane curve defined by the homogenization +of `f` has of height at most `projectionBoxFactor n d * B`. -/ +theorem projective_curve_count_le_of_moment_projection {n d k₀ k₁ : ℕ} + (I : Ideal (CoordinatePolynomial (n + 1))) [I.IsPrime] (hX : X 0 ∉ I) + (hd : 0 < d) (hdegree : HasProjectiveCurveDegree I d) + (f : MvPolynomial (Fin 2) ℚ) (hfd : f.totalDegree = d) + (hk₀ : k₀ ≤ n ^ 2 * d) (hk₁ : k₁ ≤ n * d ^ 2) + (hfI : aeval ![momentCoordinate ℚ n k₀, momentCoordinate ℚ n k₁] f ∈ projectiveChartIdeal I) + (hcount : ∀ S : Finset (Fin n → ℚ), + (∀ z ∈ S, ∀ g ∈ projectiveChartIdeal I, eval z g = 0) → + S.card ≤ (S.image (fun z i => + eval z (![momentCoordinate ℚ n k₀, momentCoordinate ℚ n k₁] i))).card + d * (d - 1)) + (B : ℝ) (hB : 1 ≤ B) : + projectiveCount (projectiveIdealZeroLocus I) B ≤ + projectiveCount (projectiveZeroLocus (scaledHomogenize d 1 f)) + (projectionBoxFactor n d * B) + d ^ 2 := by + classical + let S := projectivePoints (projectiveIdealZeroLocus I) B + let T := S.filter (fun x => x.rep 0 ≠ 0) + let U := S.filter (fun x => ¬ x.rep 0 ≠ 0) + let R := T.image projectiveAffineCoordinates + let π : RatPoint n → RatPoint 2 := fun z => + ![eval z (momentCoordinate ℚ n k₀), eval z (momentCoordinate ℚ n k₁)] + have hR : ∀ z ∈ R, ∀ g ∈ projectiveChartIdeal I, eval z g = 0 := by + intro z hz + obtain ⟨x, hx, rfl⟩ := Finset.mem_image.mp hz + obtain ⟨hxS, hx0⟩ := Finset.mem_filter.mp hx + exact projectiveChartIdeal_point I hX x (mem_projectivePoints.mp hxS).2 hx0 + have hc := hcount R hR + have hRT : R.card = T.card := by + apply Finset.card_image_of_injOn + intro x hx y hy he + exact projectiveAffineCoordinates_injective (Finset.mem_filter.mp hx).2 + (Finset.mem_filter.mp hy).2 he + have hπ : (fun z i => eval z + (![momentCoordinate ℚ n k₀, momentCoordinate ℚ n k₁] i)) = π := by + funext z i + fin_cases i <;> rfl + rw [hπ, hRT] at hc + have himage : (R.image π).card ≤ + projectiveCount (projectiveZeroLocus (scaledHomogenize d 1 f)) + (projectionBoxFactor n d * B) := by + rw [← Finset.card_image_of_injective (R.image π) (affineProjectivePoint_injective 2)] + apply Finset.card_le_card + intro y hy + obtain ⟨z, hz, rfl⟩ := Finset.mem_image.mp hy + obtain ⟨v, hv, rfl⟩ := Finset.mem_image.mp hz + obtain ⟨x, hx, rfl⟩ := Finset.mem_image.mp hv + obtain ⟨hxS, hx0⟩ := Finset.mem_filter.mp hx + obtain ⟨hxB, hxI⟩ := mem_projectivePoints.mp hxS + exact mem_projectivePoints.mpr + ⟨projectivePlaneChartProjection_height hk₀ hk₁ x hx0 hB hxB, + projectivePlaneChartProjection_zero I hX f hfd hfI x hxI hx0⟩ + have hU : U.card ≤ d := by + have h := projective_section_card_le_degree I hdegree (X 0) (isHomogeneous_X _ _) + hX (fun x : U => x.val) Subtype.val_injective + (fun x => (mem_projectivePoints.mp (Finset.mem_filter.mp x.property).1).2) + (fun x => by simpa using (Finset.mem_filter.mp x.property).2) + simpa only [Fintype.card_coe, mul_one] using h + have hsum : T.card + U.card = S.card := Finset.card_filter_add_card_filter_not _ + have hdcalc : d * (d - 1) + d = d ^ 2 := by + have h := Nat.sub_add_cancel hd + nlinarith + change S.card ≤ _ + omega + +/-- A projective curve of degree `d > 0` in `ℙⁿ`, given by a prime ideal `I` not containing +`X 0`, has at most `d ^ 2` more rational points of height at most `B` than some irreducible +projective plane curve of degree `d` has of height at most `projectionBoxFactor n d * B`. -/ +theorem exists_projective_plane_count_reduction {n d : ℕ} + (I : Ideal (CoordinatePolynomial (n + 1))) [I.IsPrime] (hX : X 0 ∉ I) + (hd : 0 < d) (hdegree : HasProjectiveCurveDegree I d) : + ∃ f : MvPolynomial (Fin 3) ℚ, f.IsHomogeneous d ∧ Irreducible f ∧ + ∀ B : ℝ, 1 ≤ B → + projectiveCount (projectiveIdealZeroLocus I) B ≤ + projectiveCount (projectiveZeroLocus f) (projectionBoxFactor n d * B) + d ^ 2 := by + classical + let J := projectiveChartIdeal I + have hJ : HasAffineCurveDegree J d := projectiveChartIdeal_degree I hX hdegree + obtain ⟨k₀, k₁, f, hk₀, hk₁, hfir, hfd, hker, hcount⟩ := + exists_bounded_projection_card_le J d hd hJ + have hfI : aeval ![momentCoordinate ℚ n k₀, momentCoordinate ℚ n k₁] f ∈ J := + (hker f).mpr dvd_rfl + have hπ : (fun z i => eval z (![momentCoordinate ℚ n k₀, momentCoordinate ℚ n k₁] i)) = + fun z : Fin n → ℚ => + ![eval z (momentCoordinate ℚ n k₀), eval z (momentCoordinate ℚ n k₁)] := by + funext z i + fin_cases i <;> rfl + refine ⟨scaledHomogenize d 1 f, scaledHomogenize_homogeneous 1 f hfd.le, ?_, + projective_curve_count_le_of_moment_projection I hX hd hdegree f hfd hk₀ hk₁ hfI ?_⟩ + · simpa only [hfd] using scaledHomogenize_irreducible f hfir one_ne_zero + · intro S hS + rw [hπ] + exact hcount S hS + +end +end CCDN diff --git a/LeanPool/CCDN/Projection/ProjectiveProjectionGrowth.lean b/LeanPool/CCDN/Projection/ProjectiveProjectionGrowth.lean new file mode 100644 index 0000000000..51b426a25d --- /dev/null +++ b/LeanPool/CCDN/Projection/ProjectiveProjectionGrowth.lean @@ -0,0 +1,98 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.Algebra.ProjectiveSection +public import LeanPool.CCDN.Height.ProjectiveBox +public import LeanPool.CCDN.Projection.BoundedBirationalHypersurfaceProjection +import LeanPool.CCDN.Hypersurface.ProjectiveHypersurfaceDimensionGrowth +import LeanPool.CCDN.Projection.ProjectiveLinearCountTransfer + +/-! The quantitative counting step for the constructed projection. The +height and finite-fiber losses remain polynomial in the original degree. -/ + +@[expose] public section +namespace CCDN +noncomputable section +open MvPolynomial + +/-- The exponent `18 + 3 * m + N + 2 * N * (m + 1)`, with `N = m + k + 2`, of the degree in the +point-count bound obtained by projecting an `(m + 1)`-dimensional variety in `ℙ^(m + k + 1)` to a +hypersurface in `ℙ^(m + 2)`. -/ +def projectionDegreeExponent (m k : ℕ) : ℕ := + 18 + 3 * m + (m + k + 2) + 2 * (m + k + 2) * (m + 1) + +/-- There is a constant `C` depending only on `m` and `k` with the following property. Let integer +forms `b`, with coefficients bounded by the birational projection height, satisfy an irreducible +homogeneous relation `f` of degree `d ≥ 5` modulo `I`, and let every affine fiber of the projection +of the zero locus of `I` have at most `d ^ (m + k + 2)` points. Then the zero locus of `I` in +`ℙ^(m + k + 1)` has at most `C * d ^ projectionDegreeExponent m k * B ^ (m + 1)` rational points of +height at most `B`. -/ +theorem projective_projection_dimension_growth (m k : ℕ) : + ∃ C : ℝ, 0 < C ∧ + ∀ (I : Ideal (MvPolynomial (Fin (m + k + 2)) ℚ)) (d : ℕ), 5 ≤ d → + ∀ b : Fin (m + 3) → Fin (m + k + 2) → ℤ, + (∀ i j, |b i j| ≤ integerBirationalProjectionHeight (m + k + 2) d) → + ∀ f : MvPolynomial (Fin (m + 3)) ℚ, + Irreducible f → f.IsHomogeneous d → f.totalDegree = d → + aeval (fun i => integerLinearForm (b i)) f ∈ I → + (∀ z : Fin (m + 3) → ℚ, ∀ s : Finset (Fin (m + k + 2) → ℚ), + (∀ x ∈ s, x ∈ idealZeroLocus I) → + (∀ x ∈ s, ∀ i, eval x (integerLinearForm (b i)) = z i) → + s.card ≤ d ^ (m + k + 2)) → + ∀ B : ℝ, 1 ≤ B → + (projectiveCount (projectiveIdealZeroLocus I) B : ℝ) ≤ + C * (d : ℝ) ^ projectionDegreeExponent m k * B ^ (m + 1) := by + obtain ⟨C, hC, hcount⟩ := projective_hypersurface_dimension_growth m + let N := m + k + 2 + let T : ℝ := N * birationalProjectionHeightConstant N + have hN : 1 ≤ N := by dsimp [N]; omega + have hT : 1 ≤ T := by + have hpos := birationalProjectionHeightConstant_pos N + have hh : 1 ≤ N * birationalProjectionHeightConstant N := by + exact Nat.succ_le_of_lt (Nat.mul_pos (by omega) hpos) + dsimp only [T] + exact_mod_cast hh + refine ⟨C * T ^ (m + 1), by positivity, ?_⟩ + intro I d hd b hb f hf hfhom hfdeg hrel hfiber B hB + let H : ℝ := birationalProjectionHeightConstant N * (d : ℝ) ^ (2 * N) + have hd1 : (1 : ℝ) ≤ d := by exact_mod_cast (show 1 ≤ d by omega) + have hH : 1 ≤ H := by + have hconst : (1 : ℝ) ≤ birationalProjectionHeightConstant N := by + exact_mod_cast Nat.succ_le_of_lt (birationalProjectionHeightConstant_pos N) + exact one_le_mul_of_one_le_of_one_le hconst (one_le_pow₀ hd1) + have hbH (i j) : |(b i j : ℝ)| ≤ H := by + have hh : |(b i j : ℝ)| ≤ integerBirationalProjectionHeight N d := by + exact_mod_cast hb i j + apply hh.trans + dsimp only [H] + exact_mod_cast integerBirationalProjectionHeight_le N d (by omega) + have hbound := projective_integer_linear_count_transfer + (n := m + k + 1) (m := m + 2) I b f (by rwa [hfdeg]) + hrel (d ^ N) hfiber H B (by linarith) hbH + have hheight : 1 ≤ (N : ℝ) * H * B := by + exact one_le_mul_of_one_le_of_one_le + (one_le_mul_of_one_le_of_one_le (by exact_mod_cast hN) hH) hB + have htarget := hcount f (by rwa [hfdeg]) hf (by omega) ((N : ℝ) * H * B) hheight + rw [hfdeg] at htarget + have hnon : (0 : ℝ) ≤ (d : ℝ) ^ N := by positivity + have hh : (projectiveCount (projectiveIdealZeroLocus I) B : ℝ) ≤ + (d : ℝ) ^ N * (C * (d : ℝ) ^ (18 + 3 * m) * + ((N : ℝ) * H * B) ^ (m + 1)) := by + apply (show (projectiveCount (projectiveIdealZeroLocus I) B : ℝ) ≤ + (d : ℝ) ^ N * projectiveCount (projectiveZeroLocus f) ((N : ℝ) * H * B) by + exact_mod_cast hbound).trans + exact mul_le_mul_of_nonneg_left htarget hnon + convert hh using 1 + dsimp only [projectionDegreeExponent, H, T, N] + simp only [pow_add, pow_mul, mul_pow] + ring + +end +end CCDN diff --git a/LeanPool/CCDN/Projection/ScaleAbsorption.lean b/LeanPool/CCDN/Projection/ScaleAbsorption.lean new file mode 100644 index 0000000000..9201437460 --- /dev/null +++ b/LeanPool/CCDN/Projection/ScaleAbsorption.lean @@ -0,0 +1,80 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.Projection.DegreePreservingProjectionBox +public import LeanPool.CCDN.Statements +import LeanPool.CCDN.ForMathlib.Log + +/-! Absorb the degree-dependent projection box factor into a constant depending +only on ambient dimension, retaining the exact exponent 1/d and d³ factor. -/ + +@[expose] public section +namespace CCDN +noncomputable section + +/-- The constant `c = 2 * n ^ 2 + 3 * n`, for which `log (projectionBoxFactor n d) ≤ c * d`. -/ +def projectionLogConstant (n : ℕ) : ℝ := 2 * (n : ℝ) ^ 2 + 3 * n + +/-- The projection log constant is nonnegative. -/ +lemma projectionLogConstant_nonneg (n : ℕ) : 0 ≤ projectionLogConstant n := by + unfold projectionLogConstant + positivity + +/-- For `d > 0`, the logarithm of the projection box factor is at most +`projectionLogConstant n * d`. -/ +lemma projectionBoxFactor_log_le (n : ℕ) {d : ℕ} (hd : 0 < d) : + Real.log (projectionBoxFactor n d) ≤ projectionLogConstant n * d := by + have hn : (0 : ℝ) ≤ n := by positivity + have hd1 : (1 : ℝ) ≤ d := by exact_mod_cast hd + have hn0 : (0 : ℝ) < n + 1 := by positivity + have hd0 : (0 : ℝ) < d + 1 := by positivity + have hln : Real.log ((n : ℝ) + 1) ≤ n := Real.log_add_one_le_self hn + have hld : Real.log ((d : ℝ) + 1) ≤ d := Real.log_add_one_le_self d.cast_nonneg + unfold projectionBoxFactor + rw [Real.log_mul hn0.ne' (pow_pos (pow_pos (mul_pos hn0 hd0) _) _).ne', + Real.log_pow, Real.log_pow, Real.log_mul hn0.ne' hd0.ne'] + unfold projectionLogConstant + have hh := mul_le_mul_of_nonneg_left (add_le_add hln hld) (show 0 ≤ 2 * (n : ℝ) by positivity) + have hh₂ := mul_le_mul_of_nonneg_left hd1 (show 0 ≤ 2 * (n : ℝ) ^ 2 + n by positivity) + norm_num only [Nat.cast_ofNat] + nlinarith + +private lemma curveBound_scale_of_log_le {d : ℕ} (hd : 0 < d) {A B C : ℝ} + (hA : 1 ≤ A) (hB : 1 ≤ B) (hC : 0 ≤ C) (hlog : Real.log A ≤ C * d) : + curveBound d (A * B) ≤ Real.exp C * (C + 1) * curveBound d B := by + have hd0 : (0 : ℝ) < d := by exact_mod_cast hd + have hA0 : 0 < A := by linarith + have hB0 : 0 < B := by linarith + have hlogB := Real.log_nonneg hB + have hlogA := Real.log_nonneg hA + have hpow : A ^ (1 / (d : ℝ)) ≤ Real.exp C := by + rw [Real.rpow_def_of_pos hA0, Real.exp_le_exp, ← div_eq_mul_one_div] + exact (div_le_iff₀ hd0).mpr hlog + have hlog₂ : Real.log A + Real.log B + d ≤ (C + 1) * (Real.log B + d) := by + nlinarith [mul_nonneg hC hlogB] + unfold curveBound + rw [Real.mul_rpow hA0.le hB0.le, Real.log_mul hA0.ne' hB0.ne'] + calc + _ ≤ (d : ℝ) ^ 3 * (Real.exp C * B ^ (1 / (d : ℝ))) * + ((C + 1) * (Real.log B + d)) := by gcongr + _ = _ := by ring + +/-- Scaling the radius by the projection box factor multiplies the curve bound by at most a +constant depending only on `n`: with `c = projectionLogConstant n`, for `d > 0` and `B ≥ 1`, +`curveBound d (projectionBoxFactor n d * B) ≤ exp c * (c + 1) * curveBound d B`. -/ +theorem projection_curveBound_scale (n : ℕ) {d : ℕ} (hd : 0 < d) + {B : ℝ} (hB : 1 ≤ B) : + curveBound d (projectionBoxFactor n d * B) ≤ + (Real.exp (projectionLogConstant n) * (projectionLogConstant n + 1)) * curveBound d B := + curveBound_scale_of_log_le hd (projectionBoxFactor_one_le n d) hB + (projectionLogConstant_nonneg n) (projectionBoxFactor_log_le n hd) + +end +end CCDN diff --git a/LeanPool/CCDN/Projection/ZeroDimensionalProjectiveCount.lean b/LeanPool/CCDN/Projection/ZeroDimensionalProjectiveCount.lean new file mode 100644 index 0000000000..d68a1f1a96 --- /dev/null +++ b/LeanPool/CCDN/Projection/ZeroDimensionalProjectiveCount.lean @@ -0,0 +1,86 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.Algebra.IntegerLinearForms +public import LeanPool.CCDN.Algebra.ProjectiveSection +public import LeanPool.CCDN.Height.ProjectiveBox +public import LeanPool.CCDN.Hilbert.FilteredHilbertPolynomial +public import LeanPool.CCDN.Hilbert.LinearProjectionFiltration +import LeanPool.CCDN.Algebra.ProjectiveFiniteFibers +import LeanPool.CCDN.Projection.BoundedDegreeNormalizationFiber +import LeanPool.CCDN.Projection.BoundedIntegerNormalization + +/-! The zero-dimensional case of projective dimension growth. A +one-parameter normalization sends every projective point to the unique +point of projective zero-space; its affine fiber bound controls +the whole source, independently of height. -/ + +@[expose] public section +namespace CCDN +noncomputable section +open MvPolynomial Matrix + +private theorem exists_integer_linear_normalization_of_le (m n : ℕ) (hmn : m + 1 ≤ n) + (I : Ideal (MvPolynomial (Fin n) ℚ)) [I.IsPrime] (d : ℕ) + (hdimdeg : HasProjectiveDimensionDegree I m d) : + ∃ a : Fin (m + 1) → Fin n → ℤ, + (projectionCoordinateMap I (fun i => integerLinearForm (a i))).IsIntegral ∧ + Function.Injective (projectionCoordinateMap I (fun i => integerLinearForm (a i))) := by + obtain ⟨k, rfl⟩ := Nat.exists_eq_add_of_le hmn + obtain ⟨a, _, hint, hi⟩ := exists_bounded_integer_normalization m k I d hdimdeg + exact ⟨a, hint, hi⟩ + +/-- If a prime ideal `I` in `n + 1` variables has Hilbert dimension one (projective dimension +zero) and degree `d`, its projective zero locus has at most `d ^ (n + 1)` rational points of any +height. -/ +theorem zero_dimensional_projective_count_le + {n : ℕ} (I : Ideal (MvPolynomial (Fin (n + 1)) ℚ)) [I.IsPrime] + (d : ℕ) (hdim : (filteredHilbertPolynomial I).natDegree = 1) + (hdegree : (filteredHilbertPolynomial I).leadingCoeff = d) (B : ℝ) : + projectiveCount (projectiveIdealZeroLocus I) B ≤ d ^ (n + 1) := by + classical + obtain ⟨a, hint, hi⟩ := exists_integer_linear_normalization_of_le 0 (n + 1) + (by omega) I d ⟨hdim, by simpa using hdegree⟩ + let L := (Matrix.map a (Int.castRingHom ℚ)).mulVecLin + have hLapply (x : Fin (n + 1) → ℚ) (i : Fin 1) : + L x i = eval x (integerLinearForm (a i)) := by + simp only [L, Matrix.mulVecLin_apply, Matrix.mulVec, dotProduct, + Int.coe_castRingHom, eval_linearForm] + rfl + have hfinite (z : Fin 1 → ℚ) (s : Finset (Fin (n + 1) → ℚ)) + (hs : ∀ x ∈ s, x ∈ idealZeroLocus I) (hz : ∀ x ∈ s, L x = z) : + s.card ≤ d ^ (n + 1) := by + refine (linear_normalization_fiber_card_le I (fun i => integerLinearForm (a i)) + (fun i => linearForm_totalDegree_le _) hi hint d ⟨hdim, by simpa using hdegree⟩ z s hs + ?_).trans (Nat.le_self_pow (Nat.succ_ne_zero n) d) + intro x hx i + rw [← hLapply, hz x hx] + let s := projectivePoints (projectiveIdealZeroLocus I) B + have hs (x) (hx : x ∈ s) : x ∈ projectiveIdealZeroLocus I := + (mem_projectivePoints.mp hx).2 + have hnon (x) (hx : x ∈ s) : L x.rep ≠ 0 := + projective_linear_image_ne_zero_of_finite_zero_fiber I L (d ^ (n + 1)) + (hfinite 0) x (hs x hx) + let y : ProjectivePoint 0 := + Projectivization.mk ℚ (fun _ : Fin 1 => 1) (by + intro h + exact one_ne_zero (congrFun h 0)) + apply projective_linear_fiber_card_le I L s hs hnon y _ (d ^ (n + 1)) (hfinite y.rep) + intro x hx + change Projectivization.mk ℚ (L x.rep) (hnon x hx) = + Projectivization.mk ℚ (fun _ : Fin 1 => 1) _ + rw [Projectivization.mk_eq_mk_iff'] + refine ⟨L x.rep 0, ?_⟩ + funext i + have hi0 : i = 0 := by apply Fin.ext; have := i.isLt; omega + simp [hi0] + +end +end CCDN diff --git a/LeanPool/CCDN/ProjectiveCurveBound.lean b/LeanPool/CCDN/ProjectiveCurveBound.lean new file mode 100644 index 0000000000..696e3445a3 --- /dev/null +++ b/LeanPool/CCDN/ProjectiveCurveBound.lean @@ -0,0 +1,33 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.Statements +import LeanPool.CCDN.Curve.ProjectiveCurveCount + +/-! CCDN Theorem 2 in every ambient dimension. Degree is the slope of +the homogeneous Hilbert function. The height and count use rational +projective points. All projection and plane-counting inputs are proved; the count with an +explicit constant is `projective_curve_count`, in `CCDN.Curve.ProjectiveCurveCount`. -/ + +@[expose] public section +namespace CCDN +noncomputable section +open MvPolynomial + +/-- CCDN (2020), Theorem 2: the bound `ProjectiveCurveBound`. -/ +theorem projectiveCurveBound : ProjectiveCurveBound := by + intro n _ + refine ⟨projectiveCurveConstant n, projectiveCurveConstant_pos n, ?_⟩ + intro I hI d hd hdegree B hB + have : I.IsPrime := hI + exact projective_curve_count I hd hdegree hB + +end +end CCDN diff --git a/LeanPool/CCDN/ProjectiveCurveLowerBound.lean b/LeanPool/CCDN/ProjectiveCurveLowerBound.lean new file mode 100644 index 0000000000..4ac296f3a7 --- /dev/null +++ b/LeanPool/CCDN/ProjectiveCurveLowerBound.lean @@ -0,0 +1,43 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.Algebra.ProjectiveSection +public import LeanPool.CCDN.Height.ProjectiveZeroLocus +import LeanPool.CCDN.AuxiliaryPolynomial.PlaneBadPrimes +import LeanPool.CCDN.LowerBound.ProjectivePlaneLowerBound +import LeanPool.CCDN.Plane.ProjectivePlaneBezout + +/-! CCDN Proposition 5 with its exact constant 1/5, for an absolutely +irreducible projective plane equation. The principal ideal is prime and its +projective Hilbert degree is the prescribed degree. -/ + +@[expose] public section +namespace CCDN +noncomputable section +open MvPolynomial + +/-- The lower-bound equation defines an integral homogeneous plane curve +whose degree is also certified by the projective Hilbert function. -/ +theorem projective_integral_curve_lower_bound (d : ℕ) (hd : 0 < d) : + ∃ F : MvPolynomial (Fin 3) ℚ, ∃ B : ℕ, 1 ≤ B ∧ + F.IsHomogeneous d ∧ F.totalDegree = d ∧ + AbsolutelyIrreducibleRat F ∧ + (Ideal.span {F}).IsPrime ∧ HasProjectiveCurveDegree (Ideal.span {F}) d ∧ + ((d : ℝ) ^ 2 / 5) * (B : ℝ) ^ (2 / (d : ℝ)) ≤ + (projectiveCount (projectiveIdealZeroLocus (Ideal.span {F})) B : ℝ) := by + obtain ⟨F, B, hB, hhom, hdeg, habs, hcount⟩ := projective_plane_lower_bound d hd + have hir : Irreducible F := + mvPolynomial_irreducible_of_field_map (algebraMap ℚ (AlgebraicClosure ℚ)) F habs + refine ⟨F, B, hB, hhom, hdeg, habs, Ideal.isPrime_span_singleton_of_prime hir.prime, + principal_plane_hasProjectiveCurveDegree F hhom hir.ne_zero, ?_⟩ + rwa [projectiveIdealZeroLocus_span_singleton F hhom] + +end +end CCDN diff --git a/LeanPool/CCDN/ProjectiveDimensionGrowth.lean b/LeanPool/CCDN/ProjectiveDimensionGrowth.lean new file mode 100644 index 0000000000..753b9715ea --- /dev/null +++ b/LeanPool/CCDN/ProjectiveDimensionGrowth.lean @@ -0,0 +1,156 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.Projection.ProjectiveProjectionGrowth +public import LeanPool.CCDN.Statements +import LeanPool.CCDN.Curve.ProjectiveHilbertCurveGrowth +import LeanPool.CCDN.Hilbert.EmptyProjectiveHilbertDimension +import LeanPool.CCDN.Hilbert.ProperHilbertDimension +import LeanPool.CCDN.Hypersurface.ProjectiveHypersurfaceDimensionGrowth +import LeanPool.CCDN.Hypersurface.ProjectiveVarietyDimensionGrowth +import LeanPool.CCDN.Projection.ZeroDimensionalProjectiveCount + +/-! CCDN Theorem 1 in the homogeneous-coordinate-ideal formulation: uniform projective +dimension growth for every rational prime projective variety of degree at least five. +The Hilbert polynomial determines the degree and dimension. Zero-dimensional and empty +loci are included. + +The bound is assembled once, in `projectiveGrowth_of_le`, for any degree exponent which +dominates the exponents of the zero-dimensional count, the curve bound, the hypersurface +bound and the projection bound. The degree exponent `2n² + 5n - 5` follows, and from it the +exponent `2n³`. -/ + +@[expose] public section +namespace CCDN +noncomputable section +open MvPolynomial + +/-- Projective dimension growth with degree exponent `E` implies it with any larger exponent +`E'`. -/ +theorem ProjectiveGrowth.mono {n E E' : ℕ} (h : ProjectiveGrowth n E) (hE : E ≤ E') : + ProjectiveGrowth n E' := by + obtain ⟨C, hC, hbound⟩ := h + refine ⟨C, hC, fun I hprime hI d hd hdegree B hB => + (hbound I hprime hI d hd hdegree B hB).trans ?_⟩ + have hd1 : (1 : ℝ) ≤ d := by exact_mod_cast (show 1 ≤ d by omega) + gcongr + +/-- Theorem 1 holds with any degree exponent `E` which dominates, in `ℙⁿ`, the exponent +`n + 1` of the zero-dimensional count, the exponent `3(n+1) + 4` of the curve bound, the +exponent `18 + 3(n-2)` of the hypersurface bound, and the exponent of the projection bound +for every dimension `m + 1 ≥ 2` and codimension `k ≥ 2`. -/ +theorem projectiveGrowth_of_le {n E : ℕ} (hpoint : 1 ≤ n → n + 1 ≤ E) + (hcurve : 2 ≤ n → 3 * (n + 1) + 4 ≤ E) + (hhyper : 3 ≤ n → 18 + 3 * (n - 2) ≤ E) + (hproj : ∀ m k, m + k + 1 = n → 1 ≤ m → 2 ≤ k → + projectionDegreeExponent m k ≤ E) : + ProjectiveGrowth n E := by + classical + have hlt (I : Ideal (MvPolynomial (Fin (n + 1)) ℚ)) (hI : I.IsPrime) (d : ℕ) (hd : 5 ≤ d) + (hdegree : (filteredHilbertPolynomial I).leadingCoeff = + (d : ℚ) / (filteredHilbertPolynomial I).natDegree.factorial) : + (filteredHilbertPolynomial I).natDegree < n + 1 := by + simpa only [Fintype.card_fin] using + hilbert_dimension_lt_ambient_of_degree_gt_one I hI.ne_top d (by omega) hdegree + have hempty (I : Ideal (MvPolynomial (Fin (n + 1)) ℚ)) + (h : (filteredHilbertPolynomial I).natDegree = 0) (B : ℝ) : + projectiveCount (projectiveIdealZeroLocus I) B = 0 := by + rw [projectiveIdealZeroLocus_eq_empty_of_hilbert_dimension_zero I h] + simp [projectiveCount, projectivePoints] + rcases Nat.eq_zero_or_pos n with rfl | hn + · refine ⟨1, one_pos, fun I hprime _ d hd hdegree B _ => ?_⟩ + rw [hempty I (by have := hlt I hprime d hd hdegree; omega), Nat.cast_zero] + positivity + choose Cp _ hp using projective_variety_dimension_growth + obtain ⟨T, hT⟩ := (((Set.finite_Iic n).prod (Set.finite_Iic n)).image + fun p : ℕ × ℕ => Cp p.1 p.2).bddAbove + obtain ⟨Cc, hCc, hc⟩ := projective_hilbert_curve_dimension_growth n hn + choose Ch hCh hh using fun n => (projective_hypersurface_dimension_growth n).ideal + let K : ℝ := max T 0 + Cc + Ch (n - 2) + 1 + have hTK : T ≤ K := by dsimp only [K]; linarith [le_max_left T 0, hCh (n - 2)] + have h1K : 1 ≤ K := by dsimp only [K]; linarith [le_max_right T 0, hCh (n - 2)] + have hCK : Cc ≤ K := by dsimp only [K]; linarith [le_max_right T 0, hCh (n - 2)] + have hhK : Ch (n - 2) ≤ K := by dsimp only [K]; linarith [le_max_right T 0] + refine ⟨K, by linarith, ?_⟩ + intro I hprime hI d hd hdegree B hB + have : I.IsPrime := hprime + -- Each dimension gives a bound with a constant at most `K` and an exponent at most `E`. + suffices h : ∃ C : ℝ, C ≤ K ∧ ∃ e ≤ E, + (projectiveCount (projectiveIdealZeroLocus I) B : ℝ) ≤ + C * (d : ℝ) ^ e * B ^ ((filteredHilbertPolynomial I).natDegree - 1) by + obtain ⟨C, hC, e, he, h⟩ := h + have hd1 : (1 : ℝ) ≤ d := by exact_mod_cast (show 1 ≤ d by omega) + refine h.trans (mul_le_mul_of_nonneg_right ?_ (by positivity)) + exact (mul_le_mul_of_nonneg_right hC (by positivity)).trans + (mul_le_mul_of_nonneg_left (pow_le_pow_right₀ hd1 he) (by linarith)) + have hdim := hlt I hprime d hd hdegree + obtain h0 | h1 | h2 | h3 : (filteredHilbertPolynomial I).natDegree = 0 ∨ + (filteredHilbertPolynomial I).natDegree = 1 ∨ + (filteredHilbertPolynomial I).natDegree = 2 ∨ + 3 ≤ (filteredHilbertPolynomial I).natDegree := by omega + · refine ⟨0, by linarith, 0, Nat.zero_le _, ?_⟩ + rw [hempty I h0, Nat.cast_zero, zero_mul, zero_mul] + · refine ⟨1, h1K, n + 1, hpoint hn, ?_⟩ + have hbound := zero_dimensional_projective_count_le I d h1 (by + simpa only [h1, Nat.factorial_one, Nat.cast_one, div_one] using hdegree) B + rw [h1, Nat.sub_self, pow_zero, mul_one, one_mul] + exact_mod_cast hbound + · refine ⟨Cc, hCK, 3 * (n + 1) + 4, hcurve (by omega), ?_⟩ + rw [h2, show 2 - 1 = 1 from rfl, pow_one] + exact hc I hprime hI d hd ⟨h2, by simpa only [h2] using hdegree⟩ B hB + obtain ⟨m, hm⟩ := Nat.exists_eq_add_of_le' h3 + obtain ⟨k, rfl⟩ : ∃ k, n = m + 1 + k + 1 := ⟨n - m - 2, by omega⟩ + rcases Nat.lt_or_ge k 2 with hk | hk + · -- A hypersurface. + obtain rfl : k = 1 := by omega + refine ⟨Ch (m + 1), by simpa using hhK, _, hhyper (by omega), ?_⟩ + rw [hm, show m + 1 + 1 + 1 - 2 = m + 1 by omega] + exact hh (m + 1) I hprime hI d hd ⟨hm, by simpa only [hm] using hdegree⟩ B hB + · -- Codimension at least two: the projection bound. + have hmk : Cp (m + 1) k ≤ T := hT (Set.mem_image_of_mem (fun p : ℕ × ℕ => Cp p.1 p.2) + (show (m + 1, k) ∈ Set.Iic _ ×ˢ Set.Iic _ from + ⟨Set.mem_Iic.mpr (by omega), Set.mem_Iic.mpr (by omega)⟩)) + refine ⟨Cp (m + 1) k, hmk.trans hTK, _, + hproj (m + 1) k rfl (by omega) hk, ?_⟩ + rw [hm] + exact hp (m + 1) k I hprime hI d hd ⟨hm, by simpa only [hm] using hdegree⟩ B hB + +/-- In `ℙⁿ` with `n = m + k + 1`, the projection exponent has the closed form +`2mn + 5m + 3n + 21`. For codimension `k ≥ 2` it is at most its value `2n² + 2n + 6` at +`k = 2`. -/ +lemma projectiveProjectionDegreeExponent_le {m k n : ℕ} (h : m + k + 1 = n) (hk : 2 ≤ k) : + projectionDegreeExponent m k ≤ 2 * n ^ 2 + 2 * n + 6 := by + subst h + unfold projectionDegreeExponent + nlinarith + +/-- The degree exponent `2n² + 5n - 5` of Theorem 1 in `ℙⁿ`. Its values `2` and `13` for +`n = 1, 2` are the exponents of the zero-dimensional count and of the curve bound. For +`n ≥ 4` it exceeds the projection exponent `2n² + 2n + 6` by `3n - 11`. -/ +def projectiveGrowthExponent (n : ℕ) : ℕ := 2 * n ^ 2 + 5 * n - 5 + +/-- CCDN Theorem 1 with the degree exponent `2n² + 5n - 5` in every ambient projective +dimension `n`. -/ +theorem projective_dimension_growth (n : ℕ) : + ProjectiveGrowth n (projectiveGrowthExponent n) := by + unfold projectiveGrowthExponent + refine projectiveGrowth_of_le (fun hn => ?_) (fun hn => ?_) (fun hn => ?_) + fun m k h hm hk => ?_ + · have : 1 ≤ n ^ 2 := Nat.one_le_pow _ _ hn + omega + · have : 4 ≤ n ^ 2 := by nlinarith + omega + · have : 9 ≤ n ^ 2 := by nlinarith + omega + · have := projectiveProjectionDegreeExponent_le h hk + omega + +end +end CCDN diff --git a/LeanPool/CCDN/Statements.lean b/LeanPool/CCDN/Statements.lean new file mode 100644 index 0000000000..c78e4b93b2 --- /dev/null +++ b/LeanPool/CCDN/Statements.lean @@ -0,0 +1,131 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.Algebra.ProjectiveSection +public import LeanPool.CCDN.Height.ProjectiveZeroLocus +public import LeanPool.CCDN.Hilbert.AffineCurveDegree +public import LeanPool.CCDN.Hilbert.FilteredHilbertPolynomial + +/-! +# Statements of the main results + +The counting statements of Theorems 1–4, as propositions, together with the plane case of +Theorem 3 and the hypersurface case of Theorem 1 that the proofs use as intermediate targets. +This module imports only the foundation directories (the counting functions of `Height/`, the +zero loci and degree predicates of `Algebra/`, and the Hilbert-function degrees of `Hilbert/`), +so each statement can be read without the proof infrastructure. + +* `PlaneCurveBound`: Theorem 3 for plane curves, proved in `CCDN.Plane.CurveCount`; +* `AffineCurveBound`: Theorem 3, proved in `CCDN.AffineCurveBound`; +* `ProjectiveCurveBound`: Theorem 2, proved in `CCDN.ProjectiveCurveBound`; +* `AffineHypersurfaceGrowth` and `RationalAffineHypersurfaceGrowth`: Theorem 4 with a given + degree exponent, proved in `CCDN.AffineHypersurfaceDimensionGrowth`; +* `ProjectiveHypersurfaceGrowth`: Theorem 1 for hypersurfaces, proved in + `CCDN.Hypersurface.ProjectiveHypersurfaceDimensionGrowth`; +* `ProjectiveGrowth`: Theorem 1 with a given degree exponent, proved in + `CCDN.ProjectiveDimensionGrowth`. + +Theorem 6 and Proposition 5 are stated directly by their endpoint theorems +`classGroup_two_torsion_bound` and `projective_integral_curve_lower_bound`. +-/ + +@[expose] public section + +namespace CCDN + +noncomputable section + +/-! ### Theorem 3: affine curves -/ + +/-- The bound `d³ B^(1/d) (log B + d)` of Theorem 3. -/ +noncomputable def curveBound (d : ℕ) (B : ℝ) : ℝ := + (d : ℝ) ^ 3 * B ^ (1 / (d : ℝ)) * (Real.log B + d) + +/-- The `n = 2` case of CCDN (2020), Theorem 3, expressed using rational defining +polynomials. The proof (`planeCurveBound`, in +`CCDN.Plane.CurveCount`) includes denominator clearing and content removal. +Irreducibility is over `ℚ`, not absolute irreducibility. The one constant is uniform in the +polynomial, its degree, its coefficients, and the real radius. -/ +def PlaneCurveBound : Prop := + ∃ C : ℝ, 0 < C ∧ ∀ f : MvPolynomial (Fin 2) ℚ, Irreducible f → + 0 < f.totalDegree → ∀ B : ℝ, 1 ≤ B → + (count (zeroLocus f) B : ℝ) ≤ C * curveBound f.totalDegree B + +/-- CCDN (2020), Theorem 3, using the affine Hilbert function to specify degree. +The constant is chosen before the ideal, degree, and real box radius. -/ +def AffineCurveBound : Prop := + ∀ n : ℕ, 2 ≤ n → ∃ C : ℝ, 0 < C ∧ + ∀ I : Ideal (CoordinatePolynomial n), I.IsPrime → + ∀ d : ℕ, 0 < d → HasAffineCurveDegree I d → + ∀ B : ℝ, 1 ≤ B → (count (idealZeroLocus I) B : ℝ) ≤ C * curveBound d B + +/-! ### Theorem 2: projective curves -/ + +/-- The bound `d⁴ B^(2/d)` of Theorem 2. -/ +def projectiveCurveEstimate (d : ℕ) (B : ℝ) : ℝ := + (d : ℝ) ^ 4 * B ^ (2 / (d : ℝ)) + +/-- CCDN (2020), Theorem 2, with projective degree specified by the +homogeneous Hilbert function. The constant precedes the ideal, degree and radius. -/ +def ProjectiveCurveBound : Prop := + ∀ n : ℕ, 2 ≤ n → ∃ C : ℝ, 0 < C ∧ + ∀ I : Ideal (CoordinatePolynomial (n + 1)), I.IsPrime → + ∀ d : ℕ, 0 < d → HasProjectiveCurveDegree I d → + ∀ B : ℝ, 1 ≤ B → + (projectiveCount (projectiveIdealZeroLocus I) B : ℝ) ≤ C * projectiveCurveEstimate d B + +/-! ### Theorem 4: affine hypersurfaces -/ + +/-- Affine dimension growth with degree exponent `e` for integer equations in `n + 3` +variables of degree at least five whose top-degree form is absolutely irreducible. The +constant depends only on `n` and `e`. -/ +def AffineHypersurfaceGrowth (n e : ℕ) : Prop := + ∃ C : ℝ, 0 < C ∧ + ∀ f : MvPolynomial (Fin (n + 3)) ℤ, HasAbsIrreducibleTopForm f → + 5 ≤ f.totalDegree → ∀ B : ℝ, 1 ≤ B → + (count (zeroLocus (MvPolynomial.map (Int.castRingHom ℚ) f)) B : ℝ) ≤ + C * (f.totalDegree : ℝ) ^ e * B ^ (n + 1) + +/-- The rational-equation form of `AffineHypersurfaceGrowth`. -/ +def RationalAffineHypersurfaceGrowth (n e : ℕ) : Prop := + ∃ C : ℝ, 0 < C ∧ + ∀ f : MvPolynomial (Fin (n + 3)) ℚ, HasAbsIrreducibleTopFormRat f → + 5 ≤ f.totalDegree → ∀ B : ℝ, 1 ≤ B → + (count (zeroLocus f) B : ℝ) ≤ C * (f.totalDegree : ℝ) ^ e * B ^ (n + 1) + +/-! ### Theorem 1: projective varieties -/ + +/-- Dimension growth with degree exponent `e` for rationally irreducible projective +hypersurfaces of degree at least five in `ℙ^(n+2)`. The height exponent is the +hypersurface dimension `n + 1`. -/ +def ProjectiveHypersurfaceGrowth (n e : ℕ) : Prop := + ∃ C : ℝ, 0 < C ∧ + ∀ f : MvPolynomial (Fin (n + 3)) ℚ, f.IsHomogeneous f.totalDegree → Irreducible f → + 5 ≤ f.totalDegree → ∀ B : ℝ, 1 ≤ B → + (projectiveCount (projectiveZeroLocus f) B : ℝ) ≤ + C * (f.totalDegree : ℝ) ^ e * B ^ (n + 1) + +/-- CCDN Theorem 1 in `ℙⁿ` with degree exponent `E`. A homogeneous prime ideal whose +Hilbert polynomial has degree `r` and leading coefficient `d / r!`, with `d ≥ 5`, has at +most `C d^E B^(r-1)` rational points of height at most `B`. The constant depends only on +`n` and `E`. -/ +def ProjectiveGrowth (n E : ℕ) : Prop := + ∃ C : ℝ, 0 < C ∧ + ∀ I : Ideal (MvPolynomial (Fin (n + 1)) ℚ), I.IsPrime → IsHomogeneousIdeal I → + ∀ d : ℕ, 5 ≤ d → + (filteredHilbertPolynomial I).leadingCoeff = + (d : ℚ) / (filteredHilbertPolynomial I).natDegree.factorial → + ∀ B : ℝ, 1 ≤ B → + (projectiveCount (projectiveIdealZeroLocus I) B : ℝ) ≤ + C * (d : ℝ) ^ E * B ^ ((filteredHilbertPolynomial I).natDegree - 1) + +end + +end CCDN diff --git a/LeanPool/CCDN/Surface/AffineSurfaceCountLargeDegree.lean b/LeanPool/CCDN/Surface/AffineSurfaceCountLargeDegree.lean new file mode 100644 index 0000000000..6f0f41a525 --- /dev/null +++ b/LeanPool/CCDN/Surface/AffineSurfaceCountLargeDegree.lean @@ -0,0 +1,114 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.Algebra.PolynomialPredicates +public import LeanPool.CCDN.Height.Box +public import Mathlib.Analysis.SpecialFunctions.Pow.Real +import LeanPool.CCDN.Surface.DegreeOneCount +import LeanPool.CCDN.Surface.TopFormSurfaceNonlinear + +/-! The corrected large-degree surface bound. The auxiliary section's +nonlinear curves and its degree-one components together account +for every bounded integer zero of the original surface. -/ + +@[expose] public section +namespace CCDN +noncomputable section +open MvPolynomial + +/-- The degree threshold of the large-degree surface bound `affine_surface_count_large_degree`. It +comes from `surface_inverse_sqrt_le_quarter`: `1 / √d ≤ 1 / 4` once `16 ≤ d`. -/ +def topFormSurfaceThreshold : ℕ := 16 + +/-- Corrected CCDN Proposition 4.3.4 for degrees at least sixteen and an +absolutely irreducible highest homogeneous part. The positive constant is +chosen before the integer polynomial and the real box radius. -/ +theorem affine_surface_count_large_degree : + ∃ C : ℝ, 0 < C ∧ + ∀ f : MvPolynomial (Fin 3) ℤ, + HasAbsIrreducibleTopForm f → + topFormSurfaceThreshold ≤ f.totalDegree → ∀ B : ℝ, 1 ≤ B → + (count (zeroLocus (map (Int.castRingHom ℚ) f)) B : ℝ) ≤ + C * (f.totalDegree : ℝ) ^ 18 * B := by + classical + obtain ⟨A, hA, hnonlinear⟩ := top_form_surface_nonlinear_count + obtain ⟨C, hC, hlines⟩ := surface_degree_one_component_contribution + let K := topFormAuxiliaryConstant 1 + have hK : 0 < K := topFormAuxiliaryConstant_pos 1 + refine ⟨A + C + K, by positivity, ?_⟩ + intro f hir hd B hB + unfold topFormSurfaceThreshold at hd + obtain ⟨S, δ, hcomponents, _, hdegree, _, houtside⟩ := + hnonlinear f hir hd B hB + let T := S.filter (fun P => δ P = 1) + let L := ⋃ P ∈ T, idealZeroLocus P + have hTcurve (P : Ideal (CoordinatePolynomial 3)) (hP : P ∈ T) : + P.IsPrime ∧ P.height = 2 ∧ HasAffineCurveDegree P 1 := by + obtain ⟨hPS, hone⟩ := Finset.mem_filter.mp hP + exact ⟨(hcomponents P hPS).1.isPrime, (hcomponents P hPS).1.height_eq, + by simpa only [hone] using (hcomponents P hPS).1.degree⟩ + have hTsurface (P : Ideal (CoordinatePolynomial 3)) (hP : P ∈ T) : + map (Int.castRingHom ℚ) f ∈ P := + (hcomponents P (Finset.mem_filter.mp hP).1).1.mem + have hlinecount : (count L B : ℝ) ≤ C * (f.totalDegree : ℝ) ^ 6 * B + T.card := + hlines f hir (by omega) B (by linarith) T hTcurve hTsurface + have hcard : T.card ≤ ∑ P ∈ S, δ P := by + calc + T.card ≤ S.card := Finset.card_le_card (Finset.filter_subset _ _) + _ = ∑ _P ∈ S, 1 := by simp + _ ≤ ∑ P ∈ S, δ P := by + apply Finset.sum_le_sum + intro P hP + exact (hcomponents P hP).2 + have hcardR : (T.card : ℝ) ≤ K * (f.totalDegree : ℝ) ^ (9 / 2 : ℝ) * + B ^ (1 / (f.totalDegree : ℝ) ^ (1 / 2 : ℝ)) := by + have hh : (T.card : ℝ) ≤ ((∑ P ∈ S, δ P : ℕ) : ℝ) := by + exact_mod_cast hcard + exact hh.trans hdegree + have hd1 : (1 : ℝ) ≤ f.totalDegree := by exact_mod_cast (show 1 ≤ f.totalDegree by omega) + have hroot : (1 : ℝ) ≤ (f.totalDegree : ℝ) ^ (1 / 2 : ℝ) := + Real.one_le_rpow hd1 (by norm_num) + have hexp : 1 / (f.totalDegree : ℝ) ^ (1 / 2 : ℝ) ≤ 1 := + (div_le_one (by positivity)).2 hroot + have hBpower : B ^ (1 / (f.totalDegree : ℝ) ^ (1 / 2 : ℝ)) ≤ B := by + simpa only [Real.rpow_one] using Real.rpow_le_rpow_of_exponent_le hB hexp + have hdpower : (f.totalDegree : ℝ) ^ (9 / 2 : ℝ) ≤ (f.totalDegree : ℝ) ^ 18 := by + simpa only [Real.rpow_ofNat] using + Real.rpow_le_rpow_of_exponent_le hd1 (show (9 / 2 : ℝ) ≤ 18 by norm_num) + have hcardfinal : (T.card : ℝ) ≤ K * (f.totalDegree : ℝ) ^ 18 * B := by + apply hcardR.trans + gcongr + have hdsix : (f.totalDegree : ℝ) ^ 6 ≤ (f.totalDegree : ℝ) ^ 18 := + pow_le_pow_right₀ hd1 (by omega) + have hlinefinal : (count L B : ℝ) ≤ (C + K) * (f.totalDegree : ℝ) ^ 18 * B := by + calc + _ ≤ C * (f.totalDegree : ℝ) ^ 6 * B + T.card := hlinecount + _ ≤ C * (f.totalDegree : ℝ) ^ 18 * B + + K * (f.totalDegree : ℝ) ^ 18 * B := by gcongr + _ = _ := by ring + have hcover : zeroLocus (map (Int.castRingHom ℚ) f) ⊆ + (zeroLocus (map (Int.castRingHom ℚ) f) \ L) ∪ L := by + intro x hx + by_cases hxL : x ∈ L + · exact Or.inr hxL + · exact Or.inl ⟨hx, hxL⟩ + have htotal : count (zeroLocus (map (Int.castRingHom ℚ) f)) B ≤ + count (zeroLocus (map (Int.castRingHom ℚ) f) \ L) B + count L B := + (count_mono hcover le_rfl).trans (count_union_le _ _ B) + calc + _ ≤ (count (zeroLocus (map (Int.castRingHom ℚ) f) \ L) B : ℝ) + count L B := by + exact_mod_cast htotal + _ ≤ A * (f.totalDegree : ℝ) ^ 18 * B + + (C + K) * (f.totalDegree : ℝ) ^ 18 * B := + add_le_add houtside hlinefinal + _ = _ := by ring + +end +end CCDN diff --git a/LeanPool/CCDN/Surface/AffineSurfaceDimensionGrowth.lean b/LeanPool/CCDN/Surface/AffineSurfaceDimensionGrowth.lean new file mode 100644 index 0000000000..60e7b3de35 --- /dev/null +++ b/LeanPool/CCDN/Surface/AffineSurfaceDimensionGrowth.lean @@ -0,0 +1,108 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.Algebra.PolynomialPredicates +public import LeanPool.CCDN.Height.Box +public import Mathlib.Analysis.SpecialFunctions.Pow.Real +import LeanPool.CCDN.Surface.AffineSurfaceCountLargeDegree +import LeanPool.CCDN.Surface.PrimitiveSurfaceFixedDegree +import LeanPool.CCDN.Surface.TopFormBadPrimes + +/-! +The affine surface bound in every degree at least five. The finitely many +degrees below the large-degree threshold are absorbed in one constant. + +Removing integer content from the fixed-degree surface estimate. +The resulting constant is uniform in all integer coefficients. +-/ + +@[expose] public section +namespace CCDN +noncomputable section +open MvPolynomial + +/-- Every fixed degree at least five has a coefficient-uniform linear +integer-point bound when the highest homogeneous part is absolutely +irreducible. The equation need not be primitive. -/ +private theorem top_form_surface_fixed_degree_linear_bound (d : ℕ) (hd : 5 ≤ d) : + ∃ C : ℝ, 0 < C ∧ + ∀ f : MvPolynomial (Fin 3) ℤ, + HasAbsIrreducibleTopForm f → + f.totalDegree = d → ∀ B : ℝ, 1 ≤ B → + (count (zeroLocus (map (Int.castRingHom ℚ) f)) B : ℝ) ≤ C * B := by + obtain ⟨C, hC, hbound⟩ := primitive_surface_fixed_degree_linear_bound d hd + refine ⟨C, hC, ?_⟩ + intro f hir hfd B hB + have hf0 : f ≠ 0 := by intro hz; exact hir.ne_zero (by simp [hz]) + obtain ⟨c, u, hc, hprim, he⟩ := exists_primitive_integer_factor f hf0 + obtain ⟨hudeg, hutop⟩ := absolute_top_irreducible_of_integer_scalar_factor hc he hir + have hzero : zeroLocus (map (Int.castRingHom ℚ) f) = + zeroLocus (map (Int.castRingHom ℚ) u) := by + ext x + change eval x (map (Int.castRingHom ℚ) f) = 0 ↔ + eval x (map (Int.castRingHom ℚ) u) = 0 + rw [he, map_mul, map_C, eval_mul, eval_C] + exact mul_eq_zero_iff_left (show (c : ℚ) ≠ 0 by exact_mod_cast hc) + rw [hzero] + exact hbound u hprim hutop (hudeg.trans hfd) B hB + +end +end CCDN + +namespace CCDN +noncomputable section +open MvPolynomial + +/-- The fixed-degree surface bounds for the degrees `5 ≤ d < t` hold with one constant. -/ +private theorem top_form_surface_bounded_degree_linear_bound (t : ℕ) : + ∃ C : ℝ, 0 < C ∧ + ∀ f : MvPolynomial (Fin 3) ℤ, + HasAbsIrreducibleTopForm f → + 5 ≤ f.totalDegree → f.totalDegree < t → ∀ B : ℝ, 1 ≤ B → + (count (zeroLocus (map (Int.castRingHom ℚ) f)) B : ℝ) ≤ C * B := by + induction t with + | zero => exact ⟨1, one_pos, fun _ _ _ ht => absurd ht (Nat.not_lt_zero _)⟩ + | succ t ih => + obtain ⟨C, hC, hbound⟩ := ih + by_cases ht : 5 ≤ t + · obtain ⟨C', hC', hbound'⟩ := top_form_surface_fixed_degree_linear_bound t ht + refine ⟨C + C', by positivity, fun f hir hd hlt B hB => ?_⟩ + have hB0 : 0 ≤ B := by linarith + rcases Nat.lt_succ_iff_lt_or_eq.mp hlt with hlt | heq + · exact (hbound f hir hd hlt B hB).trans (by gcongr; linarith) + · exact (hbound' f hir heq B hB).trans (by gcongr; linarith) + · exact ⟨C, hC, fun f hir hd hlt => hbound f hir hd (by omega)⟩ + +/-- Corrected CCDN Proposition 4.3.4 in every degree at least five: the surface bound +`C d^18 B` for integer equations in three variables with absolutely irreducible top-degree +form. -/ +theorem affine_surface_dimension_growth : + ∃ C : ℝ, 0 < C ∧ + ∀ f : MvPolynomial (Fin 3) ℤ, + HasAbsIrreducibleTopForm f → + 5 ≤ f.totalDegree → ∀ B : ℝ, 1 ≤ B → + (count (zeroLocus (map (Int.castRingHom ℚ) f)) B : ℝ) ≤ + C * (f.totalDegree : ℝ) ^ 18 * B := by + obtain ⟨Cbig, hCbig, hbig⟩ := affine_surface_count_large_degree + obtain ⟨Csmall, hCsmall, hsmall⟩ := + top_form_surface_bounded_degree_linear_bound topFormSurfaceThreshold + refine ⟨Cbig + Csmall, by positivity, fun f hir hd B hB => ?_⟩ + have hB0 : 0 ≤ B := by linarith + have hdpow : 1 ≤ (f.totalDegree : ℝ) ^ 18 := + one_le_pow₀ (by exact_mod_cast (show 1 ≤ f.totalDegree by omega)) + by_cases hlarge : topFormSurfaceThreshold ≤ f.totalDegree + · exact (hbig f hir hlarge B hB).trans (by gcongr; linarith) + · calc + _ ≤ Csmall * B := hsmall f hir hd (by omega) B hB + _ ≤ (Cbig + Csmall) * 1 * B := by gcongr; linarith + _ ≤ (Cbig + Csmall) * (f.totalDegree : ℝ) ^ 18 * B := by gcongr + +end +end CCDN diff --git a/LeanPool/CCDN/Surface/AuxiliaryExponent.lean b/LeanPool/CCDN/Surface/AuxiliaryExponent.lean new file mode 100644 index 0000000000..27b8fa9aea --- /dev/null +++ b/LeanPool/CCDN/Surface/AuxiliaryExponent.lean @@ -0,0 +1,241 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.Statements +import LeanPool.CCDN.ForMathlib.Log + +/-! The numerical component of the corrected surface estimate. The auxiliary + degree contributes a further factor d to the intersection degree, giving d^18 + after taking fourth powers. -/ + +@[expose] public section +namespace CCDN +noncomputable section + +/-- For `d ≥ 16`, the exponent `1 / √d` is at most `1 / 4`. -/ +lemma surface_inverse_sqrt_le_quarter {d : ℕ} (hd : 16 ≤ d) : + 1 / Real.sqrt (d : ℝ) ≤ (1 / 4 : ℝ) := by + have hs : (4 : ℝ) ≤ Real.sqrt (d : ℝ) := + Real.le_sqrt_of_sq_le (by exact_mod_cast hd) + exact one_div_le_one_div_of_le (by norm_num) hs + +/-- The large-component fourth moment has exactly the corrected degree + factor d^18, uniformly for every surface degree at least sixteen. -/ +private theorem surface_degree_budget_fourth {d : ℕ} (hd : 16 ≤ d) + {C B D : ℝ} (_hC : 0 ≤ C) (hB : 1 ≤ B) (hD0 : 0 ≤ D) + (hD : D ≤ C * (d : ℝ) ^ (9 / 2 : ℝ) * B ^ (1 / Real.sqrt d)) : + D ^ 4 ≤ C ^ 4 * (d : ℝ) ^ 18 * B := by + have hd0 : (0 : ℝ) < d := by exact_mod_cast (show 0 < d by omega) + have hB0 : 0 < B := by linarith + have hpow := pow_le_pow_left₀ hD0 hD 4 + rw [mul_pow, mul_pow, ← Real.rpow_mul_natCast hd0.le (9 / 2 : ℝ) 4, + ← Real.rpow_mul_natCast hB0.le (1 / Real.sqrt d) 4] at hpow + norm_num only [show (9 / 2 : ℝ) * (4 : ℕ) = 18 by norm_num, Real.rpow_ofNat] at hpow + have hexp : (1 / Real.sqrt (d : ℝ)) * (4 : ℕ) ≤ (1 : ℝ) := by + have hh := surface_inverse_sqrt_le_quarter hd + norm_num only [Nat.cast_ofNat] + linarith + have hbox : B ^ ((1 / Real.sqrt (d : ℝ)) * (4 : ℕ)) ≤ B := by + simpa only [Real.rpow_one] using Real.rpow_le_rpow_of_exponent_le hB hexp + exact hpow.trans (mul_le_mul_of_nonneg_left hbox (by positivity)) + +private lemma small_degree_rpow_weight {δ : ℕ} (hδ : 2 ≤ δ) {B : ℝ} + (hB : 1 ≤ B) (hδB : (δ : ℝ) ≤ Real.log B) : + (δ : ℝ) ^ 3 * B ^ (1 / (δ : ℝ)) ≤ + (48 * Real.exp 1) * B ^ (1 / 2 : ℝ) := by + have hδ2 : (2 : ℝ) ≤ δ := by exact_mod_cast hδ + have hδ0 : (0 : ℝ) < δ := by linarith + have hB0 : 0 < B := by linarith + have hpoly : (δ : ℝ) ^ 3 ≤ 48 * Real.exp ((δ : ℝ) / 2) := by + have hh := Real.pow_div_factorial_le_exp ((δ : ℝ) / 2) + (show 0 ≤ (δ : ℝ) / 2 by positivity) 3 + norm_num at hh + nlinarith + have hexp : (δ : ℝ) / 2 + Real.log B / δ ≤ 1 + Real.log B / 2 := by + have hh := mul_nonneg (sub_nonneg.mpr hδB) (sub_nonneg.mpr hδ2) + have hc := div_mul_cancel₀ (Real.log B) hδ0.ne' + nlinarith + calc + _ ≤ (48 * Real.exp ((δ : ℝ) / 2)) * B ^ (1 / (δ : ℝ)) := + mul_le_mul_of_nonneg_right hpoly (by positivity) + _ = 48 * Real.exp ((δ : ℝ) / 2 + Real.log B / δ) := by + rw [Real.rpow_def_of_pos hB0, Real.exp_add, mul_assoc] + congr 2 + congr 1 + ring + _ ≤ 48 * Real.exp (1 + Real.log B / 2) := by + exact mul_le_mul_of_nonneg_left (Real.exp_le_exp.mpr hexp) (by norm_num) + _ = _ := by + rw [Real.exp_add, Real.rpow_def_of_pos hB0, mul_assoc] + congr 2 + congr 1 + ring + +/-- For curve degrees between two and log B, the degree factors can be + absorbed uniformly into a square-root box bound. -/ +private theorem curveBound_small_degree_uniform {δ : ℕ} (hδ : 2 ≤ δ) {B : ℝ} + (hB : 1 ≤ B) (hδB : (δ : ℝ) ≤ Real.log B) : + curveBound δ B ≤ (96 * Real.exp 1) * B ^ (1 / 2 : ℝ) * (Real.log B + 1) := by + have hlog := Real.log_nonneg hB + have hh := mul_le_mul (small_degree_rpow_weight hδ hB hδB) + (show Real.log B + δ ≤ 2 * (Real.log B + 1) by linarith) + (show 0 ≤ Real.log B + δ by positivity) (by positivity) + unfold curveBound + convert hh using 1 + ring + +/-- The logarithm in the total contribution of the small-degree components + costs only a quarter power of the box size. -/ +private lemma surface_small_component_weight {d : ℕ} (hd : 16 ≤ d) + {B : ℝ} (hB : 1 ≤ B) : + B ^ (1 / Real.sqrt (d : ℝ)) * + (B ^ (1 / 2 : ℝ) * (Real.log B + 1)) ≤ 5 * B := by + have hB0 : 0 < B := by linarith + have hlog : B ^ (1 / 2 : ℝ) * (Real.log B + 1) ≤ + 5 * B ^ (3 / 4 : ℝ) := by + have hh := Real.rpow_half_mul_log_add_le (ε := (1 / 4 : ℝ)) (D := 1) + hB (by norm_num) (by norm_num) + norm_num at hh ⊢ + exact hh + have hexp : 1 / Real.sqrt (d : ℝ) + (3 / 4 : ℝ) ≤ 1 := by + have := surface_inverse_sqrt_le_quarter hd + linarith + calc + _ ≤ B ^ (1 / Real.sqrt (d : ℝ)) * (5 * B ^ (3 / 4 : ℝ)) := + mul_le_mul_of_nonneg_left hlog (by positivity) + _ = 5 * B ^ (1 / Real.sqrt (d : ℝ) + (3 / 4 : ℝ)) := by + rw [Real.rpow_add hB0] + ring + _ ≤ 5 * B := by + simpa only [Real.rpow_one] using + mul_le_mul_of_nonneg_left (Real.rpow_le_rpow_of_exponent_le hB hexp) + (show (0 : ℝ) ≤ 5 by norm_num) + +/-- A component count bounded by the intersection degree has uniformly + bounded small-degree contribution, with the same d^18 factor. -/ +private theorem surface_degree_budget_small_components {d : ℕ} (hd : 16 ≤ d) + {C B D : ℝ} (hC : 0 ≤ C) (hB : 1 ≤ B) + (hD : D ≤ C * (d : ℝ) ^ (9 / 2 : ℝ) * B ^ (1 / Real.sqrt d)) : + D * ((96 * Real.exp 1) * B ^ (1 / 2 : ℝ) * (Real.log B + 1)) ≤ + (480 * Real.exp 1) * C * (d : ℝ) ^ 18 * B := by + have hB0 : 0 < B := by linarith + have hlog := Real.log_nonneg hB + have hd1 : (1 : ℝ) ≤ d := by exact_mod_cast (show 1 ≤ d by omega) + have hdp : (d : ℝ) ^ (9 / 2 : ℝ) ≤ (d : ℝ) ^ 18 := by + simpa only [Real.rpow_ofNat] using + Real.rpow_le_rpow_of_exponent_le hd1 (show (9 / 2 : ℝ) ≤ 18 by norm_num) + calc + _ ≤ (C * (d : ℝ) ^ (9 / 2 : ℝ) * B ^ (1 / Real.sqrt d)) * + ((96 * Real.exp 1) * B ^ (1 / 2 : ℝ) * (Real.log B + 1)) := + mul_le_mul_of_nonneg_right hD (by positivity) + _ = (96 * Real.exp 1) * C * (d : ℝ) ^ (9 / 2 : ℝ) * + (B ^ (1 / Real.sqrt d) * (B ^ (1 / 2 : ℝ) * (Real.log B + 1))) := by ring + _ ≤ (96 * Real.exp 1) * C * (d : ℝ) ^ (9 / 2 : ℝ) * (5 * B) := + mul_le_mul_of_nonneg_left (surface_small_component_weight hd hB) (by positivity) + _ = (480 * Real.exp 1) * C * (d : ℝ) ^ (9 / 2 : ℝ) * B := by ring + _ ≤ _ := by gcongr + +/-- The sum of fourth powers of nonnegative component degrees is bounded + by the fourth power of their total degree. -/ +private lemma sum_fourth_powers_le_fourth_power_sum {ι : Type*} + (s : Finset ι) (a : ι → ℝ) (ha : ∀ i ∈ s, 0 ≤ a i) : + ∑ i ∈ s, (a i) ^ 4 ≤ (∑ i ∈ s, a i) ^ 4 := by + calc + _ = ∑ i ∈ s, a i * (a i) ^ 3 := by + apply Finset.sum_congr rfl + intro i hi + ring + _ ≤ ∑ i ∈ s, a i * (∑ j ∈ s, a j) ^ 3 := by + apply Finset.sum_le_sum + intro i hi + apply mul_le_mul_of_nonneg_left _ (ha i hi) + exact pow_le_pow_left₀ (ha i hi) (Finset.single_le_sum ha hi) 3 + _ = (∑ i ∈ s, a i) ^ 4 := by rw [← Finset.sum_mul]; ring + +/-- Above the logarithmic degree threshold, the curve bound is controlled + by the fourth power of the curve degree. -/ +private theorem curveBound_large_degree {δ : ℕ} (hδ : 0 < δ) {B : ℝ} + (hB : 1 ≤ B) (hBδ : Real.log B ≤ (δ : ℝ)) : + curveBound δ B ≤ (2 * Real.exp 1) * (δ : ℝ) ^ 4 := by + have hδ0 : (0 : ℝ) < δ := by exact_mod_cast hδ + have hB0 : 0 < B := by linarith + have hlog := Real.log_nonneg hB + have hpow : B ^ (1 / (δ : ℝ)) ≤ Real.exp 1 := by + rw [Real.rpow_def_of_pos hB0] + apply Real.exp_le_exp.mpr + simpa only [mul_one_div] using (div_le_one hδ0).mpr hBδ + have hh := mul_le_mul + (mul_le_mul_of_nonneg_left hpow (show 0 ≤ (δ : ℝ) ^ 3 by positivity)) + (show Real.log B + δ ≤ 2 * δ by linarith) + (show 0 ≤ Real.log B + δ by positivity) + (show 0 ≤ (δ : ℝ) ^ 3 * Real.exp 1 by positivity) + unfold curveBound + convert hh using 1 + ring + +/-- Both degree ranges can be summed using their common total-degree + budget. Every component here has degree at least two. -/ +private theorem sum_curveBound_le_degree_budget {ι : Type*} (s : Finset ι) (δ : ι → ℕ) + {B D : ℝ} (hB : 1 ≤ B) (hδ : ∀ i ∈ s, 2 ≤ δ i) + (hD : (∑ i ∈ s, (δ i : ℝ)) ≤ D) : + ∑ i ∈ s, curveBound (δ i) B ≤ + D * ((96 * Real.exp 1) * B ^ (1 / 2 : ℝ) * (Real.log B + 1)) + + (2 * Real.exp 1) * D ^ 4 := by + let U := (96 * Real.exp 1) * B ^ (1 / 2 : ℝ) * (Real.log B + 1) + have hlog := Real.log_nonneg hB + have hU : 0 ≤ U := by dsimp [U]; positivity + have hS : 0 ≤ ∑ i ∈ s, (δ i : ℝ) := Finset.sum_nonneg fun _ _ => by positivity + have hpoint : ∀ i ∈ s, curveBound (δ i) B ≤ + (δ i : ℝ) * U + (2 * Real.exp 1) * (δ i : ℝ) ^ 4 := by + intro i hi + by_cases hsmall : (δ i : ℝ) ≤ Real.log B + · have hc := curveBound_small_degree_uniform (hδ i hi) hB hsmall + have hd1 : (1 : ℝ) ≤ δ i := by exact_mod_cast (show 1 ≤ δ i by have := hδ i hi; omega) + have hu : U ≤ (δ i : ℝ) * U := by simpa using mul_le_mul_of_nonneg_right hd1 hU + have hp : 0 ≤ (2 * Real.exp 1) * (δ i : ℝ) ^ 4 := by positivity + exact hc.trans (hu.trans (le_add_of_nonneg_right hp)) + · have hc := curveBound_large_degree (show 0 < δ i by have := hδ i hi; omega) + hB (le_of_lt (lt_of_not_ge hsmall)) + exact hc.trans (le_add_of_nonneg_left (by positivity)) + calc + _ ≤ ∑ i ∈ s, ((δ i : ℝ) * U + (2 * Real.exp 1) * (δ i : ℝ) ^ 4) := + Finset.sum_le_sum hpoint + _ = (∑ i ∈ s, (δ i : ℝ)) * U + + (2 * Real.exp 1) * ∑ i ∈ s, (δ i : ℝ) ^ 4 := by + rw [Finset.sum_add_distrib, Finset.sum_mul, Finset.mul_sum] + _ ≤ D * U + (2 * Real.exp 1) * D ^ 4 := by + apply add_le_add (mul_le_mul_of_nonneg_right hD hU) + apply mul_le_mul_of_nonneg_left _ (by positivity) + exact (sum_fourth_powers_le_fourth_power_sum s (fun i => (δ i : ℝ)) + (fun _ _ => by positivity)).trans (pow_le_pow_left₀ hS hD 4) + +/-- The whole numerical contribution of the nonlinear curve components in + the surface argument, with the corrected d^18 factor. -/ +theorem sum_curveBound_le_surface_degree_budget {ι : Type*} + (s : Finset ι) (δ : ι → ℕ) {d : ℕ} (hd : 16 ≤ d) + {B C : ℝ} (hC : 0 ≤ C) (hB : 1 ≤ B) (hδ : ∀ i ∈ s, 2 ≤ δ i) + (hD : (∑ i ∈ s, (δ i : ℝ)) ≤ + C * (d : ℝ) ^ (9 / 2 : ℝ) * B ^ (1 / Real.sqrt d)) : + ∑ i ∈ s, curveBound (δ i) B ≤ + Real.exp 1 * (480 * C + 2 * C ^ 4) * (d : ℝ) ^ 18 * B := by + have hS : 0 ≤ ∑ i ∈ s, (δ i : ℝ) := Finset.sum_nonneg fun _ _ => by positivity + have hsplit := sum_curveBound_le_degree_budget s δ hB hδ (le_refl _) + have hsmall := surface_degree_budget_small_components hd hC hB hD + have hlarge := mul_le_mul_of_nonneg_left + (surface_degree_budget_fourth hd hC hB hS hD) + (show 0 ≤ 2 * Real.exp 1 by positivity) + calc + _ ≤ _ := hsplit + _ ≤ (480 * Real.exp 1) * C * (d : ℝ) ^ 18 * B + + (2 * Real.exp 1) * (C ^ 4 * (d : ℝ) ^ 18 * B) := add_le_add hsmall hlarge + _ = _ := by ring + +end +end CCDN diff --git a/LeanPool/CCDN/Surface/CoefficientReduction.lean b/LeanPool/CCDN/Surface/CoefficientReduction.lean new file mode 100644 index 0000000000..78f5a6730f --- /dev/null +++ b/LeanPool/CCDN/Surface/CoefficientReduction.lean @@ -0,0 +1,123 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.Algebra.IdealZeroLocus +public import LeanPool.CCDN.Height.Box +public import LeanPool.CCDN.Height.IntegerCoefficientHeight +public import LeanPool.CCDN.Height.PrimitiveIntegerPolynomial +public import LeanPool.CCDN.Hilbert.AffineCurveDegree +public import Mathlib.Analysis.SpecialFunctions.Pow.Real +import LeanPool.CCDN.AuxiliaryPolynomial.PlaneAuxiliaryCount +import LeanPool.CCDN.AuxiliaryPolynomial.UnscaledHypersurfaceAuxiliary +import LeanPool.CCDN.Hypersurface.NonabsoluteSurfaceCount +import LeanPool.CCDN.Surface.CompleteIntersectionDegreeBudget + +/-! Coefficients of a primitive surface equation can be restricted to +polynomial height in the box radius. The complementary case already has +a linear point bound from a degree-at-most-d auxiliary. -/ + +@[expose] public section +namespace CCDN +noncomputable section +open MvPolynomial + +private theorem surface_section_count_le + (f g : CoordinatePolynomial 3) (hf : Irreducible f) (hfg : ¬ f ∣ g) + {B : ℝ} (hB : 0 ≤ B) : + (count (zeroLocus f ∩ zeroLocus g) B : ℝ) ≤ + (f.totalDegree : ℝ) * g.totalDegree * (2 * B + 1) := by + obtain ⟨S, δ, hcurve, hcover, hbudget, _⟩ := + exists_completeIntersection_curve_components f g hf hfg + rw [hcover] + have hsum : (∑ P ∈ S, (δ P : ℝ)) ≤ + (f.totalDegree : ℝ) * g.totalDegree := by exact_mod_cast hbudget + exact (finite_curve_union_count_le_box_side S δ (fun P hP => + ⟨(hcurve P hP).isPrime, (hcurve P hP).height_eq, (hcurve P hP).degree⟩) hB).trans + (mul_le_mul_of_nonneg_right hsum (by linarith)) + +private theorem integer_surface_count_le_of_auxiliary + (f g : MvPolynomial (Fin 3) ℤ) + (hf : Irreducible (map (Int.castRingHom ℚ) f)) + (hfg : ¬ map (Int.castRingHom ℚ) f ∣ map (Int.castRingHom ℚ) g) + {B : ℝ} (hB : 0 ≤ B) + (hzero : ∀ x : IntPoint 3, InBox x B → + eval x f = 0 → eval x g = 0) : + (count (zeroLocus (map (Int.castRingHom ℚ) f)) B : ℝ) ≤ + (f.totalDegree : ℝ) * g.totalDegree * (2 * B + 1) := by + have hcount : count (zeroLocus (map (Int.castRingHom ℚ) f)) B ≤ + count (zeroLocus (map (Int.castRingHom ℚ) f) ∩ + zeroLocus (map (Int.castRingHom ℚ) g)) B := by + apply Finset.card_le_card + intro x hx + obtain ⟨hxB, hxf⟩ := mem_integralPoints.mp hx + have hf0 : eval x f = 0 := by + apply (show Function.Injective (Int.castRingHom ℚ) from Int.cast_injective) + rw [map_zero, map_eval] + exact hxf + have hg0 : eval (rationalPoint x) (map (Int.castRingHom ℚ) g) = 0 := by + have hh := congrArg (Int.castRingHom ℚ) (hzero x hxB hf0) + rw [map_eval, map_zero] at hh + exact hh + exact mem_integralPoints.mpr ⟨hxB, hxf, hg0⟩ + have hbound := surface_section_count_le (map (Int.castRingHom ℚ) f) + (map (Int.castRingHom ℚ) g) hf hfg hB + rw [rational_map_totalDegree, rational_map_totalDegree] at hbound + exact le_trans (by exact_mod_cast hcount) hbound + +/-- The constant `N (log N + d) + 1`, where `N = binom(d + 3, d)` is the number of monomials of +degree at most `d` in three variables, in the logarithmic coefficient-height bound for primitive +surface equations of degree `d`. -/ +def surfaceCoefficientLogConstant (d : ℕ) : ℝ := + ((3 + d).choose d : ℝ) * (Real.log ((3 + d).choose d) + d) + 1 + +/-- The coefficient-height constant `surfaceCoefficientLogConstant d` is positive. -/ +theorem surfaceCoefficientLogConstant_pos (d : ℕ) : + 0 < surfaceCoefficientLogConstant d := by + have hh := Real.log_natCast_nonneg ((3 + d).choose d) + unfold surfaceCoefficientLogConstant + positivity + +/-- The alternative to controlled coefficient height is already O(d²B). +The height constant depends on the degree alone. -/ +theorem primitive_surface_coefficient_reduction + (f : MvPolynomial (Fin 3) ℤ) (hprim : PrimitiveIntegerPolynomial f) + (hf : Irreducible (map (Int.castRingHom ℚ) f)) + {B : ℝ} (hB : 1 ≤ B) : + Real.log (integerCoefficientHeight f : ℝ) ≤ + surfaceCoefficientLogConstant f.totalDegree * (Real.log B + 1) ∨ + (count (zeroLocus (map (Int.castRingHom ℚ) f)) B : ℝ) ≤ + 3 * (f.totalDegree : ℝ) ^ 2 * B := by + have hf0 : f ≠ 0 := by intro h; exact hf.ne_zero (by rw [h, map_zero]) + rcases affine_coefficient_height_dichotomy f hf0 hprim hB with + hheight | ⟨g, ⟨_, hproper, hzero⟩, hdeg⟩ + · left + have hlog := Real.log_nonneg hB + have hNlog := Real.log_natCast_nonneg ((3 + f.totalDegree).choose f.totalDegree) + have hNd : (0 : ℝ) ≤ ((3 + f.totalDegree).choose f.totalDegree : ℝ) * f.totalDegree := by + positivity + have hNlog' : (0 : ℝ) ≤ ((3 + f.totalDegree).choose f.totalDegree : ℝ) * + Real.log ((3 + f.totalDegree).choose f.totalDegree) := by positivity + unfold surfaceCoefficientLogConstant + nlinarith + · right + have hproperQ : ¬ map (Int.castRingHom ℚ) f ∣ map (Int.castRingHom ℚ) g := + fun h => hproper (hprim.rational_map_dvd_iff.mp h) + have hcount := integer_surface_count_le_of_auxiliary f g hf hproperQ + (show 0 ≤ B by linarith) hzero + have hdegR : (g.totalDegree : ℝ) ≤ f.totalDegree := by exact_mod_cast hdeg + calc + _ ≤ (f.totalDegree : ℝ) * g.totalDegree * (2 * B + 1) := hcount + _ ≤ (f.totalDegree : ℝ) * f.totalDegree * (3 * B) := by + gcongr + linarith + _ = _ := by ring + +end +end CCDN diff --git a/LeanPool/CCDN/Surface/CompleteIntersectionDegreeBudget.lean b/LeanPool/CCDN/Surface/CompleteIntersectionDegreeBudget.lean new file mode 100644 index 0000000000..d96a38cab3 --- /dev/null +++ b/LeanPool/CCDN/Surface/CompleteIntersectionDegreeBudget.lean @@ -0,0 +1,169 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.Hilbert.FilteredPrimeDecomposition +public import LeanPool.CCDN.Surface.CurveComponents +import LeanPool.CCDN.Algebra.BinomialSectionBound +import LeanPool.CCDN.Algebra.CoordinatePowerGeneration +import LeanPool.CCDN.Hilbert.FilteredComponentDegreeBudget +import LeanPool.CCDN.Hilbert.FilteredHilbertLinearity +import LeanPool.CCDN.Hilbert.PrimeComponentTranscendence + +/-! +A finite decomposition of a proper affine surface section into +rational prime curves, with a proved additive degree budget. Components with +no rational points are discarded before requiring positive curve degrees. + +An eventual linear upper bound gives an eventual linear Hilbert +formula, with its slope bounded by the original coefficient. +-/ + +@[expose] public section +namespace CCDN +noncomputable section +open MvPolynomial + +private theorem filteredCoordinateSpace_eventually_linear_of_eventual_bound + {K σ : Type*} [Field K] [Finite σ] [LinearOrder σ] + (I : Ideal (MvPolynomial σ K)) (C T : ℕ) + (hbound : ∀ N ≥ T, Module.finrank K (filteredCoordinateSpace I N) ≤ C * (N + 2)) : + ∃ d ≤ C, HasFilteredCurveDegree I d := by + classical + let := Fintype.ofFinite σ + have hglobal (N : ℕ) (_hN : 14 ≤ N) : + Module.finrank K (filteredCoordinateSpace I N) ≤ (C * (T + 2)) * (N + 2) := by + by_cases hTN : T ≤ N + · apply (hbound N hTN).trans + exact Nat.mul_le_mul_right (N + 2) (Nat.le_mul_of_pos_right C (by omega)) + · have hNT : N ≤ T := by omega + have hh := Submodule.finrank_mono (filteredCoordinateSpace_mono I hNT) + exact (hh.trans (hbound T le_rfl)).trans (Nat.le_mul_of_pos_right _ (by omega)) + obtain ⟨d, _, N, c, hlinear⟩ := filteredCoordinateSpace_eventually_linear I (C * (T + 2)) hglobal + refine ⟨d, ?_, N, c, hlinear⟩ + apply eventual_linear_slope_le + (fun k => Module.finrank K (filteredCoordinateSpace I (k + T))) + (fun k => C * (k + T + 2)) d C + · refine ⟨N, c + (d : ℤ) * T, ?_⟩ + intro k hk + rw [hlinear (k + T) (by omega)] + push_cast + ring + · refine ⟨0, (C : ℤ) * (T + 2), ?_⟩ + intro k _ + push_cast + ring + · intro k + exact hbound (k + T) (by omega) + +/-- A proper intersection of two equations in affine three-space has an +eventually linear Hilbert function with slope at most the product of degrees. +This holds for every ideal containing the equations, not just prime ideals. -/ +private theorem proper_intersection_eventual_linear_degree {K : Type*} [Field K] + {f g : MvPolynomial (Fin 3) K} (hf : Prime f) (hfg : ¬ f ∣ g) + (I : Ideal (MvPolynomial (Fin 3) K)) (hIf : f ∈ I) (hIg : g ∈ I) : + ∃ d ≤ f.totalDegree * g.totalDegree, HasFilteredCurveDegree I d := by + apply filteredCoordinateSpace_eventually_linear_of_eventual_bound I + (f.totalDegree * g.totalDegree) (f.totalDegree + g.totalDegree) + intro N hN + have hh := filteredCoordinateSpace_codim_two_bound hf hfg I hIf hIg hN + simp only [Nat.choose_one_right] at hh + exact hh.trans (Nat.mul_le_mul_left _ (by omega)) + +end +end CCDN + +namespace CCDN +noncomputable section +open MvPolynomial + +/-- Every minimal-prime component has an eventual affine Hilbert +degree. The slope is allowed to be zero until a rational point is exhibited. -/ +theorem polynomialSection_minimalPrime_degrees_exist + (f g : CoordinatePolynomial 3) (hf : Irreducible f) (hfg : ¬ f ∣ g) : + ∃ d : minimalPrimeComponents (polynomialSectionIdeal f g) → ℕ, + (∀ P, HasAffineCurveDegree P.val (d P)) ∧ + ∑ P, d P ≤ f.totalDegree * g.totalDegree := by + classical + have hex (P : minimalPrimeComponents (polynomialSectionIdeal f g)) : + ∃ d : ℕ, HasAffineCurveDegree P.val d := by + obtain ⟨d, _, h⟩ := proper_intersection_eventual_linear_degree hf.prime hfg P.val + (le_minimalPrimeComponents P.property (polynomialSectionIdeal_first_mem f g)) + (le_minimalPrimeComponents P.property (polynomialSectionIdeal_second_mem f g)) + exact ⟨d, h⟩ + choose d hd using hex + exact ⟨d, hd, polynomialSection_minimalPrime_curveDegree_sum_le f g hf hfg d hd⟩ + +/-- A proper section in affine three-space is covered exactly by a finite +set of rational prime curves. Their positive Hilbert degrees sum to +at most the product of the two equation degrees. All components and degrees +are constructed, including the case of an empty common zero set. -/ +theorem exists_completeIntersection_curve_components + (f g : CoordinatePolynomial 3) (hf : Irreducible f) (hfg : ¬ f ∣ g) : + ∃ S : Finset (Ideal (CoordinatePolynomial 3)), + ∃ d : Ideal (CoordinatePolynomial 3) → ℕ, + (∀ P ∈ S, IsSectionCurve f g P (d P)) ∧ + zeroLocus f ∩ zeroLocus g = ⋃ P ∈ S, idealZeroLocus P ∧ + (∑ P ∈ S, d P) ≤ f.totalDegree * g.totalDegree ∧ + (∀ P ∈ S, (polynomialSectionIdeal f g).IsMinimalPrime P) := by + classical + let T := minimalPrimeComponents (polynomialSectionIdeal f g) + obtain ⟨δ, hδ, hsumδ⟩ := polynomialSection_minimalPrime_degrees_exist f g hf hfg + let d (P : Ideal (CoordinatePolynomial 3)) := if hP : P ∈ T then δ ⟨P, hP⟩ else 0 + let S := T.filter (fun P => (idealZeroLocus P).Nonempty) + have hd (P : T) : d P.val = δ P := by + simp only [d, dite_eq_left P.property] + rfl + have hTP (P : Ideal (CoordinatePolynomial 3)) (hP : P ∈ T) : + (polynomialSectionIdeal f g).IsMinimalPrime P := mem_minimalPrimeComponents.mp hP + have hcurve (P : Ideal (CoordinatePolynomial 3)) (hP : P ∈ T) : + HasAffineCurveDegree P (d P) := by + rw [hd ⟨P, hP⟩] + exact hδ ⟨P, hP⟩ + refine ⟨S, d, ?_, ?_, ?_, ?_⟩ + · intro P hP + obtain ⟨hPT, z, hz⟩ := Finset.mem_filter.mp hP + have hminimal := hTP P hPT + have : P.IsPrime := hminimal.isPrime + have hheight : P.height = 2 := + Ideal.height_eq_two_of_mem_minimalPrimes_span_pair hf.prime hfg hminimal + have hdeg := hcurve P hPT + exact ⟨⟨hminimal.isPrime, hheight, hminimal.le (polynomialSectionIdeal_first_mem f g), hdeg⟩, + hminimal.le (polynomialSectionIdeal_second_mem f g), + height_two_eventual_slope_pos_of_point P hheight z hz hdeg⟩ + · ext x + constructor + · intro hx + have hxi : x ∈ idealZeroLocus (polynomialSectionIdeal f g) := by + rw [idealZeroLocus_polynomialSectionIdeal] + exact hx + rw [idealZeroLocus_eq_union_minimalPrimeComponents] at hxi + obtain ⟨P, hxP⟩ := Set.mem_iUnion.mp hxi + obtain ⟨hPT, hxP⟩ := Set.mem_iUnion.mp hxP + exact Set.mem_iUnion.mpr ⟨P, Set.mem_iUnion.mpr + ⟨Finset.mem_filter.mpr ⟨hPT, ⟨x, hxP⟩⟩, hxP⟩⟩ + · intro hx + obtain ⟨P, hxP⟩ := Set.mem_iUnion.mp hx + obtain ⟨hPS, hxP⟩ := Set.mem_iUnion.mp hxP + have hminimal := hTP P (Finset.mem_filter.mp hPS).1 + exact ⟨hxP f (hminimal.le (polynomialSectionIdeal_first_mem f g)), + hxP g (hminimal.le (polynomialSectionIdeal_second_mem f g))⟩ + · have hsumT : (∑ P ∈ T, d P) = ∑ P, δ P := by + rw [← Finset.sum_coe_sort T d] + exact Finset.sum_congr rfl (fun P _ => hd P) + calc + _ ≤ ∑ P ∈ T, d P := + Finset.sum_le_sum_of_subset_of_nonneg (Finset.filter_subset _ _) (by intros; omega) + _ = _ := hsumT + _ ≤ _ := hsumδ + · intro P hPS + exact hTP P (Finset.mem_filter.mp hPS).1 + +end +end CCDN diff --git a/LeanPool/CCDN/Surface/CongruenceAuxiliary.lean b/LeanPool/CCDN/Surface/CongruenceAuxiliary.lean new file mode 100644 index 0000000000..99451e856d --- /dev/null +++ b/LeanPool/CCDN/Surface/CongruenceAuxiliary.lean @@ -0,0 +1,160 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.AuxiliaryPolynomial.AffineCongruenceAuxiliary +public import LeanPool.CCDN.Hilbert.PrincipalStandardMonomials +import LeanPool.CCDN.AuxiliaryPolynomial.HypersurfaceRankProfile +import LeanPool.CCDN.AuxiliaryPolynomial.PlaneAuxiliaryCount +import LeanPool.CCDN.Surface.MonomialWeight + +/-! The full standard-monomial prefix produces a proper integer auxiliary +on the entire bounded surface congruence class, with the sharp weighted +degree in the sole remaining numerical gap. -/ + +@[expose] public section +namespace CCDN +noncomputable section +open MvPolynomial Finset + +/-- The integer zeros of `f` in the box of radius `B` that are congruent to `a p` modulo `p`, +coordinatewise, for every `p ∈ P`. -/ +def affineCongruencePoints {n : ℕ} (f : MvPolynomial (Fin n) ℤ) + (B : ℝ) (P : Finset ℕ) (a : ℕ → IntPoint n) : Finset (IntPoint n) := by + classical + exact (integerBox n B).filter (fun x => + eval x f = 0 ∧ ∀ p ∈ P, ∀ j, (p : ℤ) ∣ x j - a p j) + +/-- A point lies in `affineCongruencePoints f B P a` exactly when it lies in the box of radius +`B`, is a zero of `f` and is congruent to `a p` modulo every `p ∈ P`. -/ +lemma mem_affineCongruencePoints {n : ℕ} {f : MvPolynomial (Fin n) ℤ} + {B : ℝ} {P : Finset ℕ} {a : ℕ → IntPoint n} {x : IntPoint n} : + x ∈ affineCongruencePoints f B P a ↔ + InBox x B ∧ eval x f = 0 ∧ + ∀ p ∈ P, ∀ j, (p : ℤ) ∣ x j - a p j := by + classical + simp only [affineCongruencePoints, mem_filter, mem_integerBox] + +/-- The number of standard monomials of degree at most `M`, for the degree-lexicographic order, +of the principal ideal generated by the surface equation `f` over `ℚ`: the dimension of the +space of polynomials of degree at most `M` modulo `f`. -/ +def surfaceCongruenceRank (f : MvPolynomial (Fin 3) ℤ) (M : ℕ) : ℕ := + (standardMonomialPrefix (Ideal.span {map (Int.castRingHom ℚ) f}) + MonomialOrder.degLex M).card + +/-- The bound `log s! + ((2/3) s √(2s/d) + (2d + 2) s) log B` for the logarithm of the absolute +value of a determinant formed by evaluating `s` standard monomials of a surface of degree `d` +at `s` integer points of the box of radius `B`. -/ +def surfaceWeightedDeterminantUpper (d s : ℕ) (B : ℝ) : ℝ := + Real.log (s.factorial : ℝ) + + ((2 / 3 : ℝ) * s * Real.sqrt (2 * s / (d : ℝ)) + (2 * d + 2) * s) * Real.log B + +/-- For a nonzero surface equation `f` of degree `d ≤ M`, the standard-monomial rank +`surfaceCongruenceRank f M` lies between `d (M - d + 1)² / 2` and `d (M + 2)² / 2`. -/ +theorem surface_congruence_rank_power_bounds + (f : MvPolynomial (Fin 3) ℤ) (hf : f ≠ 0) {M : ℕ} + (hM : f.totalDegree ≤ M) : + (f.totalDegree : ℝ) * ((M : ℝ) - f.totalDegree + 1) ^ 2 / 2 ≤ + surfaceCongruenceRank f M ∧ + (surfaceCongruenceRank f M : ℝ) ≤ + (f.totalDegree : ℝ) * ((M : ℝ) + 2) ^ 2 / 2 := by + have hdegree : (map (Int.castRingHom ℚ) f).totalDegree = f.totalDegree := + rational_map_totalDegree f + have hfQ : map (Int.castRingHom ℚ) f ≠ 0 := by + intro h + exact hf ((map_injective (Int.castRingHom ℚ) Int.cast_injective) (by simpa using h)) + have hr := principal_standard_prefix_rank (map (Int.castRingHom ℚ) f) + hfQ (hdegree ▸ hM) + rw [hdegree] at hr + have hb := hypersurface_rank_power_bounds hM hr + norm_num at hb + exact hb + +/-- All bounded integer zeros in specified smooth residue classes lie on +a proper auxiliary. The degree criterion is entirely numerical. -/ +theorem exists_surface_congruence_auxiliary_of_gap + (f : MvPolynomial (Fin 3) ℤ) + (hir : AbsolutelyIrreducible f) + (hd : 0 < f.totalDegree) (H : ℝ) (hH : 1 ≤ H) + (hcoeff : CoefficientHeightLE f H) + {M : ℕ} (hM : f.totalDegree ≤ M) + {B : ℝ} (hB : 1 ≤ B) + (P : Finset ℕ) (hP : ∀ p ∈ P, p.Prime) (a : ℕ → IntPoint 3) + (ha : ∀ p ∈ P, eval₂ (Int.castRingHom (ZMod p)) (fun j => (a p j : ZMod p)) f = 0) + (hsmooth : ∀ p ∈ P, ∃ j, eval (fun i => (a p i : ZMod p)) + (pderiv j (map (Int.castRingHom (ZMod p)) f)) ≠ 0) + (hgap : surfaceWeightedDeterminantUpper f.totalDegree (surfaceCongruenceRank f M) B < + affineCongruenceDeterminantLower 1 f.totalDegree (surfaceCongruenceRank f M) + H (∑ p ∈ P, Real.log p)) : + ∃ g : MvPolynomial (Fin 3) ℤ, g ≠ 0 ∧ + ¬ map (Int.castRingHom ℚ) f ∣ map (Int.castRingHom ℚ) g ∧ + g.totalDegree ≤ M ∧ ∀ x ∈ affineCongruencePoints f B P a, eval x g = 0 := by + classical + let fQ := map (Int.castRingHom ℚ) f + let S := standardMonomialPrefix (Ideal.span {fQ}) MonomialOrder.degLex M + let e := (Fintype.equivFin S).symm + let mon : Fin (Fintype.card S) → (Fin 3 →₀ ℕ) := fun j => (e j).val + have hc : Fintype.card S = surfaceCongruenceRank f M := Fintype.card_coe S + have hdegree : fQ.totalDegree = f.totalDegree := + rational_map_totalDegree f + have hf : f ≠ 0 := by intro h; exact hir.ne_zero (by rw [h, map_zero]) + have hfQ : fQ ≠ 0 := by + intro h + exact hf ((map_injective (Int.castRingHom ℚ) Int.cast_injective) (by simpa [fQ] using h)) + have hN : 0 < Fintype.card S := by + rw [hc] + have hb := (surface_congruence_rank_power_bounds f hf hM).1 + have hdR : (0 : ℝ) < f.totalDegree := by exact_mod_cast hd + have hMR : (f.totalDegree : ℝ) ≤ M := by exact_mod_cast hM + have hbase : 0 < ((M : ℝ) - f.totalDegree + 1) ^ 2 := by positivity + have hpos : (0 : ℝ) < surfaceCongruenceRank f M := lt_of_lt_of_le + (by positivity : (0 : ℝ) < (f.totalDegree : ℝ) * + ((M : ℝ) - f.totalDegree + 1) ^ 2 / 2) hb + exact_mod_cast hpos + have hmon : Function.Injective mon := Subtype.val_injective.comp e.injective + have hstd (j) : mon j ∈ Ideal.standardExponents (Ideal.span {fQ}) MonomialOrder.degLex := + (mem_standardMonomialPrefix _ _ _ _).mp (e j).property |>.1 + have hdeg (j) : Finsupp.degree (mon j) ≤ M := + (mem_standardMonomialPrefix _ _ _ _).mp (e j).property |>.2 + have hweight : (∑ j, ((Finsupp.degree (mon j) : ℕ) : ℝ)) ≤ + (2 / 3 : ℝ) * Fintype.card S * + Real.sqrt (2 * Fintype.card S / (f.totalDegree : ℝ)) + + (2 * f.totalDegree + 2) * Fintype.card S := by + have hw := surface_standard_monomial_weight fQ hfQ (hdegree ▸ hd) (hdegree ▸ hM) + change (∑ j ∈ S, ((Finsupp.degree j : ℕ) : ℝ)) ≤ _ at hw + have hsum : (∑ j, ((Finsupp.degree (mon j) : ℕ) : ℝ)) = + ∑ j ∈ S, ((Finsupp.degree j : ℕ) : ℝ) := by + rw [← Finset.sum_coe_sort S] + exact e.sum_comp (fun j : S => ((Finsupp.degree j.val : ℕ) : ℝ)) + rw [hsum, Fintype.card_coe, ← hdegree] + exact hw + have hgap' : Real.log ((Fintype.card S).factorial : ℝ) + + (∑ j, ((Finsupp.degree (mon j) : ℕ) : ℝ)) * Real.log B < + affineCongruenceDeterminantLower 1 f.totalDegree (Fintype.card S) + H (∑ p ∈ P, Real.log p) := by + apply lt_of_le_of_lt (add_le_add_right + (mul_le_mul_of_nonneg_right hweight (Real.log_nonneg hB)) _) + simpa only [hc, surfaceWeightedDeterminantUpper] using hgap + let X := affineCongruencePoints f B P a + let ex := (Fintype.equivFin X).symm + let x : Fin (Fintype.card X) → IntPoint 3 := fun i => (ex i).val + have hx (i) := mem_affineCongruencePoints.mp (ex i).property + obtain ⟨g, hg0, hgnd, hgdeg, hgz⟩ := + exists_affine_congruence_auxiliary_of_gap hN f hir hd H hH hcoeff mon hmon hstd hdeg + ⟨hB, fun i => (hx i).2.1, fun i j => (hx i).1 j⟩ P a + ⟨hP, ha, hsmooth, fun p hp i j => (hx i).2.2 p hp j⟩ hgap' + refine ⟨g, hg0, hgnd, hgdeg, ?_⟩ + intro y hy + obtain ⟨i, hi⟩ := ex.surjective ⟨y, hy⟩ + have hh := hgz i + change eval (ex i).val g = 0 at hh + simpa only [hi] using hh + +end +end CCDN diff --git a/LeanPool/CCDN/Surface/CongruenceCutoff.lean b/LeanPool/CCDN/Surface/CongruenceCutoff.lean new file mode 100644 index 0000000000..1dbdbb7927 --- /dev/null +++ b/LeanPool/CCDN/Surface/CongruenceCutoff.lean @@ -0,0 +1,273 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.Surface.CongruenceAuxiliary +import LeanPool.CCDN.AuxiliaryPolynomial.HypersurfaceRankArchimedean +import Mathlib.Algebra.Order.Star.Real + +/-! Numerical degree selection for the smooth-congruence auxiliary on a +surface. All constants are independent of coefficients and box radius. -/ + +@[expose] public section +namespace CCDN +noncomputable section + +/-- A constant, depending on the degree `d` and the logarithmic height constant `K`, bounding +the height charge of the smooth-congruence determinant estimate for a surface of degree `d` +whose coefficient height `H` satisfies `log H ≤ K (log B + 1)`. -/ +def surfaceCongruenceChargeConstant (d : ℕ) (K : ℝ) : ℝ := + Real.log (2 + 27 * (d : ℝ) ^ 4 * hypersurfaceHeightConstant 4 * + (1 + K / (d : ℝ) ^ 2)) + 2 * mertensError + 1 + +/-- The constant `8 log d + 2 surfaceCongruenceChargeConstant d K + E + 6d + 22`, with `E` the +determinant error in dimension one, controlling the numerical gap between the upper and lower +bounds for surface congruence determinants. -/ +def surfaceCongruenceGapConstant (d : ℕ) (K : ℝ) : ℝ := + 8 * Real.log d + 2 * surfaceCongruenceChargeConstant d K + + hypersurfaceDeterminantError 1 + 6 * d + 22 + +private theorem surface_congruence_charge_le {d : ℕ} (hd : 0 < d) + {K B H Q : ℝ} (hK : 0 ≤ K) (hB : 1 ≤ B) (hH : 1 ≤ H) (hQ : 0 ≤ Q) + (hheight : Real.log H ≤ K * (Real.log B + 1)) : + congruenceHeightCharge 4 d H Q ≤ + Real.log (1 + Real.log B + Q) + surfaceCongruenceChargeConstant d K := by + have hdR : (0 : ℝ) < d := by exact_mod_cast hd + have hc : 0 ≤ hypersurfaceHeightConstant 4 := + zero_le_one.trans (hypersurfaceHeightConstant_one_le 4) + have hlogB := Real.log_nonneg hB + have hlogH := Real.log_nonneg hH + have hT : 1 ≤ 1 + Real.log B + Q := by linarith + have hheight' : Real.log H ≤ K * (1 + Real.log B + Q) := by + nlinarith only [hheight, mul_nonneg hK hQ] + let A := 27 * (d : ℝ) ^ 4 * hypersurfaceHeightConstant 4 + have hA : 0 ≤ A := by positivity + have hdiv := div_le_div_of_nonneg_right hheight' (sq_nonneg (d : ℝ)) + have hinside : 1 + A * (1 + Real.log H / (d : ℝ) ^ 2) + Q ≤ + (2 + A * (1 + K / (d : ℝ) ^ 2)) * (1 + Real.log B + Q) := by + have hm := mul_le_mul_of_nonneg_left hdiv hA + have hc1 := mul_le_mul_of_nonneg_left hT hA + have he : K * (1 + Real.log B + Q) / (d : ℝ) ^ 2 = + K / (d : ℝ) ^ 2 * (1 + Real.log B + Q) := by ring + rw [he] at hm + linarith only [hm, hc1, hlogB, hQ] + have hleft : 0 < 1 + A * (1 + Real.log H / (d : ℝ) ^ 2) + Q := by positivity + have hright : 0 < 2 + A * (1 + K / (d : ℝ) ^ 2) := by positivity + have hh := Real.log_le_log hleft hinside + rw [Real.log_mul hright.ne' (by linarith : (1 + Real.log B + Q) ≠ 0)] at hh + unfold congruenceHeightCharge surfaceCongruenceChargeConstant + dsimp only [A] at hh + simp only [mul_assoc] at hh ⊢ + linarith only [hh] + +private theorem surface_congruence_lower_eq (d s : ℕ) (H Q : ℝ) : + affineCongruenceDeterminantLower 1 d s H Q = + (Real.sqrt 2 / 3 * (s : ℝ) * Real.sqrt s) * + (Real.log s + 2 * Q - 8 * Real.log d - + 2 * congruenceHeightCharge 4 d H Q - hypersurfaceDeterminantError 1) - + 3 * s * Q := by + have hs : (0 : ℝ) ≤ s := Nat.cast_nonneg s + have hp : (s : ℝ) ^ (1 + 1 / 2 : ℝ) = s * Real.sqrt s := by + rw [Real.rpow_add' hs (by norm_num), Real.rpow_one, ← Real.sqrt_eq_rpow] + norm_num at hp + unfold affineCongruenceDeterminantLower hypersurfaceDeterminantMain + norm_num only [Nat.reduceAdd, Nat.factorial, Nat.cast_ofNat, Nat.cast_mul, + Nat.cast_one, Nat.cast_add, Nat.cast_succ, mul_one] + rw [hp, ← Real.sqrt_eq_rpow] + ring + +private theorem surface_congruence_gap_of_log_rank {d s : ℕ} + (hd : 0 < d) (hs : 0 < s) {K B H Q : ℝ} + (hK : 0 ≤ K) (hB : 1 ≤ B) (hH : 1 ≤ H) (hQ : 0 ≤ Q) + (hheight : Real.log H ≤ K * (Real.log B + 1)) + (hroot : 1 + Real.log B + Q ≤ Real.sqrt (s : ℝ)) + (hrank : (2 / Real.sqrt (d : ℝ)) * Real.log B + + 2 * Real.log (1 + Real.log B + Q) + surfaceCongruenceGapConstant d K ≤ + Real.log s + 2 * Q) : + Real.log (s.factorial : ℝ) + + ((2 / 3 : ℝ) * s * Real.sqrt (2 * s / (d : ℝ)) + (2 * d + 2) * s) * + Real.log B < affineCongruenceDeterminantLower 1 d s H Q := by + have hsR : (0 : ℝ) < s := by exact_mod_cast hs + have hdR : (0 : ℝ) < d := by exact_mod_cast hd + have hsd : 0 < Real.sqrt (d : ℝ) := Real.sqrt_pos.mpr hdR + have hss : 0 < Real.sqrt (s : ℝ) := Real.sqrt_pos.mpr hsR + have hlogB := Real.log_nonneg hB + have hfac : Real.log (s.factorial : ℝ) ≤ (s : ℝ) * Real.log s := by + have hh : (s.factorial : ℝ) ≤ (s : ℝ) ^ s := by exact_mod_cast Nat.factorial_le_pow s + simpa only [Real.log_pow] using Real.log_le_log (by positivity) hh + have hlog : Real.log (s : ℝ) ≤ 2 * Real.sqrt (s : ℝ) := by + have hh := log_le_dimension_root (r := 2) (by omega) hsR + simpa only [Nat.cast_ofNat, ← Real.sqrt_eq_rpow] using hh + have hfac' := hfac.trans (mul_le_mul_of_nonneg_left hlog hsR.le) + have hcharge := surface_congruence_charge_le hd hK hB hH hQ hheight + have hsqrt2 : (1 : ℝ) ≤ Real.sqrt 2 := by + exact Real.le_sqrt_of_sq_le (by norm_num) + let A : ℝ := Real.sqrt 2 / 3 * s * Real.sqrt s + have hA : 0 < A := by positivity + have hsplit : Real.sqrt (2 * s / (d : ℝ)) = + Real.sqrt 2 * Real.sqrt s / Real.sqrt d := by + rw [Real.sqrt_div (by positivity), Real.sqrt_mul (by norm_num)] + have hmain : (2 / 3 : ℝ) * s * Real.sqrt (2 * s / (d : ℝ)) = + A * (2 / Real.sqrt (d : ℝ)) := by + rw [hsplit] + dsimp only [A] + ring + have herr : Real.log (s.factorial : ℝ) + (2 * d + 2) * s * Real.log B + 3 * s * Q ≤ + A * (6 * d + 21) := by + have hb : Real.log B ≤ Real.sqrt (s : ℝ) := by linarith only [hroot, hQ] + have hq : Q ≤ Real.sqrt (s : ℝ) := by linarith only [hroot, hlogB] + have hb' := mul_le_mul_of_nonneg_left hb + (show 0 ≤ (2 * (d : ℝ) + 2) * s by positivity) + have hq' := mul_le_mul_of_nonneg_left hq (show 0 ≤ 3 * (s : ℝ) by positivity) + have hA' : (s : ℝ) * Real.sqrt s ≤ 3 * A := by + have hh := mul_le_mul_of_nonneg_right hsqrt2 + (show 0 ≤ (s : ℝ) * Real.sqrt s by positivity) + dsimp only [A] + nlinarith only [hh] + have hscale := mul_le_mul_of_nonneg_left hA' + (show 0 ≤ 2 * (d : ℝ) + 7 by positivity) + nlinarith only [hfac', hb', hq', hscale] + have hbracket : + (2 / Real.sqrt (d : ℝ)) * Real.log B + (6 * d + 22) ≤ + Real.log s + 2 * Q - 8 * Real.log d - + 2 * congruenceHeightCharge 4 d H Q - hypersurfaceDeterminantError 1 := by + unfold surfaceCongruenceGapConstant at hrank + linarith only [hrank, hcharge] + have hm := mul_le_mul_of_nonneg_left hbracket hA.le + rw [surface_congruence_lower_eq] + rw [hmain] + change _ < A * _ - _ + nlinarith only [hm, herr, hA] + +/-- The constant `d + 2 (exp (G / 2) + 1) + 1`, with `G` the gap constant +`surfaceCongruenceGapConstant d K`, which scales the degree of the surface congruence +auxiliaries. -/ +def surfaceCongruenceDegreeConstant (d : ℕ) (K : ℝ) : ℝ := + d + 2 * (Real.exp (surfaceCongruenceGapConstant d K / 2) + 1) + 1 + +/-- The auxiliary degree constant `surfaceCongruenceDegreeConstant d K` is positive. -/ +theorem surfaceCongruenceDegreeConstant_pos (d : ℕ) (K : ℝ) : + 0 < surfaceCongruenceDegreeConstant d K := by + unfold surfaceCongruenceDegreeConstant + positivity + +/-- One integer degree, with a constant fixed before the height and congruences, +works for every standard-monomial rank satisfying the elementary power bound. -/ +private theorem surface_congruence_degree_selection {d : ℕ} (hd : 0 < d) + {K B H Q : ℝ} (hK : 0 ≤ K) (hB : 1 ≤ B) (hH : 1 ≤ H) (hQ : 0 ≤ Q) + (hheight : Real.log H ≤ K * (Real.log B + 1)) : + ∃ M : ℕ, d ≤ M ∧ + (M : ℝ) ≤ surfaceCongruenceDegreeConstant d K * + (B ^ (1 / Real.sqrt (d : ℝ)) / Real.exp Q + 1) * (1 + Real.log B + Q) ∧ + ∀ s : ℕ, (d : ℝ) * ((M : ℝ) - d + 1) ^ 2 / 2 ≤ s → + surfaceWeightedDeterminantUpper d s B < + affineCongruenceDeterminantLower 1 d s H Q := by + let T := 1 + Real.log B + Q + let ρ := B ^ (1 / Real.sqrt (d : ℝ)) / Real.exp Q + let E := Real.exp (surfaceCongruenceGapConstant d K / 2) + 1 + let S := E * (ρ + 1) * T + have hB0 : 0 < B := zero_lt_one.trans_le hB + have hdR : (1 : ℝ) ≤ d := by exact_mod_cast hd + have hT : 1 ≤ T := by dsimp only [T]; linarith [Real.log_nonneg hB] + have hT0 : 0 < T := zero_lt_one.trans_le hT + have hρ : 0 < ρ := by dsimp only [ρ]; positivity + have hE : 1 ≤ E := by + dsimp only [E]; linarith [Real.exp_pos (surfaceCongruenceGapConstant d K / 2)] + have hE0 : 0 < E := zero_lt_one.trans_le hE + have hS0 : 0 < S := by dsimp only [S]; positivity + have hST : T ≤ S := by + have hh := mul_le_mul hE (show 1 ≤ ρ + 1 by linarith) (by norm_num) hE0.le + have hh' := mul_le_mul_of_nonneg_right hh hT0.le + simpa only [one_mul] using hh' + let M := d + ⌈2 * S⌉₊ + have hMd : d ≤ M := Nat.le_add_right _ _ + have hMl : 2 * S ≤ (M : ℝ) - d + 1 := by + have hh := Nat.le_ceil (2 * S) + dsimp only [M] + push_cast + linarith + have hMu : (M : ℝ) ≤ surfaceCongruenceDegreeConstant d K * (ρ + 1) * T := by + have hh := Nat.ceil_lt_add_one (show 0 ≤ 2 * S by positivity) + have hprod : 1 ≤ (ρ + 1) * T := by + nlinarith only [hT, mul_nonneg hρ.le hT0.le] + have hscale := mul_le_mul_of_nonneg_left hprod (show 0 ≤ (d : ℝ) + 1 by positivity) + dsimp only [M] at * + push_cast + change _ ≤ ((d : ℝ) + 2 * E + 1) * (ρ + 1) * T + dsimp only [S] at hh + nlinarith only [hh, hscale] + refine ⟨M, hMd, hMu, ?_⟩ + intro s hs + have hsq : S ^ 2 ≤ (s : ℝ) := by + have hpow : (2 * S) ^ 2 ≤ ((M : ℝ) - d + 1) ^ 2 := by + exact pow_le_pow_left₀ (by positivity) hMl 2 + have hmul := mul_le_mul_of_nonneg_right hdR + (sq_nonneg ((M : ℝ) - d + 1)) + nlinarith only [hs, hpow, hmul, sq_nonneg S] + have hsR : (0 : ℝ) < s := lt_of_lt_of_le (sq_pos_of_pos hS0) hsq + have hsN : 0 < s := by exact_mod_cast hsR + have hroot : T ≤ Real.sqrt (s : ℝ) := + hST.trans (Real.le_sqrt_of_sq_le hsq) + have hlogS : 2 * Real.log S ≤ Real.log (s : ℝ) := by + simpa only [Real.log_pow, Nat.cast_ofNat] using Real.log_le_log (sq_pos_of_pos hS0) hsq + have hlogE : surfaceCongruenceGapConstant d K / 2 ≤ Real.log E := by + have hh := Real.log_le_log (Real.exp_pos (surfaceCongruenceGapConstant d K / 2)) + (show Real.exp (surfaceCongruenceGapConstant d K / 2) ≤ E by dsimp only [E]; linarith) + simpa only [Real.log_exp] using hh + have hlogρ : Real.log ρ ≤ Real.log (ρ + 1) := + Real.log_le_log hρ (by linarith) + have hρeq : Real.log ρ = (1 / Real.sqrt (d : ℝ)) * Real.log B - Q := by + dsimp only [ρ] + rw [Real.log_div (by positivity) (Real.exp_ne_zero _), Real.log_rpow hB0, Real.log_exp] + have hSeq : Real.log S = Real.log E + Real.log (ρ + 1) + Real.log T := by + dsimp only [S] + rw [Real.log_mul (by positivity) hT0.ne', Real.log_mul hE0.ne' (by positivity)] + have hrank : (2 / Real.sqrt (d : ℝ)) * Real.log B + + 2 * Real.log T + surfaceCongruenceGapConstant d K ≤ Real.log s + 2 * Q := by + rw [hSeq] at hlogS + rw [hρeq] at hlogρ + have heq : (2 / Real.sqrt (d : ℝ)) * Real.log B = + 2 * ((1 / Real.sqrt (d : ℝ)) * Real.log B) := by ring + rw [heq] + linarith only [hlogS, hlogE, hlogρ] + exact surface_congruence_gap_of_log_rank hd hsN hK hB hH hQ hheight hroot hrank + +/-- The smooth residue classes have a proper auxiliary of degree +O_d,K((B^(1/√d)/exp Q + 1)(1 + log B + Q)). -/ +theorem exists_surface_congruence_auxiliary + (f : MvPolynomial (Fin 3) ℤ) + (hir : AbsolutelyIrreducible f) + (hd : 0 < f.totalDegree) {K H B : ℝ} (hK : 0 ≤ K) (hH : 1 ≤ H) (hB : 1 ≤ B) + (hcoeff : CoefficientHeightLE f H) + (hheight : Real.log H ≤ K * (Real.log B + 1)) + (P : Finset ℕ) (hP : ∀ p ∈ P, p.Prime) (a : ℕ → IntPoint 3) + (ha : ∀ p ∈ P, MvPolynomial.eval₂ (Int.castRingHom (ZMod p)) + (fun j => (a p j : ZMod p)) f = 0) + (hsmooth : ∀ p ∈ P, ∃ j, MvPolynomial.eval (fun i => (a p i : ZMod p)) + (MvPolynomial.pderiv j (MvPolynomial.map (Int.castRingHom (ZMod p)) f)) ≠ 0) : + ∃ g : MvPolynomial (Fin 3) ℤ, g ≠ 0 ∧ + ¬ MvPolynomial.map (Int.castRingHom ℚ) f ∣ MvPolynomial.map (Int.castRingHom ℚ) g ∧ + (g.totalDegree : ℝ) ≤ surfaceCongruenceDegreeConstant f.totalDegree K * + (B ^ (1 / Real.sqrt (f.totalDegree : ℝ)) / + Real.exp (∑ p ∈ P, Real.log p) + 1) * + (1 + Real.log B + ∑ p ∈ P, Real.log p) ∧ + ∀ x ∈ affineCongruencePoints f B P a, MvPolynomial.eval x g = 0 := by + have hQ : 0 ≤ ∑ p ∈ P, Real.log p := Finset.sum_nonneg fun p hp => + Real.log_nonneg (by exact_mod_cast (hP p hp).one_le) + obtain ⟨M, hdM, hM, hgap⟩ := + surface_congruence_degree_selection hd hK hB hH hQ hheight + have hf : f ≠ 0 := by intro h; exact hir.ne_zero (by rw [h, map_zero]) + have hg := hgap (surfaceCongruenceRank f M) (surface_congruence_rank_power_bounds f hf hdM).1 + obtain ⟨g, hg0, hgproper, hgM, hgz⟩ := exists_surface_congruence_auxiliary_of_gap + f hir hd H hH hcoeff hdM hB P hP a ha hsmooth hg + refine ⟨g, hg0, hgproper, le_trans ?_ hM, hgz⟩ + exact_mod_cast hgM + +end +end CCDN diff --git a/LeanPool/CCDN/Surface/CongruenceFamily.lean b/LeanPool/CCDN/Surface/CongruenceFamily.lean new file mode 100644 index 0000000000..6d05ef7132 --- /dev/null +++ b/LeanPool/CCDN/Surface/CongruenceFamily.lean @@ -0,0 +1,126 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.Determinant.SmoothAffineResidueClasses +public import LeanPool.CCDN.Surface.CongruenceCutoff + +/-! A coherent choice of proper auxiliaries for every finite +smooth residue family. The structure stores conclusions of the determinant +theorem; its existence is proved from the equation. -/ + +@[expose] public section +namespace CCDN +noncomputable section +open MvPolynomial Finset + +/-- The restriction of a family of smooth residues, indexed by the primes of `P`, to a subset +`Q ⊆ P`. -/ +def SmoothAffineResidueFamily.restrict {n : ℕ} {f : MvPolynomial (Fin n) ℤ} + {P Q : Finset ℕ} (r : SmoothAffineResidueFamily f P) (hQP : Q ⊆ P) : + SmoothAffineResidueFamily f Q := fun q => r ⟨q.val, hQP q.property⟩ + +/-- The unique family of smooth residues indexed by the empty set of primes. -/ +def emptySmoothAffineResidueFamily {n : ℕ} (f : MvPolynomial (Fin n) ℤ) : + SmoothAffineResidueFamily f ∅ := fun p => False.elim (notMem_empty p.val p.property) + +/-- A choice, for every finite set `P` of primes and every family `r` of smooth residues of `f` +modulo the primes of `P`, of a nonzero integer polynomial that is not divisible by `f` over `ℚ`, +has degree at most `A (B^(1/√d) / ∏ p + 1)(1 + log B + ∑ log p)` (with `d` the degree of `f` +and `p` ranging over `P`) and vanishes at every integer zero of `f` in the box of radius `B` +lying in the residue classes `r`. -/ +structure SurfaceCongruenceFamily (f : MvPolynomial (Fin 3) ℤ) (B A : ℝ) where + /-- The auxiliary polynomial attached to a set `P` of primes and smooth residues modulo + `P`. -/ + auxiliary : (P : Finset ℕ) → (∀ p ∈ P, p.Prime) → + SmoothAffineResidueFamily f P → MvPolynomial (Fin 3) ℤ + /-- Every auxiliary is nonzero. -/ + nonzero : ∀ P hP r, auxiliary P hP r ≠ 0 + /-- No auxiliary is divisible by `f` over `ℚ`. -/ + proper : ∀ P hP r, ¬ map (Int.castRingHom ℚ) f ∣ + map (Int.castRingHom ℚ) (auxiliary P hP r) + /-- The degree of the auxiliary attached to `P` is at most + `A (B^(1/√d) / ∏ p + 1)(1 + log B + ∑ log p)`. -/ + degree_le : ∀ P hP r, ((auxiliary P hP r).totalDegree : ℝ) ≤ + A * (B ^ (1 / Real.sqrt (f.totalDegree : ℝ)) / + Real.exp (∑ p ∈ P, Real.log p) + 1) * (1 + Real.log B + ∑ p ∈ P, Real.log p) + /-- The auxiliary attached to `P` and `r` vanishes at the integer zeros of `f` in the box + that lie in the residue classes `r`. -/ + vanishes : ∀ P hP r x, x ∈ affineCongruencePoints f B P (smoothResidueIntegerLift r) → + eval x (auxiliary P hP r) = 0 + +/-- An absolutely irreducible integer surface equation `f` of positive degree, whose +coefficient height `H` satisfies `log H ≤ K (log B + 1)`, admits a congruence family of +auxiliaries with degree constant `surfaceCongruenceDegreeConstant d K`, where `d` is the degree +of `f`. -/ +theorem exists_surface_congruence_family + (f : MvPolynomial (Fin 3) ℤ) + (hir : AbsolutelyIrreducible f) + (hd : 0 < f.totalDegree) {K H B : ℝ} (hK : 0 ≤ K) (hH : 1 ≤ H) (hB : 1 ≤ B) + (hcoeff : CoefficientHeightLE f H) + (hheight : Real.log H ≤ K * (Real.log B + 1)) : + Nonempty (SurfaceCongruenceFamily f B (surfaceCongruenceDegreeConstant f.totalDegree K)) := by + classical + have hex (P : Finset ℕ) (hP : ∀ p ∈ P, p.Prime) (r : SmoothAffineResidueFamily f P) := + exists_surface_congruence_auxiliary f hir hd hK hH hB hcoeff hheight P hP + (smoothResidueIntegerLift r) + (fun p hp => by + let : NeZero p := ⟨(hP p hp).ne_zero⟩ + exact smoothResidueIntegerLift_zero r p hp) + (fun p hp => by + let : NeZero p := ⟨(hP p hp).ne_zero⟩ + exact smoothResidueIntegerLift_smooth r p hp) + choose g hg0 hgproper hgdegree hgzero using hex + exact ⟨⟨g, hg0, hgproper, hgdegree, hgzero⟩⟩ + +/-- The auxiliary polynomial of a congruence family attached to the empty set of primes: it +vanishes at every integer zero of `f` in the box. -/ +def SurfaceCongruenceFamily.initial {f : MvPolynomial (Fin 3) ℤ} {B A : ℝ} + (F : SurfaceCongruenceFamily f B A) : MvPolynomial (Fin 3) ℤ := + F.auxiliary ∅ (by simp) (emptySmoothAffineResidueFamily f) + +/-- The auxiliary attached to the empty set of primes is `F.initial`, whatever the (unique) +residue family and primality proof. -/ +lemma SurfaceCongruenceFamily.initial_eq {f : MvPolynomial (Fin 3) ℤ} {B A : ℝ} + (F : SurfaceCongruenceFamily f B A) (r : SmoothAffineResidueFamily f ∅) + (hP : ∀ p ∈ (∅ : Finset ℕ), p.Prime) : + F.auxiliary ∅ hP r = F.initial := by + have he : r = emptySmoothAffineResidueFamily f := by + funext p + exact False.elim (notMem_empty p.val p.property) + rw [he] + rfl + +/-- The initial auxiliary `F.initial` vanishes at every integer zero of `f` in the box of +radius `B`. -/ +lemma SurfaceCongruenceFamily.initial_vanishes {f : MvPolynomial (Fin 3) ℤ} {B A : ℝ} + (F : SurfaceCongruenceFamily f B A) (x : IntPoint 3) + (hx : InBox x B) (hxf : eval x f = 0) : + eval x F.initial = 0 := + F.vanishes ∅ (by simp) (emptySmoothAffineResidueFamily f) x + (mem_affineCongruencePoints.mpr ⟨hx, hxf, by simp⟩) + +/-- If an integer zero `x` of `f` in the box is a smooth point of `f` modulo every prime of `P`, +then the auxiliary attached to `P` and to the residues of `x` vanishes at `x`. -/ +lemma SurfaceCongruenceFamily.point_vanishes {f : MvPolynomial (Fin 3) ℤ} {B A : ℝ} + (F : SurfaceCongruenceFamily f B A) (x : IntPoint 3) + (hx : InBox x B) (hxf : eval x f = 0) + (P : Finset ℕ) (hP : ∀ p ∈ P, p.Prime) + (hs : ∀ p ∈ P, ∃ j, eval (fun i => (x i : ZMod p)) + (pderiv j (map (Int.castRingHom (ZMod p)) f)) ≠ 0) : + eval x (F.auxiliary P hP (smoothResidueFamilyOfPoint x hxf P hs)) = 0 := by + apply F.vanishes + apply mem_affineCongruencePoints.mpr + refine ⟨hx, hxf, ?_⟩ + intro p hp j + let : NeZero p := ⟨(hP p hp).ne_zero⟩ + exact smoothResidueFamilyOfPoint_congruence x hxf P hs p hp j + +end +end CCDN diff --git a/LeanPool/CCDN/Surface/CongruencePrimeWindow.lean b/LeanPool/CCDN/Surface/CongruencePrimeWindow.lean new file mode 100644 index 0000000000..e294386fc5 --- /dev/null +++ b/LeanPool/CCDN/Surface/CongruencePrimeWindow.lean @@ -0,0 +1,139 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.AuxiliaryPolynomial.ScalingPrime +import LeanPool.CCDN.AuxiliaryPolynomial.BadPrimeCharge + +/-! A fixed multiplicative prime window has enough product to avoid every +integer of prescribed logarithmic height, and only O(L/log L) primes. +These are the arithmetic inputs to the residue-class cover. -/ + +@[expose] public section +namespace CCDN +noncomputable section +open Finset Real + +/-- The primes `p` with `L < p ≤ c L`, where `c` is `scalingPrimeRatio`. -/ +def congruencePrimeWindow (L : ℝ) : Finset ℕ := + ((Ioc 0 ⌊scalingPrimeRatio * L⌋₊).filter Nat.Prime) \ + ((Ioc 0 ⌊L⌋₊).filter Nat.Prime) + +/-- For `L ≥ 1`, a natural number lies in `congruencePrimeWindow L` exactly when it is a prime +`p` with `L < p ≤ c L`, where `c` is `scalingPrimeRatio`. -/ +lemma mem_congruencePrimeWindow {L : ℝ} (hL : 1 ≤ L) {p : ℕ} : + p ∈ congruencePrimeWindow L ↔ + p.Prime ∧ L < (p : ℝ) ∧ (p : ℝ) ≤ scalingPrimeRatio * L := by + have hA := scalingPrimeRatio_one_le + have hAL : 0 ≤ scalingPrimeRatio * L := by positivity + constructor + · intro hp + obtain ⟨hpV, hpU⟩ := mem_sdiff.mp hp + have hprime := (mem_filter.mp hpV).2 + refine ⟨hprime, ?_, (prime_mem_upto_iff hAL hprime).mp hpV⟩ + exact lt_of_not_ge (fun h => hpU + ((prime_mem_upto_iff (by linarith) hprime).mpr h)) + · rintro ⟨hp, hlo, hhi⟩ + exact mem_sdiff.mpr ⟨(prime_mem_upto_iff hAL hp).mpr hhi, + fun hh => (not_le_of_gt hlo) ((prime_mem_upto_iff (by linarith) hp).mp hh)⟩ + +private lemma congruencePrimeWindow_weight_lower {L : ℝ} (hL : 1 ≤ L) : + 2 ≤ ∑ p ∈ congruencePrimeWindow L, Real.log p / p := by + have hA := scalingPrimeRatio_one_le + have hL0 : 0 < L := by linarith + have hA0 : 0 < scalingPrimeRatio := by linarith + have h := prime_interval_weight_lower hL + (show L ≤ scalingPrimeRatio * L by nlinarith) + rw [Real.log_mul hA0.ne' hL0.ne'] at h + have hlogA : Real.log scalingPrimeRatio = 2 * mertensError + 2 := Real.log_exp _ + rw [hlogA] at h + dsimp only [congruencePrimeWindow] + linarith + +private theorem congruencePrimeWindow_log_mass {L : ℝ} (hL : 1 ≤ L) : + 2 * L ≤ ∑ p ∈ congruencePrimeWindow L, Real.log p := by + have hL0 : 0 ≤ L := by linarith + have hm := mul_le_mul_of_nonneg_left (congruencePrimeWindow_weight_lower hL) hL0 + have hterm : L * (∑ p ∈ congruencePrimeWindow L, Real.log p / p) ≤ + ∑ p ∈ congruencePrimeWindow L, Real.log p := by + rw [mul_sum] + apply sum_le_sum + intro p hp + have hmem := (mem_congruencePrimeWindow hL).mp hp + have hp0 : (0 : ℝ) < p := by exact_mod_cast hmem.1.pos + calc + _ ≤ (p : ℝ) * (Real.log p / p) := + mul_le_mul_of_nonneg_right hmem.2.1.le (by positivity) + _ = _ := by field_simp + linarith + +/-- For `L ≥ 1`, the sum of `log p` over the primes of `congruencePrimeWindow L` is at most +`(log 4 + 4) c L`, where `c` is `scalingPrimeRatio`. -/ +theorem congruencePrimeWindow_log_upper {L : ℝ} (hL : 1 ≤ L) : + (∑ p ∈ congruencePrimeWindow L, Real.log p) ≤ + (Real.log 4 + 4) * scalingPrimeRatio * L := by + have hA := scalingPrimeRatio_one_le + have hs : (∑ p ∈ congruencePrimeWindow L, Real.log p) ≤ + Chebyshev.theta (scalingPrimeRatio * L) := + sum_le_sum_of_subset_of_nonneg sdiff_subset (fun p _ _ => Real.log_natCast_nonneg p) + exact hs.trans ((Chebyshev.theta_le_psi _).trans + (by simpa only [mul_assoc] using + Chebyshev.psi_le_const_mul_self (show 0 ≤ scalingPrimeRatio * L by positivity))) + +/-- For `L ≥ 1`, the number of primes in `congruencePrimeWindow L`, multiplied by `log L`, is +at most `(log 4 + 4) c L`, where `c` is `scalingPrimeRatio`. -/ +theorem congruencePrimeWindow_card_log_bound {L : ℝ} (hL : 1 ≤ L) : + (congruencePrimeWindow L).card * Real.log L ≤ + (Real.log 4 + 4) * scalingPrimeRatio * L := by + calc + _ = ∑ _p ∈ congruencePrimeWindow L, Real.log L := by simp + _ ≤ ∑ p ∈ congruencePrimeWindow L, Real.log p := by + apply sum_le_sum + intro p hp + exact Real.log_le_log (by linarith) ((mem_congruencePrimeWindow hL).mp hp).2.1.le + _ ≤ _ := congruencePrimeWindow_log_upper hL + +/-- Removing the prime divisors of D loses at most log|D| of logarithmic +mass. Thus every bounded nonsingular point retains a large smooth modulus +once D is chosen as a nonzero derivative value. -/ +private theorem congruencePrimeWindow_avoiding_log_mass {L : ℝ} (hL : 1 ≤ L) + {D : ℤ} (hD : D ≠ 0) : + 2 * L - Real.log |(D : ℝ)| ≤ + ∑ p ∈ (congruencePrimeWindow L).filter (fun p : ℕ => ¬ (p : ℤ) ∣ D), Real.log p := by + classical + let bad := (congruencePrimeWindow L).filter (fun p : ℕ => (p : ℤ) ∣ D) + have hbad : (∑ p ∈ bad, Real.log p) ≤ Real.log |(D : ℝ)| := by + have h := prime_divisor_log_sum_le bad (abs_pos.mpr hD) + (fun p hp => ((mem_congruencePrimeWindow hL).mp (mem_filter.mp hp).1).1) + (fun p hp => (dvd_abs _ _).mpr (mem_filter.mp hp).2) + simpa only [Int.cast_abs] using h + have hsplit := sum_filter_add_sum_filter_not (congruencePrimeWindow L) + (fun p : ℕ => (p : ℤ) ∣ D) (fun p : ℕ => Real.log p) + have htotal := congruencePrimeWindow_log_mass hL + change (∑ p ∈ bad, Real.log p) + _ = _ at hsplit + linarith + +/-- For `L ≥ 1` and a nonzero integer `D` with `log |D| ≤ L`, the product of the primes of +`congruencePrimeWindow L` not dividing `D` is at least `exp L`. -/ +theorem congruencePrimeWindow_avoiding_product {L : ℝ} (hL : 1 ≤ L) + {D : ℤ} (hD : D ≠ 0) (hheight : Real.log |(D : ℝ)| ≤ L) : + Real.exp L ≤ + ∏ p ∈ (congruencePrimeWindow L).filter (fun p : ℕ => ¬ (p : ℤ) ∣ D), (p : ℝ) := by + classical + have hm := congruencePrimeWindow_avoiding_log_mass hL hD + have hlo : L ≤ ∑ p ∈ (congruencePrimeWindow L).filter + (fun p : ℕ => ¬ (p : ℤ) ∣ D), Real.log p := by linarith + have hp (p : ℕ) (hp : p ∈ (congruencePrimeWindow L).filter + (fun p : ℕ => ¬ (p : ℤ) ∣ D)) : (0 : ℝ) < p := by + exact_mod_cast ((mem_congruencePrimeWindow hL).mp (mem_filter.mp hp).1).1.pos + rw [← Real.log_prod (fun p hp' => (hp p hp').ne')] at hlo + exact (Real.exp_le_exp.mpr hlo).trans_eq (Real.exp_log (prod_pos hp)) + +end +end CCDN diff --git a/LeanPool/CCDN/Surface/CurveComponents.lean b/LeanPool/CCDN/Surface/CurveComponents.lean new file mode 100644 index 0000000000..fa8d9de5c7 --- /dev/null +++ b/LeanPool/CCDN/Surface/CurveComponents.lean @@ -0,0 +1,47 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.Algebra.IdealZeroLocus +public import LeanPool.CCDN.Height.Box +public import LeanPool.CCDN.Hilbert.AffineCurveDegree +public import Mathlib.Analysis.SpecialFunctions.Pow.Real +public import Mathlib.RingTheory.Ideal.Height + +/-! Named bundles for the curve components of affine surfaces in three-space: an integral +curve of a given degree on a surface, and a positive-degree component of the intersection of +two surfaces. -/ + +@[expose] public section +namespace CCDN + +/-- `I` is an integral affine curve of degree `e` on the surface `f = 0` in affine +three-space: a height-two prime ideal containing `f` whose affine Hilbert function has +slope `e`. -/ +structure IsSurfaceCurve (f : CoordinatePolynomial 3) (I : Ideal (CoordinatePolynomial 3)) + (e : ℕ) : Prop where + /-- The ideal `I` is prime. -/ + isPrime : I.IsPrime + /-- The ideal `I` has height two, so it defines a curve. -/ + height_eq : I.height = 2 + /-- The curve lies on the surface: `f ∈ I`. -/ + mem : f ∈ I + /-- The curve has degree `e`. -/ + degree : HasAffineCurveDegree I e + +/-- `I` is a curve component, of positive degree `e`, of the intersection of the surfaces +`f = 0` and `g = 0` in affine three-space. -/ +structure IsSectionCurve (f g : CoordinatePolynomial 3) (I : Ideal (CoordinatePolynomial 3)) + (e : ℕ) : Prop extends IsSurfaceCurve f I e where + /-- The curve lies on the second surface: `g ∈ I`. -/ + mem_right : g ∈ I + /-- The degree `e` is positive. -/ + pos : 0 < e + +end CCDN diff --git a/LeanPool/CCDN/Surface/CurveCrossing.lean b/LeanPool/CCDN/Surface/CurveCrossing.lean new file mode 100644 index 0000000000..4de820c1d7 --- /dev/null +++ b/LeanPool/CCDN/Surface/CurveCrossing.lean @@ -0,0 +1,112 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.Hilbert.FilteredPrimeDecomposition +public import LeanPool.CCDN.Surface.CurveComponents +import LeanPool.CCDN.ForMathlib.KrullHeight +import LeanPool.CCDN.Plane.Bezout +import LeanPool.CCDN.Surface.CompleteIntersectionDegreeBudget +import LeanPool.CCDN.Surface.NonlinearCount + +/-! Degree and proper-section budgets for any finite family of distinct +prime curves on a surface section. A curve constructed by another +auxiliary is counted as a component of the section whenever its ideal +contains the section's equation. -/ + +@[expose] public section +namespace CCDN +noncomputable section + +private theorem height_two_prime_isMinimalPrime_section + (f g : CoordinatePolynomial 3) (hf : Irreducible f) (hfg : ¬ f ∣ g) + (P : Ideal (CoordinatePolynomial 3)) (hP : P.IsPrime) (hheight : P.height = 2) + (hfP : f ∈ P) (hgP : g ∈ P) : + (polynomialSectionIdeal f g).IsMinimalPrime P := by + have : P.IsPrime := hP + have hle : polynomialSectionIdeal f g ≤ P := by + apply Ideal.span_le.mpr + intro x hx + rcases hx with rfl | hx + · exact hfP + · have hxg : x = g := Set.mem_singleton_iff.mp hx + subst x + exact hgP + obtain ⟨Q, hQ, hQP⟩ := Ideal.exists_minimalPrimes_le hle + have : Q.IsPrime := hQ.isPrime + have hQheight : Q.height = 2 := Ideal.height_eq_two_of_mem_minimalPrimes_span_pair hf.prime hfg hQ + have hQP' : Q = P := by + by_contra hne + have hh := Ideal.height_add_one_le_of_lt_of_isPrime (lt_of_le_of_ne hQP hne) + rw [hQheight, hheight] at hh + norm_num at hh + subst Q + exact hQ + +/-- Distinct prime curves of height two contained in a proper surface +section have total degree at most the product of the equation degrees. -/ +theorem surface_prime_curve_degree_sum_le + (f g : CoordinatePolynomial 3) (hf : Irreducible f) (hfg : ¬ f ∣ g) + (S : Finset (Ideal (CoordinatePolynomial 3))) + (δ : Ideal (CoordinatePolynomial 3) → ℕ) + (hS : ∀ P ∈ S, IsSurfaceCurve f P (δ P) ∧ g ∈ P) : + ∑ P ∈ S, δ P ≤ f.totalDegree * g.totalDegree := by + classical + let T := minimalPrimeComponents (polynomialSectionIdeal f g) + obtain ⟨d, hd, hsum⟩ := polynomialSection_minimalPrime_degrees_exist f g hf hfg + let e (P : Ideal (CoordinatePolynomial 3)) := if hP : P ∈ T then d ⟨P, hP⟩ else 0 + have hST : S ⊆ T := by + intro P hPS + obtain ⟨⟨hP, hheight, hfP, _⟩, hgP⟩ := hS P hPS + exact mem_minimalPrimeComponents.mpr + (height_two_prime_isMinimalPrime_section f g hf hfg P hP hheight hfP hgP) + have he (P : T) : e P.val = d P := by + simp only [e, dite_eq_left P.property] + rfl + have heq (P) (hPS : P ∈ S) : δ P = e P := by + rw [he ⟨P, hST hPS⟩] + exact affineCurveDegree_unique (hS P hPS).1.degree (hd ⟨P, hST hPS⟩) + calc + _ = ∑ P ∈ S, e P := Finset.sum_congr rfl heq + _ ≤ ∑ P ∈ T, e P := + Finset.sum_le_sum_of_subset_of_nonneg hST (by intros; omega) + _ = ∑ P, d P := by + rw [← Finset.sum_coe_sort T e] + exact Finset.sum_congr rfl (fun P _ => he P) + _ ≤ _ := hsum + +open Classical in +/-- For curves `P ∈ S` on the irreducible surface `f = 0` whose ideals contain `g` (with `f` +not dividing `g`), the integral points of height at most `B` on `h = 0` lying on those curves +whose ideals do not contain `h` number at most `deg f * deg g * deg h`. -/ +theorem surface_curve_crossing_count_le + (f g h : CoordinatePolynomial 3) (hf : Irreducible f) (hfg : ¬ f ∣ g) + (S : Finset (Ideal (CoordinatePolynomial 3))) + (δ : Ideal (CoordinatePolynomial 3) → ℕ) + (hS : ∀ P ∈ S, IsSurfaceCurve f P (δ P) ∧ g ∈ P) (B : ℝ) : + count (⋃ P ∈ S.filter (fun P => h ∉ P), idealZeroLocus P ∩ zeroLocus h) B ≤ + f.totalDegree * g.totalDegree * h.totalDegree := by + classical + let T := S.filter (fun P => h ∉ P) + have hcurve (P) (hPT : P ∈ T) : + count (idealZeroLocus P ∩ zeroLocus h) B ≤ δ P * h.totalDegree := by + obtain ⟨hPS, hhP⟩ := Finset.mem_filter.mp hPT + have : P.IsPrime := (hS P hPS).1.isPrime + exact count_curve_section_le P (hS P hPS).1.degree h hhP B + have hbudget := surface_prime_curve_degree_sum_le f g hf hfg T δ + (fun P hP => hS P (Finset.mem_filter.mp hP).1) + calc + _ ≤ ∑ P ∈ T, count (idealZeroLocus P ∩ zeroLocus h) B := + count_biUnion_finset_le T _ B + _ ≤ ∑ P ∈ T, δ P * h.totalDegree := Finset.sum_le_sum hcurve + _ = (∑ P ∈ T, δ P) * h.totalDegree := (Finset.sum_mul _ _ _).symm + _ ≤ _ := Nat.mul_le_mul_right _ hbudget + +end +end CCDN diff --git a/LeanPool/CCDN/Surface/DegreeOneCount.lean b/LeanPool/CCDN/Surface/DegreeOneCount.lean new file mode 100644 index 0000000000..d4be5334ec --- /dev/null +++ b/LeanPool/CCDN/Surface/DegreeOneCount.lean @@ -0,0 +1,108 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.Algebra.IdealZeroLocus +public import LeanPool.CCDN.Algebra.PolynomialPredicates +public import LeanPool.CCDN.Hilbert.AffineCurveDegree +public import LeanPool.CCDN.Plane.IntegralAffineLine +public import Mathlib.RingTheory.Ideal.Height +import LeanPool.CCDN.Plane.DegreeOneAffineLine +import LeanPool.CCDN.Surface.LineContribution + +/-! +The proved line contribution applies to degree-one prime +components, with the original number of components as the additive cost. + +A finite family of degree-one prime components gives a finite +set of rational affine lines with the same rational zero-set union. Empty +rational loci are discarded, and repeated line sets are counted only once. +-/ + +@[expose] public section +namespace CCDN +noncomputable section + +/-- The union of the rational zero sets of finitely many degree-one affine curves in +three-space is a union of at most as many rational affine lines, each of which is the zero set +of one of the curves. -/ +theorem degree_one_components_line_cover + (S : Finset (Ideal (CoordinatePolynomial 3))) + (hcurve : ∀ P ∈ S, P.IsPrime ∧ P.height = 2 ∧ HasAffineCurveDegree P 1) : + ∃ lines : Finset (Set (RatPoint 3)), + (⋃ P ∈ S, idealZeroLocus P) = (⋃ L ∈ lines, L) ∧ + lines.card ≤ S.card ∧ + ∀ L ∈ lines, (∃ P ∈ S, L = idealZeroLocus P) ∧ + ∃ a v : RatPoint 3, v ≠ 0 ∧ L = rationalAffineLine a v := by + classical + let T := S.filter (fun P => (idealZeroLocus P).Nonempty) + let lines := T.image idealZeroLocus + refine ⟨lines, ?_, (Finset.card_image_le).trans (Finset.card_le_card + (Finset.filter_subset _ _)), ?_⟩ + · ext x + constructor + · intro hx + obtain ⟨P, hx⟩ := Set.mem_iUnion.mp hx + obtain ⟨hPS, hxP⟩ := Set.mem_iUnion.mp hx + have hPT : P ∈ T := Finset.mem_filter.mpr ⟨hPS, ⟨x, hxP⟩⟩ + exact Set.mem_iUnion.mpr ⟨idealZeroLocus P, + Set.mem_iUnion.mpr ⟨Finset.mem_image.mpr ⟨P, hPT, rfl⟩, hxP⟩⟩ + · intro hx + obtain ⟨L, hx⟩ := Set.mem_iUnion.mp hx + obtain ⟨hLs, hxL⟩ := Set.mem_iUnion.mp hx + obtain ⟨P, hPT, rfl⟩ := Finset.mem_image.mp hLs + exact Set.mem_iUnion.mpr ⟨P, + Set.mem_iUnion.mpr ⟨(Finset.mem_filter.mp hPT).1, hxL⟩⟩ + · intro L hL + obtain ⟨P, hPT, rfl⟩ := Finset.mem_image.mp hL + obtain ⟨hPS, z, hz⟩ := Finset.mem_filter.mp hPT + obtain ⟨hp, hheight, hdegree⟩ := hcurve P hPS + have : P.IsPrime := hp + obtain ⟨v, hv, he, _⟩ := + degree_one_zeroLocus_eq_rationalAffineLine P hheight hdegree z hz + exact ⟨⟨P, hPS, rfl⟩, z, v, hv, he⟩ + +end +end CCDN + +namespace CCDN +noncomputable section +open MvPolynomial + +/-- There is an absolute constant `C` such that, for an integer surface equation `f` of degree +`d ≥ 3` with absolutely irreducible top-degree form, the integral points of height at most `B` +on a finite family `S` of degree-one curves on the surface number at most +`C d⁶ B + #S`. -/ +theorem surface_degree_one_component_contribution : + ∃ C : ℝ, 0 < C ∧ + ∀ f : MvPolynomial (Fin 3) ℤ, + HasAbsIrreducibleTopForm f → + 3 ≤ f.totalDegree → ∀ B : ℝ, 0 ≤ B → + ∀ S : Finset (Ideal (CoordinatePolynomial 3)), + (∀ P ∈ S, P.IsPrime ∧ P.height = 2 ∧ HasAffineCurveDegree P 1) → + (∀ P ∈ S, map (Int.castRingHom ℚ) f ∈ P) → + (count (⋃ P ∈ S, idealZeroLocus P) B : ℝ) ≤ + C * (f.totalDegree : ℝ) ^ 6 * B + S.card := by + obtain ⟨C, hC, hline⟩ := surface_line_contribution + refine ⟨C, hC, ?_⟩ + intro f hir hd B hB S hcurve hSf + obtain ⟨lines, hcover, hcard, hlines⟩ := degree_one_components_line_cover S hcurve + have hh := (hline f hir hd B hB lines (by + intro L hL + obtain ⟨a, v, _, he⟩ := (hlines L hL).2 + exact ⟨a, v, he⟩) (by + intro L hL x hx + obtain ⟨P, hPS, rfl⟩ := (hlines L hL).1 + exact hx _ (hSf P hPS))).2 + rw [← hcover] at hh + have hcardR : (lines.card : ℝ) ≤ S.card := by exact_mod_cast hcard + exact hh.trans (add_le_add le_rfl hcardR) + +end +end CCDN diff --git a/LeanPool/CCDN/Surface/DirectionSum.lean b/LeanPool/CCDN/Surface/DirectionSum.lean new file mode 100644 index 0000000000..cfd6e0efda --- /dev/null +++ b/LeanPool/CCDN/Surface/DirectionSum.lean @@ -0,0 +1,182 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.Algebra.PolynomialPredicates +public import LeanPool.CCDN.Height.ProjectiveZeroLocus +import LeanPool.CCDN.AuxiliaryPolynomial.PlaneAuxiliaryCount +import LeanPool.CCDN.Plane.ProjectivePlaneLinearCount + +/-! The reciprocal heights of distinct rational projective points on a +plane curve of degree at least three have a uniform, coefficient-independent +sum. Geometric height bands turn the proved projective point count into +the weighted estimate used for surface-line directions. -/ + +@[expose] public section +namespace CCDN +noncomputable section +open MvPolynomial + +private theorem sum_inv_height_geometric_bound {α : Type*} + (s : Finset α) (h : α → ℝ) (M : ℝ) (_hM : 0 ≤ M) (N : ℕ) + (hheight : ∀ x ∈ s, 1 ≤ h x) + (hmax : ∀ x ∈ s, h x ≤ (8 : ℝ) ^ N) + (hcount : ∀ k : ℕ, + ((s.filter (fun x => h x ≤ (8 : ℝ) ^ k)).card : ℝ) ≤ M * 4 ^ k) : + ∑ x ∈ s, 1 / h x ≤ 9 * M - 8 * M * (1 / 2 : ℝ) ^ N := by + classical + induction N generalizing s with + | zero => + have he (x : α) (hx : x ∈ s) : h x = 1 := by + have := hmax x hx + have := hheight x hx + norm_num at * + linarith + have hs : s.filter (fun x => h x ≤ (8 : ℝ) ^ 0) = s := by + exact Finset.filter_eq_self.mpr hmax + have hc := hcount 0 + rw [hs] at hc + have hsum : ∑ x ∈ s, 1 / h x = (s.card : ℝ) := by + calc + _ = ∑ _x ∈ s, (1 : ℝ) := Finset.sum_congr rfl (fun x hx => by rw [he x hx]; norm_num) + _ = _ := by simp + rw [hsum] + simpa only [pow_zero, mul_one, show 9 * M - 8 * M = M by ring] using hc + | succ N ih => + let t := s.filter (fun x => h x ≤ (8 : ℝ) ^ N) + let u := s.filter (fun x => ¬h x ≤ (8 : ℝ) ^ N) + have ht : ∑ x ∈ t, 1 / h x ≤ 9 * M - 8 * M * (1 / 2 : ℝ) ^ N := by + apply ih t + · intro x hx + exact hheight x (Finset.mem_filter.mp hx).1 + · intro x hx + exact (Finset.mem_filter.mp hx).2 + · intro k + have hs : (t.filter (fun x => h x ≤ (8 : ℝ) ^ k)).card ≤ + (s.filter (fun x => h x ≤ (8 : ℝ) ^ k)).card := by + apply Finset.card_le_card + intro x hx + obtain ⟨hx, hk⟩ := Finset.mem_filter.mp hx + exact Finset.mem_filter.mpr ⟨(Finset.mem_filter.mp hx).1, hk⟩ + exact (show ((t.filter (fun x => h x ≤ (8 : ℝ) ^ k)).card : ℝ) ≤ + ((s.filter (fun x => h x ≤ (8 : ℝ) ^ k)).card : ℝ) by + exact_mod_cast hs).trans (hcount k) + have hscard : (s.card : ℝ) ≤ M * 4 ^ (N + 1) := by + have hs : s.filter (fun x => h x ≤ (8 : ℝ) ^ (N + 1)) = s := + Finset.filter_eq_self.mpr hmax + simpa only [hs] using hcount (N + 1) + have hu : ∑ x ∈ u, 1 / h x ≤ 4 * M * (1 / 2 : ℝ) ^ N := by + calc + _ ≤ ∑ _x ∈ u, 1 / (8 : ℝ) ^ N := by + apply Finset.sum_le_sum + intro x hx + exact one_div_le_one_div_of_le (by positivity) + (le_of_not_ge (Finset.mem_filter.mp hx).2) + _ = (u.card : ℝ) / (8 : ℝ) ^ N := by simp [div_eq_mul_inv] + _ ≤ (s.card : ℝ) / (8 : ℝ) ^ N := by + apply div_le_div_of_nonneg_right _ (by positivity) + exact_mod_cast Finset.card_le_card (Finset.filter_subset _ s) + _ ≤ (M * 4 ^ (N + 1)) / (8 : ℝ) ^ N := + div_le_div_of_nonneg_right hscard (by positivity) + _ = 4 * M * (1 / 2 : ℝ) ^ N := by + rw [pow_succ] + calc + _ = 4 * M * ((4 : ℝ) ^ N / (8 : ℝ) ^ N) := by ring + _ = _ := by rw [← div_pow]; norm_num + have he : (∑ x ∈ t, 1 / h x) + (∑ x ∈ u, 1 / h x) = + ∑ x ∈ s, 1 / h x := Finset.sum_filter_add_sum_filter_not _ _ _ + rw [pow_succ] + linarith + +/-- A finite weighted-count lemma with geometric counting thresholds. Its +application below obtains the threshold counts from the proved curve theorem. -/ +private theorem sum_inv_height_le_of_geometric_count {α : Type*} + (s : Finset α) (h : α → ℝ) (M : ℝ) (hM : 0 ≤ M) + (hheight : ∀ x ∈ s, 1 ≤ h x) + (hcount : ∀ k : ℕ, + ((s.filter (fun x => h x ≤ (8 : ℝ) ^ k)).card : ℝ) ≤ M * 4 ^ k) : + ∑ x ∈ s, 1 / h x ≤ 9 * M := by + classical + obtain ⟨N, hN⟩ := pow_unbounded_of_one_lt (∑ x ∈ s, h x) (show (1 : ℝ) < 8 by norm_num) + have hmax (x : α) (hx : x ∈ s) : h x ≤ (8 : ℝ) ^ N := by + have hs : h x ≤ ∑ x ∈ s, h x := Finset.single_le_sum + (fun y hy => (zero_le_one.trans (hheight y hy))) hx + exact hs.trans hN.le + have hh := sum_inv_height_geometric_bound s h M hM N hheight hmax hcount + have hnonneg : 0 ≤ 8 * M * (1 / 2 : ℝ) ^ N := by positivity + linarith + +private lemma eight_pow_two_thirds (k : ℕ) : + ((8 : ℝ) ^ k) ^ (2 / 3 : ℝ) = (4 : ℝ) ^ k := by + have he : (8 : ℝ) ^ (2 / 3 : ℝ) = 4 := by + calc + _ = ((2 : ℝ) ^ (3 : ℝ)) ^ (2 / 3 : ℝ) := by norm_num + _ = (2 : ℝ) ^ ((3 : ℝ) * (2 / 3 : ℝ)) := (Real.rpow_mul (by norm_num) _ _).symm + _ = 4 := by norm_num + rw [← Real.rpow_natCast_mul (by norm_num), mul_comm (k : ℝ), + Real.rpow_mul_natCast (by norm_num), he] + +/-- A uniform reciprocal-height sum for distinct rational projective +zeros. The constant precedes the equation, its degree and the finite set. -/ +private theorem projective_plane_reciprocal_height_sum : + ∃ C : ℝ, 0 < C ∧ + ∀ (d : ℕ), 3 ≤ d → + ∀ f : MvPolynomial (Fin 3) ℚ, f.IsHomogeneous d → Irreducible f → + ∀ s : Finset (ProjectivePoint 2), (∀ x ∈ s, x ∈ projectiveZeroLocus f) → + ∑ x ∈ s, 1 / x.mulHeight ≤ C * (d : ℝ) ^ 4 := by + classical + refine ⟨9 * projectivePlaneAbsoluteConstant, by + exact mul_pos (by norm_num) projectivePlaneAbsoluteConstant_pos, ?_⟩ + intro d hd f hf hir s hs + have hC := projectivePlaneAbsoluteConstant_pos + have hd0 : (0 : ℝ) < d := by exact_mod_cast (show 0 < d by omega) + have hd3 : (3 : ℝ) ≤ d := by exact_mod_cast hd + have h := sum_inv_height_le_of_geometric_count s (fun x => x.mulHeight) + (projectivePlaneAbsoluteConstant * (d : ℝ) ^ 4) + (by positivity) (fun x _ => x.one_le_mulHeight) ?_ + · convert h using 1 + ring + intro k + have hB : (1 : ℝ) ≤ 8 ^ k := one_le_pow₀ (by norm_num) + have hc : (s.filter (fun x => x.mulHeight ≤ (8 : ℝ) ^ k)).card ≤ + projectiveCount (projectiveZeroLocus f) ((8 : ℝ) ^ k) := by + apply Finset.card_le_card + intro x hx + obtain ⟨hxs, hheight⟩ := Finset.mem_filter.mp hx + exact mem_projectivePoints.mpr ⟨hheight, hs x hxs⟩ + have hcR : ((s.filter (fun x => x.mulHeight ≤ (8 : ℝ) ^ k)).card : ℝ) ≤ + (projectiveCount (projectiveZeroLocus f) ((8 : ℝ) ^ k) : ℝ) := by exact_mod_cast hc + have hp : ((8 : ℝ) ^ k) ^ (2 / (d : ℝ)) ≤ (4 : ℝ) ^ k := by + calc + _ ≤ ((8 : ℝ) ^ k) ^ (2 / 3 : ℝ) := Real.rpow_le_rpow_of_exponent_le hB + ((div_le_div_iff₀ hd0 (by norm_num)).mpr (by nlinarith)) + _ = _ := eight_pow_two_thirds k + exact (hcR.trans (projective_plane_equation_bound f hf (by omega) hir hB)).trans + (mul_le_mul_of_nonneg_left hp (by positivity)) + +/-- There is an absolute constant `C` such that, for every absolutely irreducible integer +homogeneous polynomial `F` of degree `d ≥ 3` in three variables, the sum of the reciprocal +multiplicative heights of any finite set of rational points of the plane curve `F = 0` is at +most `C d⁴`; no primitivity or coefficient-height hypothesis is needed. -/ +theorem integer_projective_plane_reciprocal_height_sum : + ∃ C : ℝ, 0 < C ∧ + ∀ (d : ℕ), 3 ≤ d → + ∀ F : MvPolynomial (Fin 3) ℤ, F.IsHomogeneous d → + AbsolutelyIrreducible F → + ∀ s : Finset (ProjectivePoint 2), + (∀ x ∈ s, x ∈ projectiveZeroLocus (map (Int.castRingHom ℚ) F)) → + ∑ x ∈ s, 1 / x.mulHeight ≤ C * (d : ℝ) ^ 4 := by + obtain ⟨C, hC, hbound⟩ := projective_plane_reciprocal_height_sum + refine ⟨C, hC, ?_⟩ + intro d hd F hF hir s hs + exact hbound d hd (map (Int.castRingHom ℚ) F) (hF.map _) + (rational_irreducible_of_charZero_map F hir) s hs + +end +end CCDN diff --git a/LeanPool/CCDN/Surface/DirectionalDerivative.lean b/LeanPool/CCDN/Surface/DirectionalDerivative.lean new file mode 100644 index 0000000000..bdd8604251 --- /dev/null +++ b/LeanPool/CCDN/Surface/DirectionalDerivative.lean @@ -0,0 +1,277 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.Algebra.AffineLineLeading +public import LeanPool.CCDN.Algebra.HomogeneousTranslation +public import LeanPool.CCDN.Algebra.IdealZeroLocus +public import LeanPool.CCDN.Algebra.PolynomialPredicates +public import LeanPool.CCDN.Hilbert.AffineCurveDegree +public import LeanPool.CCDN.Plane.IntegralAffineLine +import LeanPool.CCDN.Algebra.BinaryHomogeneousIrreducibility +import LeanPool.CCDN.Determinant.PlaneSingularities +import LeanPool.CCDN.ForMathlib.MvPolynomialDerivative +import LeanPool.CCDN.Surface.LineDirections +import LeanPool.CCDN.Surface.TopFormIrreducibility + +/-! Directional derivatives of surfaces with absolutely irreducible top form. -/ + +@[expose] public section +namespace CCDN +noncomputable section +open MvPolynomial + +/-- The derivative `∑ i, v i * ∂f/∂xᵢ` of the polynomial `f` in the direction `v`. -/ +def directionalDerivative {R σ : Type*} [CommRing R] [Fintype σ] + (v : σ → R) (f : MvPolynomial σ R) : MvPolynomial σ R := + ∑ i, C (v i) * pderiv i f + +/-- A directional derivative of `f` has total degree at most `f.totalDegree - 1`. -/ +lemma directionalDerivative_totalDegree_le {K σ : Type*} [Field K] [Fintype σ] + (v : σ → K) (f : MvPolynomial σ K) : + (directionalDerivative v f).totalDegree ≤ f.totalDegree - 1 := by + classical + apply totalDegree_finsetSum_le + intro i _ + exact (totalDegree_mul _ _).trans (by simpa using pderiv_totalDegree_le f i) + +private lemma directionalDerivative_map {R S σ : Type*} [CommRing R] [CommRing S] [Fintype σ] + (φ : R →+* S) (v : σ → R) (f : MvPolynomial σ R) : + directionalDerivative (fun i => φ (v i)) (map φ f) = map φ (directionalDerivative v f) := by + classical + simp [directionalDerivative, pderiv_map] + +private lemma pderiv_homogeneousComponent {R σ : Type*} [CommRing R] + (i : σ) (f : MvPolynomial σ R) {d : ℕ} (hd : 0 < d) : + pderiv i (homogeneousComponent d f) = homogeneousComponent (d - 1) (pderiv i f) := by + classical + ext m + rw [coeff_pderiv, coeff_homogeneousComponent, coeff_homogeneousComponent, coeff_pderiv] + have he : (m + Finsupp.single i 1).degree = m.degree + 1 := by + change (m + Finsupp.single i 1).sum (fun _ e => e) = m.sum (fun _ e => e) + 1 + rw [Finsupp.sum_add_index (by simp) (by intros; rfl)] + simp + rw [he] + by_cases hm : m.degree = d - 1 + · simp [hm, Nat.sub_add_cancel hd] + · have hn : m.degree + 1 ≠ d := by omega + simp [hm, hn] + +private lemma directionalDerivative_homogeneousComponent {R σ : Type*} [CommRing R] [Fintype σ] + (v : σ → R) (f : MvPolynomial σ R) {d : ℕ} (hd : 0 < d) : + directionalDerivative v (homogeneousComponent d f) = + homogeneousComponent (d - 1) (directionalDerivative v f) := by + classical + simp only [directionalDerivative, map_sum, homogeneousComponent_C_mul, + pderiv_homogeneousComponent _ _ hd] + +private lemma irreducible_of_rename_succ {K : Type*} [Field K] {n : ℕ} + (f : MvPolynomial (Fin n) K) (hf : Irreducible (rename Fin.succ f)) : + Irreducible f := by + have he (g : MvPolynomial (Fin n) K) : + eval₂Hom C (Fin.cons 0 X) (rename Fin.succ g) = g := by + rw [eval₂Hom_rename] + have hh : Fin.cons (0 : MvPolynomial (Fin n) K) X ∘ Fin.succ = X := by + funext i + simp + rw [hh] + simp + refine ⟨fun h => hf.not_isUnit (h.map (rename Fin.succ)), ?_⟩ + intro a b hab + have h := hf.isUnit_or_isUnit (by rw [hab, map_mul]) + rcases h with ha | hb + · exact Or.inl (by simpa only [he] using ha.map (eval₂Hom C (Fin.cons 0 X))) + · exact Or.inr (by simpa only [he] using hb.map (eval₂Hom C (Fin.cons 0 X))) + +private lemma ternary_homogeneous_irreducible_pderiv_zero_ne_zero + {K : Type*} [Field K] [CharZero K] [IsAlgClosed K] {d : ℕ} + (f : MvPolynomial (Fin 3) K) (hf : f.IsHomogeneous d) (hir : Irreducible f) + (hd : 2 ≤ d) : pderiv 0 f ≠ 0 := by + intro hz + have hv := notMem_vars_of_pderiv_eq_zero hz fun n hn _ ↦ Nat.cast_ne_zero.mpr hn.ne' + have hs : ↑f.vars ⊆ Set.range (Fin.succ : Fin 2 → Fin 3) := by + intro i hi + revert hi + refine Fin.cases ?_ (fun j => ?_) i + · exact fun hi => False.elim (hv hi) + · exact fun _ => ⟨j, rfl⟩ + obtain ⟨g, hg⟩ := exists_rename_eq_of_vars_subset_range f Fin.succ (Fin.succ_injective 2) hs + have hgh : g.IsHomogeneous d := + (IsHomogeneous.rename_isHomogeneous_iff (Fin.succ_injective 2)).mp (hg ▸ hf) + have hgi : Irreducible g := irreducible_of_rename_succ g (hg ▸ hir) + have := binary_homogeneous_irreducible_degree_le_one g hgh hgi + omega + +private lemma homogeneousTranslation_directionalDerivative {K : Type*} [Field K] {n : ℕ} + (v : Fin (n + 1) → K) (hv : v 0 ≠ 0) (f : MvPolynomial (Fin (n + 1)) K) : + C (v 0) * pderiv 0 (homogeneousTranslation K (fun j => v j.succ / v 0) f) = + homogeneousTranslation K (fun j => v j.succ / v 0) (directionalDerivative v f) := by + classical + let e := homogeneousTranslation K (fun j => v j.succ / v 0) + have hX (i : Fin (n + 1)) : C (v 0) * pderiv 0 (e (X i)) = C (v i) := by + refine Fin.cases ?_ (fun j => ?_) i + · simp [e, homogeneousTranslation, homogeneousTranslationHom] + · simp only [e, homogeneousTranslation, homogeneousTranslationHom, AlgEquiv.ofAlgHom_apply, + aeval_X, Fin.cons_succ, map_add, pderiv_mul, pderiv_X_of_ne (Fin.succ_ne_zero j), pderiv_C, + zero_mul, pderiv_X_self, mul_one, zero_add] + rw [← C_mul] + congr 1 + field_simp + change C (v 0) * pderiv 0 (e f) = e (directionalDerivative v f) + have hchain := MvPolynomial.pderiv_algHom e.toAlgHom f 0 + change pderiv 0 (e f) = ∑ i, e (pderiv i f) * pderiv 0 (e (X i)) at hchain + rw [hchain, Finset.mul_sum] + simp only [directionalDerivative, map_sum, map_mul] + apply Finset.sum_congr rfl + intro i _ + have hC : e (C (v i)) = C (v i) := e.commutes _ + rw [hC] + calc + C (v 0) * (e (pderiv i f) * pderiv 0 (e (X i))) = + (C (v 0) * pderiv 0 (e (X i))) * e (pderiv i f) := by ring + _ = _ := by rw [hX] + +private lemma directionalDerivative_renameEquiv {K σ : Type*} [CommRing K] [Fintype σ] + (e : σ ≃ σ) (v : σ → K) (f : MvPolynomial σ K) : + directionalDerivative (fun i => v (e.symm i)) (rename e f) = + rename e (directionalDerivative v f) := by + classical + unfold directionalDerivative + rw [← e.sum_comp] + simp only [Equiv.symm_apply_apply, pderiv_rename e.injective, map_sum, map_mul, rename_C] + +private lemma homogeneous_surface_directionalDerivative_ne_zero + {K : Type*} [Field K] [CharZero K] [IsAlgClosed K] {d : ℕ} + (f : MvPolynomial (Fin 3) K) (hf : f.IsHomogeneous d) (hir : Irreducible f) + (hd : 2 ≤ d) (v : Fin 3 → K) (hv : v ≠ 0) : directionalDerivative v f ≠ 0 := by + classical + have hex : ∃ j, v j ≠ 0 := by + by_contra h + push Not at h + exact hv (funext h) + obtain ⟨j, hj⟩ := hex + let e : Fin 3 ≃ Fin 3 := Equiv.swap j 0 + let w : Fin 3 → K := fun i => v (e.symm i) + have hw : w 0 ≠ 0 := by simpa [w, e] using hj + let F := rename e f + have hF : F.IsHomogeneous d := hf.rename_isHomogeneous + have hiF : Irreducible F := + (MulEquiv.irreducible_iff (f := (renameEquiv K e).toMulEquiv)).mpr hir + let a : Fin 2 → K := fun i => w i.succ / w 0 + have hG : (homogeneousTranslation K a F).IsHomogeneous d := + homogeneousTranslation_homogeneous a hF + have hiG : Irreducible (homogeneousTranslation K a F) := + (MulEquiv.irreducible_iff (f := (homogeneousTranslation K a).toMulEquiv)).mpr hiF + intro hz + have hzw : directionalDerivative w F = 0 := by + change directionalDerivative (fun i => v (e.symm i)) (rename e f) = 0 + rw [directionalDerivative_renameEquiv, hz, map_zero] + have h := homogeneousTranslation_directionalDerivative w hw F + rw [hzw, map_zero] at h + exact (ternary_homogeneous_irreducible_pderiv_zero_ne_zero _ hG hiG hd) + ((mul_eq_zero.mp h).resolve_left (by simpa using hw)) + +private theorem top_irreducible_directionalDerivative_ne_zero + {K L : Type*} [Field K] [Field L] [CharZero L] [IsAlgClosed L] + (φ : K →+* L) (f : MvPolynomial (Fin 3) K) {d : ℕ} + (hir : Irreducible (map φ (homogeneousComponent d f))) (hd : 2 ≤ d) + (v : Fin 3 → K) (hv : v ≠ 0) : directionalDerivative v f ≠ 0 := by + have hhom : (map φ (homogeneousComponent d f)).IsHomogeneous d := by + rw [map_homogeneousComponent] + exact homogeneousComponent_isHomogeneous _ _ + have hvm : (fun i => φ (v i)) ≠ 0 := by + intro hh + apply hv + funext i + apply φ.injective + simpa using congrFun hh i + intro hz + have hh := homogeneous_surface_directionalDerivative_ne_zero _ hhom hir hd + (fun i => φ (v i)) hvm + apply hh + rw [directionalDerivative_map, directionalDerivative_homogeneousComponent v f (by omega), + hz, map_zero, map_zero] + +/-- If the integer surface equation `f` has degree at least two and an absolutely irreducible +top-degree form, then its derivative over `ℚ` in every nonzero direction `v` is nonzero. -/ +theorem surface_directionalDerivative_ne_zero + (f : MvPolynomial (Fin 3) ℤ) + (hir : HasAbsIrreducibleTopForm f) (hd : 2 ≤ f.totalDegree) + (v : Fin 3 → ℚ) (hv : v ≠ 0) : + directionalDerivative v (map (Int.castRingHom ℚ) f) ≠ 0 := by + apply top_irreducible_directionalDerivative_ne_zero (algebraMap ℚ (AlgebraicClosure ℚ)) + (map (Int.castRingHom ℚ) f) (d := f.totalDegree) _ hd v hv + rw [← map_homogeneousComponent, map_map] + convert hir using 1 + congr 1 + +/-- Every directional derivative of a constant polynomial is zero. -/ +@[simp] lemma directionalDerivative_C {R σ : Type*} [CommRing R] [Fintype σ] + (v : σ → R) (a : R) : directionalDerivative v (C a) = 0 := by + simp [directionalDerivative] + +/-- The derivative of the coordinate `X j` in the direction `v` is the constant `v j`. -/ +@[simp] lemma directionalDerivative_X {R σ : Type*} [CommRing R] [Fintype σ] + (v : σ → R) (j : σ) : directionalDerivative v (X j) = C (v j) := by + classical + simp [directionalDerivative, pderiv_X, Pi.single_apply] + +private lemma directionalDerivative_add {R σ : Type*} [CommRing R] [Fintype σ] + (v : σ → R) (f g : MvPolynomial σ R) : + directionalDerivative v (f + g) = directionalDerivative v f + directionalDerivative v g := by + simp [directionalDerivative, mul_add, Finset.sum_add_distrib] + +private lemma directionalDerivative_mul {R σ : Type*} [CommRing R] [Fintype σ] + (v : σ → R) (f g : MvPolynomial σ R) : + directionalDerivative v (f * g) = + directionalDerivative v f * g + f * directionalDerivative v g := by + classical + simp only [directionalDerivative, pderiv_mul, mul_add, Finset.sum_add_distrib, + Finset.sum_mul, Finset.mul_sum] + congr 1 <;> apply Finset.sum_congr rfl <;> intro i _ <;> ring + +private lemma derivative_affineLinePolynomial {R σ : Type*} [CommRing R] [Fintype σ] + (a v : σ → R) (f : MvPolynomial σ R) : + Polynomial.derivative (affineLinePolynomial a v f) = + affineLinePolynomial a v (directionalDerivative v f) := by + classical + induction f using MvPolynomial.induction_on with + | C r => simp [affineLinePolynomial] + | add f g hf hg => simp only [map_add, directionalDerivative_add, hf, hg] + | mul_X f j hf => + rw [map_mul, Polynomial.derivative_mul, hf, directionalDerivative_mul, + map_add, map_mul, map_mul, directionalDerivative_X] + simp [affineLinePolynomial, Polynomial.derivative_add, Polynomial.derivative_mul] + +private theorem directionalDerivative_zero_on_line {K σ : Type*} [Field K] [Infinite K] [Fintype σ] + (a v : σ → K) (f : MvPolynomial σ K) + (hline : ∀ t : K, eval (a + t • v) f = 0) : + ∀ t : K, eval (a + t • v) (directionalDerivative v f) = 0 := by + have hpoly : affineLinePolynomial a v f = 0 := by + apply Polynomial.zero_of_eval_zero + intro t + rw [eval_affineLinePolynomial] + exact hline t + have hder := derivative_affineLinePolynomial a v f + rw [hpoly, Polynomial.derivative_zero] at hder + intro t + rw [← eval_affineLinePolynomial, ← hder] + simp + +/-- If the rational affine line through `a` with direction `v` lies on the hypersurface +`f = 0`, then it also lies on the zero set of the derivative of `f` in the direction `v`. -/ +theorem rationalAffineLine_contained_directionalDerivative {n : ℕ} + (a v : RatPoint n) (f : CoordinatePolynomial n) + (hline : rationalAffineLine a v ⊆ zeroLocus f) : + rationalAffineLine a v ⊆ zeroLocus (directionalDerivative v f) := by + rintro z ⟨t, rfl⟩ + exact directionalDerivative_zero_on_line a v f (fun t => hline ⟨t, rfl⟩) t + +end +end CCDN diff --git a/LeanPool/CCDN/Surface/LineContribution.lean b/LeanPool/CCDN/Surface/LineContribution.lean new file mode 100644 index 0000000000..fcf8bb2dbc --- /dev/null +++ b/LeanPool/CCDN/Surface/LineContribution.lean @@ -0,0 +1,166 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.Algebra.PolynomialPredicates +public import LeanPool.CCDN.Plane.IntegralAffineLine +import LeanPool.CCDN.Surface.DirectionSum +import LeanPool.CCDN.Surface.LineDirections +import LeanPool.CCDN.Surface.NonlinearCount +import LeanPool.CCDN.Surface.ParallelLines + +/-! Finite families of rational affine lines on a surface. Grouping +lines by their projective direction combines the primitive spacing bound +with the uniform reciprocal-height sum. -/ + +@[expose] public section +namespace CCDN +noncomputable section +open MvPolynomial + +/-- Weighted finite sums with a uniform bound on the cardinality of each +fiber. No injectivity of the indexing map is required. -/ +private theorem sum_comp_le_fiber_card_mul {α β : Type*} [DecidableEq β] + (s : Finset α) (p : α → β) (w : β → ℝ) (M : ℝ) + (hw : ∀ q ∈ s.image p, 0 ≤ w q) + (hfiber : ∀ q ∈ s.image p, ((s.filter (fun x => p x = q)).card : ℝ) ≤ M) : + ∑ x ∈ s, w (p x) ≤ M * ∑ q ∈ s.image p, w q := by + classical + calc + _ = ∑ q ∈ s.image p, ∑ x ∈ s.filter (fun x => p x = q), w (p x) := + (Finset.sum_fiberwise_of_maps_to (fun x hx => Finset.mem_image_of_mem p hx) _).symm + _ ≤ ∑ q ∈ s.image p, M * w q := by + apply Finset.sum_le_sum + intro q hq + calc + _ = ∑ _x ∈ s.filter (fun x => p x = q), w q := + Finset.sum_congr rfl (fun x hx => by rw [(Finset.mem_filter.mp hx).2]) + _ = ((s.filter (fun x => p x = q)).card : ℝ) * w q := by simp + _ ≤ M * w q := mul_le_mul_of_nonneg_right (hfiber q hq) (hw q hq) + _ = _ := (Finset.mul_sum _ _ _).symm + +/-- The aggregation step, stated separately so that the geometric +parallel-line theorem can supply its only remaining input. -/ +private theorem surface_line_contribution_of_parallel_bound : + ∃ C : ℝ, 0 < C ∧ + ∀ f : MvPolynomial (Fin 3) ℤ, + HasAbsIrreducibleTopForm f → + 3 ≤ f.totalDegree → + ∀ (B : ℝ), 0 ≤ B → + ∀ s : Finset (Set (RatPoint 3)), + (∀ L ∈ s, ∃ a v, L = rationalAffineLine a v) → + (∀ L ∈ s, L ⊆ zeroLocus (map (Int.castRingHom ℚ) f)) → + (∀ (v : RatPoint 3), v ≠ 0 → + ∀ t : Finset (Set (RatPoint 3)), t ⊆ s → + (∀ L ∈ t, ∃ a, L = rationalAffineLine a v) → + t.card ≤ f.totalDegree * (f.totalDegree - 1)) → + (∑ L ∈ s, (count L B : ℝ)) ≤ C * (f.totalDegree : ℝ) ^ 6 * B + s.card ∧ + (count (⋃ L ∈ s, L) B : ℝ) ≤ C * (f.totalDegree : ℝ) ^ 6 * B + s.card := by + classical + obtain ⟨K, hK, hweight⟩ := integer_projective_plane_reciprocal_height_sum + refine ⟨4 * K, by positivity, ?_⟩ + intro f hir hd B hB s hlines hsurface hparallel + let t := s.filter (fun L => 1 < count L B) + have hdir (L : Set (RatPoint 3)) (hL : L ∈ t) : + ∃ p : ProjectivePoint 2, + p ∈ projectiveZeroLocus (map (Int.castRingHom ℚ) (homogeneousComponent f.totalDegree f)) ∧ + 0 < p.mulHeight ∧ p.mulHeight ≤ 2 * B ∧ + (∃ a, L = rationalAffineLine a p.rep) ∧ + (count L B : ℝ) ≤ 4 * B / p.mulHeight := + surface_line_projective_direction f L (hlines L (Finset.mem_filter.mp hL).1) + (hsurface L (Finset.mem_filter.mp hL).1) hB (Finset.mem_filter.mp hL).2 + let p : Set (RatPoint 3) → ProjectivePoint 2 := fun L => + if hL : L ∈ t then (hdir L hL).choose else Classical.choice inferInstance + have hp (L : Set (RatPoint 3)) (hL : L ∈ t) : + p L ∈ projectiveZeroLocus (map (Int.castRingHom ℚ) (homogeneousComponent f.totalDegree f)) ∧ + 0 < (p L).mulHeight ∧ (p L).mulHeight ≤ 2 * B ∧ + (∃ a, L = rationalAffineLine a (p L).rep) ∧ + (count L B : ℝ) ≤ 4 * B / (p L).mulHeight := by + simpa only [p, dite_eq_left hL] using (hdir L hL).choose_spec + have hfib (q : ProjectivePoint 2) (_hq : q ∈ t.image p) : + ((t.filter (fun L => p L = q)).card : ℝ) ≤ (f.totalDegree : ℝ) ^ 2 := by + have hc := hparallel q.rep q.rep_nonzero (t.filter (fun L => p L = q)) + (fun L hL => (Finset.mem_filter.mp (Finset.mem_filter.mp hL).1).1) (by + intro L hL + obtain ⟨hLt, he⟩ := Finset.mem_filter.mp hL + obtain ⟨-, -, -, hLdir, -⟩ := hp L hLt + simpa only [he] using hLdir) + have hdle : f.totalDegree * (f.totalDegree - 1) ≤ f.totalDegree ^ 2 := by + nlinarith [Nat.sub_le f.totalDegree 1] + exact_mod_cast hc.trans hdle + have himage : ∑ q ∈ t.image p, 1 / q.mulHeight ≤ K * (f.totalDegree : ℝ) ^ 4 := by + apply hweight f.totalDegree hd (homogeneousComponent f.totalDegree f) + (homogeneousComponent_isHomogeneous _ _) hir + intro q hq + obtain ⟨L, hLt, rfl⟩ := Finset.mem_image.mp hq + exact (hp L hLt).1 + have hsum : ∑ L ∈ t, 1 / (p L).mulHeight ≤ K * (f.totalDegree : ℝ) ^ 6 := by + calc + _ ≤ (f.totalDegree : ℝ) ^ 2 * ∑ q ∈ t.image p, 1 / q.mulHeight := + sum_comp_le_fiber_card_mul t p (fun q => 1 / q.mulHeight) _ + (fun q _ => by positivity) hfib + _ ≤ (f.totalDegree : ℝ) ^ 2 * (K * (f.totalDegree : ℝ) ^ 4) := + mul_le_mul_of_nonneg_left himage (by positivity) + _ = _ := by ring + have hgood : ∑ L ∈ t, (count L B : ℝ) ≤ (4 * K) * (f.totalDegree : ℝ) ^ 6 * B := by + calc + _ ≤ ∑ L ∈ t, 4 * B / (p L).mulHeight := + Finset.sum_le_sum (fun L hL => by obtain ⟨-, -, -, -, hLcount⟩ := hp L hL; exact hLcount) + _ = 4 * B * ∑ L ∈ t, 1 / (p L).mulHeight := by + rw [Finset.mul_sum] + apply Finset.sum_congr rfl + intros + ring + _ ≤ 4 * B * (K * (f.totalDegree : ℝ) ^ 6) := + mul_le_mul_of_nonneg_left hsum (by positivity) + _ = _ := by ring + have hbad : ∑ L ∈ s.filter (fun L => ¬1 < count L B), (count L B : ℝ) ≤ s.card := by + calc + _ ≤ ∑ _L ∈ s.filter (fun L => ¬1 < count L B), (1 : ℝ) := by + apply Finset.sum_le_sum + intro L hL + exact_mod_cast (show count L B ≤ 1 from Nat.le_of_not_gt (Finset.mem_filter.mp hL).2) + _ = ((s.filter (fun L => ¬1 < count L B)).card : ℝ) := by simp + _ ≤ s.card := by exact_mod_cast Finset.card_le_card (Finset.filter_subset _ _) + have hall : ∑ L ∈ s, (count L B : ℝ) ≤ + (4 * K) * (f.totalDegree : ℝ) ^ 6 * B + s.card := by + have he : (∑ L ∈ t, (count L B : ℝ)) + + (∑ L ∈ s.filter (fun L => ¬1 < count L B), (count L B : ℝ)) = + ∑ L ∈ s, (count L B : ℝ) := Finset.sum_filter_add_sum_filter_not _ _ _ + linarith + refine ⟨hall, ?_⟩ + have hc : (count (⋃ L ∈ s, L) B : ℝ) ≤ ∑ L ∈ s, (count L B : ℝ) := by + exact_mod_cast count_biUnion_finset_le s (fun L => L) B + exact hc.trans hall + +/-- Corrected CCDN Proposition 4.3.3 for a finite set of rational +affine lines. Absolute irreducibility of the leading form supplies the +parallel-line bound inside the proof. The constant is chosen before the +surface, its degree, the radius and the family of lines. -/ +theorem surface_line_contribution : + ∃ C : ℝ, 0 < C ∧ + ∀ f : MvPolynomial (Fin 3) ℤ, + HasAbsIrreducibleTopForm f → + 3 ≤ f.totalDegree → + ∀ (B : ℝ), 0 ≤ B → + ∀ s : Finset (Set (RatPoint 3)), + (∀ L ∈ s, ∃ a v, L = rationalAffineLine a v) → + (∀ L ∈ s, L ⊆ zeroLocus (map (Int.castRingHom ℚ) f)) → + (∑ L ∈ s, (count L B : ℝ)) ≤ C * (f.totalDegree : ℝ) ^ 6 * B + s.card ∧ + (count (⋃ L ∈ s, L) B : ℝ) ≤ C * (f.totalDegree : ℝ) ^ 6 * B + s.card := by + obtain ⟨C, hC, hbound⟩ := surface_line_contribution_of_parallel_bound + refine ⟨C, hC, ?_⟩ + intro f hir hd B hB s hlines hsurface + apply hbound f hir hd B hB s hlines hsurface + intro v hv t ht htv + exact surface_parallel_lines_card_le f hir (by omega) v hv t htv + (fun L hL => hsurface L (ht hL)) + +end +end CCDN diff --git a/LeanPool/CCDN/Surface/LineDirections.lean b/LeanPool/CCDN/Surface/LineDirections.lean new file mode 100644 index 0000000000..e226788000 --- /dev/null +++ b/LeanPool/CCDN/Surface/LineDirections.lean @@ -0,0 +1,112 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.Algebra.AffineLineLeading +public import LeanPool.CCDN.Algebra.IdealZeroLocus +public import LeanPool.CCDN.Height.ProjectiveZeroLocus +public import LeanPool.CCDN.Hilbert.AffineCurveDegree +public import LeanPool.CCDN.Plane.IntegralAffineLine +import LeanPool.CCDN.ForMathlib.MvPolynomial +import LeanPool.CCDN.Surface.TopFormIrreducibility + +/-! Directions of rational affine lines on a surface. Lines with two +bounded integer points have a bounded projective direction and a spacing +bound expressed using its representative-independent projective height. -/ + +@[expose] public section +namespace CCDN +noncomputable section +open MvPolynomial + +/-- Evaluating the restriction `affineLinePolynomial a v f` of `f` to the line through `a` with +direction `v` at `t` gives the value of `f` at `a + t • v`. -/ +lemma eval_affineLinePolynomial {K σ : Type*} [Field K] + (a v : σ → K) (f : MvPolynomial σ K) (t : K) : + (affineLinePolynomial a v f).eval t = eval (a + t • v) f := by + have he : (Polynomial.evalRingHom t).comp (affineLinePolynomial a v) = + eval (a + t • v) := by + apply MvPolynomial.ringHom_ext + · intro r + simp [affineLinePolynomial] + · intro i + simp [affineLinePolynomial] + ring + exact RingHom.congr_fun he f + +private theorem affineLinePolynomial_eq_zero_of_contained {n : ℕ} + (a v : RatPoint n) (f : CoordinatePolynomial n) + (hline : rationalAffineLine a v ⊆ zeroLocus f) : + affineLinePolynomial a v f = 0 := by + apply Polynomial.zero_of_eval_zero + intro t + rw [eval_affineLinePolynomial] + exact hline ⟨t, rfl⟩ + +private theorem rationalAffineLine_direction_top_zero {n : ℕ} + (a v : RatPoint n) (f : MvPolynomial (Fin n) ℤ) + (hline : rationalAffineLine a v ⊆ zeroLocus (map (Int.castRingHom ℚ) f)) : + eval v (map (Int.castRingHom ℚ) (homogeneousComponent f.totalDegree f)) = 0 := by + have hpoly := affineLinePolynomial_eq_zero_of_contained a v _ hline + have hdegree : (map (Int.castRingHom ℚ) f).totalDegree ≤ f.totalDegree := totalDegree_map_le _ _ + have hc := affineLinePolynomial_top_coefficient a v (map (Int.castRingHom ℚ) f) + f.totalDegree hdegree + rw [hpoly, Polynomial.coeff_zero, ← map_homogeneousComponent] at hc + exact hc.symm + +private lemma rationalAffineLine_eq_projective_rep {n : ℕ} + (a v : RatPoint (n + 1)) (hv : v ≠ 0) (p : ProjectivePoint n) + (hp : Projectivization.mk ℚ v hv = p) : + rationalAffineLine a v = rationalAffineLine a p.rep := by + have he : Projectivization.mk ℚ v hv = + Projectivization.mk ℚ p.rep p.rep_nonzero := hp.trans p.mk_rep.symm + obtain ⟨c, hc⟩ := (Projectivization.mk_eq_mk_iff ℚ v p.rep hv p.rep_nonzero).mp he + have heq : v = (c : ℚ) • p.rep := hc.symm + rw [heq] + exact rationalAffineLine_smul_direction a p.rep c.ne_zero + +/-- A line with more than one bounded integer point has a projective +direction on the leading form. Its integer point count is bounded using the +height of that projective point, independently of all chosen representatives. -/ +theorem surface_line_projective_direction {n : ℕ} + (f : MvPolynomial (Fin (n + 1)) ℤ) (L : Set (RatPoint (n + 1))) + (hL : ∃ a v, L = rationalAffineLine a v) + (hLf : L ⊆ zeroLocus (map (Int.castRingHom ℚ) f)) + {B : ℝ} (hB : 0 ≤ B) (hcount : 1 < count L B) : + ∃ p : ProjectivePoint n, + p ∈ projectiveZeroLocus (map (Int.castRingHom ℚ) (homogeneousComponent f.totalDegree f)) ∧ + 0 < p.mulHeight ∧ p.mulHeight ≤ 2 * B ∧ + (∃ a, L = rationalAffineLine a p.rep) ∧ + (count L B : ℝ) ≤ 4 * B / p.mulHeight := by + obtain ⟨a, v, rfl⟩ := hL + obtain ⟨z, w, hw, hprim, _, hheight, hline, hcnt⟩ := + rationalAffineLine_integer_rep_and_count a v hB hcount + let p := Projectivization.mk ℚ (rationalPoint w) (rationalPoint_ne_zero hw) + have hpheight : p.mulHeight = (integerDirectionHeight w : ℝ) := + projective_direction_height w hw hprim + have hp0 : 0 < p.mulHeight := by + rw [hpheight] + exact_mod_cast integerDirectionHeight_pos hw + have hzero := rationalAffineLine_direction_top_zero + (rationalPoint z) (rationalPoint w) f (by rwa [← hline]) + refine ⟨p, ?_, hp0, hpheight ▸ hheight, ?_, ?_⟩ + · exact (mem_projectiveZeroLocus_mk ((homogeneousComponent_isHomogeneous _ _).map _) + (rationalPoint w) (rationalPoint_ne_zero hw)).mpr hzero + · exact ⟨rationalPoint z, hline.trans + (rationalAffineLine_eq_projective_rep _ _ (rationalPoint_ne_zero hw) p rfl)⟩ + · rw [← hpheight] at hcnt + have htwo : 1 ≤ 2 * B / p.mulHeight := + (le_div_iff₀ hp0).mpr (by simpa only [one_mul, hpheight] using hheight) + calc + _ ≤ 1 + 2 * B / p.mulHeight := hcnt + _ ≤ 2 * B / p.mulHeight + 2 * B / p.mulHeight := by linarith + _ = _ := by ring + +end +end CCDN diff --git a/LeanPool/CCDN/Surface/MonomialWeight.lean b/LeanPool/CCDN/Surface/MonomialWeight.lean new file mode 100644 index 0000000000..1a549721bc --- /dev/null +++ b/LeanPool/CCDN/Surface/MonomialWeight.lean @@ -0,0 +1,86 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.Hilbert.PrincipalStandardMonomials +public import Mathlib.Analysis.Real.Sqrt +import LeanPool.CCDN.Analysis.SurfaceMonomialWeightNumerics +import LeanPool.CCDN.AuxiliaryPolynomial.HypersurfaceRankProfile +import LeanPool.CCDN.ForMathlib.MvPolynomial +import LeanPool.CCDN.Hilbert.PlaneHilbert +import Mathlib.Algebra.Order.Star.Real + +/-! The sharp weighted degree of a full standard-monomial prefix for an +affine surface. The coefficient 2/3 of the square-root rank is retained, +with an explicit linear error depending only on the equation degree. -/ + +@[expose] public section +namespace CCDN +noncomputable section +open MvPolynomial Finset + +/-- Standard monomials up to degree M have weighted degree at most +(2/3)s sqrt(2s/d)+(2d+2)s, where s is their cardinality and d is +the surface equation degree. -/ +theorem surface_standard_monomial_weight {K : Type*} [Field K] + (f : MvPolynomial (Fin 3) K) (hf : f ≠ 0) (hd : 0 < f.totalDegree) + {M : ℕ} (hM : f.totalDegree ≤ M) : + let S := standardMonomialPrefix (Ideal.span {f}) MonomialOrder.degLex M + (∑ a ∈ S, ((Finsupp.degree a : ℕ) : ℝ)) ≤ + (2 / 3 : ℝ) * S.card * Real.sqrt (2 * S.card / (f.totalDegree : ℝ)) + + (2 * f.totalDegree + 2) * S.card := by + classical + dsimp only + let H : ℕ → ℕ := fun k => Module.finrank K (filteredCoordinateSpace (Ideal.span {f}) k) + have hrank (k : ℕ) (hk : f.totalDegree ≤ k) : + H k + (2 + 1 + (k - f.totalDegree)).choose (k - f.totalDegree) = + (2 + 1 + k).choose k := by + simpa only [H, finrank_restrictTotalDegree, Fintype.card_fin] using + principal_filtered_finrank f hf hk + have hbounds (k : ℕ) (hk : f.totalDegree ≤ k) : + (f.totalDegree : ℝ) * ((k : ℝ) - f.totalDegree + 1) ^ 2 / 2 ≤ H k ∧ + (H k : ℝ) ≤ (f.totalDegree : ℝ) * ((k : ℝ) + 2) ^ 2 / 2 := by + have hb := hypersurface_rank_power_bounds hk (hrank k hk) + norm_num at hb + exact hb + have hsum := surface_rank_prefix_sum_lower H hM (fun k hk => (hbounds k hk).1) + have hid := standardMonomialPrefix_weight_identity (Ideal.span {f}) M + have hidR : + (∑ a ∈ standardMonomialPrefix (Ideal.span {f}) MonomialOrder.degLex M, + ((Finsupp.degree a : ℕ) : ℝ)) + (∑ k ∈ range M, (H k : ℝ)) = (M : ℝ) * H M := by + exact_mod_cast hid + have hdR : (0 : ℝ) < f.totalDegree := by exact_mod_cast hd + have hMR : (f.totalDegree : ℝ) ≤ M := by exact_mod_cast hM + have hbase : 0 ≤ 2 * (H M : ℝ) / (f.totalDegree : ℝ) := by positivity + let R : ℝ := Real.sqrt (2 * (H M : ℝ) / (f.totalDegree : ℝ)) + have hR : 0 ≤ R := Real.sqrt_nonneg _ + have hroot : (f.totalDegree : ℝ) * R ^ 2 = 2 * H M := by + dsimp only [R] + rw [Real.sq_sqrt hbase] + field_simp + have hlo : (M : ℝ) - f.totalDegree ≤ R := by + apply Real.le_sqrt_of_sq_le + apply (le_div_iff₀ hdR).mpr + have hb := (hbounds M hM).1 + nlinarith + have hhi : R ≤ (M : ℝ) + 2 := by + apply (Real.sqrt_le_left (by positivity)).mpr + apply (div_le_iff₀ hdR).mpr + have hb := (hbounds M hM).2 + nlinarith + have hw : (∑ a ∈ standardMonomialPrefix (Ideal.span {f}) MonomialOrder.degLex M, + ((Finsupp.degree a : ℕ) : ℝ)) + (f.totalDegree : ℝ) * + ((M : ℝ) - f.totalDegree) ^ 3 / 6 ≤ (M : ℝ) * H M := by + linarith + have hh := surface_weight_root_bound hdR hMR (Nat.cast_nonneg (H M)) R hR + hroot hlo hhi hw + simpa only [standardMonomialPrefix_card, H, R] using hh + +end +end CCDN diff --git a/LeanPool/CCDN/Surface/NonlinearCount.lean b/LeanPool/CCDN/Surface/NonlinearCount.lean new file mode 100644 index 0000000000..d10864fbae --- /dev/null +++ b/LeanPool/CCDN/Surface/NonlinearCount.lean @@ -0,0 +1,65 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.Statements +import LeanPool.CCDN.Curve.AffineCurveCount +import LeanPool.CCDN.Surface.AuxiliaryExponent + +/-! Counting the nonlinear components of a surface section using their +total Hilbert degree and the affine curve counting theorem. -/ + +@[expose] public section +namespace CCDN +noncomputable section + +/-- The number of integral points of height at most `B` on a finite union of sets is at most +the sum of the numbers of such points on the individual sets. -/ +theorem count_biUnion_finset_le {n : ℕ} {ι : Type*} + (s : Finset ι) (X : ι → Set (RatPoint n)) (B : ℝ) : + count (⋃ i ∈ s, X i) B ≤ ∑ i ∈ s, count (X i) B := by + classical + induction s using Finset.induction_on with + | empty => simp [count, integralPoints] + | @insert i s hi ih => + simpa only [Finset.set_biUnion_insert, Finset.sum_insert hi] using + (count_union_le (X i) (⋃ j ∈ s, X j) B).trans (Nat.add_le_add_left ih _) + +/-- The curve counting constant is chosen before the finite family and its +degree budget. This is the nonlinear-component part of the corrected surface +estimate for degrees at least sixteen. -/ +theorem nonlinear_curve_union_bound : + ∃ K : ℝ, 0 < K ∧ + ∀ (s : Finset (Ideal (CoordinatePolynomial 3))) + (δ : Ideal (CoordinatePolynomial 3) → ℕ) (d : ℕ), + 16 ≤ d → ∀ C B : ℝ, 0 ≤ C → 1 ≤ B → + (∀ P ∈ s, P.IsPrime ∧ 2 ≤ δ P ∧ HasAffineCurveDegree P (δ P)) → + (∑ P ∈ s, (δ P : ℝ)) ≤ C * (d : ℝ) ^ (9 / 2 : ℝ) * B ^ (1 / Real.sqrt d) → + (count (⋃ P ∈ s, idealZeroLocus P) B : ℝ) ≤ + K * Real.exp 1 * (480 * C + 2 * C ^ 4) * (d : ℝ) ^ 18 * B := by + obtain ⟨K, hK, hcount⟩ := affine_curve_count 3 (by omega) + refine ⟨K, hK, ?_⟩ + intro s δ d hd C B hC hB hcurve hdegree + calc + _ ≤ ∑ P ∈ s, (count (idealZeroLocus P) B : ℝ) := by + exact_mod_cast count_biUnion_finset_le s idealZeroLocus B + _ ≤ ∑ P ∈ s, K * curveBound (δ P) B := by + apply Finset.sum_le_sum + intro P hP + obtain ⟨hp, hδ, hdeg⟩ := hcurve P hP + exact hcount P hp (δ P) (by omega) hdeg B hB + _ = K * ∑ P ∈ s, curveBound (δ P) B := (Finset.mul_sum _ _ _).symm + _ ≤ K * (Real.exp 1 * (480 * C + 2 * C ^ 4) * (d : ℝ) ^ 18 * B) := + mul_le_mul_of_nonneg_left + (sum_curveBound_le_surface_degree_budget s δ hd hC hB (fun P hP => (hcurve P hP).2.1) + hdegree) hK.le + _ = _ := by ring + +end +end CCDN diff --git a/LeanPool/CCDN/Surface/ParallelLines.lean b/LeanPool/CCDN/Surface/ParallelLines.lean new file mode 100644 index 0000000000..24d72c7c02 --- /dev/null +++ b/LeanPool/CCDN/Surface/ParallelLines.lean @@ -0,0 +1,144 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.Plane.IntegralAffineLine +public import LeanPool.CCDN.Surface.TransversePlane +import LeanPool.CCDN.AuxiliaryPolynomial.PlaneAuxiliaryCount +import LeanPool.CCDN.Plane.Bezout +import LeanPool.CCDN.Surface.DirectionalDerivative +import LeanPool.CCDN.Surface.TopFormBadPrimes + +/-! A transverse plane injects distinct parallel lines on a surface into +the common zeros of two proper plane equations. The second equation will +be the directional derivative. -/ + +@[expose] public section +namespace CCDN +noncomputable section +open MvPolynomial + +private lemma planeDirectionMatrix_mulVec (b c v w : Fin 3 → ℚ) (i : Fin 3) : + (planeDirectionMatrix b c v).mulVec w i = b i * w 0 + c i * w 1 + v i * w 2 := by + simp [Matrix.mulVec, dotProduct, Fin.sum_univ_succ, planeDirectionMatrix] + ring + +/-- Every line in the prescribed direction meets a transverse plane. -/ +private theorem exists_parallel_line_plane_intersection (a b c v x : Fin 3 → ℚ) + (hdet : (planeDirectionMatrix b c v).det ≠ 0) : + ∃ (z : Fin 2 → ℚ) (t : ℚ), + (fun i => a i + b i * z 0 + c i * z 1) = x + t • v := by + have hunit : IsUnit (planeDirectionMatrix b c v) := + (Matrix.isUnit_iff_isUnit_det _).mpr (isUnit_iff_ne_zero.mpr hdet) + obtain ⟨w, hw⟩ := Matrix.mulVec_surjective_iff_isUnit.mpr hunit (x - a) + refine ⟨![w 0,w 1], -w 2, ?_⟩ + funext i + have hi := congrFun hw i + rw [planeDirectionMatrix_mulVec] at hi + simp only [Pi.sub_apply] at hi + simp only [Matrix.cons_val_zero, Matrix.cons_val_one, Pi.add_apply, Pi.smul_apply, + smul_eq_mul] + linarith + +/-- Distinct parallel lines have distinct intersections with any +transverse plane. The index family is arbitrary but its line sets are injective. -/ +private theorem surface_parallel_line_family_card_le_of_second_equation + {ι : Type*} [Fintype ι] + (f : MvPolynomial (Fin 3) ℤ) + (hir : AbsolutelyIrreducible f) + (hd : 0 < f.totalDegree) + (v : Fin 3 → ℚ) (hv : v ≠ 0) (x : ι → RatPoint 3) + (hinj : Function.Injective (fun i => rationalAffineLine (x i) v)) + (g : MvPolynomial (Fin 3) ℚ) (hg : g ≠ 0) (hgdeg : g.totalDegree < f.totalDegree) + (hxf : ∀ i (t : ℚ), eval (x i + t • v) (map (Int.castRingHom ℚ) f) = 0) + (hxg : ∀ i (t : ℚ), eval (x i + t • v) g = 0) : + Fintype.card ι ≤ f.totalDegree * g.totalDegree := by + classical + obtain ⟨a,b,c,hdet,hF,hFd,hG⟩ := exists_surface_transverse_plane f hir hd v hv g hg + have hex (i : ι) := exists_parallel_line_plane_intersection a b c v (x i) hdet + choose z t hzt using hex + have hz : Function.Injective z := by + intro i j he + apply hinj + calc + rationalAffineLine (x i) v = rationalAffineLine (x i + t i • v) v := + (rationalAffineLine_shift_base _ _ _).symm + _ = rationalAffineLine (x j + t j • v) v := by rw [← hzt i, ← hzt j, he] + _ = rationalAffineLine (x j) v := rationalAffineLine_shift_base _ _ _ + have hFG : ¬ affinePlaneMap a b c (map (Int.castRingHom ℚ) f) ∣ affinePlaneMap a b c g := by + rintro ⟨q,hq⟩ + have hq0 : q ≠ 0 := by + intro hz + exact hG (by rw [hq, hz, mul_zero]) + have hh : f.totalDegree ≤ (affinePlaneMap a b c g).totalDegree := by + rw [hq, totalDegree_mul_of_isDomain hF.ne_zero hq0, hFd] + omega + exact (not_le_of_gt hgdeg) (hh.trans (affinePlaneMap_totalDegree_le a b c g)) + have hcount := plane_common_zeros_card_le + (affinePlaneMap a b c (map (Int.castRingHom ℚ) f)) (affinePlaneMap a b c g) + hF hFG z hz + (fun i => by rw [affinePlaneMap_eval, hzt i]; exact hxf i (t i)) + (fun i => by rw [affinePlaneMap_eval, hzt i]; exact hxg i (t i)) + rw [hFd] at hcount + exact hcount.trans (Nat.mul_le_mul_left _ (affinePlaneMap_totalDegree_le a b c g)) + +/-- A finite set of line sets has no duplicate lines; its cardinality +is therefore controlled by the same proper plane intersection. -/ +private theorem surface_parallel_lines_card_le_of_second_equation + (f : MvPolynomial (Fin 3) ℤ) + (hir : AbsolutelyIrreducible f) + (hd : 0 < f.totalDegree) + (v : Fin 3 → ℚ) (hv : v ≠ 0) + (S : Finset (Set (RatPoint 3))) + (hlines : ∀ L ∈ S, ∃ a, L = rationalAffineLine a v) + (g : MvPolynomial (Fin 3) ℚ) (hg : g ≠ 0) (hgdeg : g.totalDegree < f.totalDegree) + (hSf : ∀ L ∈ S, L ⊆ zeroLocus (map (Int.castRingHom ℚ) f)) + (hSg : ∀ L ∈ S, L ⊆ zeroLocus g) : + S.card ≤ f.totalDegree * g.totalDegree := by + classical + have hex (L : S) : ∃ a, L.val = rationalAffineLine a v := hlines L.val L.property + choose a ha using hex + have hinj : Function.Injective (fun L : S => rationalAffineLine (a L) v) := by + intro L M h + apply Subtype.ext + exact (ha L).trans (h.trans (ha M).symm) + have hh := surface_parallel_line_family_card_le_of_second_equation + f hir hd v hv a hinj g hg hgdeg + (fun L t => hSf L.val L.property (by rw [ha L]; exact ⟨t,rfl⟩)) + (fun L t => hSg L.val L.property (by rw [ha L]; exact ⟨t,rfl⟩)) + simpa only [Fintype.card_coe] using hh + +/-- A surface with absolutely irreducible top form has at most d(d-1) +distinct lines in any prescribed nonzero rational direction. The directional +derivative and the transverse plane are constructed inside the proof. -/ +theorem surface_parallel_lines_card_le + (f : MvPolynomial (Fin 3) ℤ) + (hir : HasAbsIrreducibleTopForm f) + (hd : 2 ≤ f.totalDegree) + (v : RatPoint 3) (hv : v ≠ 0) + (S : Finset (Set (RatPoint 3))) + (hlines : ∀ L ∈ S, ∃ a, L = rationalAffineLine a v) + (hSf : ∀ L ∈ S, L ⊆ zeroLocus (map (Int.castRingHom ℚ) f)) : + S.card ≤ f.totalDegree * (f.totalDegree - 1) := by + let g := directionalDerivative v (map (Int.castRingHom ℚ) f) + have hg : g ≠ 0 := surface_directionalDerivative_ne_zero f hir hd v hv + have hgdeg : g.totalDegree ≤ f.totalDegree - 1 := by + simpa only [rational_map_totalDegree] using + directionalDerivative_totalDegree_le v (map (Int.castRingHom ℚ) f) + have hh := surface_parallel_lines_card_le_of_second_equation f + (absolute_irreducible_of_top_homogeneousComponent_irreducible f hir) + (by omega) v hv S hlines g hg (by omega) hSf (by + intro L hL + obtain ⟨a, rfl⟩ := hlines L hL + exact rationalAffineLine_contained_directionalDerivative a v + (map (Int.castRingHom ℚ) f) (hSf _ hL)) + exact hh.trans (Nat.mul_le_mul_left _ hgdeg) + +end +end CCDN diff --git a/LeanPool/CCDN/Surface/PrimeWindowSubpower.lean b/LeanPool/CCDN/Surface/PrimeWindowSubpower.lean new file mode 100644 index 0000000000..6feaf7ca53 --- /dev/null +++ b/LeanPool/CCDN/Surface/PrimeWindowSubpower.lean @@ -0,0 +1,174 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.Surface.CongruencePrimeWindow +import LeanPool.CCDN.Analysis.SmallDegreeSurfaceNumerics + +/-! The number of subsets and edges of a common logarithmic prime window +costs only an arbitrarily small positive power of the box radius. Constants +are fixed before the radius, including the bounded range near B = 1. -/ + +@[expose] public section +namespace CCDN +noncomputable section +open Finset + +/-- For `L ≥ 1`, the number of primes in `congruencePrimeWindow L`, multiplied by `log 2`, is +at most `(log 4 + 4) c L`, where `c` is `scalingPrimeRatio`. -/ +theorem congruencePrimeWindow_card_log_two {L : ℝ} (hL : 1 ≤ L) : + (congruencePrimeWindow L).card * Real.log 2 ≤ + (Real.log 4 + 4) * scalingPrimeRatio * L := by + calc + _ = ∑ _p ∈ congruencePrimeWindow L, Real.log 2 := by simp + _ ≤ ∑ p ∈ congruencePrimeWindow L, Real.log p := by + apply sum_le_sum + intro p hp + exact Real.log_le_log (by norm_num) + (by exact_mod_cast ((mem_congruencePrimeWindow hL).mp hp).1.two_le) + _ ≤ _ := congruencePrimeWindow_log_upper hL + +/-- A uniform bound for any fixed exponential in the size of the window. +This retains the arbitrary epsilon needed to absorb residue multiplicities. -/ +private theorem congruencePrimeWindow_pow_subpower {K R ε : ℝ} + (hK : 1 ≤ K) (hR : 1 ≤ R) (hε : 0 < ε) : + ∃ C : ℝ, 0 < C ∧ ∀ B : ℝ, 1 ≤ B → + R ^ (congruencePrimeWindow (K * (Real.log B + 1))).card ≤ C * B ^ ε := by + let A := (Real.log 4 + 4) * scalingPrimeRatio + have hA : 0 < A := by + have ha := scalingPrimeRatio_one_le + have hl := Real.log_nonneg (by norm_num : (1 : ℝ) ≤ 4) + dsimp only [A] + positivity + have hK0 : 0 < K := zero_lt_one.trans_le hK + have hR0 : 0 < R := zero_lt_one.trans_le hR + have hlogR : 0 ≤ Real.log R := Real.log_nonneg hR + have hlog2 : 0 < Real.log 2 := Real.log_pos (by norm_num) + let D := A * K * Real.log R / ε + 1 + let E := A * Real.exp D / Real.log 2 * Real.log R + have hE : 0 ≤ E := by dsimp only [E]; positivity + refine ⟨Real.exp (E + ε), Real.exp_pos _, ?_⟩ + intro B hB + have hB0 : 0 < B := zero_lt_one.trans_le hB + have hlogB : 0 ≤ Real.log B := Real.log_nonneg hB + let L := K * (Real.log B + 1) + let m := (congruencePrimeWindow L).card + have hL : 1 ≤ L := by dsimp only [L]; nlinarith + have hL0 : 0 < L := zero_lt_one.trans_le hL + have hcard : (m : ℝ) * Real.log L ≤ A * L := + congruencePrimeWindow_card_log_bound hL + have hcard2 : (m : ℝ) * Real.log 2 ≤ A * L := + congruencePrimeWindow_card_log_two hL + have hm : (0 : ℝ) ≤ m := Nat.cast_nonneg m + have hbound : (m : ℝ) * Real.log R ≤ E + ε * (Real.log B + 1) := by + by_cases hlarge : D ≤ Real.log L + · have hd : A * K * Real.log R / ε ≤ Real.log L := by + dsimp only [D] at hlarge + linarith + have hd' : A * K * Real.log R ≤ ε * Real.log L := by + have hh := (div_le_iff₀ hε).mp hd + nlinarith + have hmul := mul_le_mul_of_nonneg_left hd' hm + have hupper := mul_le_mul_of_nonneg_left hcard hε.le + have hmain : (m : ℝ) * Real.log R ≤ ε * (Real.log B + 1) := by + apply (mul_le_mul_iff_left₀ (mul_pos hA hK0)).mp + dsimp only [L] at hupper + nlinarith + nlinarith + · have hLbound : L ≤ Real.exp D := by + have hh := Real.exp_le_exp.mpr (le_of_not_ge hlarge) + rwa [Real.exp_log hL0] at hh + have hm' : (m : ℝ) ≤ A * Real.exp D / Real.log 2 := by + apply (le_div_iff₀ hlog2).mpr + exact hcard2.trans (mul_le_mul_of_nonneg_left hLbound hA.le) + have hh := mul_le_mul_of_nonneg_right hm' hlogR + change (m : ℝ) * Real.log R ≤ E at hh + nlinarith + have hexp := Real.exp_le_exp.mpr hbound + rw [← Real.log_pow, Real.exp_log (pow_pos hR0 _)] at hexp + have heq : Real.exp (E + ε * (Real.log B + 1)) = + Real.exp (E + ε) * B ^ ε := by + rw [Real.rpow_def_of_pos hB0, ← Real.exp_add] + congr 1 + ring + rw [heq] at hexp + exact hexp + +/-- The edge count of the subset lattice, together with a fixed cost per +selected prime, also has arbitrarily small power growth. -/ +private theorem congruencePrimeWindow_edge_subpower {K R ε : ℝ} + (hK : 1 ≤ K) (hR : 1 ≤ R) (hε : 0 < ε) : + ∃ C : ℝ, 0 < C ∧ ∀ B : ℝ, 1 ≤ B → + (congruencePrimeWindow (K * (Real.log B + 1))).card * + R ^ (congruencePrimeWindow (K * (Real.log B + 1))).card ≤ C * B ^ ε := by + obtain ⟨C, hC, hpow⟩ := congruencePrimeWindow_pow_subpower hK + (show 1 ≤ 2 * R by linarith) hε + refine ⟨C, hC, ?_⟩ + intro B hB + let m := (congruencePrimeWindow (K * (Real.log B + 1))).card + have hm : (m : ℝ) ≤ 2 ^ m := by exact_mod_cast (Nat.le_of_lt m.lt_two_pow_self) + calc + _ ≤ (2 : ℝ) ^ m * R ^ m := mul_le_mul_of_nonneg_right hm (by positivity) + _ = (2 * R) ^ m := (mul_pow _ _ _).symm + _ ≤ _ := hpow B hB + +/-- Both prime-subset multiplicities and any fixed logarithmic power fit +inside the exponent gap used by the surface argument. -/ +private theorem congruencePrimeWindow_edge_polylog_linear {K R : ℝ} + (hK : 1 ≤ K) (hR : 1 ≤ R) (k : ℕ) : + ∃ C : ℝ, 0 < C ∧ ∀ α B : ℝ, α ≤ 19 / 20 → 1 ≤ B → + ((congruencePrimeWindow (K * (Real.log B + 1))).card * + R ^ (congruencePrimeWindow (K * (Real.log B + 1))).card) * + B ^ α * (Real.log B + 1) ^ k ≤ C * B := by + obtain ⟨C₁, hC₁, h₁⟩ := congruencePrimeWindow_edge_subpower hK hR + (by norm_num : (0 : ℝ) < 1 / 40) + let C₂ := (k.factorial : ℝ) / (1 / 40 : ℝ) ^ k * Real.exp (1 / 40) + have hC₂ : 0 < C₂ := by dsimp only [C₂]; positivity + refine ⟨C₁ * C₂, mul_pos hC₁ hC₂, ?_⟩ + intro α B hα hB + have hB0 : 0 < B := zero_lt_one.trans_le hB + have hlogB : 0 ≤ Real.log B := Real.log_nonneg hB + have h₂ := logarithm_add_one_pow_le_rpow k (by norm_num : (0 : ℝ) < 1 / 40) hB + change (Real.log B + 1) ^ k ≤ C₂ * B ^ (1 / 40 : ℝ) at h₂ + calc + _ ≤ (C₁ * B ^ (1 / 40 : ℝ)) * B ^ α * (C₂ * B ^ (1 / 40 : ℝ)) := by + apply mul_le_mul + (mul_le_mul_of_nonneg_right (h₁ B hB) (Real.rpow_nonneg hB0.le _)) h₂ + · positivity + · positivity + _ = (C₁ * C₂) * B ^ (α + 1 / 20) := by + rw [Real.rpow_add hB0] + have hh : B ^ (1 / 20 : ℝ) = B ^ (1 / 40 : ℝ) * B ^ (1 / 40 : ℝ) := by + rw [← Real.rpow_add hB0] + norm_num + rw [hh] + ring + _ ≤ (C₁ * C₂) * B ^ (1 : ℝ) := + mul_le_mul_of_nonneg_left (Real.rpow_le_rpow_of_exponent_le hB (by linarith)) + (mul_pos hC₁ hC₂).le + _ = _ := by rw [Real.rpow_one] + +/-- For `K, R ≥ 1` and `k : ℕ`, there is a constant `C` such that, for every degree `d ≥ 5` and +every `B ≥ 1`, the prime window `W` of logarithmic size `K (log B + 1)` satisfies +`#W R^#W B^(2/√d) (log B + 1)^k ≤ C B`. -/ +theorem congruencePrimeWindow_surface_residual_bound {K R : ℝ} + (hK : 1 ≤ K) (hR : 1 ≤ R) (k : ℕ) : + ∃ C : ℝ, 0 < C ∧ ∀ (d : ℕ), 5 ≤ d → ∀ B : ℝ, 1 ≤ B → + ((congruencePrimeWindow (K * (Real.log B + 1))).card * + R ^ (congruencePrimeWindow (K * (Real.log B + 1))).card) * + B ^ (2 / Real.sqrt (d : ℝ)) * (Real.log B + 1) ^ k ≤ C * B := by + obtain ⟨C, hC, hbound⟩ := congruencePrimeWindow_edge_polylog_linear hK hR k + refine ⟨C, hC, fun d hd B hB => hbound _ B ?_ hB⟩ + have hh := surface_inverse_sqrt_le_nine_twentieths hd + have he : 2 / Real.sqrt (d : ℝ) = 2 * (1 / Real.sqrt (d : ℝ)) := by ring + rw [he] + linarith + +end +end CCDN diff --git a/LeanPool/CCDN/Surface/PrimitiveSurfaceFixedDegree.lean b/LeanPool/CCDN/Surface/PrimitiveSurfaceFixedDegree.lean new file mode 100644 index 0000000000..ba02e7c640 --- /dev/null +++ b/LeanPool/CCDN/Surface/PrimitiveSurfaceFixedDegree.lean @@ -0,0 +1,116 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.Algebra.PolynomialPredicates +public import LeanPool.CCDN.Height.Box +public import LeanPool.CCDN.Height.PrimitiveIntegerPolynomial +public import Mathlib.Analysis.SpecialFunctions.Pow.Real +import LeanPool.CCDN.AuxiliaryPolynomial.PlaneAuxiliaryCount +import LeanPool.CCDN.Surface.CoefficientReduction +import LeanPool.CCDN.Surface.ResidueLinearCount +import LeanPool.CCDN.Surface.RetainedCurveCount +import LeanPool.CCDN.Surface.SmallCurveCover +import LeanPool.CCDN.Surface.TopFormBadPrimes +import Mathlib.Algebra.Order.Star.Real + +/-! A coefficient-uniform linear bound for primitive affine surface +polynomials of each fixed degree at least five. The argument constructs +its congruence auxiliaries and curve cover. -/ + +@[expose] public section +namespace CCDN +noncomputable section +open MvPolynomial Finset + +/-- For each degree `d ≥ 5`, there is a constant `C` such that every primitive integer +surface equation of degree `d` with absolutely irreducible top-degree form has at most `C B` +integral points of height at most `B` on its zero set, for every `B ≥ 1`. -/ +theorem primitive_surface_fixed_degree_linear_bound (d : ℕ) (hd : 5 ≤ d) : + ∃ C : ℝ, 0 < C ∧ + ∀ f : MvPolynomial (Fin 3) ℤ, PrimitiveIntegerPolynomial f → + HasAbsIrreducibleTopForm f → + f.totalDegree = d → ∀ B : ℝ, 1 ≤ B → + (count (zeroLocus (map (Int.castRingHom ℚ) f)) B : ℝ) ≤ C * B := by + let K := surfaceCoefficientLogConstant d + have hK : 0 ≤ K := (surfaceCoefficientLogConstant_pos d).le + let A := surfaceCongruenceDegreeConstant d K + have hA : 0 ≤ A := (surfaceCongruenceDegreeConstant_pos d K).le + let A₀ := 2 * (d : ℝ) * A + have hA₀ : 0 ≤ A₀ := by dsimp only [A₀]; positivity + let D := A₀ * surfaceResidueLogScale d K + have hlogscale := surfaceResidueLogScale_one_le d hK + have hD : 0 ≤ D := by dsimp only [D]; positivity + obtain ⟨Ccurve, hCcurve, hcurve⟩ := retained_surface_curve_union_bound + obtain ⟨Cres, hCres, hres⟩ := surface_residue_linear_count d hd K A hK hA + let Cret := Ccurve * ((d : ℝ) ^ 6 + A₀ * (D ^ 2 * (D + 1) + 1)) + have hCret : 0 ≤ Cret := by dsimp only [Cret]; positivity + refine ⟨12 * (d : ℝ) ^ 2 + Cret + Cres + 1, by positivity, ?_⟩ + intro f hprim htop hfd B hB + subst d + have habs := absolute_irreducible_of_top_homogeneousComponent_irreducible f htop + have hir := rational_irreducible_of_charZero_map f habs + have hf0 : f ≠ 0 := by intro hz; exact hir.ne_zero (by rw [hz, map_zero]) + have hB0 : 0 < B := zero_lt_one.trans_le hB + have hlogB := Real.log_nonneg hB + rcases primitive_surface_coefficient_reduction f hprim hir hB with hheight | hlarge + · have hH : 1 ≤ (integerCoefficientHeight f : ℝ) := by + exact_mod_cast integerCoefficientHeight_pos f hf0 + have hheight' : Real.log (integerCoefficientHeight f : ℝ) ≤ + K * (Real.log B + 1) := hheight + have hL := surfaceResidueWindow_one_le f.totalDegree hK hB + obtain ⟨F, S, δ, hS, hbudget, hcover⟩ := + exists_surface_small_curve_residual_cover f habs (by omega) hK hheight' + (isSurfaceResidueWindow_of_log_height (by omega) hK hB hH + (coefficient_abs_le_integerCoefficientHeight f) hheight') + let small := S.filter (fun I => (δ I : ℝ) ≤ + 2 * f.totalDegree * A * surfaceResidueLogBudget B + (surfaceResidueWindow f.totalDegree K B)) + have hsmall (I : Ideal (CoordinatePolynomial 3)) (hI : I ∈ small) : + IsSurfaceCurve (map (Int.castRingHom ℚ) f) I (δ I) ∧ 0 < δ I := + ⟨(hS I (mem_filter.mp hI).1).toIsSurfaceCurve, (hS I (mem_filter.mp hI).1).pos⟩ + have hcap (I : Ideal (CoordinatePolynomial 3)) (hI : I ∈ small) : + (δ I : ℝ) ≤ D * (Real.log B + 1) := by + have hh := (mem_filter.mp hI).2 + rw [surfaceResidueLogBudget_window] at hh + simpa only [D, A₀, mul_assoc] using hh + have hroot : 1 ≤ B ^ (1 / Real.sqrt (f.totalDegree : ℝ)) := + Real.one_le_rpow hB (by positivity) + have hbudget' : (∑ I ∈ small, (δ I : ℝ)) ≤ + A₀ * B ^ (1 / Real.sqrt (f.totalDegree : ℝ)) * (Real.log B + 1) := by + calc + _ ≤ ∑ I ∈ S, (δ I : ℝ) := + sum_le_sum_of_subset_of_nonneg (filter_subset _ _) (by intros; positivity) + _ ≤ (f.totalDegree : ℝ) * A * + (B ^ (1 / Real.sqrt (f.totalDegree : ℝ)) + 1) * (1 + Real.log B) := hbudget + _ ≤ (f.totalDegree : ℝ) * A * + (2 * B ^ (1 / Real.sqrt (f.totalDegree : ℝ))) * (1 + Real.log B) := by + gcongr + linarith + _ = _ := by dsimp only [A₀]; ring + have hret : (count (⋃ I ∈ small, idealZeroLocus I) B : ℝ) ≤ Cret * B := + hcurve f htop hd small δ A₀ D B hA₀ hD hB hsmall hcap hbudget' + have hresidual := hres f rfl B hB F hir S δ (fun I hI => (hS I hI).toIsSurfaceCurve) + have hsingular : (count (singularHypersurfaceLocus (map (Int.castRingHom ℚ) f)) B : ℝ) ≤ + 9 * (f.totalDegree : ℝ) ^ 2 * B := by + have hh := singular_hypersurface_dimension_growth (n := 1) + (map (Int.castRingHom ℚ) f) hir hB + simpa only [rational_map_totalDegree, Nat.cast_one, Nat.cast_ofNat, pow_one, + show (3 * (1 + 2) : ℝ) = 9 by norm_num] using hh + have hcoverR : (count (zeroLocus (map (Int.castRingHom ℚ) f)) B : ℝ) ≤ + (count (singularHypersurfaceLocus (map (Int.castRingHom ℚ) f)) B : ℝ) + + count (⋃ I ∈ small, idealZeroLocus I) B + + count (surfaceResidueResidualLocus F S (surfaceResidueWindow f.totalDegree K B) + (2 * B ^ (1 / Real.sqrt (f.totalDegree : ℝ))) hL) B := by + simpa only [Nat.cast_add, small, A] using (Nat.cast_le (α := ℝ)).mpr hcover + nlinarith [hcoverR, hret, hresidual, hsingular, sq_nonneg (f.totalDegree : ℝ)] + · nlinarith [hlarge, sq_nonneg (f.totalDegree : ℝ)] + +end +end CCDN diff --git a/LeanPool/CCDN/Surface/ResidueBoundary.lean b/LeanPool/CCDN/Surface/ResidueBoundary.lean new file mode 100644 index 0000000000..9fc567d742 --- /dev/null +++ b/LeanPool/CCDN/Surface/ResidueBoundary.lean @@ -0,0 +1,218 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.Surface.CongruenceFamily +public import LeanPool.CCDN.Surface.CongruencePrimeWindow +public import LeanPool.CCDN.Surface.CurveComponents +import LeanPool.CCDN.AuxiliaryPolynomial.PlaneAuxiliaryCount +import LeanPool.CCDN.Surface.CurveCrossing +import LeanPool.CCDN.Surface.ResidueSubsetBoundary +import LeanPool.CCDN.Surface.SmoothPrimeWindow + +/-! A nonsingular point on a large-degree initial curve must cross a +proper auxiliary section along a one-prime deletion edge. This constructs +the residual cover from smooth reductions and auxiliaries. -/ + +@[expose] public section +namespace CCDN +noncomputable section +open MvPolynomial Finset + +/-- The quantity `1 + log B + (log 4 + 4) c L`, with `c` the constant `scalingPrimeRatio`, +which bounds `1 + log B + ∑ p ∈ R, log p` for every set `R` of primes in the prime window of +logarithmic size `L`. -/ +def surfaceResidueLogBudget (B L : ℝ) : ℝ := + 1 + Real.log B + (Real.log 4 + 4) * scalingPrimeRatio * L + +/-- The parameters of the residue cover of the surface `f = 0` in the box of radius `B ≥ 1`: +the coefficients of `f` are bounded by `H ≥ 1`; the logarithmic prime window `L ≥ 1` dominates +`4 log(d + 1) + log H + d log B`, which bounds the logarithm of a partial derivative of `f` at +a point of the box, where `d` is the degree of `f`; and the residue-modulus cutoff `X` +satisfies `1 < X`, `B^(1/√d) ≤ X` and `X ≤ e^L`. -/ +structure IsSurfaceResidueWindow (f : MvPolynomial (Fin 3) ℤ) (B H L X : ℝ) : Prop where + /-- The box radius is at least one. -/ + one_le_radius : 1 ≤ B + /-- The height bound is at least one. -/ + one_le_height : 1 ≤ H + /-- The logarithmic size of the prime window is at least one. -/ + one_le_window : 1 ≤ L + /-- The coefficients of `f` are bounded by `H` in absolute value. -/ + coeff_le : CoefficientHeightLE f H + /-- The window dominates the logarithmic size of a partial derivative of `f` on the + box. -/ + log_le_window : 4 * Real.log ((f.totalDegree : ℝ) + 1) + + Real.log H + f.totalDegree * Real.log B ≤ L + /-- The modulus cutoff exceeds one. -/ + one_lt_cutoff : 1 < X + /-- The modulus cutoff is at least `B^(1/√d)`. -/ + root_le_cutoff : B ^ (1 / Real.sqrt (f.totalDegree : ℝ)) ≤ X + /-- The modulus cutoff is at most `exp L`. -/ + cutoff_le_exp : X ≤ Real.exp L + +/-- For a finite set `P` of primes, the exponential of `∑ p ∈ P, log p` is `∏ p ∈ P, p`. -/ +lemma exp_sum_log_primes (P : Finset ℕ) (hP : ∀ p ∈ P, p.Prime) : + Real.exp (∑ p ∈ P, Real.log p) = ∏ p ∈ P, (p : ℝ) := by + rw [Real.exp_sum] + apply prod_congr rfl + intro p hp + exact Real.exp_log (by exact_mod_cast (hP p hp).pos) + +private lemma SurfaceCongruenceFamily.terminal_degree_le + {f : MvPolynomial (Fin 3) ℤ} {B A L X : ℝ} + (F : SurfaceCongruenceFamily f B A) (hB : 1 ≤ B) (hA : 0 ≤ A) (hL : 1 ≤ L) + (P : Finset ℕ) (hPW : P ⊆ congruencePrimeWindow L) + (hP : ∀ p ∈ P, p.Prime) (r : SmoothAffineResidueFamily f P) + (hX : B ^ (1 / Real.sqrt (f.totalDegree : ℝ)) ≤ X) + (hprod : X ≤ ∏ p ∈ P, (p : ℝ)) : + ((F.auxiliary P hP r).totalDegree : ℝ) ≤ 2 * A * surfaceResidueLogBudget B L := by + have hQ : 0 ≤ ∑ p ∈ P, Real.log p := sum_nonneg (fun p _ => Real.log_natCast_nonneg p) + have hlogB := Real.log_nonneg hB + have hlog : (∑ p ∈ P, Real.log p) ≤ + (Real.log 4 + 4) * scalingPrimeRatio * L := + (sum_le_sum_of_subset_of_nonneg hPW (fun p _ _ => Real.log_natCast_nonneg p)).trans + (congruencePrimeWindow_log_upper hL) + have hden : 0 < ∏ p ∈ P, (p : ℝ) := prod_pos (fun p hp => by exact_mod_cast (hP p hp).pos) + have hratio : B ^ (1 / Real.sqrt (f.totalDegree : ℝ)) / + Real.exp (∑ p ∈ P, Real.log p) ≤ 1 := by + rw [exp_sum_log_primes P hP] + exact (div_le_one hden).mpr (hX.trans hprod) + have hT : 0 ≤ surfaceResidueLogBudget B L := by + unfold surfaceResidueLogBudget + have hlogB := Real.log_nonneg hB + linarith + have hd := F.degree_le P hP r + calc + _ ≤ A * (B ^ (1 / Real.sqrt (f.totalDegree : ℝ)) / + Real.exp (∑ p ∈ P, Real.log p) + 1) * + (1 + Real.log B + ∑ p ∈ P, Real.log p) := hd + _ ≤ A * 2 * surfaceResidueLogBudget B L := by + apply mul_le_mul + · exact mul_le_mul_of_nonneg_left (by linarith) hA + · dsimp only [surfaceResidueLogBudget] + linarith + · positivity + · positivity + _ = _ := by ring + +private lemma surface_prime_curve_degree_le + (f g : CoordinatePolynomial 3) (hf : Irreducible f) (hfg : ¬ f ∣ g) + (I : Ideal (CoordinatePolynomial 3)) {d : ℕ} (hI : IsSurfaceCurve f I d) (hgI : g ∈ I) : + d ≤ f.totalDegree * g.totalDegree := by + have hh := surface_prime_curve_degree_sum_le f g hf hfg {I} (fun _ => d) + (fun P hP => by + have he : P = I := mem_singleton.mp hP + subst P + exact ⟨hI, hgI⟩) + simpa only [sum_singleton] using hh + +/-- A one-prime deletion edge `R.erase p ⊂ R` of the congruence family `F`, at the smooth +residues `r`, crossing the curve `I` near the point `x`: the primes of `R` lie in the window of +log-size `L`, the modulus of `R` is less than `c L X` and that of `R.erase p` is less than `X` +(where `c` is `scalingPrimeRatio`), the parent auxiliary lies in `I`, the child auxiliary does +not, and the child vanishes at `x`. -/ +structure IsSurfaceResidueEdge {f : MvPolynomial (Fin 3) ℤ} {A B : ℝ} + (F : SurfaceCongruenceFamily f B A) (I : Ideal (CoordinatePolynomial 3)) (L X : ℝ) + (x : IntPoint 3) (R : Finset ℕ) (hR : ∀ p ∈ R, p.Prime) (p : ℕ) + (r : SmoothAffineResidueFamily f R) : Prop where + /-- The primes of `R` lie in the prime window of logarithmic size `L`. -/ + subset_window : R ⊆ congruencePrimeWindow L + /-- The product of the primes of `R` is less than `c L X`, with `c` the constant + `scalingPrimeRatio`. -/ + prod_lt : (∏ q ∈ R, (q : ℝ)) < scalingPrimeRatio * L * X + /-- The product of the primes of `R.erase p` is less than `X`. -/ + erase_prod_lt : (∏ q ∈ R.erase p, (q : ℝ)) < X + /-- The auxiliary attached to `R.erase p`, over `ℚ`, lies in the ideal `I` of the curve. -/ + parent_mem : map (Int.castRingHom ℚ) + (F.auxiliary (R.erase p) (fun q hq => hR q (mem_of_mem_erase hq)) + (r.restrict (erase_subset p R))) ∈ I + /-- The auxiliary attached to `R`, over `ℚ`, does not lie in the ideal `I`. -/ + child_not_mem : map (Int.castRingHom ℚ) (F.auxiliary R hR r) ∉ I + /-- The auxiliary attached to `R` vanishes at `x`. -/ + child_vanishes : eval x (F.auxiliary R hR r) = 0 + +/-- A point on an initial prime curve above the terminal degree cutoff +lies in a proper section across a bounded-modulus residue edge. -/ +theorem surface_residue_boundary_of_large_curve + (f : MvPolynomial (Fin 3) ℤ) + (hir : Irreducible (map (Int.castRingHom ℚ) f)) + {A B H L X : ℝ} (hA : 0 ≤ A) (hW : IsSurfaceResidueWindow f B H L X) + (F : SurfaceCongruenceFamily f B A) + (I : Ideal (CoordinatePolynomial 3)) {d : ℕ} + (hI : IsSurfaceCurve (map (Int.castRingHom ℚ) f) I d) + (hinitI : map (Int.castRingHom ℚ) F.initial ∈ I) + (hlarge : 2 * f.totalDegree * A * surfaceResidueLogBudget B L < d) + (x : IntPoint 3) (hx : InBox x B) (hxf : eval x f = 0) + (hsmooth : ∃ j, eval x (pderiv j f) ≠ 0) : + ∃ R : Finset ℕ, ∃ hR : ∀ p ∈ R, p.Prime, + ∃ p ∈ R, ∃ r : SmoothAffineResidueFamily f R, IsSurfaceResidueEdge F I L X x R hR p r := by + classical + have hL := hW.one_le_window + obtain ⟨P, hPW, hPprod, hPsmooth⟩ := exists_smooth_prime_window_subset f hW.one_le_height + hW.one_le_radius hL hW.coeff_le (by norm_num; exact hW.log_le_window) x hx hsmooth + have hPprime (p : ℕ) (hp : p ∈ P) : p.Prime := + ((mem_congruencePrimeWindow hL).mp (hPW hp)).1 + obtain ⟨R₀, hR₀P, _, hR₀prod, hR₀proper, hR₀upper⟩ := + exists_minimal_product_subset P hW.one_lt_cutoff + (fun p hp => by exact_mod_cast (hPprime p hp).pos) + (fun p hp => ((mem_congruencePrimeWindow hL).mp (hPW hp)).2.2) + (hW.cutoff_le_exp.trans hPprod) + have hR₀W : R₀ ⊆ congruencePrimeWindow L := hR₀P.trans hPW + have hR₀prime (q : ℕ) (hq : q ∈ R₀) : q.Prime := hPprime q (hR₀P hq) + have hR₀smooth (q : ℕ) (hq : q ∈ R₀) := hPsmooth q (hR₀P hq) + let aux (T : Finset ℕ) : MvPolynomial (Fin 3) ℤ := + if hT : T ⊆ R₀ then F.auxiliary T (fun q hq => hR₀prime q (hT hq)) + (smoothResidueFamilyOfPoint x hxf T (fun q hq => hR₀smooth q (hT hq))) else 1 + have haux (T : Finset ℕ) (hT : T ⊆ R₀) : + aux T = F.auxiliary T (fun q hq => hR₀prime q (hT hq)) + (smoothResidueFamilyOfPoint x hxf T (fun q hq => hR₀smooth q (hT hq))) := by + simp only [aux, dite_eq_left hT] + have hterminal : map (Int.castRingHom ℚ) (aux R₀) ∉ I := by + intro hmem + rw [haux R₀ (Subset.refl _)] at hmem + have hdeg := F.terminal_degree_le hW.one_le_radius hA hL R₀ hR₀W hR₀prime + (smoothResidueFamilyOfPoint x hxf R₀ hR₀smooth) hW.root_le_cutoff hR₀prod + have hcurve := surface_prime_curve_degree_le (map (Int.castRingHom ℚ) f) + (map (Int.castRingHom ℚ) (F.auxiliary R₀ hR₀prime + (smoothResidueFamilyOfPoint x hxf R₀ hR₀smooth))) hir (F.proper _ _ _) + I hI hmem + rw [rational_map_totalDegree, rational_map_totalDegree] at hcurve + have hcurveR : (d : ℝ) ≤ (f.totalDegree : ℝ) * + (F.auxiliary R₀ hR₀prime + (smoothResidueFamilyOfPoint x hxf R₀ hR₀smooth)).totalDegree := by exact_mod_cast hcurve + have hh := mul_le_mul_of_nonneg_left hdeg (Nat.cast_nonneg (α := ℝ) f.totalDegree) + nlinarith + have hempty : ¬ (map (Int.castRingHom ℚ) (aux ∅) ∉ I) := by + rw [haux ∅ (empty_subset _), F.initial_eq] + exact not_not.mpr hinitI + obtain ⟨R, hRR₀, p, hp, hchild, hparent⟩ := exists_subset_erase_boundary R₀ + (fun T => map (Int.castRingHom ℚ) (aux T) ∉ I) hempty hterminal + have hRprime (q : ℕ) (hq : q ∈ R) : q.Prime := hR₀prime q (hRR₀ hq) + have hRsmooth (q : ℕ) (hq : q ∈ R) := hR₀smooth q (hRR₀ hq) + let r := smoothResidueFamilyOfPoint x hxf R hRsmooth + have hparentSub : R.erase p ⊆ R₀ := (erase_subset p R).trans hRR₀ + have hparentEq : aux (R.erase p) = + F.auxiliary (R.erase p) (fun q hq => hRprime q (mem_of_mem_erase hq)) + (r.restrict (erase_subset p R)) := by + rw [haux (R.erase p) hparentSub] + rfl + have hchildEq : aux R = F.auxiliary R hRprime r := haux R hRR₀ + refine ⟨R, hRprime, p, hp, r, hRR₀.trans hR₀W, ?_, ?_, ?_, ?_, ?_⟩ + · have hm : (∏ q ∈ R, (q : ℝ)) ≤ ∏ q ∈ R₀, (q : ℝ) := + prod_le_prod_of_subset_of_one_le₀ hRR₀ (fun _ _ => Nat.cast_nonneg _) + (fun q hq _ => by exact_mod_cast (hR₀prime q hq).one_le) + exact hm.trans_lt hR₀upper + · exact hR₀proper _ ((erase_ssubset hp).trans_le hRR₀) + · rw [← hparentEq] + exact not_not.mp hparent + · rwa [← hchildEq] + · exact F.point_vanishes x hx hxf R hRprime hRsmooth + +end +end CCDN diff --git a/LeanPool/CCDN/Surface/ResidueCover.lean b/LeanPool/CCDN/Surface/ResidueCover.lean new file mode 100644 index 0000000000..7c7cc62661 --- /dev/null +++ b/LeanPool/CCDN/Surface/ResidueCover.lean @@ -0,0 +1,170 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.Surface.ResidueEdgeCount +import LeanPool.CCDN.Surface.NonlinearCount + +/-! The finite residue-edge cover and its complete counting bound. -/ + +@[expose] public section +namespace CCDN +noncomputable section +open MvPolynomial Finset + +/-- For a subset `R` of the prime window of logarithmic size `L` whose product is at most +`c L X` (with `c` the constant `scalingPrimeRatio`), the union over the primes `p ∈ R` and the +smooth residue families `r` modulo `R` of the crossing sets of the deletion edge +`R.erase p ⊂ R`; the empty set for every other `R`. -/ +def surfaceResidueSubsetLocus {f : MvPolynomial (Fin 3) ℤ} {A B : ℝ} + (F : SurfaceCongruenceFamily f B A) + (S : Finset (Ideal (CoordinatePolynomial 3))) (L X : ℝ) (hL : 1 ≤ L) + (R : Finset ℕ) : Set (RatPoint 3) := + if hR : R ⊆ congruencePrimeWindow L then + if (∏ p ∈ R, (p : ℝ)) ≤ scalingPrimeRatio * L * X then + ⋃ p ∈ R, ⋃ r : SmoothAffineResidueFamily f R, + surfaceResidueCrossing F S R + (fun _q hq => ((mem_congruencePrimeWindow hL).mp (hR hq)).1) p r + else ∅ + else ∅ + +/-- The residual locus of the surface residue cover: the union of the edge crossing sets +`surfaceResidueSubsetLocus F S L X hL R` over all subsets `R` of the prime window of +logarithmic size `L`. -/ +def surfaceResidueResidualLocus {f : MvPolynomial (Fin 3) ℤ} {A B : ℝ} + (F : SurfaceCongruenceFamily f B A) + (S : Finset (Ideal (CoordinatePolynomial 3))) (L X : ℝ) (hL : 1 ≤ L) : + Set (RatPoint 3) := + ⋃ R ∈ (congruencePrimeWindow L).powerset, surfaceResidueSubsetLocus F S L X hL R + +private theorem mem_surfaceResidueResidualLocus + {f : MvPolynomial (Fin 3) ℤ} {A B L X : ℝ} + (F : SurfaceCongruenceFamily f B A) + (S : Finset (Ideal (CoordinatePolynomial 3))) (hL : 1 ≤ L) + (R : Finset ℕ) (hRW : R ⊆ congruencePrimeWindow L) + (hR : ∀ p ∈ R, p.Prime) (p : ℕ) (hp : p ∈ R) + (hprod : (∏ q ∈ R, (q : ℝ)) ≤ scalingPrimeRatio * L * X) + (r : SmoothAffineResidueFamily f R) {x : RatPoint 3} + (hx : x ∈ surfaceResidueCrossing F S R hR p r) : + x ∈ surfaceResidueResidualLocus F S L X hL := by + apply Set.mem_iUnion.mpr + refine ⟨R, Set.mem_iUnion.mpr ⟨mem_powerset.mpr hRW, ?_⟩⟩ + simp only [surfaceResidueSubsetLocus, dite_eq_left hRW, ite_eq_left hprod] + exact Set.mem_iUnion.mpr ⟨p, Set.mem_iUnion.mpr ⟨hp, Set.mem_iUnion.mpr ⟨r, hx⟩⟩⟩ + +/-- For an irreducible surface `f = 0` of degree `d ≥ 1` with curves `S`, a congruence family +`F` with constant `A` and a cutoff `X ≥ B^(1/√d)`, the residual locus has at most +`2 d A² b² (c L + 1)² X² #W (2d)^#W` integral points of height at most `B`, where `b` is the +logarithmic budget `surfaceResidueLogBudget B L`, `c` is `scalingPrimeRatio` and `W` is the +prime window of logarithmic size `L`. -/ +theorem surface_residue_residual_count_le + {f : MvPolynomial (Fin 3) ℤ} {A B L X : ℝ} + (F : SurfaceCongruenceFamily f B A) (hA : 0 ≤ A) (hB : 1 ≤ B) (hL : 1 ≤ L) + (hd : 1 ≤ f.totalDegree) (hX0 : 0 ≤ X) + (hX : B ^ (1 / Real.sqrt (f.totalDegree : ℝ)) ≤ X) + (hir : Irreducible (map (Int.castRingHom ℚ) f)) + (S : Finset (Ideal (CoordinatePolynomial 3))) + (δ : Ideal (CoordinatePolynomial 3) → ℕ) + (hS : ∀ I ∈ S, IsSurfaceCurve (map (Int.castRingHom ℚ) f) I (δ I)) : + (count (surfaceResidueResidualLocus F S L X hL) B : ℝ) ≤ + 2 * f.totalDegree * A ^ 2 * (surfaceResidueLogBudget B L) ^ 2 * + (scalingPrimeRatio * L + 1) ^ 2 * X ^ 2 * + (congruencePrimeWindow L).card * + (2 * f.totalDegree : ℝ) ^ (congruencePrimeWindow L).card := by + let W := congruencePrimeWindow L + let C : ℝ := 2 * f.totalDegree * A ^ 2 * (surfaceResidueLogBudget B L) ^ 2 * + (scalingPrimeRatio * L + 1) ^ 2 * X ^ 2 + have hC : 0 ≤ C := by dsimp only [C]; positivity + have hdR : (1 : ℝ) ≤ f.totalDegree := by exact_mod_cast hd + have hsubset (R : Finset ℕ) (hRW : R ∈ W.powerset) : + (count (surfaceResidueSubsetLocus F S L X hL R) B : ℝ) ≤ + C * W.card * (f.totalDegree : ℝ) ^ W.card := by + have hRW' : R ⊆ congruencePrimeWindow L := mem_powerset.mp hRW + have hcard : R.card ≤ W.card := card_le_card hRW' + have hR (p : ℕ) (hp : p ∈ R) : p.Prime := + ((mem_congruencePrimeWindow hL).mp (hRW' hp)).1 + by_cases hq : (∏ p ∈ R, (p : ℝ)) ≤ scalingPrimeRatio * L * X + · have hcount := count_biUnion_finset_le R + (fun p => ⋃ r : SmoothAffineResidueFamily f R, surfaceResidueCrossing F S R hR p r) B + have hcountR : + (count (surfaceResidueSubsetLocus F S L X hL R) B : ℝ) ≤ + ∑ p ∈ R, (count (⋃ r : SmoothAffineResidueFamily f R, + surfaceResidueCrossing F S R hR p r) B : ℝ) := by + simp only [surfaceResidueSubsetLocus, dite_eq_left hRW', ite_eq_left hq] + exact_mod_cast hcount + calc + _ ≤ ∑ p ∈ R, (count (⋃ r : SmoothAffineResidueFamily f R, + surfaceResidueCrossing F S R hR p r) B : ℝ) := hcountR + _ ≤ ∑ _p ∈ R, C * (f.totalDegree : ℝ) ^ R.card := by + apply sum_le_sum + intro p hp + have hh := surface_residue_edge_count_le F hA hB hL hX0 hX hir S δ hS + R hRW' hR p hp hq + convert hh using 1 + dsimp only [C] + rw [pow_succ] + ring + _ = C * R.card * (f.totalDegree : ℝ) ^ R.card := by simp; ring + _ ≤ C * W.card * (f.totalDegree : ℝ) ^ W.card := by + apply mul_le_mul + · exact mul_le_mul_of_nonneg_left (by exact_mod_cast hcard) hC + · exact pow_le_pow_right₀ hdR hcard + · positivity + · positivity + · simp only [surfaceResidueSubsetLocus, dite_eq_left hRW', ite_eq_right hq] + have hzero : count (∅ : Set (RatPoint 3)) B = 0 := by simp [count, integralPoints] + rw [hzero, Nat.cast_zero] + positivity + have hunion : + (count (surfaceResidueResidualLocus F S L X hL) B : ℝ) ≤ + ∑ R ∈ W.powerset, (count (surfaceResidueSubsetLocus F S L X hL R) B : ℝ) := by + exact_mod_cast count_biUnion_finset_le W.powerset + (surfaceResidueSubsetLocus F S L X hL) B + calc + _ ≤ ∑ R ∈ W.powerset, (count (surfaceResidueSubsetLocus F S L X hL R) B : ℝ) := hunion + _ ≤ ∑ _R ∈ W.powerset, C * W.card * (f.totalDegree : ℝ) ^ W.card := + sum_le_sum hsubset + _ = (2 : ℝ) ^ W.card * (C * W.card * (f.totalDegree : ℝ) ^ W.card) := by + simp only [sum_const, card_powerset, nsmul_eq_mul, Nat.cast_pow, Nat.cast_ofNat] + _ = _ := by + change _ = C * W.card * (2 * f.totalDegree : ℝ) ^ W.card + rw [mul_pow] + ring + +/-- An integer point of the box at which the surface `f = 0` is nonsingular, lying on a curve +`I ∈ S` that contains the initial auxiliary and has degree greater than `2 d A` times the +logarithmic budget, belongs to the residual locus `surfaceResidueResidualLocus F S L X`. -/ +theorem large_curve_point_mem_surfaceResidueResidualLocus + (f : MvPolynomial (Fin 3) ℤ) + (hir : Irreducible (map (Int.castRingHom ℚ) f)) + {A B H L X : ℝ} (hA : 0 ≤ A) (hW : IsSurfaceResidueWindow f B H L X) + (F : SurfaceCongruenceFamily f B A) + (S : Finset (Ideal (CoordinatePolynomial 3))) + (I : Ideal (CoordinatePolynomial 3)) (hIS : I ∈ S) {d : ℕ} + (hI : IsSurfaceCurve (map (Int.castRingHom ℚ) f) I d) + (hinitI : map (Int.castRingHom ℚ) F.initial ∈ I) + (hlarge : 2 * f.totalDegree * A * surfaceResidueLogBudget B L < d) + (x : IntPoint 3) (hx : InBox x B) (hxf : eval x f = 0) + (hxI : rationalPoint x ∈ idealZeroLocus I) + (hsmooth : ∃ j, eval x (pderiv j f) ≠ 0) : + rationalPoint x ∈ surfaceResidueResidualLocus F S L X hW.one_le_window := by + classical + obtain ⟨R, hR, p, hp, r, hedge⟩ := + surface_residue_boundary_of_large_curve f hir hA hW F I hI hinitI hlarge x hx hxf hsmooth + apply mem_surfaceResidueResidualLocus F S hW.one_le_window R hedge.subset_window hR p hp + hedge.prod_lt.le r + apply Set.mem_iUnion.mpr + refine ⟨I, Set.mem_iUnion.mpr + ⟨mem_filter.mpr ⟨hIS, hedge.parent_mem, hedge.child_not_mem⟩, hxI, ?_⟩⟩ + have hh := congrArg (Int.castRingHom ℚ) hedge.child_vanishes + rw [map_eval, map_zero] at hh + exact hh + +end +end CCDN diff --git a/LeanPool/CCDN/Surface/ResidueEdgeCount.lean b/LeanPool/CCDN/Surface/ResidueEdgeCount.lean new file mode 100644 index 0000000000..75b797f49c --- /dev/null +++ b/LeanPool/CCDN/Surface/ResidueEdgeCount.lean @@ -0,0 +1,231 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.Surface.ResidueBoundary +import LeanPool.CCDN.AuxiliaryPolynomial.PlaneAuxiliaryCount +import LeanPool.CCDN.Surface.CurveCrossing +import LeanPool.CCDN.Surface.NonlinearCount +import Mathlib.Algebra.Order.Star.Real + +/-! Counting the proper sections attached to one residue-subset +edge, including the sum over all smooth residue tuples. -/ + +@[expose] public section +namespace CCDN +noncomputable section +open MvPolynomial Finset + +open Classical in +/-- For a congruence family `F`, a set `R` of primes, a prime `p` and smooth residues `r` +modulo `R`, the rational points on curves `I ∈ S` whose ideal contains the auxiliary of +`R.erase p` but not the one of `R`, and at which the latter auxiliary vanishes. -/ +def surfaceResidueCrossing {f : MvPolynomial (Fin 3) ℤ} {A B : ℝ} + (F : SurfaceCongruenceFamily f B A) + (S : Finset (Ideal (CoordinatePolynomial 3))) + (R : Finset ℕ) (hR : ∀ p ∈ R, p.Prime) (p : ℕ) + (r : SmoothAffineResidueFamily f R) : Set (RatPoint 3) := + let g := map (Int.castRingHom ℚ) + (F.auxiliary (R.erase p) (fun q hq => hR q (mem_of_mem_erase hq)) + (r.restrict (erase_subset p R))) + let h := map (Int.castRingHom ℚ) (F.auxiliary R hR r) + ⋃ I ∈ S.filter (fun I => g ∈ I ∧ h ∉ I), idealZeroLocus I ∩ zeroLocus h + +private theorem surface_residue_crossing_count_le + {f : MvPolynomial (Fin 3) ℤ} {A B : ℝ} + (F : SurfaceCongruenceFamily f B A) + (hir : Irreducible (map (Int.castRingHom ℚ) f)) + (S : Finset (Ideal (CoordinatePolynomial 3))) + (δ : Ideal (CoordinatePolynomial 3) → ℕ) + (hS : ∀ I ∈ S, IsSurfaceCurve (map (Int.castRingHom ℚ) f) I (δ I)) + (R : Finset ℕ) (hR : ∀ p ∈ R, p.Prime) (p : ℕ) + (r : SmoothAffineResidueFamily f R) : + count (surfaceResidueCrossing F S R hR p r) B ≤ + f.totalDegree * + (F.auxiliary (R.erase p) (fun q hq => hR q (mem_of_mem_erase hq)) + (r.restrict (erase_subset p R))).totalDegree * + (F.auxiliary R hR r).totalDegree := by + classical + let g := map (Int.castRingHom ℚ) + (F.auxiliary (R.erase p) (fun q hq => hR q (mem_of_mem_erase hq)) + (r.restrict (erase_subset p R))) + let h := map (Int.castRingHom ℚ) (F.auxiliary R hR r) + have hh := surface_curve_crossing_count_le (map (Int.castRingHom ℚ) f) g h hir + (F.proper _ _ _) (S.filter (fun I => g ∈ I)) δ + (fun I hI => ⟨hS I (mem_filter.mp hI).1, (mem_filter.mp hI).2⟩) B + have he : (S.filter (fun I => g ∈ I)).filter (fun I => h ∉ I) = + S.filter (fun I => g ∈ I ∧ h ∉ I) := by ext I; simp only [mem_filter]; tauto + rw [he] at hh + change count (surfaceResidueCrossing F S R hR p r) B ≤ _ at hh + simpa only [g, h, rational_map_totalDegree] using hh + +private lemma SurfaceCongruenceFamily.scaled_degree_le + {f : MvPolynomial (Fin 3) ℤ} {A B L X : ℝ} + (F : SurfaceCongruenceFamily f B A) (hA : 0 ≤ A) (hB : 1 ≤ B) (hL : 1 ≤ L) + (hX : B ^ (1 / Real.sqrt (f.totalDegree : ℝ)) ≤ X) + (R : Finset ℕ) (hRW : R ⊆ congruencePrimeWindow L) + (hR : ∀ p ∈ R, p.Prime) (r : SmoothAffineResidueFamily f R) : + ((F.auxiliary R hR r).totalDegree : ℝ) * (∏ p ∈ R, (p : ℝ)) ≤ + A * (X + ∏ p ∈ R, (p : ℝ)) * surfaceResidueLogBudget B L := by + have hlogB := Real.log_nonneg hB + have hQ : 0 ≤ ∑ p ∈ R, Real.log p := sum_nonneg (fun p _ => Real.log_natCast_nonneg p) + have hlog : (∑ p ∈ R, Real.log p) ≤ + (Real.log 4 + 4) * scalingPrimeRatio * L := + (sum_le_sum_of_subset_of_nonneg hRW (fun p _ _ => Real.log_natCast_nonneg p)).trans + (congruencePrimeWindow_log_upper hL) + have hq : 0 < ∏ p ∈ R, (p : ℝ) := + prod_pos (fun p hp => by exact_mod_cast (hR p hp).pos) + have hB0 : 0 < B := by linarith + have hX0 : 0 ≤ X := (Real.rpow_nonneg hB0.le _).trans hX + have hd := F.degree_le R hR r + rw [exp_sum_log_primes R hR] at hd + have hh := mul_le_mul_of_nonneg_right hd hq.le + have he : A * (B ^ (1 / Real.sqrt (f.totalDegree : ℝ)) / + (∏ p ∈ R, (p : ℝ)) + 1) * + (1 + Real.log B + ∑ p ∈ R, Real.log p) * (∏ p ∈ R, (p : ℝ)) = + A * (B ^ (1 / Real.sqrt (f.totalDegree : ℝ)) + (∏ p ∈ R, (p : ℝ))) * + (1 + Real.log B + ∑ p ∈ R, Real.log p) := by field_simp + rw [he] at hh + apply hh.trans + apply mul_le_mul + · exact mul_le_mul_of_nonneg_left (by linarith) hA + · dsimp only [surfaceResidueLogBudget]; linarith + · positivity + · positivity + +private theorem surface_residue_crossing_scaled_count + {f : MvPolynomial (Fin 3) ℤ} {A B L X : ℝ} + (F : SurfaceCongruenceFamily f B A) (hA : 0 ≤ A) (hB : 1 ≤ B) (hL : 1 ≤ L) + (hX0 : 0 ≤ X) (hX : B ^ (1 / Real.sqrt (f.totalDegree : ℝ)) ≤ X) + (hir : Irreducible (map (Int.castRingHom ℚ) f)) + (S : Finset (Ideal (CoordinatePolynomial 3))) + (δ : Ideal (CoordinatePolynomial 3) → ℕ) + (hS : ∀ I ∈ S, IsSurfaceCurve (map (Int.castRingHom ℚ) f) I (δ I)) + (R : Finset ℕ) (hRW : R ⊆ congruencePrimeWindow L) + (hR : ∀ p ∈ R, p.Prime) (p : ℕ) (hp : p ∈ R) + (hq : (∏ q ∈ R, (q : ℝ)) ≤ scalingPrimeRatio * L * X) + (r : SmoothAffineResidueFamily f R) : + (count (surfaceResidueCrossing F S R hR p r) B : ℝ) * + (∏ q ∈ R, (q : ℝ)) ^ 2 ≤ + 2 * f.totalDegree * A ^ 2 * (surfaceResidueLogBudget B L) ^ 2 * + (scalingPrimeRatio * L + 1) ^ 2 * X ^ 2 := by + let U := scalingPrimeRatio * L + let T := surfaceResidueLogBudget B L + let q : ℝ := ∏ t ∈ R, (t : ℝ) + let g := F.auxiliary (R.erase p) (fun t ht => hR t (mem_of_mem_erase ht)) + (r.restrict (erase_subset p R)) + let h := F.auxiliary R hR r + have hU : 0 ≤ U := by dsimp only [U]; have := scalingPrimeRatio_one_le; positivity + have hT : 0 ≤ T := by + dsimp only [T, surfaceResidueLogBudget] + have := scalingPrimeRatio_one_le + have := Real.log_nonneg hB + have := Real.log_nonneg (show (1 : ℝ) ≤ 4 by norm_num) + positivity + have hpU : (p : ℝ) ≤ U := ((mem_congruencePrimeWindow hL).mp (hRW hp)).2.2 + have hqp : (∏ t ∈ R.erase p, (t : ℝ)) * p = q := prod_erase_mul R _ hp + have hpar := F.scaled_degree_le hA hB hL hX (R.erase p) + ((erase_subset p R).trans hRW) (fun t ht => hR t (mem_of_mem_erase ht)) + (r.restrict (erase_subset p R)) + have hchild := F.scaled_degree_le hA hB hL hX R hRW hR r + have hpar' : (g.totalDegree : ℝ) * q ≤ A * (X * p + q) * T := by + have hh := mul_le_mul_of_nonneg_right hpar (Nat.cast_nonneg (α := ℝ) p) + change (g.totalDegree : ℝ) * _ * p ≤ _ at hh + calc + _ = (g.totalDegree : ℝ) * (∏ t ∈ R.erase p, (t : ℝ)) * p := by rw [mul_assoc, hqp] + _ ≤ _ := hh + _ = _ := by dsimp only [T]; rw [← hqp]; ring + have hchild' : (h.totalDegree : ℝ) * q ≤ A * (X + q) * T := hchild + have hfirst : X * p + q ≤ 2 * (U + 1) * X := by + have hpX := mul_le_mul_of_nonneg_left hpU hX0 + change q ≤ U * X at hq + nlinarith + have hsecond : X + q ≤ (U + 1) * X := by + change q ≤ U * X at hq + linarith + have hpar'' : (g.totalDegree : ℝ) * q ≤ A * (2 * (U + 1) * X) * T := + hpar'.trans (mul_le_mul_of_nonneg_right (mul_le_mul_of_nonneg_left hfirst hA) hT) + have hchild'' : (h.totalDegree : ℝ) * q ≤ A * ((U + 1) * X) * T := + hchild'.trans (mul_le_mul_of_nonneg_right (mul_le_mul_of_nonneg_left hsecond hA) hT) + have hpair := mul_le_mul hpar'' hchild'' (by dsimp only [q]; positivity) + (by positivity) + have hcount : (count (surfaceResidueCrossing F S R hR p r) B : ℝ) ≤ + (f.totalDegree : ℝ) * g.totalDegree * h.totalDegree := by + exact_mod_cast surface_residue_crossing_count_le F hir S δ hS R hR p r + have hc := mul_le_mul_of_nonneg_right hcount (sq_nonneg q) + have hm := mul_le_mul_of_nonneg_left hpair (Nat.cast_nonneg (α := ℝ) f.totalDegree) + change _ ≤ 2 * f.totalDegree * A ^ 2 * T ^ 2 * (U + 1) ^ 2 * X ^ 2 + dsimp only [q] at hc hm + nlinarith [hc, hm] + +/-- Summing all smooth residue tuples cancels the square of the +modulus. Only d to the number of selected primes remains. -/ +theorem surface_residue_edge_count_le + {f : MvPolynomial (Fin 3) ℤ} {A B L X : ℝ} + (F : SurfaceCongruenceFamily f B A) (hA : 0 ≤ A) (hB : 1 ≤ B) (hL : 1 ≤ L) + (hX0 : 0 ≤ X) (hX : B ^ (1 / Real.sqrt (f.totalDegree : ℝ)) ≤ X) + (hir : Irreducible (map (Int.castRingHom ℚ) f)) + (S : Finset (Ideal (CoordinatePolynomial 3))) + (δ : Ideal (CoordinatePolynomial 3) → ℕ) + (hS : ∀ I ∈ S, IsSurfaceCurve (map (Int.castRingHom ℚ) f) I (δ I)) + (R : Finset ℕ) (hRW : R ⊆ congruencePrimeWindow L) + (hR : ∀ p ∈ R, p.Prime) (p : ℕ) (hp : p ∈ R) + (hq : (∏ q ∈ R, (q : ℝ)) ≤ scalingPrimeRatio * L * X) : + (count (⋃ r : SmoothAffineResidueFamily f R, surfaceResidueCrossing F S R hR p r) B : ℝ) ≤ + 2 * (f.totalDegree : ℝ) ^ (R.card + 1) * A ^ 2 * + (surfaceResidueLogBudget B L) ^ 2 * (scalingPrimeRatio * L + 1) ^ 2 * X ^ 2 := by + classical + let (p : R) : NeZero p.val := ⟨(hR p.val p.property).ne_zero⟩ + let : Fintype (SmoothAffineResidueFamily f R) := Pi.instFintype + let q : ℝ := ∏ p ∈ R, (p : ℝ) + let C : ℝ := 2 * f.totalDegree * A ^ 2 * (surfaceResidueLogBudget B L) ^ 2 * + (scalingPrimeRatio * L + 1) ^ 2 * X ^ 2 + have hqpos : 0 < q := prod_pos (fun p hp => by exact_mod_cast (hR p hp).pos) + have hC : 0 ≤ C := by dsimp only [C]; positivity + have hcard : (Fintype.card (SmoothAffineResidueFamily f R) : ℝ) ≤ + (f.totalDegree : ℝ) ^ R.card * q ^ 2 := by + have hh := smoothAffineResidueFamily_card_le (n := 2) f R hR + rw [Nat.card_eq_fintype_card] at hh + have hhR : (Fintype.card (SmoothAffineResidueFamily f R) : ℝ) ≤ + ((f.totalDegree ^ R.card * (∏ p ∈ R, p) ^ 2 : ℕ) : ℝ) := by exact_mod_cast hh + dsimp only [q] + simpa only [Nat.cast_mul, Nat.cast_pow, Nat.cast_prod] using hhR + have hpoint (r : SmoothAffineResidueFamily f R) : + (count (surfaceResidueCrossing F S R hR p r) B : ℝ) * q ^ 2 ≤ C := + surface_residue_crossing_scaled_count F hA hB hL hX0 hX hir S δ hS R hRW hR p hp hq r + have hunion : (count (⋃ r : SmoothAffineResidueFamily f R, + surfaceResidueCrossing F S R hR p r) B : ℝ) ≤ + ∑ r : SmoothAffineResidueFamily f R, (count (surfaceResidueCrossing F S R hR p r) B : ℝ) := by + have hh := count_biUnion_finset_le (univ : Finset (SmoothAffineResidueFamily f R)) + (fun r => surfaceResidueCrossing F S R hR p r) B + have hh' : count (⋃ r : SmoothAffineResidueFamily f R, + surfaceResidueCrossing F S R hR p r) B ≤ + ∑ r : SmoothAffineResidueFamily f R, count (surfaceResidueCrossing F S R hR p r) B := by + simpa only [Finset.mem_univ, Set.iUnion_true] using hh + exact_mod_cast hh' + have hscaled : + (count (⋃ r : SmoothAffineResidueFamily f R, surfaceResidueCrossing F S R hR p r) B : ℝ) * + q ^ 2 ≤ + (2 * (f.totalDegree : ℝ) ^ (R.card + 1) * A ^ 2 * + (surfaceResidueLogBudget B L) ^ 2 * (scalingPrimeRatio * L + 1) ^ 2 * X ^ 2) * q ^ 2 := by + calc + _ ≤ (∑ r : SmoothAffineResidueFamily f R, + (count (surfaceResidueCrossing F S R hR p r) B : ℝ)) * q ^ 2 := + mul_le_mul_of_nonneg_right hunion (sq_nonneg q) + _ = ∑ r : SmoothAffineResidueFamily f R, + (count (surfaceResidueCrossing F S R hR p r) B : ℝ) * q ^ 2 := sum_mul _ _ _ + _ ≤ ∑ _r : SmoothAffineResidueFamily f R, C := sum_le_sum (fun r _ => hpoint r) + _ = (Fintype.card (SmoothAffineResidueFamily f R) : ℝ) * C := by simp + _ ≤ ((f.totalDegree : ℝ) ^ R.card * q ^ 2) * C := + mul_le_mul_of_nonneg_right hcard hC + _ = _ := by dsimp only [C]; rw [pow_succ]; ring + exact le_of_mul_le_mul_right hscaled (sq_pos_of_pos hqpos) + +end +end CCDN diff --git a/LeanPool/CCDN/Surface/ResidueLinearCount.lean b/LeanPool/CCDN/Surface/ResidueLinearCount.lean new file mode 100644 index 0000000000..344a225d7e --- /dev/null +++ b/LeanPool/CCDN/Surface/ResidueLinearCount.lean @@ -0,0 +1,191 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.Surface.ResidueCover +import LeanPool.CCDN.Surface.PrimeWindowSubpower +import Mathlib.Algebra.Order.Star.Real + +/-! A degree-dependent logarithmic prime window discharges all height +conditions of the surface residue cover. Its complete residual point count +is O(B), uniformly in coefficients, auxiliaries and initial curve families. -/ + +@[expose] public section +namespace CCDN +noncomputable section + +/-- The constant `4 log (d + 1) + K + d + 2` scaling the logarithmic prime window of the residue +cover for a surface of degree `d` with logarithmic height constant `K`. -/ +def surfaceResidueWindowScale (d : ℕ) (K : ℝ) : ℝ := + 4 * Real.log ((d : ℝ) + 1) + K + d + 2 + +/-- The logarithmic size `surfaceResidueWindowScale d K * (log B + 1)` of the prime window of +the residue cover for a surface of degree `d` in the box of radius `B`. -/ +def surfaceResidueWindow (d : ℕ) (K B : ℝ) : ℝ := + surfaceResidueWindowScale d K * (Real.log B + 1) + +/-- The constant `1 + (log 4 + 4) c s`, with `c` the constant `scalingPrimeRatio` and `s` the +window scale `surfaceResidueWindowScale d K`, by which the logarithmic budget of the residue +cover is a multiple of `log B + 1`. -/ +def surfaceResidueLogScale (d : ℕ) (K : ℝ) : ℝ := + 1 + (Real.log 4 + 4) * scalingPrimeRatio * surfaceResidueWindowScale d K + +/-- For `K ≥ 0`, the window scale `surfaceResidueWindowScale d K` is at least two. -/ +theorem surfaceResidueWindowScale_two_le (d : ℕ) {K : ℝ} (hK : 0 ≤ K) : + 2 ≤ surfaceResidueWindowScale d K := by + have hh := Real.log_nonneg (show (1 : ℝ) ≤ (d : ℝ) + 1 by + linarith [Nat.cast_nonneg (α := ℝ) d]) + unfold surfaceResidueWindowScale + nlinarith [Nat.cast_nonneg (α := ℝ) d] + +/-- For `K ≥ 0` and `B ≥ 1`, the prime window size `surfaceResidueWindow d K B` is at +least one. -/ +theorem surfaceResidueWindow_one_le (d : ℕ) {K B : ℝ} (hK : 0 ≤ K) (hB : 1 ≤ B) : + 1 ≤ surfaceResidueWindow d K B := by + have hscale := surfaceResidueWindowScale_two_le d hK + have hlog := Real.log_nonneg hB + unfold surfaceResidueWindow + nlinarith + +/-- For `K ≥ 0`, the budget constant `surfaceResidueLogScale d K` is at least one. -/ +theorem surfaceResidueLogScale_one_le (d : ℕ) {K : ℝ} (hK : 0 ≤ K) : + 1 ≤ surfaceResidueLogScale d K := by + have hs := surfaceResidueWindowScale_two_le d hK + have hp := scalingPrimeRatio_one_le + have hl := Real.log_nonneg (by norm_num : (1 : ℝ) ≤ 4) + unfold surfaceResidueLogScale + have hh : 0 ≤ (Real.log 4 + 4) * scalingPrimeRatio * surfaceResidueWindowScale d K := by + positivity + linarith + +/-- For the prime window `surfaceResidueWindow d K B`, the logarithmic budget of the residue +cover equals `surfaceResidueLogScale d K * (log B + 1)`. -/ +theorem surfaceResidueLogBudget_window (d : ℕ) (K B : ℝ) : + surfaceResidueLogBudget B (surfaceResidueWindow d K B) = + surfaceResidueLogScale d K * (Real.log B + 1) := by + unfold surfaceResidueLogBudget surfaceResidueWindow surfaceResidueLogScale + ring + +/-- The chosen window meets both the derivative-height condition and the +terminal-modulus threshold, even at B = 1. -/ +private theorem surfaceResidueWindow_parameters {d : ℕ} (hd : 1 ≤ d) + {K B H : ℝ} (hK : 0 ≤ K) (hB : 1 ≤ B) + (hheight : Real.log H ≤ K * (Real.log B + 1)) : + 4 * Real.log ((d : ℝ) + 1) + Real.log H + d * Real.log B ≤ + surfaceResidueWindow d K B ∧ + 1 < 2 * B ^ (1 / Real.sqrt (d : ℝ)) ∧ + B ^ (1 / Real.sqrt (d : ℝ)) ≤ 2 * B ^ (1 / Real.sqrt (d : ℝ)) ∧ + 2 * B ^ (1 / Real.sqrt (d : ℝ)) ≤ Real.exp (surfaceResidueWindow d K B) := by + have hB0 : 0 < B := zero_lt_one.trans_le hB + have hlogB := Real.log_nonneg hB + have hlogd := Real.log_nonneg (show (1 : ℝ) ≤ (d : ℝ) + 1 by + linarith [Nat.cast_nonneg (α := ℝ) d]) + have hdR : (1 : ℝ) ≤ d := by exact_mod_cast hd + have hsqrt : 1 ≤ Real.sqrt (d : ℝ) := Real.le_sqrt_of_sq_le (by simpa using hdR) + have hroot : 0 ≤ 1 / Real.sqrt (d : ℝ) := by positivity + have hroot1 : 1 / Real.sqrt (d : ℝ) ≤ 1 := by + simpa using one_div_le_one_div_of_le zero_lt_one hsqrt + have hp : 1 ≤ B ^ (1 / Real.sqrt (d : ℝ)) := Real.one_le_rpow hB hroot + refine ⟨?_, by linarith, by linarith, ?_⟩ + · unfold surfaceResidueWindow surfaceResidueWindowScale + nlinarith + · have hlog2 : Real.log 2 ≤ (2 : ℝ) := Real.log_le_self (by norm_num) + have hscale := surfaceResidueWindowScale_two_le d hK + have hlogX : Real.log (2 * B ^ (1 / Real.sqrt (d : ℝ))) ≤ + surfaceResidueWindow d K B := by + rw [Real.log_mul (by norm_num) (by positivity), Real.log_rpow hB0] + unfold surfaceResidueWindow + have hh := mul_le_mul_of_nonneg_right hroot1 hlogB + nlinarith + have hh := Real.exp_le_exp.mpr hlogX + rwa [Real.exp_log (by positivity)] at hh + +/-- For a surface equation of degree `d ≥ 1` whose coefficients are bounded by `H ≥ 1` with +`log H ≤ K (log B + 1)`, the chosen window `surfaceResidueWindow d K B` and the cutoff +`2 B^(1/√d)` are residue-cover parameters. -/ +theorem isSurfaceResidueWindow_of_log_height {f : MvPolynomial (Fin 3) ℤ} + (hd : 1 ≤ f.totalDegree) {K B H : ℝ} (hK : 0 ≤ K) (hB : 1 ≤ B) (hH : 1 ≤ H) + (hcoeff : CoefficientHeightLE f H) (hheight : Real.log H ≤ K * (Real.log B + 1)) : + IsSurfaceResidueWindow f B H (surfaceResidueWindow f.totalDegree K B) + (2 * B ^ (1 / Real.sqrt (f.totalDegree : ℝ))) := by + obtain ⟨hderivative, hX, hBX, hXL⟩ := surfaceResidueWindow_parameters hd hK hB hheight + exact ⟨hB, hH, surfaceResidueWindow_one_le _ hK hB, hcoeff, hderivative, hX, hBX, hXL⟩ + +/-- The residual locus has a uniform linear count. The constant is +chosen before the equation, radius, congruence auxiliaries and curve family. -/ +theorem surface_residue_linear_count (d : ℕ) (hd : 5 ≤ d) + (K A : ℝ) (hK : 0 ≤ K) (hA : 0 ≤ A) : + ∃ C : ℝ, 0 < C ∧ + ∀ (f : MvPolynomial (Fin 3) ℤ), f.totalDegree = d → + ∀ B : ℝ, ∀ hB : 1 ≤ B, ∀ F : SurfaceCongruenceFamily f B A, + Irreducible (MvPolynomial.map (Int.castRingHom ℚ) f) → + ∀ (S : Finset (Ideal (CoordinatePolynomial 3))) + (δ : Ideal (CoordinatePolynomial 3) → ℕ), + (∀ I ∈ S, IsSurfaceCurve (MvPolynomial.map (Int.castRingHom ℚ) f) I (δ I)) → + (count (surfaceResidueResidualLocus F S (surfaceResidueWindow d K B) + (2 * B ^ (1 / Real.sqrt (d : ℝ))) (surfaceResidueWindow_one_le d hK hB)) B : ℝ) ≤ + C * B := by + have hscale := surfaceResidueWindowScale_two_le d hK + have hlogscale := surfaceResidueLogScale_one_le d hK + have hratio := scalingPrimeRatio_one_le + have hdR : (5 : ℝ) ≤ d := by exact_mod_cast hd + let U := scalingPrimeRatio * surfaceResidueWindowScale d K + 1 + have hU : 0 ≤ U := by dsimp only [U]; positivity + obtain ⟨C₀, hC₀, hbound⟩ := congruencePrimeWindow_surface_residual_bound + (show 1 ≤ surfaceResidueWindowScale d K by linarith) + (show 1 ≤ 2 * (d : ℝ) by linarith) 4 + let E := 8 * (d : ℝ) * A ^ 2 * (surfaceResidueLogScale d K) ^ 2 * U ^ 2 + have hE : 0 ≤ E := by dsimp only [E]; positivity + refine ⟨E * C₀ + 1, by positivity, ?_⟩ + intro f hfd B hB F hir S δ hS + have hB0 : 0 < B := zero_lt_one.trans_le hB + have hlogB := Real.log_nonneg hB + have hL := surfaceResidueWindow_one_le d hK hB + have hpoly := surface_residue_residual_count_le F hA hB hL + (by omega : 1 ≤ f.totalDegree) + (show 0 ≤ 2 * B ^ (1 / Real.sqrt (d : ℝ)) by positivity) + (by rw [hfd]; nlinarith [Real.rpow_pos_of_pos hB0 (1 / Real.sqrt (d : ℝ))]) + hir S δ hS + rw [hfd, surfaceResidueLogBudget_window] at hpoly + have hu : scalingPrimeRatio * surfaceResidueWindow d K B + 1 ≤ + U * (Real.log B + 1) := by + dsimp only [surfaceResidueWindow, U] + nlinarith + have hx : (2 * B ^ (1 / Real.sqrt (d : ℝ))) ^ 2 = + 4 * B ^ (2 / Real.sqrt (d : ℝ)) := by + rw [mul_pow, ← Real.rpow_natCast (B ^ (1 / Real.sqrt (d : ℝ))) 2, + ← Real.rpow_mul hB0.le] + norm_num only [Nat.cast_ofNat] + congr 2 + ring + rw [hx] at hpoly + have hestimate : + (count (surfaceResidueResidualLocus F S (surfaceResidueWindow d K B) + (2 * B ^ (1 / Real.sqrt (d : ℝ))) hL) B : ℝ) ≤ + E * (((congruencePrimeWindow (surfaceResidueWindow d K B)).card * + (2 * (d : ℝ)) ^ (congruencePrimeWindow (surfaceResidueWindow d K B)).card) * + B ^ (2 / Real.sqrt (d : ℝ)) * (Real.log B + 1) ^ 4) := by + calc + _ ≤ _ := hpoly + _ ≤ 2 * (d : ℝ) * A ^ 2 * + (surfaceResidueLogScale d K * (Real.log B + 1)) ^ 2 * + (U * (Real.log B + 1)) ^ 2 * + (4 * B ^ (2 / Real.sqrt (d : ℝ))) * + (congruencePrimeWindow (surfaceResidueWindow d K B)).card * + (2 * (d : ℝ)) ^ (congruencePrimeWindow (surfaceResidueWindow d K B)).card := by + gcongr + _ = _ := by dsimp only [E]; ring + have hh := mul_le_mul_of_nonneg_left (hbound d hd B hB) hE + have hfinal : (count (surfaceResidueResidualLocus F S (surfaceResidueWindow d K B) + (2 * B ^ (1 / Real.sqrt (d : ℝ))) hL) B : ℝ) ≤ E * (C₀ * B) := + hestimate.trans hh + nlinarith + +end +end CCDN diff --git a/LeanPool/CCDN/Surface/ResidueSubsetBoundary.lean b/LeanPool/CCDN/Surface/ResidueSubsetBoundary.lean new file mode 100644 index 0000000000..6a267e6ef8 --- /dev/null +++ b/LeanPool/CCDN/Surface/ResidueSubsetBoundary.lean @@ -0,0 +1,83 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import Mathlib.Algebra.BigOperators.Group.Finset.Defs +public import Mathlib.Basic.Real.Basic +import Mathlib.Algebra.BigOperators.Group.Finset.Basic +import Mathlib.Data.Finset.Max +import Mathlib.Data.Finset.Powerset +import Mathlib.Tactic.Linarith + +/-! Finite subset selection for the residue cover. A minimal modulus +crosses its target by at most one prime factor. A change of containment +then occurs across a single deletion edge. -/ + +@[expose] public section +namespace CCDN +noncomputable section +open Finset + +/-- If a finite set `P` of positive integers bounded by `U` has product at least `X > 1`, then +it contains a nonempty subset `R` with product at least `X` and less than `U X`, all of whose +proper subsets have product less than `X`. -/ +theorem exists_minimal_product_subset (P : Finset ℕ) {X U : ℝ} + (hX : 1 < X) (hP : ∀ p ∈ P, (0 : ℝ) < p) + (hU : ∀ p ∈ P, (p : ℝ) ≤ U) + (hprod : X ≤ ∏ p ∈ P, (p : ℝ)) : + ∃ R : Finset ℕ, R ⊆ P ∧ R.Nonempty ∧ X ≤ ∏ p ∈ R, (p : ℝ) ∧ + (∀ T ⊂ R, (∏ p ∈ T, (p : ℝ)) < X) ∧ + (∏ p ∈ R, (p : ℝ)) < U * X := by + classical + let S := P.powerset.filter (fun R : Finset ℕ => X ≤ ∏ p ∈ R, (p : ℝ)) + have hS : S.Nonempty := ⟨P, mem_filter.mpr ⟨mem_powerset.mpr (Subset.refl _), hprod⟩⟩ + obtain ⟨R, hRS, hmin⟩ := S.exists_min_image Finset.card hS + obtain ⟨hRP, hRX⟩ := mem_filter.mp hRS + have hRP' : R ⊆ P := mem_powerset.mp hRP + have hRne : R.Nonempty := by + apply nonempty_iff_ne_empty.mpr + intro he + simp only [he, prod_empty] at hRX + linarith + have hproper : ∀ T ⊂ R, (∏ p ∈ T, (p : ℝ)) < X := by + intro T hTR + by_contra hn + have hTS : T ∈ S := mem_filter.mpr + ⟨mem_powerset.mpr (hTR.subset.trans hRP'), le_of_not_gt hn⟩ + exact (not_le_of_gt (card_lt_card hTR)) (hmin T hTS) + refine ⟨R, hRP', hRne, hRX, hproper, ?_⟩ + obtain ⟨p, hp⟩ := hRne + have hsmall := hproper (R.erase p) (erase_ssubset hp) + have hmul := mul_lt_mul_of_pos_left hsmall (hP p (hRP' hp)) + rw [mul_prod_erase R _ hp] at hmul + exact hmul.trans_le (mul_le_mul_of_nonneg_right (hU p (hRP' hp)) (by linarith)) + +/-- Any property false at the empty node and true at a terminal node +changes across a one-element subset edge. -/ +theorem exists_subset_erase_boundary {α : Type*} [DecidableEq α] (R : Finset α) + (W : Finset α → Prop) (hempty : ¬ W ∅) (hR : W R) : + ∃ T : Finset α, T ⊆ R ∧ ∃ p ∈ T, W T ∧ ¬ W (T.erase p) := by + classical + let S := R.powerset.filter W + have hS : S.Nonempty := ⟨R, mem_filter.mpr ⟨mem_powerset.mpr (Subset.refl _), hR⟩⟩ + obtain ⟨T, hTS, hmin⟩ := S.exists_min_image Finset.card hS + obtain ⟨hTR, hWT⟩ := mem_filter.mp hTS + have hTne : T.Nonempty := by + apply nonempty_iff_ne_empty.mpr + rintro rfl + exact hempty hWT + obtain ⟨p, hp⟩ := hTne + refine ⟨T, mem_powerset.mp hTR, p, hp, hWT, ?_⟩ + intro he + have herase : T.erase p ∈ S := mem_filter.mpr + ⟨mem_powerset.mpr (Subset.trans (erase_subset p T) (mem_powerset.mp hTR)), he⟩ + exact (not_le_of_gt (card_lt_card (erase_ssubset hp))) (hmin _ herase) + +end +end CCDN diff --git a/LeanPool/CCDN/Surface/RetainedCurveCount.lean b/LeanPool/CCDN/Surface/RetainedCurveCount.lean new file mode 100644 index 0000000000..c0a4f1670c --- /dev/null +++ b/LeanPool/CCDN/Surface/RetainedCurveCount.lean @@ -0,0 +1,118 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.Algebra.PolynomialPredicates +public import LeanPool.CCDN.Surface.CurveComponents +import LeanPool.CCDN.Analysis.SmallDegreeSurfaceNumerics +import LeanPool.CCDN.Surface.DegreeOneCount +import LeanPool.CCDN.Surface.SmallDegreeSurfaceCurveCount + +/-! The retained part of the small-degree surface cover. Lines and +nonlinear prime curves are counted together, using their common degree +budget and the logarithmic cap on the retained degrees. -/ + +@[expose] public section +namespace CCDN +noncomputable section +open MvPolynomial Finset + +private theorem surface_curve_number_weight {d : ℕ} (hd : 5 ≤ d) {B : ℝ} (hB : 1 ≤ B) : + B ^ (1 / Real.sqrt (d : ℝ)) * (Real.log B + 1) ≤ + (20 * Real.exp (1 / 20 : ℝ)) * B := by + calc + _ ≤ B ^ (1 / Real.sqrt (d : ℝ) + 1 / 2) * (Real.log B + 1) := by + apply mul_le_mul_of_nonneg_right + · exact Real.rpow_le_rpow_of_exponent_le hB (by linarith) + · linarith [Real.log_nonneg hB] + _ ≤ _ := by + have hh := surface_nonlinear_polylog_weight hd 1 hB + norm_num only [Nat.factorial, Nat.cast_one, pow_one, div_one] at hh + convert hh using 1 + +/-- There is an absolute constant `C` such that, for an integer surface equation `f` of degree +`d ≥ 5` with absolutely irreducible top-degree form and a finite family `S` of curves of +positive degree on the surface, each of degree at most `D (log B + 1)` and of total degree at +most `A B^(1/√d) (log B + 1)`, the union of the curves has at most +`C (d⁶ + A (D² (D + 1) + 1)) B` integral points of height at most `B`. -/ +theorem retained_surface_curve_union_bound : + ∃ C : ℝ, 0 < C ∧ + ∀ (f : MvPolynomial (Fin 3) ℤ), + HasAbsIrreducibleTopForm f → + 5 ≤ f.totalDegree → + ∀ (S : Finset (Ideal (CoordinatePolynomial 3))) + (δ : Ideal (CoordinatePolynomial 3) → ℕ) (A D B : ℝ), + 0 ≤ A → 0 ≤ D → 1 ≤ B → + (∀ I ∈ S, IsSurfaceCurve (map (Int.castRingHom ℚ) f) I (δ I) ∧ 0 < δ I) → + (∀ I ∈ S, (δ I : ℝ) ≤ D * (Real.log B + 1)) → + (∑ I ∈ S, (δ I : ℝ)) ≤ + A * B ^ (1 / Real.sqrt (f.totalDegree : ℝ)) * (Real.log B + 1) → + (count (⋃ I ∈ S, idealZeroLocus I) B : ℝ) ≤ + C * ((f.totalDegree : ℝ) ^ 6 + A * (D ^ 2 * (D + 1) + 1)) * B := by + obtain ⟨Cline, hCline, hline⟩ := surface_degree_one_component_contribution + obtain ⟨Ccurve, hCcurve, hcurve⟩ := logarithmic_degree_curve_union_bound 1 + let Ccard := 20 * Real.exp (1 / 20 : ℝ) + have hCcard : 0 < Ccard := by dsimp only [Ccard]; positivity + let C := Cline + Ccurve + Ccard + have hC : 0 < C := by dsimp only [C]; positivity + refine ⟨C, hC, ?_⟩ + intro f hir hd S δ A D B hA hD hB hS hcap hbudget + let lines := S.filter (fun I => δ I = 1) + let curves := S.filter (fun I => 2 ≤ δ I) + have hlineCount := hline f hir (by omega) B (by linarith) lines + (fun I hI => by + obtain ⟨hIS, hδ⟩ := mem_filter.mp hI + exact ⟨(hS I hIS).1.isPrime, (hS I hIS).1.height_eq, hδ ▸ (hS I hIS).1.degree⟩) + (fun I hI => (hS I (mem_filter.mp hI).1).1.mem) + have hcurveBudget : (∑ I ∈ curves, (δ I : ℝ)) ≤ + A * B ^ (1 / Real.sqrt (f.totalDegree : ℝ)) * (Real.log B + 1) ^ 1 := by + rw [pow_one] + exact (sum_le_sum_of_subset_of_nonneg (filter_subset _ _) (by intros; positivity)).trans hbudget + have hcurveCount := hcurve curves δ f.totalDegree hd A D B hA hD hB + (fun I hI => by + obtain ⟨hIS, hδ⟩ := mem_filter.mp hI + exact ⟨(hS I hIS).1.isPrime, hδ, (hS I hIS).1.degree⟩) + (fun I hI => hcap I (mem_filter.mp hI).1) hcurveBudget + have hcard : (lines.card : ℝ) ≤ ∑ I ∈ S, (δ I : ℝ) := by + calc + _ ≤ (S.card : ℝ) := by exact_mod_cast card_le_card (filter_subset (fun I => δ I = 1) S) + _ = ∑ _I ∈ S, (1 : ℝ) := by simp + _ ≤ _ := sum_le_sum (fun I hI => by exact_mod_cast (hS I hI).2) + have hcardBound : (lines.card : ℝ) ≤ A * Ccard * B := by + have hh := mul_le_mul_of_nonneg_left (surface_curve_number_weight hd hB) hA + exact (hcard.trans hbudget).trans (by simpa only [mul_assoc, Ccard] using hh) + have hsub : (⋃ I ∈ S, idealZeroLocus I) ⊆ + (⋃ I ∈ lines, idealZeroLocus I) ∪ ⋃ I ∈ curves, idealZeroLocus I := by + intro x hx + obtain ⟨I, hxI⟩ := Set.mem_iUnion.mp hx + obtain ⟨hIS, hxI⟩ := Set.mem_iUnion.mp hxI + by_cases hδ : δ I = 1 + · exact Or.inl (Set.mem_iUnion.mpr ⟨I, Set.mem_iUnion.mpr + ⟨mem_filter.mpr ⟨hIS, hδ⟩, hxI⟩⟩) + · have hδ2 : 2 ≤ δ I := by have := (hS I hIS).2; omega + exact Or.inr (Set.mem_iUnion.mpr ⟨I, Set.mem_iUnion.mpr + ⟨mem_filter.mpr ⟨hIS, hδ2⟩, hxI⟩⟩) + have hcount : (count (⋃ I ∈ S, idealZeroLocus I) B : ℝ) ≤ + (count (⋃ I ∈ lines, idealZeroLocus I) B : ℝ) + + count (⋃ I ∈ curves, idealZeroLocus I) B := by + exact_mod_cast (count_mono hsub (le_refl B)).trans + (count_union_le (⋃ I ∈ lines, idealZeroLocus I) (⋃ I ∈ curves, idealZeroLocus I) B) + have hCl : Cline ≤ C := by dsimp only [C]; linarith + have hCc : Ccurve ≤ C := by dsimp only [C]; linarith + have hCn : Ccard ≤ C := by dsimp only [C]; linarith + have hB0 : 0 ≤ B := by linarith + have hl := mul_le_mul_of_nonneg_right (mul_le_mul_of_nonneg_right hCl + (show 0 ≤ (f.totalDegree : ℝ) ^ 6 by positivity)) hB0 + have hn := mul_le_mul_of_nonneg_right (mul_le_mul_of_nonneg_left hCn hA) hB0 + have hc := mul_le_mul_of_nonneg_right hCc + (show 0 ≤ A * D ^ 2 * (D + 1) * B by positivity) + nlinarith [hcount, hlineCount, hcurveCount, hcardBound, hl, hn, hc] + +end +end CCDN diff --git a/LeanPool/CCDN/Surface/SmallCurveCover.lean b/LeanPool/CCDN/Surface/SmallCurveCover.lean new file mode 100644 index 0000000000..8b6e1fbf31 --- /dev/null +++ b/LeanPool/CCDN/Surface/SmallCurveCover.lean @@ -0,0 +1,144 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.Hypersurface.SingularHypersurfaceCount +public import LeanPool.CCDN.Surface.ResidueCover +import LeanPool.CCDN.AuxiliaryPolynomial.PlaneAuxiliaryCount +import LeanPool.CCDN.Surface.CompleteIntersectionDegreeBudget + +/-! A small-curve and residual cover for a surface of controlled +coefficient height. Both the auxiliaries and the initial prime components +are constructed. -/ + +@[expose] public section +namespace CCDN +noncomputable section +open MvPolynomial Finset + +/-- The curve components cut out on the surface `f = 0` by the initial auxiliary of a +congruence family: height-two primes containing `f` and the auxiliary, whose zero loci cover +the common zero locus and whose total degree is at most `d A (B^(1/√d) + 1)(1 + log B)`. -/ +theorem exists_surface_initial_curve_components (f : MvPolynomial (Fin 3) ℤ) + (hirQ : Irreducible (map (Int.castRingHom ℚ) f)) {A B : ℝ} + (F : SurfaceCongruenceFamily f B A) : + ∃ S : Finset (Ideal (CoordinatePolynomial 3)), + ∃ δ : Ideal (CoordinatePolynomial 3) → ℕ, + (∀ I ∈ S, IsSectionCurve (map (Int.castRingHom ℚ) f) + (map (Int.castRingHom ℚ) F.initial) I (δ I)) ∧ + zeroLocus (map (Int.castRingHom ℚ) f) ∩ + zeroLocus (map (Int.castRingHom ℚ) F.initial) = ⋃ I ∈ S, idealZeroLocus I ∧ + (∑ I ∈ S, (δ I : ℝ)) ≤ (f.totalDegree : ℝ) * A * + (B ^ (1 / Real.sqrt (f.totalDegree : ℝ)) + 1) * (1 + Real.log B) := by + obtain ⟨S, δ, hS, hcover, hbudget, _⟩ := + exists_completeIntersection_curve_components (map (Int.castRingHom ℚ) f) + (map (Int.castRingHom ℚ) F.initial) hirQ + (F.proper ∅ (by simp) (emptySmoothAffineResidueFamily f)) + refine ⟨S, δ, hS, hcover, ?_⟩ + have hinitial : (F.initial.totalDegree : ℝ) ≤ + A * (B ^ (1 / Real.sqrt (f.totalDegree : ℝ)) + 1) * (1 + Real.log B) := by + have hh := F.degree_le ∅ (by simp) (emptySmoothAffineResidueFamily f) + simpa only [SurfaceCongruenceFamily.initial, sum_empty, Real.exp_zero, div_one, add_zero] + using hh + rw [rational_map_totalDegree, rational_map_totalDegree] at hbudget + have hb : (∑ I ∈ S, (δ I : ℝ)) ≤ (f.totalDegree : ℝ) * F.initial.totalDegree := by + exact_mod_cast hbudget + have hm := mul_le_mul_of_nonneg_left hinitial (Nat.cast_nonneg (α := ℝ) f.totalDegree) + exact hb.trans (by simpa only [mul_assoc] using hm) + +/-- An integral point of the surface `f = 0` is singular, or lies on a component of the initial +cover of degree at most `2 d A Λ(B, L)`, or lies in the residual locus. -/ +private theorem surface_point_mem_small_curve_cover (f : MvPolynomial (Fin 3) ℤ) + (hirQ : Irreducible (map (Int.castRingHom ℚ) f)) + {A B H L X : ℝ} (hA : 0 ≤ A) (hW : IsSurfaceResidueWindow f B H L X) + (F : SurfaceCongruenceFamily f B A) + (S : Finset (Ideal (CoordinatePolynomial 3))) (δ : Ideal (CoordinatePolynomial 3) → ℕ) + (hS : ∀ I ∈ S, IsSectionCurve (map (Int.castRingHom ℚ) f) + (map (Int.castRingHom ℚ) F.initial) I (δ I)) + (hcover : zeroLocus (map (Int.castRingHom ℚ) f) ∩ + zeroLocus (map (Int.castRingHom ℚ) F.initial) = ⋃ I ∈ S, idealZeroLocus I) + (x : IntPoint 3) (hx : InBox x B) (hxf : eval x f = 0) : + rationalPoint x ∈ singularHypersurfaceLocus (map (Int.castRingHom ℚ) f) ∪ + (⋃ I ∈ S.filter (fun I => (δ I : ℝ) ≤ + 2 * f.totalDegree * A * surfaceResidueLogBudget B L), idealZeroLocus I) ∪ + surfaceResidueResidualLocus F S L X hW.one_le_window := by + have hrat {g : MvPolynomial (Fin 3) ℤ} (hg : eval x g = 0) : + eval (rationalPoint x) (map (Int.castRingHom ℚ) g) = 0 := by + have hh := congrArg (Int.castRingHom ℚ) hg + rwa [map_eval, map_zero] at hh + by_cases hsmooth : ∃ j, eval x (pderiv j f) ≠ 0 + · have hxcover : rationalPoint x ∈ ⋃ I ∈ S, idealZeroLocus I := by + rw [← hcover] + exact ⟨hrat hxf, hrat (F.initial_vanishes x hx hxf)⟩ + obtain ⟨I, hxI⟩ := Set.mem_iUnion.mp hxcover + obtain ⟨hIS, hxI⟩ := Set.mem_iUnion.mp hxI + by_cases hsmall : (δ I : ℝ) ≤ 2 * f.totalDegree * A * surfaceResidueLogBudget B L + · exact Or.inl (Or.inr (Set.mem_iUnion.mpr + ⟨I, Set.mem_iUnion.mpr ⟨mem_filter.mpr ⟨hIS, hsmall⟩, hxI⟩⟩)) + · right + exact large_curve_point_mem_surfaceResidueResidualLocus f hirQ hA hW F S I hIS + (hS I hIS).toIsSurfaceCurve (hS I hIS).mem_right (lt_of_not_ge hsmall) x hx hxf hxI + hsmooth + · refine Or.inl (Or.inl ?_) + change eval (rationalPoint x) (map (Int.castRingHom ℚ) f) = 0 ∧ _ + refine ⟨hrat hxf, fun j => ?_⟩ + rw [pderiv_map] + exact hrat (not_not.mp (fun hn => hsmooth ⟨j, hn⟩)) + +/-- A small-curve and residual cover of the integral points of the surface `f = 0`: they are +singular, lie on a component of degree at most `2 d A Λ(B, L)` of the curve cover cut out by the +initial auxiliary, or lie in the residual locus. -/ +theorem exists_surface_small_curve_residual_cover + (f : MvPolynomial (Fin 3) ℤ) + (hir : AbsolutelyIrreducible f) + (hd : 0 < f.totalDegree) {K H B L X : ℝ} (hK : 0 ≤ K) + (hheight : Real.log H ≤ K * (Real.log B + 1)) (hW : IsSurfaceResidueWindow f B H L X) : + ∃ F : SurfaceCongruenceFamily f B (surfaceCongruenceDegreeConstant f.totalDegree K), + ∃ S : Finset (Ideal (CoordinatePolynomial 3)), + ∃ δ : Ideal (CoordinatePolynomial 3) → ℕ, + (∀ I ∈ S, IsSectionCurve (map (Int.castRingHom ℚ) f) + (map (Int.castRingHom ℚ) F.initial) I (δ I)) ∧ + (∑ I ∈ S, (δ I : ℝ)) ≤ (f.totalDegree : ℝ) * + surfaceCongruenceDegreeConstant f.totalDegree K * + (B ^ (1 / Real.sqrt (f.totalDegree : ℝ)) + 1) * (1 + Real.log B) ∧ + count (zeroLocus (map (Int.castRingHom ℚ) f)) B ≤ + count (singularHypersurfaceLocus (map (Int.castRingHom ℚ) f)) B + + count (⋃ I ∈ S.filter (fun I => (δ I : ℝ) ≤ + 2 * f.totalDegree * surfaceCongruenceDegreeConstant f.totalDegree K * + surfaceResidueLogBudget B L), idealZeroLocus I) B + + count (surfaceResidueResidualLocus F S L X hW.one_le_window) B := by + classical + have hA : 0 ≤ surfaceCongruenceDegreeConstant f.totalDegree K := + (surfaceCongruenceDegreeConstant_pos f.totalDegree K).le + have hirQ := rational_irreducible_of_charZero_map f hir + obtain ⟨F⟩ := exists_surface_congruence_family f hir hd hK hW.one_le_height hW.one_le_radius + hW.coeff_le hheight + obtain ⟨S, δ, hS, hcover, hbudget⟩ := exists_surface_initial_curve_components f hirQ F + refine ⟨F, S, δ, hS, hbudget, ?_⟩ + let singular := singularHypersurfaceLocus (map (Int.castRingHom ℚ) f) + let retained := ⋃ I ∈ S.filter (fun I => (δ I : ℝ) ≤ + 2 * f.totalDegree * surfaceCongruenceDegreeConstant f.totalDegree K * + surfaceResidueLogBudget B L), idealZeroLocus I + let residual := surfaceResidueResidualLocus F S L X hW.one_le_window + have hcount : count (zeroLocus (map (Int.castRingHom ℚ) f)) B ≤ + count (singular ∪ retained ∪ residual) B := by + apply card_le_card + intro x hx + obtain ⟨hbox, hzero⟩ := mem_integralPoints.mp hx + have hxf : eval x f = 0 := by + apply (show Function.Injective (Int.castRingHom ℚ) from Int.cast_injective) + rw [map_eval, map_zero] + exact hzero + exact mem_integralPoints.mpr + ⟨hbox, surface_point_mem_small_curve_cover f hirQ hA hW F S δ hS hcover x hbox hxf⟩ + exact hcount.trans ((count_union_le (singular ∪ retained) residual B).trans + (Nat.add_le_add_right (count_union_le singular retained B) _)) + +end +end CCDN diff --git a/LeanPool/CCDN/Surface/SmallDegreeSurfaceCurveCount.lean b/LeanPool/CCDN/Surface/SmallDegreeSurfaceCurveCount.lean new file mode 100644 index 0000000000..4ea65084f5 --- /dev/null +++ b/LeanPool/CCDN/Surface/SmallDegreeSurfaceCurveCount.lean @@ -0,0 +1,122 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.Statements +import LeanPool.CCDN.Analysis.SmallDegreeSurfaceNumerics +import LeanPool.CCDN.Curve.AffineCurveCount +import LeanPool.CCDN.Surface.NonlinearCount + +/-! Nonlinear initial curves whose degrees are at most logarithmic in the +box radius. The full affine curve theorem and the strict surface exponent +gap give a linear bound in every surface degree at least five. -/ + +@[expose] public section +namespace CCDN +noncomputable section + +private theorem curveBound_logarithmic_degree {δ : ℕ} (hδ : 2 ≤ δ) + {B D : ℝ} (hB : 1 ≤ B) (_hD : 0 ≤ D) + (hδD : (δ : ℝ) ≤ D * (Real.log B + 1)) : + curveBound δ B ≤ (δ : ℝ) * D ^ 2 * (D + 1) * + B ^ (1 / 2 : ℝ) * (Real.log B + 1) ^ 3 := by + have hB0 : 0 < B := zero_lt_one.trans_le hB + have hlog := Real.log_nonneg hB + have hδR : (2 : ℝ) ≤ δ := by exact_mod_cast hδ + have hpow : B ^ (1 / (δ : ℝ)) ≤ B ^ (1 / 2 : ℝ) := + Real.rpow_le_rpow_of_exponent_le hB + (one_div_le_one_div_of_le (by norm_num) hδR) + have hsq : (δ : ℝ) ^ 2 ≤ (D * (Real.log B + 1)) ^ 2 := + pow_le_pow_left₀ (by positivity) hδD 2 + have hlogδ : Real.log B + δ ≤ (D + 1) * (Real.log B + 1) := by nlinarith + have hcube : (δ : ℝ) ^ 3 ≤ (δ : ℝ) * (D * (Real.log B + 1)) ^ 2 := by + have hh := mul_le_mul_of_nonneg_left hsq (show (0 : ℝ) ≤ δ by positivity) + nlinarith + unfold curveBound + calc + _ ≤ ((δ : ℝ) * (D * (Real.log B + 1)) ^ 2) * + B ^ (1 / 2 : ℝ) * ((D + 1) * (Real.log B + 1)) := by + apply mul_le_mul (mul_le_mul hcube hpow (by positivity) (by positivity)) hlogδ + · positivity + · positivity + _ = _ := by ring + +private theorem sum_curveBound_logarithmic_degrees {ι : Type*} + (S : Finset ι) (δ : ι → ℕ) (k : ℕ) {d : ℕ} (hd : 5 ≤ d) + {A D B : ℝ} (hA : 0 ≤ A) (hD : 0 ≤ D) (hB : 1 ≤ B) + (hδ : ∀ i ∈ S, 2 ≤ δ i) + (hcap : ∀ i ∈ S, (δ i : ℝ) ≤ D * (Real.log B + 1)) + (hbudget : (∑ i ∈ S, (δ i : ℝ)) ≤ + A * B ^ (1 / Real.sqrt (d : ℝ)) * (Real.log B + 1) ^ k) : + ∑ i ∈ S, curveBound (δ i) B ≤ + ((k + 3).factorial / (1 / 20 : ℝ) ^ (k + 3) * Real.exp (1 / 20)) * + A * D ^ 2 * (D + 1) * B := by + have hB0 : 0 < B := zero_lt_one.trans_le hB + have hlog := Real.log_nonneg hB + let C := ((k + 3).factorial / (1 / 20 : ℝ) ^ (k + 3) * Real.exp (1 / 20)) + have hsum : (∑ i ∈ S, curveBound (δ i) B) ≤ + (∑ i ∈ S, (δ i : ℝ)) * D ^ 2 * (D + 1) * + B ^ (1 / 2 : ℝ) * (Real.log B + 1) ^ 3 := by + calc + _ ≤ ∑ i ∈ S, (δ i : ℝ) * D ^ 2 * (D + 1) * + B ^ (1 / 2 : ℝ) * (Real.log B + 1) ^ 3 := + Finset.sum_le_sum (fun i hi => curveBound_logarithmic_degree + (hδ i hi) hB hD (hcap i hi)) + _ = _ := by simp only [Finset.sum_mul] + calc + _ ≤ _ := hsum + _ ≤ (A * B ^ (1 / Real.sqrt (d : ℝ)) * (Real.log B + 1) ^ k) * + D ^ 2 * (D + 1) * + B ^ (1 / 2 : ℝ) * (Real.log B + 1) ^ 3 := by + gcongr + _ = (A * D ^ 2 * (D + 1)) * + (B ^ (1 / Real.sqrt (d : ℝ) + 1 / 2) * (Real.log B + 1) ^ (k + 3)) := by + rw [Real.rpow_add hB0, pow_add] + ring + _ ≤ (A * D ^ 2 * (D + 1)) * (C * B) := + mul_le_mul_of_nonneg_left (surface_nonlinear_polylog_weight hd (k + 3) hB) + (by positivity) + _ = _ := by dsimp only [C]; ring + +/-- For each `k`, there is a constant `C` such that, for `d ≥ 5` and a finite family of +integral affine curves of degree at least two, each of degree at most `D (log B + 1)` and of +total degree at most `A B^(1/√d) (log B + 1)^k`, the union of the curves has at most +`C A D² (D + 1) B` integral points of height at most `B`. -/ +theorem logarithmic_degree_curve_union_bound (k : ℕ) : + ∃ C : ℝ, 0 < C ∧ + ∀ (S : Finset (Ideal (CoordinatePolynomial 3))) + (δ : Ideal (CoordinatePolynomial 3) → ℕ) (d : ℕ), 5 ≤ d → + ∀ A D B : ℝ, 0 ≤ A → 0 ≤ D → 1 ≤ B → + (∀ P ∈ S, P.IsPrime ∧ 2 ≤ δ P ∧ HasAffineCurveDegree P (δ P)) → + (∀ P ∈ S, (δ P : ℝ) ≤ D * (Real.log B + 1)) → + (∑ P ∈ S, (δ P : ℝ)) ≤ + A * B ^ (1 / Real.sqrt (d : ℝ)) * (Real.log B + 1) ^ k → + (count (⋃ P ∈ S, idealZeroLocus P) B : ℝ) ≤ C * A * D ^ 2 * (D + 1) * B := by + obtain ⟨K, hK, hcount⟩ := affine_curve_count 3 (by omega) + let C₀ := ((k + 3).factorial / (1 / 20 : ℝ) ^ (k + 3) * Real.exp (1 / 20)) + have hC₀ : 0 < C₀ := by dsimp only [C₀]; positivity + refine ⟨K * C₀, mul_pos hK hC₀, ?_⟩ + intro S δ d hd A D B hA hD hB hcurve hcap hbudget + have hsum := sum_curveBound_logarithmic_degrees S δ k hd hA hD hB + (fun P hP => (hcurve P hP).2.1) hcap hbudget + calc + _ ≤ ∑ P ∈ S, (count (idealZeroLocus P) B : ℝ) := by + exact_mod_cast count_biUnion_finset_le S idealZeroLocus B + _ ≤ ∑ P ∈ S, K * curveBound (δ P) B := by + apply Finset.sum_le_sum + intro P hP + obtain ⟨hp, hδ, hdeg⟩ := hcurve P hP + exact hcount P hp (δ P) (by omega) hdeg B hB + _ = K * ∑ P ∈ S, curveBound (δ P) B := (Finset.mul_sum _ _ _).symm + _ ≤ K * (C₀ * A * D ^ 2 * (D + 1) * B) := + mul_le_mul_of_nonneg_left hsum hK.le + _ = _ := by ring + +end +end CCDN diff --git a/LeanPool/CCDN/Surface/SmoothPrimeWindow.lean b/LeanPool/CCDN/Surface/SmoothPrimeWindow.lean new file mode 100644 index 0000000000..3c45139a4e --- /dev/null +++ b/LeanPool/CCDN/Surface/SmoothPrimeWindow.lean @@ -0,0 +1,141 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.Height.Box +public import LeanPool.CCDN.Height.IntegerCoefficientHeight +public import LeanPool.CCDN.Surface.CongruencePrimeWindow +public import Mathlib.Algebra.MvPolynomial.PDeriv +import LeanPool.CCDN.AuxiliaryPolynomial.PlaneAuxiliaryCount +import LeanPool.CCDN.Determinant.PlaneSingularities +import LeanPool.CCDN.Height.PolynomialBoxHeight + +/-! Nonsingular integer points have many smooth reduction primes +in one common finite window. The excluded primes divide one nonzero partial +derivative value, whose height is bounded directly from the equation. -/ + +@[expose] public section +namespace CCDN +noncomputable section +open MvPolynomial Finset + +private theorem integer_pderiv_coefficient_bound {σ : Type*} [Finite σ] + (f : MvPolynomial σ ℤ) (j : σ) {H : ℝ} (hH : 0 ≤ H) + (hcoeff : CoefficientHeightLE f H) (e : σ →₀ ℕ) : + |((pderiv j f).coeff e : ℝ)| ≤ ((f.totalDegree : ℝ) + 1) * H := by + classical + let := Fintype.ofFinite σ + rw [coeff_pderiv] + by_cases hz : f.coeff (e + Finsupp.single j 1) = 0 + · simp only [hz, zero_mul, Int.cast_zero, abs_zero] + positivity + · have he : e j + 1 ≤ f.totalDegree := by + have h := ((degreeOf_le_iff).mp (degreeOf_le_totalDegree f j)) + (e + Finsupp.single j 1) (mem_support_iff.mpr hz) + simpa only [Finsupp.add_apply, Finsupp.single_eq_same] using h + push_cast + rw [abs_mul, abs_of_nonneg (by positivity : (0 : ℝ) ≤ (e j : ℝ) + 1)] + have heR : (e j : ℝ) + 1 ≤ (f.totalDegree : ℝ) + 1 := by + exact_mod_cast (by omega : e j + 1 ≤ f.totalDegree + 1) + calc + _ ≤ H * ((e j : ℝ) + 1) := + mul_le_mul_of_nonneg_right (hcoeff _) (by positivity) + _ ≤ H * ((f.totalDegree : ℝ) + 1) := + mul_le_mul_of_nonneg_left heR hH + _ = _ := by ring + +private theorem integer_pderiv_totalDegree_le {σ : Type*} + (f : MvPolynomial σ ℤ) (j : σ) : + (pderiv j f).totalDegree ≤ f.totalDegree := by + rw [← rational_map_totalDegree (pderiv j f), ← pderiv_map] + exact (pderiv_totalDegree_le (map (Int.castRingHom ℚ) f) j).trans + (by rw [rational_map_totalDegree]; omega) + +private theorem integer_pderiv_evaluation_bound {n : ℕ} + (f : MvPolynomial (Fin n) ℤ) (j : Fin n) {H B : ℝ} + (hH : 0 ≤ H) (hB : 1 ≤ B) + (hcoeff : CoefficientHeightLE f H) + (x : IntPoint n) (hx : InBox x B) : + |(eval x (pderiv j f) : ℝ)| ≤ + ((f.totalDegree : ℝ) + 1) ^ (n + 1) * H * B ^ f.totalDegree := by + have hb := polynomial_eval₂_box_bound (pderiv j f) + (((f.totalDegree : ℝ) + 1) * H) B (by positivity) hB + (integer_pderiv_coefficient_bound f j hH hcoeff) + (fun i => (x i : ℝ)) hx + have heval : (eval x (pderiv j f) : ℝ) = + eval₂ (Int.castRingHom ℝ) (fun i => (x i : ℝ)) (pderiv j f) := by + simpa only [eval₂_id, RingHom.comp_id, Function.comp_def, Int.coe_castRingHom] using + eval₂_comp_left (Int.castRingHom ℝ) (RingHom.id ℤ) x (pderiv j f) + rw [← heval, Fintype.card_fin] at hb + have hdeg := integer_pderiv_totalDegree_le f j + have hdegR : ((pderiv j f).totalDegree : ℝ) ≤ f.totalDegree := by exact_mod_cast hdeg + calc + _ ≤ (((pderiv j f).totalDegree : ℝ) + 1) ^ n * + (((f.totalDegree : ℝ) + 1) * H) * B ^ (pderiv j f).totalDegree := hb + _ ≤ ((f.totalDegree : ℝ) + 1) ^ n * + (((f.totalDegree : ℝ) + 1) * H) * B ^ f.totalDegree := by + gcongr + _ = _ := by rw [pow_succ]; ring + +private theorem integer_pderiv_log_bound {n : ℕ} + (f : MvPolynomial (Fin n) ℤ) (j : Fin n) {H B : ℝ} + (hH : 1 ≤ H) (hB : 1 ≤ B) + (hcoeff : CoefficientHeightLE f H) + (x : IntPoint n) (hx : InBox x B) + (hne : eval x (pderiv j f) ≠ 0) : + Real.log |(eval x (pderiv j f) : ℝ)| ≤ + (n + 1 : ℝ) * Real.log ((f.totalDegree : ℝ) + 1) + + Real.log H + f.totalDegree * Real.log B := by + have hpos : (0 : ℝ) < |(eval x (pderiv j f) : ℝ)| := + abs_pos.mpr (by exact_mod_cast hne) + have hdpos : (0 : ℝ) < (f.totalDegree : ℝ) + 1 := by positivity + have hH0 : 0 < H := by linarith + have hB0 : 0 < B := by linarith + have hlog := Real.log_le_log hpos + (integer_pderiv_evaluation_bound f j hH0.le hB hcoeff x hx) + rw [Real.log_mul (mul_pos (pow_pos hdpos _) hH0).ne' (pow_pos hB0 _).ne', + Real.log_mul (pow_pos hdpos _).ne' hH0.ne', Real.log_pow, Real.log_pow] at hlog + simpa only [Nat.cast_add, Nat.cast_one] using hlog + +/-- If `x` is an integer point of the box at which some partial derivative of `f` is nonzero, +and `L` dominates the logarithmic size of such a derivative, then the prime window of +logarithmic size `L` contains a set of primes with product at least `exp L` modulo each of +which `x` is a smooth point of `f`. -/ +theorem exists_smooth_prime_window_subset {n : ℕ} + (f : MvPolynomial (Fin n) ℤ) {H B L : ℝ} + (hH : 1 ≤ H) (hB : 1 ≤ B) (hL : 1 ≤ L) + (hcoeff : CoefficientHeightLE f H) + (hheight : (n + 1 : ℝ) * Real.log ((f.totalDegree : ℝ) + 1) + + Real.log H + f.totalDegree * Real.log B ≤ L) + (x : IntPoint n) (hx : InBox x B) + (hsmooth : ∃ j, eval x (pderiv j f) ≠ 0) : + ∃ P : Finset ℕ, P ⊆ congruencePrimeWindow L ∧ + Real.exp L ≤ ∏ p ∈ P, (p : ℝ) ∧ + ∀ p ∈ P, ∃ j, eval (fun i => (x i : ZMod p)) + (pderiv j (map (Int.castRingHom (ZMod p)) f)) ≠ 0 := by + classical + obtain ⟨j, hj⟩ := hsmooth + let D := eval x (pderiv j f) + have hD : D ≠ 0 := hj + have hbound : Real.log |(D : ℝ)| ≤ L := + (integer_pderiv_log_bound f j hH hB hcoeff x hx hj).trans hheight + let P := (congruencePrimeWindow L).filter (fun p : ℕ => ¬ (p : ℤ) ∣ D) + refine ⟨P, filter_subset _ _, congruencePrimeWindow_avoiding_product hL hD hbound, ?_⟩ + intro p hp + refine ⟨j, ?_⟩ + have hnd := (mem_filter.mp hp).2 + have he : eval (fun i => (x i : ZMod p)) + (pderiv j (map (Int.castRingHom (ZMod p)) f)) = (D : ZMod p) := by + rw [pderiv_map] + exact (map_eval (Int.castRingHom (ZMod p)) x (pderiv j f)).symm + rw [he] + exact fun h => hnd ((ZMod.intCast_zmod_eq_zero_iff_dvd D p).mp h) + +end +end CCDN diff --git a/LeanPool/CCDN/Surface/TopFormAuxiliary.lean b/LeanPool/CCDN/Surface/TopFormAuxiliary.lean new file mode 100644 index 0000000000..3cd163583a --- /dev/null +++ b/LeanPool/CCDN/Surface/TopFormAuxiliary.lean @@ -0,0 +1,102 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.AuxiliaryPolynomial.AffineHypersurfaceAuxiliary +import LeanPool.CCDN.ForMathlib.MvPolynomial +import LeanPool.CCDN.Height.ScaledHypersurfaceHeight +import LeanPool.CCDN.Surface.TopFormAuxiliaryEstimate +import LeanPool.CCDN.Surface.TopFormBadPrimes + +/-! A coefficient-uniform auxiliary for an affine hypersurface with absolutely +irreducible leading form. This is the dimension-general form of CCDN (4-3-1). -/ + +@[expose] public section +namespace CCDN +noncomputable section +open MvPolynomial + +/-- The constant in the degree bound of the auxiliary polynomial for affine hypersurfaces in +`n + 2` variables with absolutely irreducible top-degree form, independent of the +coefficients. -/ +def topFormAuxiliaryConstant (n : ℕ) : ℝ := + affineHypersurfaceAuxiliaryConstant n * + ((n + 1 : ℝ) * Real.exp (2 * mertensError + 1) * hypersurfaceHeightConstant (n + 2) + 2) + +/-- The auxiliary constant `topFormAuxiliaryConstant n` is positive. -/ +lemma topFormAuxiliaryConstant_pos (n : ℕ) : 0 < topFormAuxiliaryConstant n := by + have hC := affineHypersurfaceAuxiliaryConstant_pos n + have hH := zero_le_one.trans (hypersurfaceHeightConstant_one_le (n + 2)) + unfold topFormAuxiliaryConstant + positivity + +private theorem primitive_top_form_auxiliary {n : ℕ} + (f : MvPolynomial (Fin (n + 2)) ℤ) (hprim : PrimitiveIntegerPolynomial f) + (hir : HasAbsIrreducibleTopForm f) + {B : ℝ} (hB : 1 ≤ B) : + ∃ g, IsBoxAuxiliary f g B ∧ + (g.totalDegree : ℝ) ≤ topFormAuxiliaryConstant n * + (f.totalDegree : ℝ) ^ (4 - 1 / (n + 1 : ℝ)) * + B ^ (1 / (f.totalDegree : ℝ) ^ (1 / (n + 1 : ℝ))) := by + have hfIr := hprim.irreducible_of_absolute_top hir + have habs := absolute_irreducible_of_top_homogeneousComponent_irreducible f hir + have hd := absolute_integer_polynomial_totalDegree_pos f habs + let d := f.totalDegree + let F := homogeneousComponent d f + have hF0 : F ≠ 0 := homogeneousComponent_totalDegree_ne_zero hfIr.ne_zero + have hFd : F.totalDegree = d := (homogeneousComponent_isHomogeneous d f).totalDegree hF0 + let L := (integerCoefficientHeight F : ℝ) + let q := 1 / ((n + 1 : ℝ) * d * (d : ℝ) ^ (1 / (n + 1 : ℝ))) + let K := (n + 1 : ℝ) * Real.exp (2 * mertensError + 1) * hypersurfaceHeightConstant (n + 2) + have hL : 1 ≤ L := leading_height_one_le f hfIr.ne_zero + have hratio : hypersurfaceBadPrimeFactor f / L ^ q ≤ K := by + calc + _ ≤ hypersurfaceBadPrimeFactor F / L ^ q := div_le_div_of_nonneg_right + (hypersurfaceBadPrimeFactor_le_top_homogeneousComponent f hir) (by positivity) + _ ≤ _ := by + simpa only [Nat.cast_add, Nat.cast_one] using + hypersurface_badPrime_fractional_height_bound F hF0 hFd (r := n + 1) (by omega) hd + obtain ⟨g, hg, hgdeg⟩ := affine_hypersurface_auxiliary f hprim hfIr hd hB + refine ⟨g, hg, hgdeg.trans ?_⟩ + have hh := mul_le_mul_of_nonneg_left + (affineAuxiliaryDegreeExpression_le_of_badPrime_ratio n hd hB hL hratio) + (affineHypersurfaceAuxiliaryConstant_pos n).le + unfold topFormAuxiliaryConstant + convert hh using 1 + ring + +/-- Uniform auxiliary existence for every integer equation whose highest +homogeneous component is absolutely irreducible. Integer content is removed +inside the proof; the final constant depends only on ambient dimension. -/ +theorem top_form_auxiliary_rational {n : ℕ} + (f : MvPolynomial (Fin (n + 2)) ℤ) + (hir : HasAbsIrreducibleTopForm f) + {B : ℝ} (hB : 1 ≤ B) : + ∃ g : MvPolynomial (Fin (n + 2)) ℤ, g ≠ 0 ∧ + ¬ map (Int.castRingHom ℚ) f ∣ map (Int.castRingHom ℚ) g ∧ + (g.totalDegree : ℝ) ≤ topFormAuxiliaryConstant n * + (f.totalDegree : ℝ) ^ (4 - 1 / (n + 1 : ℝ)) * + B ^ (1 / (f.totalDegree : ℝ) ^ (1 / (n + 1 : ℝ))) ∧ + ∀ x : IntPoint (n + 2), InBox x B → eval x f = 0 → eval x g = 0 := by + have hf0 : f ≠ 0 := by intro hz; exact hir.ne_zero (by simp [hz]) + obtain ⟨c, u, hc, huprim, he⟩ := exists_primitive_integer_factor f hf0 + obtain ⟨huDegree, huTop⟩ := absolute_top_irreducible_of_integer_scalar_factor hc he hir + obtain ⟨g, ⟨hg0, hgnd, hgz⟩, hgdeg⟩ := primitive_top_form_auxiliary u huprim huTop hB + have hudvd : u ∣ f := ⟨C c, by rw [he]; ring⟩ + refine ⟨g, hg0, ?_, ?_, ?_⟩ + · intro h + exact hgnd (huprim.rational_map_dvd_iff.mp ((map_dvd (map (Int.castRingHom ℚ)) hudvd).trans h)) + · simpa only [huDegree] using hgdeg + · intro x hx hxf + apply hgz x hx + rw [he, eval_mul, eval_C] at hxf + exact (mul_eq_zero.mp hxf).resolve_left hc + +end +end CCDN diff --git a/LeanPool/CCDN/Surface/TopFormAuxiliaryEstimate.lean b/LeanPool/CCDN/Surface/TopFormAuxiliaryEstimate.lean new file mode 100644 index 0000000000..5b1ccba2dd --- /dev/null +++ b/LeanPool/CCDN/Surface/TopFormAuxiliaryEstimate.lean @@ -0,0 +1,121 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.AuxiliaryPolynomial.AffineHypersurfaceAuxiliary +import LeanPool.CCDN.AuxiliaryPolynomial.HypersurfaceHeightTransfer + +/-! Uniform coefficient absorption and affine-box estimates for the +auxiliary theorem with an irreducible leading homogeneous form. -/ + +@[expose] public section +namespace CCDN +noncomputable section +open MvPolynomial + +/-- For a nonzero integer polynomial `f` of degree `d > 0` with coefficient height `H` and for +`r > 0`, the bad-prime factor of `f` divided by `H^(1/(r d d^(1/r)))` is bounded by a constant +depending only on `r` and the number of variables. -/ +theorem hypersurface_badPrime_fractional_height_bound {m r d : ℕ} + (f : MvPolynomial (Fin (m + 1)) ℤ) (hf : f ≠ 0) + (hdegree : f.totalDegree = d) (hr : 0 < r) (hd : 0 < d) : + hypersurfaceBadPrimeFactor f / + (integerCoefficientHeight f : ℝ) ^ (1 / ((r : ℝ) * d * (d : ℝ) ^ (1 / (r : ℝ)))) ≤ + r * Real.exp (2 * mertensError + 1) * hypersurfaceHeightConstant (m + 1) := by + let H := (integerCoefficientHeight f : ℝ) + let q := 1 / ((r : ℝ) * d * (d : ℝ) ^ (1 / (r : ℝ))) + let K := Real.exp (2 * mertensError + 1) * hypersurfaceHeightConstant (m + 1) + have hd0 : (0 : ℝ) < d := by exact_mod_cast hd + have hH : 1 ≤ H := by dsimp only [H]; exact_mod_cast integerCoefficientHeight_pos f hf + have hK : 0 ≤ K := mul_nonneg (Real.exp_pos _).le + (zero_le_one.trans (hypersurfaceHeightConstant_one_le _)) + have hb : hypersurfaceBadPrimeFactor f ≤ K * (1 + Real.log H / (d : ℝ) ^ 2) := by + simpa only [hdegree] using hypersurfaceBadPrimeFactor_le_height f H hH + (coefficient_abs_le_integerCoefficientHeight f) + have hratio : (Real.log H + (d : ℝ) ^ 2) / H ^ q ≤ r * (d : ℝ) ^ 2 := by + simpa only [Real.log_one, zero_add, Real.one_rpow, div_one] using + hypersurface_logarithmic_height_transfer hr hd (Y := 1) le_rfl hH + calc + _ ≤ (K / (d : ℝ) ^ 2) * ((Real.log H + (d : ℝ) ^ 2) / H ^ q) := by + have hh := div_le_div_of_nonneg_right hb (show 0 ≤ H ^ q by positivity) + convert hh using 1 + field_simp + ring + _ ≤ (K / (d : ℝ) ^ 2) * (r * (d : ℝ) ^ 2) := + mul_le_mul_of_nonneg_left hratio (by positivity) + _ = _ := by dsimp only [K]; field_simp + +private lemma affine_auxiliary_lower_terms_bound (n : ℕ) {d : ℕ} (hd : 0 < d) + {B : ℝ} (hB : 1 ≤ B) : + (d : ℝ) ^ (1 - 1 / (n + 1 : ℝ)) * Real.log B + (d : ℝ) ^ (4 - 1 / (n + 1 : ℝ)) ≤ + 2 * (d : ℝ) ^ (4 - 1 / (n + 1 : ℝ)) * B ^ (1 / (d : ℝ) ^ (1 / (n + 1 : ℝ))) := by + have hd1 : (1 : ℝ) ≤ d := by exact_mod_cast hd + have hd0 : (0 : ℝ) < d := by linarith + have hn1 : (1 : ℝ) ≤ n + 1 := by have := Nat.cast_nonneg (α := ℝ) n; linarith + have hexp : (1 : ℝ) ≤ 4 - 1 / (n + 1 : ℝ) := by + have hh := (div_le_one (by positivity : (0 : ℝ) < n + 1)).mpr hn1 + linarith + have hdD : (d : ℝ) ≤ (d : ℝ) ^ (4 - 1 / (n + 1 : ℝ)) := by + simpa only [Real.rpow_one] using Real.rpow_le_rpow_of_exponent_le hd1 hexp + let Q := B ^ (1 / (d : ℝ) ^ (1 / (n + 1 : ℝ))) + have hQ : 1 ≤ Q := Real.one_le_rpow hB (by positivity) + have hlog : Real.log B ≤ (d : ℝ) ^ (1 / (n + 1 : ℝ)) * Q := by + have hh := Real.log_le_rpow_div (by linarith : 0 ≤ B) + (by positivity : (0 : ℝ) < 1 / (d : ℝ) ^ (1 / (n + 1 : ℝ))) + simpa only [Q, one_div, div_inv_eq_mul, mul_comm] using hh + have hpowers : (d : ℝ) ^ (1 - 1 / (n + 1 : ℝ)) * + (d : ℝ) ^ (1 / (n + 1 : ℝ)) = d := by + rw [← Real.rpow_add hd0, sub_add_cancel, Real.rpow_one] + have hfirst : (d : ℝ) ^ (1 - 1 / (n + 1 : ℝ)) * Real.log B ≤ + (d : ℝ) ^ (4 - 1 / (n + 1 : ℝ)) * Q := by + calc + _ ≤ (d : ℝ) ^ (1 - 1 / (n + 1 : ℝ)) * + ((d : ℝ) ^ (1 / (n + 1 : ℝ)) * Q) := + mul_le_mul_of_nonneg_left hlog (by positivity) + _ = d * Q := by rw [← mul_assoc, hpowers] + _ ≤ _ := mul_le_mul_of_nonneg_right hdD (by positivity) + have hsecond : (d : ℝ) ^ (4 - 1 / (n + 1 : ℝ)) ≤ + (d : ℝ) ^ (4 - 1 / (n + 1 : ℝ)) * Q := + le_mul_of_one_le_right (by positivity) hQ + change _ ≤ 2 * (d : ℝ) ^ (4 - 1 / (n + 1 : ℝ)) * Q + linarith + +/-- A uniform bound on the coefficient-weighted bad-prime factor removes +both the coefficient dependence and the logarithmic box term. -/ +theorem affineAuxiliaryDegreeExpression_le_of_badPrime_ratio (n : ℕ) {d : ℕ} + (hd : 0 < d) {B L b K : ℝ} (hB : 1 ≤ B) (hL : 1 ≤ L) + (hratio : b / L ^ (1 / ((n + 1 : ℝ) * d * (d : ℝ) ^ (1 / (n + 1 : ℝ)))) ≤ K) : + affineAuxiliaryDegreeExpression n d B L b ≤ + (K + 2) * (d : ℝ) ^ (4 - 1 / (n + 1 : ℝ)) * + B ^ (1 / (d : ℝ) ^ (1 / (n + 1 : ℝ))) := by + have hd0 : (0 : ℝ) < d := by exact_mod_cast hd + let q := 1 / ((n + 1 : ℝ) * d * (d : ℝ) ^ (1 / (n + 1 : ℝ))) + let Q := B ^ (1 / (d : ℝ) ^ (1 / (n + 1 : ℝ))) + have hpowers : (d : ℝ) ^ (2 - 1 / (n + 1 : ℝ)) * (d : ℝ) ^ 2 = + (d : ℝ) ^ (4 - 1 / (n + 1 : ℝ)) := + (rpow_four_sub_eq_mul_sq hd0 n).symm + have hmain : + (d : ℝ) ^ (2 - 1 / (n + 1 : ℝ)) * + min (Real.log L + d * Real.log B + (d : ℝ) ^ 2) ((d : ℝ) ^ 2 * b) * Q / L ^ q ≤ + K * (d : ℝ) ^ (4 - 1 / (n + 1 : ℝ)) * Q := by + calc + _ ≤ (d : ℝ) ^ (2 - 1 / (n + 1 : ℝ)) * ((d : ℝ) ^ 2 * b) * Q / L ^ q := by + gcongr + exact min_le_right _ _ + _ = ((d : ℝ) ^ (2 - 1 / (n + 1 : ℝ)) * (d : ℝ) ^ 2) * Q * (b / L ^ q) := by ring + _ ≤ ((d : ℝ) ^ (2 - 1 / (n + 1 : ℝ)) * (d : ℝ) ^ 2) * Q * K := + mul_le_mul_of_nonneg_left hratio (by positivity) + _ = _ := by rw [hpowers]; ring + have hlower := affine_auxiliary_lower_terms_bound n hd hB + unfold affineAuxiliaryDegreeExpression + dsimp only [q, Q] at hmain + nlinarith only [hmain, hlower] + +end +end CCDN diff --git a/LeanPool/CCDN/Surface/TopFormBadPrimes.lean b/LeanPool/CCDN/Surface/TopFormBadPrimes.lean new file mode 100644 index 0000000000..7dd0f4b92b --- /dev/null +++ b/LeanPool/CCDN/Surface/TopFormBadPrimes.lean @@ -0,0 +1,99 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.AuxiliaryPolynomial.HypersurfaceBadPrimeFactor +public import LeanPool.CCDN.Height.PrimitiveIntegerPolynomial +import LeanPool.CCDN.Algebra.PrimitiveNormalization +import LeanPool.CCDN.AuxiliaryPolynomial.PlaneAuxiliaryCount +import LeanPool.CCDN.AuxiliaryPolynomial.UnscaledHypersurfaceBadPrimes +import LeanPool.CCDN.Surface.TopFormIrreducibility + +/-! Bad reduction of an affine equation is contained in bad reduction of + its top homogeneous form. This includes degree loss in the defined predicate. -/ + +@[expose] public section +namespace CCDN +noncomputable section +open MvPolynomial + +/-- If the top form has good geometric reduction, the original affine + equation keeps its degree and has good geometric reduction as well. -/ +private theorem hypersurfaceBadReduction_imp_top_homogeneousComponent {σ : Type*} + (f : MvPolynomial σ ℤ) (p : ℕ) : + HypersurfaceBadReduction f p → + HypersurfaceBadReduction (homogeneousComponent f.totalDegree f) p := by + intro hbad + let : Fact p.Prime := ⟨hbad.1⟩ + by_contra hgood + have hir : Irreducible (map (Int.castRingHom (AlgebraicClosure (ZMod p))) + (homogeneousComponent f.totalDegree f)) := by + by_contra h + exact hgood ⟨hbad.1, Or.inr h⟩ + obtain ⟨hdegree, hfull⟩ := map_degree_and_irreducible_of_top_irreducible _ f hir + exact hbad.2.elim (fun h => h hdegree) (fun h => h hfull) + +/-- Absolute irreducibility of the highest homogeneous component implies + absolute irreducibility of the original integer equation. -/ +theorem absolute_irreducible_of_top_homogeneousComponent_irreducible {σ : Type*} + (f : MvPolynomial σ ℤ) + (hir : HasAbsIrreducibleTopForm f) : + AbsolutelyIrreducible f := + (map_degree_and_irreducible_of_top_irreducible _ f hir).2 + +/-- The affine bad-prime factor is no greater than that of the top + form when the latter is absolutely irreducible. -/ +theorem hypersurfaceBadPrimeFactor_le_top_homogeneousComponent {n : ℕ} + (f : MvPolynomial (Fin (n + 1)) ℤ) + (hir : HasAbsIrreducibleTopForm f) : + hypersurfaceBadPrimeFactor f ≤ + hypersurfaceBadPrimeFactor (homogeneousComponent f.totalDegree f) := by + have htop : homogeneousComponent f.totalDegree f ≠ 0 := by + intro hz + exact hir.ne_zero (by rw [hz, map_zero]) + apply hypersurfaceBadPrimeFactor_mono_of_badReduction_imp + · exact ((homogeneousComponent_isHomogeneous _ _).totalDegree htop).symm + · exact fun _ => hir + · exact hypersurfaceBadReduction_imp_top_homogeneousComponent f + +/-- Primitivity reflects the irreducibility supplied by the absolute top form + back to the integer polynomial ring. -/ +theorem PrimitiveIntegerPolynomial.irreducible_of_absolute_top {σ : Type*} + {f : MvPolynomial σ ℤ} (hprim : PrimitiveIntegerPolynomial f) + (hir : HasAbsIrreducibleTopForm f) : Irreducible f := by + apply hprim.irreducible_of_rational_map + exact rational_irreducible_of_charZero_map f + (absolute_irreducible_of_top_homogeneousComponent_irreducible f hir) + +/-- Removing nonzero integer content preserves absolute irreducibility of + the top homogeneous form, along with the affine degree. -/ +theorem absolute_top_irreducible_of_integer_scalar_factor {σ : Type*} + {f g : MvPolynomial σ ℤ} {c : ℤ} (hc : c ≠ 0) (he : f = C c * g) + (hir : HasAbsIrreducibleTopForm f) : + g.totalDegree = f.totalDegree ∧ + HasAbsIrreducibleTopForm g := by + have hf : f ≠ 0 := by + intro hz + exact hir.ne_zero (by rw [hz, map_zero, map_zero]) + have hdegree := integer_scalar_factor_degree hf hc he + refine ⟨hdegree, ?_⟩ + have htop : homogeneousComponent f.totalDegree f = + C c * homogeneousComponent g.totalDegree g := by + calc + _ = homogeneousComponent f.totalDegree (C c * g) := congrArg (homogeneousComponent _) he + _ = C c * homogeneousComponent f.totalDegree g := homogeneousComponent_C_mul _ _ _ + _ = _ := by rw [hdegree] + dsimp only [HasAbsIrreducibleTopForm, AbsolutelyIrreducible] at hir + rw [htop, map_mul, map_C] at hir + have hcK : (Int.castRingHom (AlgebraicClosure ℚ)) c ≠ 0 := by + simpa only [Int.coe_castRingHom, ne_eq, Int.cast_eq_zero] using hc + exact (irreducible_isUnit_mul ((isUnit_iff_ne_zero.mpr hcK).map C)).mp hir + +end +end CCDN diff --git a/LeanPool/CCDN/Surface/TopFormIrreducibility.lean b/LeanPool/CCDN/Surface/TopFormIrreducibility.lean new file mode 100644 index 0000000000..da297374ef --- /dev/null +++ b/LeanPool/CCDN/Surface/TopFormIrreducibility.lean @@ -0,0 +1,89 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import Mathlib.RingTheory.MvPolynomial.Homogeneous +import LeanPool.CCDN.Algebra.HomogeneousLinearDescent +import LeanPool.CCDN.ForMathlib.MvPolynomial +import Mathlib.Algebra.MvPolynomial.Nilpotent +import Mathlib.Algebra.MvPolynomial.NoZeroDivisors + +/-! An irreducible highest homogeneous component forces irreducibility of + the full polynomial. Coefficient reduction preserves this implication and + the full degree whenever the reduced highest component remains irreducible. -/ + +@[expose] public section +namespace CCDN +noncomputable section +open MvPolynomial + +/-- Applying a ring homomorphism to the coefficients commutes with taking the homogeneous +component of degree `d`. -/ +lemma map_homogeneousComponent {R S σ : Type*} [CommRing R] [CommRing S] + (φ : R →+* S) (d : ℕ) (f : MvPolynomial σ R) : + map φ (homogeneousComponent d f) = homogeneousComponent d (map φ f) := by + classical + ext m + simp only [coeff_map, coeff_homogeneousComponent] + split_ifs <;> simp + +private lemma isUnit_of_isUnit_top_homogeneousComponent {K σ : Type*} [Field K] + (f : MvPolynomial σ K) (hu : IsUnit (homogeneousComponent f.totalDegree f)) : + IsUnit f := by + have hd : f.totalDegree = 0 := by + have hhom := (homogeneousComponent_isHomogeneous f.totalDegree f).totalDegree hu.ne_zero + have hzero := (isUnit_iff_totalDegree_of_isReduced.mp hu).2 + omega + have he := totalDegree_eq_zero_iff_eq_C.mp hd + rw [hd, homogeneousComponent_zero] at hu + rwa [he] + +/-- The product of highest components detects every proper factorization of + the full equation, over any field. -/ +private theorem irreducible_of_top_homogeneousComponent_irreducible {K σ : Type*} [Field K] + (f : MvPolynomial σ K) + (hir : Irreducible (homogeneousComponent f.totalDegree f)) : Irreducible f := by + have hf : f ≠ 0 := by intro hz; exact hir.ne_zero (by rw [hz, map_zero]) + refine ⟨?_, ?_⟩ + · intro hu + have hd := (isUnit_iff_totalDegree_of_isReduced.mp hu).2 + have he := totalDegree_eq_zero_iff_eq_C.mp hd + apply hir.not_isUnit + rw [hd, homogeneousComponent_zero] + rwa [he] at hu + · intro g h he + have hg : g ≠ 0 := by intro hz; exact hf (by rw [he, hz, zero_mul]) + have hh : h ≠ 0 := by intro hz; exact hf (by rw [he, hz, mul_zero]) + have hprod : homogeneousComponent f.totalDegree f = + homogeneousComponent g.totalDegree g * homogeneousComponent h.totalDegree h := by + rw [he, totalDegree_mul_of_isDomain hg hh, top_homogeneousComponent_mul] + rcases hir.isUnit_or_isUnit hprod with hu | hu + · exact Or.inl (isUnit_of_isUnit_top_homogeneousComponent g hu) + · exact Or.inr (isUnit_of_isUnit_top_homogeneousComponent h hu) + +/-- If the coefficient image of the original top component is irreducible, + the full coefficient image has unchanged total degree and is irreducible. -/ +theorem map_degree_and_irreducible_of_top_irreducible + {R K σ : Type*} [CommRing R] [Field K] + (φ : R →+* K) (f : MvPolynomial σ R) + (hir : Irreducible (map φ (homogeneousComponent f.totalDegree f))) : + (map φ f).totalDegree = f.totalDegree ∧ Irreducible (map φ f) := by + have htop : Irreducible (homogeneousComponent f.totalDegree (map φ f)) := by + rwa [map_homogeneousComponent] at hir + have hle : (map φ f).totalDegree ≤ f.totalDegree := totalDegree_map_le _ _ + have hge : f.totalDegree ≤ (map φ f).totalDegree := by + by_contra h + exact htop.ne_zero (homogeneousComponent_eq_zero _ _ (by omega)) + have he := le_antisymm hle hge + refine ⟨he, ?_⟩ + apply irreducible_of_top_homogeneousComponent_irreducible + rwa [he] + +end +end CCDN diff --git a/LeanPool/CCDN/Surface/TopFormSurfaceComponents.lean b/LeanPool/CCDN/Surface/TopFormSurfaceComponents.lean new file mode 100644 index 0000000000..c9063bbb9d --- /dev/null +++ b/LeanPool/CCDN/Surface/TopFormSurfaceComponents.lean @@ -0,0 +1,109 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.Hilbert.FilteredPrimeDecomposition +public import LeanPool.CCDN.Surface.CurveComponents +public import LeanPool.CCDN.Surface.TopFormAuxiliary +import LeanPool.CCDN.AuxiliaryPolynomial.PlaneAuxiliaryCount +import LeanPool.CCDN.ForMathlib.MvPolynomial +import LeanPool.CCDN.Surface.CompleteIntersectionDegreeBudget +import LeanPool.CCDN.Surface.TopFormBadPrimes + +/-! The uniform surface auxiliary gives a finite curve cover of +all bounded integer zeros, with total curve degree O(d^(9/2) B^(1/sqrt d)). -/ + +@[expose] public section +namespace CCDN +noncomputable section +open MvPolynomial + +/-- An absolutely irreducible top form supplies the auxiliary equation, the +rational curve components, and their total degree bound. The component +cover is exact on the auxiliary section and covers every original integer +zero in the specified box. -/ +theorem top_form_surface_curve_components + (f : MvPolynomial (Fin 3) ℤ) + (hir : HasAbsIrreducibleTopForm f) + {B : ℝ} (hB : 1 ≤ B) : + ∃ g : MvPolynomial (Fin 3) ℤ, g ≠ 0 ∧ + ¬ map (Int.castRingHom ℚ) f ∣ map (Int.castRingHom ℚ) g ∧ + (g.totalDegree : ℝ) ≤ topFormAuxiliaryConstant 1 * + (f.totalDegree : ℝ) ^ (7 / 2 : ℝ) * + B ^ (1 / (f.totalDegree : ℝ) ^ (1 / 2 : ℝ)) ∧ + (∀ x : IntPoint 3, InBox x B → eval x f = 0 → eval x g = 0) ∧ + ∃ S : Finset (Ideal (CoordinatePolynomial 3)), + ∃ d : Ideal (CoordinatePolynomial 3) → ℕ, + (∀ P ∈ S, IsSectionCurve (map (Int.castRingHom ℚ) f) (map (Int.castRingHom ℚ) g) P + (d P)) ∧ + zeroLocus (map (Int.castRingHom ℚ) f) ∩ zeroLocus (map (Int.castRingHom ℚ) g) = + ⋃ P ∈ S, idealZeroLocus P ∧ + ((∑ P ∈ S, d P : ℕ) : ℝ) ≤ topFormAuxiliaryConstant 1 * + (f.totalDegree : ℝ) ^ (9 / 2 : ℝ) * + B ^ (1 / (f.totalDegree : ℝ) ^ (1 / 2 : ℝ)) ∧ + (∀ P ∈ S, (polynomialSectionIdeal (map (Int.castRingHom ℚ) f) + (map (Int.castRingHom ℚ) g)).IsMinimalPrime P) ∧ + ∀ x : IntPoint 3, InBox x B → eval x f = 0 → + ∃ P ∈ S, rationalPoint x ∈ idealZeroLocus P := by + classical + have habs := absolute_irreducible_of_top_homogeneousComponent_irreducible f hir + have hfQ : Irreducible (map (Int.castRingHom ℚ) f) := + rational_irreducible_of_charZero_map f habs + have hd : 0 < f.totalDegree := absolute_integer_polynomial_totalDegree_pos f habs + have hdR : (0 : ℝ) < f.totalDegree := by exact_mod_cast hd + obtain ⟨g, hg0, hgnd, hgdeg, hgz⟩ := top_form_auxiliary_rational (n := 1) f hir hB + have hgdegree : (g.totalDegree : ℝ) ≤ topFormAuxiliaryConstant 1 * + (f.totalDegree : ℝ) ^ (7 / 2 : ℝ) * + B ^ (1 / (f.totalDegree : ℝ) ^ (1 / 2 : ℝ)) := by + norm_num at hgdeg ⊢ + exact hgdeg + obtain ⟨S, d, hcomponents, hcover, hbudget, hminimal⟩ := + exists_completeIntersection_curve_components (map (Int.castRingHom ℚ) f) + (map (Int.castRingHom ℚ) g) hfQ hgnd + have hmapdegree (p : MvPolynomial (Fin 3) ℤ) : + (map (Int.castRingHom ℚ) p).totalDegree = p.totalDegree := + totalDegree_map_of_injective Int.cast_injective _ + have hpower : (f.totalDegree : ℝ) * (f.totalDegree : ℝ) ^ (7 / 2 : ℝ) = + (f.totalDegree : ℝ) ^ (9 / 2 : ℝ) := by + calc + _ = (f.totalDegree : ℝ) ^ (1 : ℝ) * (f.totalDegree : ℝ) ^ (7 / 2 : ℝ) := by + rw [Real.rpow_one] + _ = (f.totalDegree : ℝ) ^ ((1 : ℝ) + 7 / 2) := (Real.rpow_add hdR _ _).symm + _ = _ := by norm_num + refine ⟨g, hg0, hgnd, hgdegree, hgz, S, d, hcomponents, hcover, ?_, hminimal, ?_⟩ + · have hbudgetR : ((∑ P ∈ S, d P : ℕ) : ℝ) ≤ + (f.totalDegree : ℝ) * g.totalDegree := by + rw [hmapdegree f, hmapdegree g] at hbudget + exact_mod_cast hbudget + calc + _ ≤ (f.totalDegree : ℝ) * g.totalDegree := hbudgetR + _ ≤ (f.totalDegree : ℝ) * (topFormAuxiliaryConstant 1 * + (f.totalDegree : ℝ) ^ (7 / 2 : ℝ) * + B ^ (1 / (f.totalDegree : ℝ) ^ (1 / 2 : ℝ))) := + mul_le_mul_of_nonneg_left hgdegree hdR.le + _ = topFormAuxiliaryConstant 1 * + ((f.totalDegree : ℝ) * (f.totalDegree : ℝ) ^ (7 / 2 : ℝ)) * + B ^ (1 / (f.totalDegree : ℝ) ^ (1 / 2 : ℝ)) := by ring + _ = _ := by rw [hpower] + · intro x hx hxf + have hmapzero (p : MvPolynomial (Fin 3) ℤ) (hp : eval x p = 0) : + eval (rationalPoint x) (map (Int.castRingHom ℚ) p) = 0 := by + have hh := congrArg (Int.castRingHom ℚ) hp + rw [map_eval, map_zero] at hh + exact hh + have hxsection : rationalPoint x ∈ + zeroLocus (map (Int.castRingHom ℚ) f) ∩ zeroLocus (map (Int.castRingHom ℚ) g) := + ⟨hmapzero f hxf, hmapzero g (hgz x hx hxf)⟩ + rw [hcover] at hxsection + obtain ⟨P, hxP⟩ := Set.mem_iUnion.mp hxsection + obtain ⟨hPS, hxP⟩ := Set.mem_iUnion.mp hxP + exact ⟨P, hPS, hxP⟩ + +end +end CCDN diff --git a/LeanPool/CCDN/Surface/TopFormSurfaceNonlinear.lean b/LeanPool/CCDN/Surface/TopFormSurfaceNonlinear.lean new file mode 100644 index 0000000000..9f3ef981bc --- /dev/null +++ b/LeanPool/CCDN/Surface/TopFormSurfaceNonlinear.lean @@ -0,0 +1,99 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.Surface.CurveComponents +public import LeanPool.CCDN.Surface.TopFormAuxiliary +import LeanPool.CCDN.Surface.NonlinearCount +import LeanPool.CCDN.Surface.TopFormSurfaceComponents + +/-! For an auxiliary section of a surface of degree at least sixteen, +all nonlinear components together contribute O(d^18 B) bounded integer points. +The union of the degree-one components is not covered by this estimate. -/ + +@[expose] public section +namespace CCDN +noncomputable section +open MvPolynomial + +/-- The nonlinear part of the corrected large-degree surface bound, using +constructed components and the curve counting theorem. The +constant precedes the surface equation, its coefficients, and the box radius. -/ +theorem top_form_surface_nonlinear_count : + ∃ A : ℝ, 0 < A ∧ + ∀ f : MvPolynomial (Fin 3) ℤ, + HasAbsIrreducibleTopForm f → + 16 ≤ f.totalDegree → ∀ B : ℝ, 1 ≤ B → + ∃ S : Finset (Ideal (CoordinatePolynomial 3)), + ∃ δ : Ideal (CoordinatePolynomial 3) → ℕ, + (∀ P ∈ S, IsSurfaceCurve (map (Int.castRingHom ℚ) f) P (δ P) ∧ 0 < δ P) ∧ + (∀ x : IntPoint 3, InBox x B → eval x f = 0 → + ∃ P ∈ S, rationalPoint x ∈ idealZeroLocus P) ∧ + ((∑ P ∈ S, δ P : ℕ) : ℝ) ≤ topFormAuxiliaryConstant 1 * + (f.totalDegree : ℝ) ^ (9 / 2 : ℝ) * + B ^ (1 / (f.totalDegree : ℝ) ^ (1 / 2 : ℝ)) ∧ + (count (⋃ P ∈ S.filter (fun P => 2 ≤ δ P), idealZeroLocus P) B : ℝ) ≤ + A * (f.totalDegree : ℝ) ^ 18 * B ∧ + (count (zeroLocus (map (Int.castRingHom ℚ) f) \ + (⋃ P ∈ S.filter (fun P => δ P = 1), idealZeroLocus P)) B : ℝ) ≤ + A * (f.totalDegree : ℝ) ^ 18 * B := by + classical + obtain ⟨K, hK, hcount⟩ := nonlinear_curve_union_bound + let C := topFormAuxiliaryConstant 1 + have hC : 0 < C := topFormAuxiliaryConstant_pos 1 + let A := K * Real.exp 1 * (480 * C + 2 * C ^ 4) + refine ⟨A, by dsimp only [A]; positivity, ?_⟩ + intro f hir hd B hB + obtain ⟨g, _, _, _, _, S, δ, hcomponents, _, hsum, _, hcover⟩ := + top_form_surface_curve_components f hir hB + let T := S.filter (fun P => 2 ≤ δ P) + let L := ⋃ P ∈ S.filter (fun P => δ P = 1), idealZeroLocus P + have hcurves (P : Ideal (CoordinatePolynomial 3)) (hP : P ∈ T) : + P.IsPrime ∧ 2 ≤ δ P ∧ HasAffineCurveDegree P (δ P) := by + obtain ⟨hPS, hδ⟩ := Finset.mem_filter.mp hP + exact ⟨(hcomponents P hPS).isPrime, hδ, (hcomponents P hPS).degree⟩ + have hsubsum : (∑ P ∈ T, δ P) ≤ ∑ P ∈ S, δ P := + Finset.sum_le_sum_of_subset_of_nonneg (Finset.filter_subset _ _) (by intros; omega) + have hTsum : (∑ P ∈ T, (δ P : ℝ)) ≤ + C * (f.totalDegree : ℝ) ^ (9 / 2 : ℝ) * B ^ (1 / Real.sqrt f.totalDegree) := by + have hcast : ((∑ P ∈ T, δ P : ℕ) : ℝ) ≤ ((∑ P ∈ S, δ P : ℕ) : ℝ) := by + exact_mod_cast hsubsum + simpa only [Nat.cast_sum, ← Real.sqrt_eq_rpow] using hcast.trans hsum + have hnonlinear : (count (⋃ P ∈ T, idealZeroLocus P) B : ℝ) ≤ + A * (f.totalDegree : ℝ) ^ 18 * B := + hcount T δ f.totalDegree hd C B hC.le hB hcurves hTsum + have houtside : + count (zeroLocus (map (Int.castRingHom ℚ) f) \ L) B ≤ + count (⋃ P ∈ T, idealZeroLocus P) B := by + apply Finset.card_le_card + intro x hx + obtain ⟨hbox, hxf, hxL⟩ := mem_integralPoints.mp hx + have hzero : eval x f = 0 := by + apply Int.cast_injective (α := ℚ) + change (Int.castRingHom ℚ) (eval x f) = (Int.castRingHom ℚ) 0 + rw [map_eval, map_zero] + exact hxf + obtain ⟨P, hPS, hxP⟩ := hcover x hbox hzero + have hpositive := (hcomponents P hPS).pos + have hnotone : δ P ≠ 1 := by + intro hone + apply hxL + exact Set.mem_iUnion.mpr ⟨P, Set.mem_iUnion.mpr + ⟨Finset.mem_filter.mpr ⟨hPS, hone⟩, hxP⟩⟩ + have hPT : P ∈ T := Finset.mem_filter.mpr ⟨hPS, by omega⟩ + exact mem_integralPoints.mpr ⟨hbox, + Set.mem_iUnion.mpr ⟨P, Set.mem_iUnion.mpr ⟨hPT, hxP⟩⟩⟩ + refine ⟨S, δ, ?_, hcover, hsum, hnonlinear, ?_⟩ + · exact fun P hPS => ⟨(hcomponents P hPS).toIsSurfaceCurve, (hcomponents P hPS).pos⟩ + · have hh : (count (zeroLocus (map (Int.castRingHom ℚ) f) \ L) B : ℝ) ≤ + (count (⋃ P ∈ T, idealZeroLocus P) B : ℝ) := by exact_mod_cast houtside + exact hh.trans hnonlinear + +end +end CCDN diff --git a/LeanPool/CCDN/Surface/TransversePlane.lean b/LeanPool/CCDN/Surface/TransversePlane.lean new file mode 100644 index 0000000000..abcf86aafb --- /dev/null +++ b/LeanPool/CCDN/Surface/TransversePlane.lean @@ -0,0 +1,236 @@ +/- +Copyright (c) 2026 Jason Davies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jason Davies +-/ +module + +/- Modified for Lean Pool: module paths, toolchain compatibility, and quality repairs. +Upstream: jasondavies/ccdn-dimension-growth @ bb025e7207f3a35a21b898629f5678b72aa7e44f. -/ + +public import LeanPool.CCDN.Algebra.PolynomialPredicates +public import LeanPool.CCDN.Plane.FiniteFieldBadPlanes +import LeanPool.CCDN.Algebra.PrimitiveNormalization +import LeanPool.CCDN.AuxiliaryPolynomial.PlaneAuxiliaryCount +import LeanPool.CCDN.AuxiliaryPolynomial.PlaneBadPrimes +import LeanPool.CCDN.Determinant.PlaneMultiplicity +import LeanPool.CCDN.ForMathlib.MvPolynomial +import LeanPool.CCDN.Noether.ShearNormalization +import LeanPool.CCDN.Noether.UniversalPlaneSpecialization + +/-! Irreducible plane sections transverse to a prescribed direction. +The avoidance condition also ensures that a second equation restricts +nontrivially, by requiring its value at the plane's base point to be nonzero. -/ + +@[expose] public section +namespace CCDN +noncomputable section +open MvPolynomial + +/-- The algebra map restricting a polynomial to the plane parametrized by +`(s, t) ↦ a + s b + t c`, sending `Xᵢ` to `aᵢ + bᵢ X 0 + cᵢ X 1`. -/ +def affinePlaneMap {K σ : Type*} [CommRing K] (a b c : σ → K) : + MvPolynomial σ K →ₐ[K] MvPolynomial (Fin 2) K := + aeval (fun i => C (a i) + C (b i) * X 0 + C (c i) * X 1) + +/-- Evaluating the restriction of `f` to the plane `a + s b + t c` at `z` gives the value of +`f` at `a + z 0 • b + z 1 • c`. -/ +lemma affinePlaneMap_eval {K σ : Type*} [CommRing K] (a b c : σ → K) + (f : MvPolynomial σ K) (z : Fin 2 → K) : + eval z (affinePlaneMap a b c f) = + eval (fun i => a i + b i * z 0 + c i * z 1) f := by + have he : (eval z).comp (affinePlaneMap a b c).toRingHom = + eval (fun i => a i + b i * z 0 + c i * z 1) := by + apply MvPolynomial.ringHom_ext + · intro r; simp [affinePlaneMap] + · intro i; simp [affinePlaneMap] + exact RingHom.congr_fun he f + +/-- Restricting a polynomial to an affine plane does not increase its total degree. -/ +lemma affinePlaneMap_totalDegree_le {K σ : Type*} [CommRing K] + (a b c : σ → K) (f : MvPolynomial σ K) : + (affinePlaneMap a b c f).totalDegree ≤ f.totalDegree := by + change (eval₂Hom C (fun i => C (a i) + C (b i) * X 0 + C (c i) * X 1) f).totalDegree ≤ _ + apply linear_eval₂_totalDegree_le (RingHom.id K) _ _ f + intro i + have hx (j : Fin 2) : (X j : MvPolynomial (Fin 2) K).totalDegree ≤ 1 := by + simpa [X] using totalDegree_monomial_le (Finsupp.single j 1) (1 : K) + apply (totalDegree_add _ _).trans + apply max_le + · apply (totalDegree_add _ _).trans + apply max_le + · simp + · exact (totalDegree_mul _ _).trans (by simpa using hx 0) + · exact (totalDegree_mul _ _).trans (by simpa using hx 1) + +private lemma affinePlaneMap_rename {K σ τ : Type*} [CommRing K] + (e : σ → τ) (a b c : τ → K) (f : MvPolynomial σ K) : + affinePlaneMap a b c (rename e f) = + affinePlaneMap (a ∘ e) (b ∘ e) (c ∘ e) f := by + change eval₂Hom _ _ (rename e f) = eval₂Hom _ _ f + simp only [eval₂Hom_rename, Function.comp_def] + +private lemma map_affinePlaneMap {K L σ : Type*} [CommRing K] [CommRing L] + (φ : K →+* L) (a b c : σ → K) (f : MvPolynomial σ K) : + map φ (affinePlaneMap a b c f) = + affinePlaneMap (φ ∘ a) (φ ∘ b) (φ ∘ c) (map φ f) := by + have he : (map φ).comp (affinePlaneMap a b c).toRingHom = + (affinePlaneMap (φ ∘ a) (φ ∘ b) (φ ∘ c)).toRingHom.comp (map φ) := by + apply MvPolynomial.ringHom_ext + · intro r; simp [affinePlaneMap] + · intro i; simp [affinePlaneMap] + exact RingHom.congr_fun he f + +private lemma universalPlaneSection_eq_affinePlaneMap {K σ : Type*} [CommRing K] + (f : MvPolynomial σ K) (u : PlaneParameters σ → K) : + universalPlaneSection f u = bivariatePolynomialEquiv K + (affinePlaneMap (fun i => u (i,0)) (fun i => u (i,1)) (fun i => u (i,2)) f) := by + have he : eval u = planeParameterEval (RingHom.id K) + (fun i => u (i,0)) (fun i => u (i,1)) (fun i => u (i,2)) := by + apply MvPolynomial.ringHom_ext + · intro r; simp [planeParameterEval] + · intro i + rcases i with ⟨i,j⟩ + fin_cases j <;> simp [planeParameterEval] + rw [universalPlaneSection, he, universalPlanePolynomial_specialize] + rfl + +/-- The `3 × 3` matrix with columns `b`, `c` and `v`; over a field, its determinant is nonzero +exactly when `b`, `c` and `v` are linearly independent. -/ +def planeDirectionMatrix {K : Type*} (b c v : Fin 3 → K) : + Matrix (Fin 3) (Fin 3) K := + fun i j => ![b i, c i, v i] j + +private def planeTransversalityPolynomial (v : Fin 3 → ℚ) : + MvPolynomial (PlaneParameters (Fin 3)) ℚ := + (planeDirectionMatrix (fun i => X (i,1)) (fun i => X (i,2)) (fun i => C (v i))).det + +private lemma planeTransversalityPolynomial_eval (v : Fin 3 → ℚ) + (u : PlaneParameters (Fin 3) → ℚ) : + eval u (planeTransversalityPolynomial v) = + (planeDirectionMatrix (fun i => u (i,1)) (fun i => u (i,2)) v).det := by + rw [planeTransversalityPolynomial, RingHom.map_det] + congr 1 + ext i j + fin_cases j <;> simp [planeDirectionMatrix, Matrix.map_apply] + +private lemma planeTransversalityPolynomial_ne_zero (v : Fin 3 → ℚ) (hv : v ≠ 0) : + planeTransversalityPolynomial v ≠ 0 := by + obtain ⟨j, hj⟩ := Function.ne_iff.mp hv + intro he + have hz (b c : Fin 3 → ℚ) : (planeDirectionMatrix b c v).det = 0 := by + let u : PlaneParameters (Fin 3) → ℚ := fun i => ![0, b i.1, c i.1] i.2 + have hh := congrArg (eval u) he + rw [planeTransversalityPolynomial_eval, map_zero] at hh + exact hh + fin_cases j + · have hh := hz ![0,1,0] ![0,0,1] + exact hj (by simpa [planeDirectionMatrix, Matrix.det_fin_three] using hh) + · have hh := hz ![1,0,0] ![0,0,1] + exact hj (by simpa [planeDirectionMatrix, Matrix.det_fin_three] using hh) + · have hh := hz ![1,0,0] ![0,1,0] + exact hj (by simpa [planeDirectionMatrix, Matrix.det_fin_three] using hh) + +/-- The integer specialization theorem, reindexed to three +coordinates, can avoid any nonzero rational parameter polynomial. -/ +private theorem exists_good_rational_plane_avoiding + (f : MvPolynomial (Fin 3) ℤ) + (hir : AbsolutelyIrreducible f) + (hd : 0 < f.totalDegree) + (P : MvPolynomial (PlaneParameters (Fin 3)) ℚ) (hP : P ≠ 0) : + ∃ a b c : Fin 3 → ℚ, + eval (fun i => ![a i.1, b i.1, c i.1] i.2) P ≠ 0 ∧ + Irreducible (affinePlaneMap a b c (map (Int.castRingHom ℚ) f)) ∧ + (affinePlaneMap a b c (map (Int.castRingHom ℚ) f)).totalDegree = f.totalDegree := by + classical + let e : Fin 3 ≃ Option (Fin 2) := finSuccEquiv' 0 + let ep : PlaneParameters (Fin 3) ≃ PlaneParameters (Option (Fin 2)) := + Equiv.prodCongr e (Equiv.refl _) + let F := rename e f + have hF : AbsolutelyIrreducible F := by + rw [AbsolutelyIrreducible, show F = rename e f from rfl, map_rename] + exact (MulEquiv.irreducible_iff (f := renameEquiv _ e)).mpr hir + have hFd : F.totalDegree = f.totalDegree := totalDegree_renameEquiv e f + obtain ⟨PZ, t, _, ht, hmodel⟩ := exists_primitive_integer_model P hP + have hPZ : PZ ≠ 0 := by + intro h + rw [h, map_zero] at hmodel + exact mul_ne_zero (C_eq_zero.not.mpr ht) hP hmodel.symm + have hren : rename ep PZ ≠ 0 := + (map_ne_zero_iff _ (rename_injective ep ep.injective)).mpr hPZ + obtain ⟨u, _, hu, _, hdeg, hgood⟩ := + exists_integer_geometrically_good_plane_avoiding F hF (hFd ▸ hd) (rename ep PZ) hren + let a : Fin 3 → ℤ := fun i => u (e i,0) + let b : Fin 3 → ℤ := fun i => u (e i,1) + let c : Fin 3 → ℤ := fun i => u (e i,2) + let q : MvPolynomial (Fin 2) ℤ := affinePlaneMap a b c f + have hsection : universalPlaneSection F u = bivariatePolynomialEquiv ℤ q := by + rw [universalPlaneSection_eq_affinePlaneMap] + congr 1 + exact affinePlaneMap_rename e _ _ _ f + rw [hsection, bivariatePolynomialEquiv_map] at hgood hdeg + have hqir : AbsolutelyIrreducible q := + (MulEquiv.irreducible_iff (f := bivariatePolynomialEquiv _)).mp hgood + have hqd : q.totalDegree = f.totalDegree := by + rw [bivariateTotalDegree_equiv] at hdeg + have hm : (map (Int.castRingHom (AlgebraicClosure ℚ)) q).totalDegree = q.totalDegree := + totalDegree_map_of_injective Int.cast_injective _ + simpa only [hm, hFd] using hdeg + refine ⟨(Int.castRingHom ℚ) ∘ a, (Int.castRingHom ℚ) ∘ b, + (Int.castRingHom ℚ) ∘ c, ?_, ?_, ?_⟩ + · have hu' : eval (fun i => ![a i.1, b i.1, c i.1] i.2) PZ ≠ 0 := by + have he : (fun i : PlaneParameters (Fin 3) => ![a i.1, b i.1, c i.1] i.2) = + u ∘ ep := by + funext i + rcases i with ⟨i,j⟩ + fin_cases j <;> rfl + rw [he] + simpa only [eval_rename] using hu + have hmap : eval + (fun i => ![((Int.castRingHom ℚ) ∘ a) i.1, + ((Int.castRingHom ℚ) ∘ b) i.1, ((Int.castRingHom ℚ) ∘ c) i.1] i.2) + (map (Int.castRingHom ℚ) PZ) ≠ 0 := by + have hh : (Int.castRingHom ℚ) + (eval (fun i => ![a i.1, b i.1, c i.1] i.2) PZ) ≠ 0 := + (map_ne_zero_iff _ Int.cast_injective).mpr hu' + rw [map_eval] at hh + convert hh using 1 + congr 2 + funext i + rcases i with ⟨i,j⟩ + fin_cases j <;> rfl + rw [hmodel, map_mul, eval_C] at hmap + exact (mul_ne_zero_iff.mp hmap).2 + · rw [← map_affinePlaneMap] + exact rational_irreducible_of_charZero_map q hqir + · rw [← map_affinePlaneMap] + exact (rational_map_totalDegree q).trans hqd + +/-- A geometrically irreducible surface admits a transverse plane +whose restriction of any prescribed nonzero second equation is nonzero. -/ +theorem exists_surface_transverse_plane + (f : MvPolynomial (Fin 3) ℤ) + (hir : AbsolutelyIrreducible f) + (hd : 0 < f.totalDegree) (v : Fin 3 → ℚ) (hv : v ≠ 0) + (g : MvPolynomial (Fin 3) ℚ) (hg : g ≠ 0) : + ∃ a b c : Fin 3 → ℚ, + (planeDirectionMatrix b c v).det ≠ 0 ∧ + Irreducible (affinePlaneMap a b c (map (Int.castRingHom ℚ) f)) ∧ + (affinePlaneMap a b c (map (Int.castRingHom ℚ) f)).totalDegree = f.totalDegree ∧ + affinePlaneMap a b c g ≠ 0 := by + let P := planeTransversalityPolynomial v * rename (fun i : Fin 3 => (i, (0 : Fin 3))) g + have hP : P ≠ 0 := mul_ne_zero (planeTransversalityPolynomial_ne_zero v hv) + ((map_ne_zero_iff _ (rename_injective _ (by intro i j h; exact congrArg Prod.fst h))).mpr hg) + obtain ⟨a,b,c,havoid,hgood,hdegree⟩ := exists_good_rational_plane_avoiding f hir hd P hP + rw [show P = _ * _ from rfl, map_mul, planeTransversalityPolynomial_eval, eval_rename] at havoid + obtain ⟨htrans, hbase⟩ := mul_ne_zero_iff.mp havoid + refine ⟨a,b,c,htrans,hgood,hdegree,?_⟩ + intro hz + have hh := affinePlaneMap_eval a b c g (0 : Fin 2 → ℚ) + rw [hz, map_zero] at hh + change eval a g ≠ 0 at hbase + apply hbase + simpa using hh.symm + +end +end CCDN diff --git a/LeanPool/projects/ccdn-dimension-growth.yaml b/LeanPool/projects/ccdn-dimension-growth.yaml new file mode 100644 index 0000000000..eea14bb6f6 --- /dev/null +++ b/LeanPool/projects/ccdn-dimension-growth.yaml @@ -0,0 +1,72 @@ +slug: ccdn-dimension-growth +title: Polynomial degree bounds for rational points on varieties +summary: >- + Proves the six main results of Castryck, Cluckers, Dittmann and Nguyen on + dimension growth, uniform rational-point counts on curves and hypersurfaces, + a lower-bound curve construction, and two-torsion in number-field class groups. + Preserves the determinant method, Hilbert-function degree and dimension, + bounded birational projections, and the required Bombieri-Vaaler and Mertens + dependencies. Imports the main-result closure, excluding the separate + intermediate-statement and counterexample library. +branch: arithmetic geometry +entry_module: LeanPool.CCDN +authors: +- Jason Davies +- Ralf Stephan +- PrimeNumberTheoremAnd contributors +source: + title: The dimension growth conjecture, polynomial in the degree and without logarithmic factors + doi: 10.2140/ant.2020.14.2261 + arxiv: '1904.13109' + url: https://github.com/jasondavies/ccdn-dimension-growth/tree/bb025e7207f3a35a21b898629f5678b72aa7e44f + github_repo: jasondavies/ccdn-dimension-growth + commit: bb025e7207f3a35a21b898629f5678b72aa7e44f + announcement_url: https://leanprover.zulipchat.com/#narrow/channel/583339/near/633471181 + announcement_date: '2026-10-20' + bombieri_vaaler_repository: https://github.com/rwst/Subspace-Theorems + bombieri_vaaler_commit: 09835cb23557ae5ae0cf55a9c4ab9055f5d60338 + mertens_repository: https://github.com/AlexKontorovich/PrimeNumberTheoremAnd + mertens_commit: c39a751132c88b6e8080b74c74023fd95b3d8be0 +license: Apache-2.0 +status: verified +provenance: AI +main_declarations: +- CCDN.projective_dimension_growth +main_results: +- declaration: CCDN.projective_dimension_growth + informal: >- + For every ambient projective dimension n, a constant depending only on n + bounds the rational points of height at most B on every integral projective + variety of degree d at least five by C d^(2n^2+5n-5) B^dim(X), for B at least one. +- declaration: CCDN.projectiveCurveBound + informal: >- + In ambient dimension n at least two and for B at least one, every integral + projective curve of degree d has at most C(n) d^4 B^(2/d) rational points + of height at most B, uniformly in its coefficients. +- declaration: CCDN.affineCurveBound + informal: >- + In ambient dimension n at least two and for B at least one, every integral + affine curve of degree d has at most C(n) d^3 B^(1/d) (log B+d) integer + points in the box of radius B. +- declaration: CCDN.affine_hypersurface_dimension_growth + informal: >- + In N at least three variables, an affine hypersurface of degree d at least + five whose top form is absolutely irreducible has at most + C(N) d^(3N+9) B^(N-2) integer points in the box of radius B, for B at least one. +- declaration: CCDN.projective_integral_curve_lower_bound + informal: >- + For every positive degree d, an integral projective plane curve and a radius + B at least one attain at least d^2 B^(2/d)/5 rational points of height at most B. +- declaration: CCDN.classGroup_two_torsion_bound + informal: >- + For number fields K of degree n at least three, the two-torsion cardinality + is at most C(n) |disc K|^(1/2-1/(2n)) (log |disc K|)^(1-(n mod 2)). +tags: +- arithmetic-geometry +- rational-points +- determinant-method +- class-groups +msc: +- 11D45 +- 14G05 +- 11R29