/- Copyright (c) 2026 Michael R. Douglas. All rights reserved. Released under Apache 2.0 license as described in the file LICENSE. # Circulant DFT: Eigenvectors, Spectral Expansion, and Convergence Proves that the lattice Fourier basis functions are eigenvectors of the 1D discrete Laplacian, constructs the DFT eigenbasis, proves the corrected spectral expansion of the heat kernel bilinear form, and establishes the full 1D lattice-to-continuum convergence. ## Main results - `negLaplacian1d_cos_eigenvalue` / `sin_eigenvalue` — eigenvalue formulas - `negLaplacian1d_dft_eigenvector` — normalized DFT basis eigenvector property - `latticeFourierBasis` — DFT eigenbasis construction via linear independence + span - `dft_expansion` — DFT expansion: f = Σ (⟨f,φ_m⟩/‖φ_m‖²) · φ_m - `latticeHeatKernelBilinear1d_eq_spectral'` — corrected spectral expansion with /normSq - `lattice_heatKernel_tendsto_continuum_1d` — full 1D heat kernel convergence - `latticeDFTCoeff1d_quadratic_bound'` — |c_m(f)| ≤ C/(1+m)² ## References - Davis, *Circulant Matrices*, Ch. 3 - Katznelson, *Harmonic Analysis*, §I.2 -/ import Lattice.HeatKernelConvergence1d noncomputable section namespace GaussianField open Real _root_.GaussianField.Matrix Filter variable (L : ℝ) [hL : Fact (0 < L)] /-! ## ZMod.val shift lemmas The key property: `cos(2πk·z/N)` and `sin(2πk·z/N)` are N-periodic in z, so the ZMod wrapping of `.val` does not affect their values. The proof uses ℤ-divisibility: `z.val + 1` and `(z+1).val` are congruent mod N in ℤ, so their cos/sin values at `2πk·_/N` agree. -/ /-- `cos(2πk·(z+1).val/N) = cos(2πk·(z.val+1)/N)` for `z : ZMod N`. -/ theorem cos_zmod_succ (N : ℕ) [NeZero N] (k : ℕ) (z : ZMod N) : cos (2 * π * k * ((z + 1 : ZMod N).val : ℝ) / N) = cos (2 * π * k * ((z.val : ℝ) + 1) / N) := by have hN_ne : (N : ℝ) ≠ 0 := Nat.cast_ne_zero.mpr (NeZero.ne N) -- Both (z+1).val and z.val+1 represent z+1 in ZMod N, so they differ by N*q in ℤ have hdvd : (N : ℤ) ∣ ((z.val : ℤ) + 1 - ((z + 1 : ZMod N).val : ℤ)) := by rw [← Int.modEq_iff_dvd, ← ZMod.intCast_eq_intCast_iff] simp obtain ⟨q, hq⟩ := hdvd -- Cast the divisibility identity to ℝ have hqR : (z.val : ℝ) + 1 - ((z + 1 : ZMod N).val : ℝ) = ↑N * (q : ℝ) := by have := congr_arg (Int.cast (R := ℝ)) hq push_cast at this ⊢; linarith -- The cos arguments differ by 2πkq, and cos is 2π-periodic suffices h : 2 * π * ↑k * ((z.val : ℝ) + 1) / ↑N = 2 * π * ↑k * ((z + 1 : ZMod N).val : ℝ) / ↑N + ↑(↑k * q) * (2 * π) by rw [h, cos_add_int_mul_two_pi] push_cast; field_simp; nlinarith [hqR] /-- `sin(2πk·(z+1).val/N) = sin(2πk·(z.val+1)/N)` for `z : ZMod N`. -/ theorem sin_zmod_succ (N : ℕ) [NeZero N] (k : ℕ) (z : ZMod N) : sin (2 * π * k * ((z + 1 : ZMod N).val : ℝ) / N) = sin (2 * π * k * ((z.val : ℝ) + 1) / N) := by have hN_ne : (N : ℝ) ≠ 0 := Nat.cast_ne_zero.mpr (NeZero.ne N) have hdvd : (N : ℤ) ∣ ((z.val : ℤ) + 1 - ((z + 1 : ZMod N).val : ℤ)) := by rw [← Int.modEq_iff_dvd, ← ZMod.intCast_eq_intCast_iff] simp obtain ⟨q, hq⟩ := hdvd have hqR : (z.val : ℝ) + 1 - ((z + 1 : ZMod N).val : ℝ) = ↑N * (q : ℝ) := by have := congr_arg (Int.cast (R := ℝ)) hq push_cast at this ⊢; linarith suffices h : 2 * π * ↑k * ((z.val : ℝ) + 1) / ↑N = 2 * π * ↑k * ((z + 1 : ZMod N).val : ℝ) / ↑N + ↑(↑k * q) * (2 * π) by rw [h, sin_add_int_mul_two_pi] push_cast; field_simp; nlinarith [hqR] /-- `cos(2πk·(z-1).val/N) = cos(2πk·(z.val-1)/N)` for `z : ZMod N`. -/ theorem cos_zmod_pred (N : ℕ) [NeZero N] (k : ℕ) (z : ZMod N) : cos (2 * π * k * ((z - 1 : ZMod N).val : ℝ) / N) = cos (2 * π * k * ((z.val : ℝ) - 1) / N) := by have hN_ne : (N : ℝ) ≠ 0 := Nat.cast_ne_zero.mpr (NeZero.ne N) have hdvd : (N : ℤ) ∣ ((z.val : ℤ) - 1 - ((z - 1 : ZMod N).val : ℤ)) := by rw [← Int.modEq_iff_dvd, ← ZMod.intCast_eq_intCast_iff] simp obtain ⟨q, hq⟩ := hdvd have hqR : (z.val : ℝ) - 1 - ((z - 1 : ZMod N).val : ℝ) = ↑N * (q : ℝ) := by have := congr_arg (Int.cast (R := ℝ)) hq push_cast at this ⊢; linarith suffices h : 2 * π * ↑k * ((z.val : ℝ) - 1) / ↑N = 2 * π * ↑k * ((z - 1 : ZMod N).val : ℝ) / ↑N + ↑(↑k * q) * (2 * π) by rw [h, cos_add_int_mul_two_pi] push_cast; field_simp; nlinarith [hqR] /-- `sin(2πk·(z-1).val/N) = sin(2πk·(z.val-1)/N)` for `z : ZMod N`. -/ theorem sin_zmod_pred (N : ℕ) [NeZero N] (k : ℕ) (z : ZMod N) : sin (2 * π * k * ((z - 1 : ZMod N).val : ℝ) / N) = sin (2 * π * k * ((z.val : ℝ) - 1) / N) := by have hN_ne : (N : ℝ) ≠ 0 := Nat.cast_ne_zero.mpr (NeZero.ne N) have hdvd : (N : ℤ) ∣ ((z.val : ℤ) - 1 - ((z - 1 : ZMod N).val : ℤ)) := by rw [← Int.modEq_iff_dvd, ← ZMod.intCast_eq_intCast_iff] simp obtain ⟨q, hq⟩ := hdvd have hqR : (z.val : ℝ) - 1 - ((z - 1 : ZMod N).val : ℝ) = ↑N * (q : ℝ) := by have := congr_arg (Int.cast (R := ℝ)) hq push_cast at this ⊢; linarith suffices h : 2 * π * ↑k * ((z.val : ℝ) - 1) / ↑N = 2 * π * ↑k * ((z - 1 : ZMod N).val : ℝ) / ↑N + ↑(↑k * q) * (2 * π) by rw [h, sin_add_int_mul_two_pi] push_cast; field_simp; nlinarith [hqR] /-! ## Pure trig eigenvector identities -/ /-- `1 - cos(2x) = 2sin²(x)` -/ private lemma one_sub_cos_double (x : ℝ) : 1 - cos (2 * x) = 2 * sin x ^ 2 := by linarith [sin_sq_eq_half_sub x] /-- **Cosine eigenvector identity (pure trig).** `a⁻² [2cos(θ) - cos(θ+δ) - cos(θ-δ)] = a⁻² · 2(1-cos(δ)) · cos(θ)` Proof: `cos(θ+δ) + cos(θ-δ) = 2cos(δ)cos(θ)`. -/ theorem negLaplacian1d_cos_eigenvector (a : ℝ) (θ δ : ℝ) : a⁻¹ ^ 2 * (2 * cos θ - cos (θ + δ) - cos (θ - δ)) = a⁻¹ ^ 2 * (2 * (1 - cos δ)) * cos θ := by have : cos (θ + δ) + cos (θ - δ) = 2 * cos δ * cos θ := by rw [cos_add, cos_sub]; ring nlinarith /-- The eigenvalue identity: `a⁻² · 2(1 - cos(2πk/N)) = (4/a²)sin²(πk/N)`. -/ theorem eigenvalue_formula (a : ℝ) (N k : ℕ) : a⁻¹ ^ 2 * (2 * (1 - cos (2 * π * k / N))) = (4 / a ^ 2) * sin (π * k / N) ^ 2 := by have h1 : 1 - cos (2 * π * (k : ℝ) / N) = 2 * sin (π * k / N) ^ 2 := by rw [show 2 * π * (k : ℝ) / (N : ℝ) = 2 * (π * k / N) from by ring] exact one_sub_cos_double _ rw [h1, show a⁻¹ ^ 2 = 1 / a ^ 2 from by rw [inv_pow, one_div]] ring /-- **Sine eigenvector identity.** Same eigenvalue. -/ theorem negLaplacian1d_sin_eigenvector (a : ℝ) (θ δ : ℝ) : a⁻¹ ^ 2 * (2 * sin θ - sin (θ + δ) - sin (θ - δ)) = a⁻¹ ^ 2 * (2 * (1 - cos δ)) * sin θ := by have : sin (θ + δ) + sin (θ - δ) = 2 * cos δ * sin θ := by rw [sin_add, sin_sub]; ring nlinarith /-! ## Bijection ZMod N ≃ FinLatticeSites 1 N -/ /-- Bijection between `ZMod N` and `FinLatticeSites 1 N`. -/ def zmodToFin1 (N : ℕ) : ZMod N ≃ FinLatticeSites 1 N where toFun z := fun _ => z invFun x := x 0 left_inv _ := rfl right_inv x := by ext i; fin_cases i; rfl /-- Sum over `FinLatticeSites 1 N` equals sum over `ZMod N`. -/ theorem sum_fin1_eq_sum_zmod {N : ℕ} [NeZero N] (f : FinLatticeSites 1 N → ℝ) : ∑ x : FinLatticeSites 1 N, f x = ∑ z : ZMod N, f (fun _ => z) := Fintype.sum_equiv (zmodToFin1 N).symm _ _ (fun x => by show f x = f (fun _ => x 0) congr 1; ext i; fin_cases i; rfl) /-! ## Eigenvector transfer identity -/ /-- **The eigenvector transfer identity.** For the m-th Fourier mode with m < N: `λ_m · c_m(f) = ∑_z (-Δ_a fN)(z) · φ_m(z)` This transfers the Laplacian from the eigenvector to the test function via self-adjointness. -/ theorem eigenvector_transfer (N : ℕ) [NeZero N] (f : SmoothMap_Circle L ℝ) (m : ℕ) (hm : m < N) : latticeEigenvalue1d N (circleSpacing L N) m * latticeDFTCoeff1d L N f m = ∑ z : ZMod N, ((circleSpacing L N)⁻¹ ^ 2 * (2 * circleRestriction L N f z - circleRestriction L N f (z + 1) - circleRestriction L N f (z - 1))) * latticeFourierBasisFun N m z := by -- Step 1: Unfold latticeDFTCoeff1d and pull eigenvalue into sum simp only [latticeDFTCoeff1d, if_pos hm] rw [Finset.mul_sum] -- Step 2: Eigenvector property (pointwise) have h_eigvec : ∀ z : ZMod N, latticeEigenvalue1d N (circleSpacing L N) m * latticeFourierBasisFun N m z = (circleSpacing L N)⁻¹ ^ 2 * (2 * latticeFourierBasisFun N m z - latticeFourierBasisFun N m (z + 1) - latticeFourierBasisFun N m (z - 1)) := by intro z cases m with | zero => simp [latticeEigenvalue1d, latticeFourierBasisFun, SmoothMap_Circle.fourierFreq, sin_zero] right; ring | succ n => simp only [latticeFourierBasisFun] set k := n / 2 + 1 have hk : SmoothMap_Circle.fourierFreq (n + 1) = k := rfl split · -- cos mode dsimp only [] rw [cos_zmod_succ N k z, cos_zmod_pred N k z] set θ := 2 * π * ↑k * (↑z.val : ℝ) / ↑N set δ := 2 * π * ↑k / ↑N rw [show 2 * π * ↑k * ((↑z.val : ℝ) + 1) / ↑N = θ + δ from by simp only [θ, δ]; ring, show 2 * π * ↑k * ((↑z.val : ℝ) - 1) / ↑N = θ - δ from by simp only [θ, δ]; ring] have h_cos_eig := negLaplacian1d_cos_eigenvector (circleSpacing L N) θ δ have h_ev : latticeEigenvalue1d N (circleSpacing L N) (n + 1) = (circleSpacing L N)⁻¹ ^ 2 * (2 * (1 - cos δ)) := by simp only [δ, latticeEigenvalue1d, hk] linarith [eigenvalue_formula (circleSpacing L N) N k] rw [h_ev]; linear_combination -√(2 / ↑N) * h_cos_eig · -- sin mode dsimp only [] rw [sin_zmod_succ N k z, sin_zmod_pred N k z] set θ := 2 * π * ↑k * (↑z.val : ℝ) / ↑N set δ := 2 * π * ↑k / ↑N rw [show 2 * π * ↑k * ((↑z.val : ℝ) + 1) / ↑N = θ + δ from by simp only [θ, δ]; ring, show 2 * π * ↑k * ((↑z.val : ℝ) - 1) / ↑N = θ - δ from by simp only [θ, δ]; ring] have h_sin_eig := negLaplacian1d_sin_eigenvector (circleSpacing L N) θ δ have h_ev : latticeEigenvalue1d N (circleSpacing L N) (n + 1) = (circleSpacing L N)⁻¹ ^ 2 * (2 * (1 - cos δ)) := by simp only [δ, latticeEigenvalue1d, hk] linarith [eigenvalue_formula (circleSpacing L N) N k] rw [h_ev]; linear_combination -√(2 / ↑N) * h_sin_eig -- Step 3: Self-adjointness of discrete Laplacian have h_adj : ∀ (u v : ZMod N → ℝ), ∑ z : ZMod N, u z * (2 * v z - v (z + 1) - v (z - 1)) = ∑ z : ZMod N, (2 * u z - u (z + 1) - u (z - 1)) * v z := by intro u v have hs : ∑ z : ZMod N, u z * v (z + 1) = ∑ z : ZMod N, u (z - 1) * v z := Fintype.sum_equiv (Equiv.addRight (1 : ZMod N)) _ _ (fun z => by show u z * v (z + 1) = u ((z + 1) - 1) * v (z + 1) rw [add_sub_cancel_right]) have hp : ∑ z : ZMod N, u z * v (z - 1) = ∑ z : ZMod N, u (z + 1) * v z := Fintype.sum_equiv (Equiv.addRight ((-1 : ZMod N))) _ _ (fun z => by have h1 : (Equiv.addRight (-1 : ZMod N)) z = z - 1 := by simp [Equiv.addRight, sub_eq_add_neg] rw [h1, sub_add_cancel]) calc ∑ z, u z * (2 * v z - v (z + 1) - v (z - 1)) = ∑ z, (2 * (u z * v z) - u z * v (z + 1) - u z * v (z - 1)) := by congr 1; ext z; ring _ = 2 * ∑ z, u z * v z - ∑ z, u z * v (z + 1) - ∑ z, u z * v (z - 1) := by simp only [Finset.sum_sub_distrib, Finset.mul_sum] _ = 2 * ∑ z, u z * v z - ∑ z, u (z - 1) * v z - ∑ z, u (z + 1) * v z := by rw [hs, hp] _ = ∑ z, (2 * u z - u (z + 1) - u (z - 1)) * v z := by conv_rhs => arg 2; ext z; rw [show (2 * u z - u (z + 1) - u (z - 1)) * v z = 2 * (u z * v z) - u (z + 1) * v z - u (z - 1) * v z from by ring] simp only [Finset.sum_sub_distrib, ← Finset.mul_sum]; linarith -- Step 4: Combine using calc set fN := circleRestriction L N f set φ := latticeFourierBasisFun N m set c := (circleSpacing L N)⁻¹ ^ 2 conv_lhs => arg 2; ext z rw [show latticeEigenvalue1d N (circleSpacing L N) m * (fN z * φ z) = fN z * (latticeEigenvalue1d N (circleSpacing L N) m * φ z) from by ring, h_eigvec z] simp_rw [show ∀ z : ZMod N, fN z * (c * (2 * φ z - φ (z + 1) - φ (z - 1))) = c * (fN z * (2 * φ z - φ (z + 1) - φ (z - 1))) from fun z => by ring] rw [← Finset.mul_sum, h_adj fN φ, Finset.mul_sum] congr 1; ext z; ring /-! ## Second difference bound -/ /-- **Symmetric second difference bound.** `|f(x+h) + f(x-h) - 2f(x)| ≤ h² · ‖f''‖_∞` -/ theorem symmetric_second_diff_bound (f : SmoothMap_Circle L ℝ) (x h : ℝ) : |f (x + h) + f (x - h) - 2 * f x| ≤ h ^ 2 * SmoothMap_Circle.sobolevSeminorm 2 f := by -- Reduce to h ≥ 0 by symmetry (h² = (-h)²) suffices key : ∀ t : ℝ, 0 ≤ t → |f (x + t) + f (x - t) - 2 * f x| ≤ t ^ 2 * SmoothMap_Circle.sobolevSeminorm 2 f by rcases le_or_gt 0 h with hh | hh · exact key h hh · have key_neg := key (-h) (by linarith) -- key_neg : |f(x+(-h)) + f(x-(-h)) - 2*f(x)| ≤ (-h)² * ... rw [sub_neg_eq_add] at key_neg -- Now: |f(x+(-h)) + f(x+h) - 2*f(x)| ≤ (-h)² * ... rw [show f (x + -h) + f (x + h) - 2 * f x = f (x + h) + f (x + -h) - 2 * f x from by ring, neg_sq] at key_neg -- key_neg : |f(x+h) + f(x-h) - 2*f(x)| ≤ h² * ... (x-h = x+(-h) def'ly) exact key_neg intro t ht rcases eq_or_lt_of_le ht with rfl | ht_pos · simp; linarith [SmoothMap_Circle.sobolevSeminorm_nonneg (L := L) 2 f] -- For t > 0, apply the Lagrange Taylor remainder with n = 1 have hlt_fwd : x < x + t := by linarith have hf_cd2 : ContDiffOn ℝ (↑(1 + 1 : ℕ)) (⇑f) (Set.uIcc x (x + t)) := f.smooth.contDiffOn.of_le (WithTop.coe_le_coe.mpr le_top) obtain ⟨c₁, hc₁, hc₁_eq⟩ := taylor_mean_remainder_lagrange_iteratedDeriv hlt_fwd.ne hf_cd2 -- Backward direction: expand g(s) = f(x-s) on [0, t] have hg_cd2 : ContDiffOn ℝ (↑(1 + 1 : ℕ)) (fun s => (f : ℝ → ℝ) (x - s)) (Set.uIcc 0 t) := by apply ContDiffOn.of_le _ (WithTop.coe_le_coe.mpr le_top) exact (f.smooth.comp ((contDiff_const.sub contDiff_id).of_le le_top)).contDiffOn obtain ⟨c₂, hc₂, hc₂_eq⟩ := taylor_mean_remainder_lagrange_iteratedDeriv ht_pos.ne hg_cd2 -- Simplify taylorWithinEval at n=1 have hP₁ : taylorWithinEval (⇑f) 1 (Set.uIcc x (x + t)) x (x + t) = f x + (x + t - x) * derivWithin (⇑f) (Set.uIcc x (x + t)) x := by rw [taylorWithinEval_succ, taylor_within_zero_eval] simp [iteratedDerivWithin_one, smul_eq_mul] have hQ₁ : taylorWithinEval (fun s => (f : ℝ → ℝ) (x - s)) 1 (Set.uIcc 0 t) 0 t = (f : ℝ → ℝ) (x - 0) + (t - 0) * derivWithin (fun s => (f : ℝ → ℝ) (x - s)) (Set.uIcc 0 t) 0 := by rw [taylorWithinEval_succ, taylor_within_zero_eval] simp [iteratedDerivWithin_one, smul_eq_mul] -- derivWithin on smooth functions = deriv have hDW_f : derivWithin (⇑f) (Set.uIcc x (x + t)) x = deriv (⇑f) x := (f.smooth.differentiable (by simp)).differentiableAt.derivWithin ((uniqueDiffOn_Icc (by simp; linarith)) x ⟨min_le_left _ _, le_max_left _ _⟩) have hDW_g : derivWithin (fun s => (f : ℝ → ℝ) (x - s)) (Set.uIcc 0 t) 0 = -deriv (⇑f) x := by have hud : UniqueDiffWithinAt ℝ (Set.uIcc 0 t) 0 := (uniqueDiffOn_Icc (by simp [ht_pos])) 0 ⟨min_le_left _ _, le_max_left _ _⟩ have hg_diff : DifferentiableAt ℝ (fun s => (f : ℝ → ℝ) (x - s)) 0 := ((f.smooth.comp ((contDiff_const.sub contDiff_id).of_le le_top)).differentiable (by simp)).differentiableAt rw [hg_diff.derivWithin hud] have hd : HasDerivAt (fun s => (f : ℝ → ℝ) (x - s)) (-deriv (⇑f) x) 0 := by have hf_da : DifferentiableAt ℝ (⇑f) x := (f.smooth.differentiable (by simp)).differentiableAt have h1 : HasDerivAt (⇑f) (deriv (⇑f) x) (x - 0) := by simpa only [sub_zero] using hf_da.hasDerivAt exact h1.comp_const_sub x 0 exact hd.deriv rw [hP₁, hDW_f] at hc₁_eq; simp only [add_sub_cancel_left] at hc₁_eq rw [hQ₁, hDW_g] at hc₂_eq; simp only [sub_zero] at hc₂_eq have hsum : f (x + t) + f (x - t) - 2 * f x = (iteratedDeriv 2 (⇑f) c₁ + iteratedDeriv 2 (fun s => (f : ℝ → ℝ) (x - s)) c₂) * t ^ 2 / ↑(Nat.factorial 2) := by linarith [hc₁_eq, hc₂_eq] rw [hsum] have h2_fact : (Nat.factorial 2 : ℝ) = 2 := by norm_num rw [h2_fact, abs_div, abs_of_pos (by norm_num : (0:ℝ) < 2), abs_mul, abs_of_nonneg (sq_nonneg t)] have hbd₁ : |iteratedDeriv 2 (⇑f) c₁| ≤ SmoothMap_Circle.sobolevSeminorm 2 f := by rw [← Real.norm_eq_abs] exact SmoothMap_Circle.norm_iteratedDeriv_le_sobolevSeminorm' f 2 c₁ have hbd₂ : |iteratedDeriv 2 (fun s => (f : ℝ → ℝ) (x - s)) c₂| ≤ SmoothMap_Circle.sobolevSeminorm 2 f := by have hchain : iteratedDeriv 2 (fun s => (f : ℝ → ℝ) (x - s)) c₂ = iteratedDeriv 2 (⇑f) (x - c₂) := by rw [show (fun s => (f : ℝ → ℝ) (x - s)) = (fun s => (f : ℝ → ℝ) ((-1) * s + x)) from by ext s; ring_nf] have hg_cd : ContDiff ℝ (↑(2 : ℕ)) (fun t => (f : ℝ → ℝ) (t + x)) := by apply ContDiff.of_le _ (WithTop.coe_le_coe.mpr le_top) exact f.smooth.comp ((contDiff_id.add contDiff_const).of_le le_top) rw [congr_fun (iteratedDeriv_comp_const_mul hg_cd (-1)) c₂, congr_fun (iteratedDeriv_comp_add_const 2 (⇑f) x) ((-1) * c₂)] simp; ring_nf rw [hchain, ← Real.norm_eq_abs] exact SmoothMap_Circle.norm_iteratedDeriv_le_sobolevSeminorm' f 2 (x - c₂) calc |iteratedDeriv 2 (⇑f) c₁ + iteratedDeriv 2 (fun s => (f : ℝ → ℝ) (x - s)) c₂| * t ^ 2 / 2 ≤ (|iteratedDeriv 2 (⇑f) c₁| + |iteratedDeriv 2 (fun s => (f : ℝ → ℝ) (x - s)) c₂|) * t ^ 2 / 2 := by apply div_le_div_of_nonneg_right _ (by norm_num : (0:ℝ) ≤ 2) exact mul_le_mul_of_nonneg_right (abs_add_le _ _) (sq_nonneg t) _ ≤ (SmoothMap_Circle.sobolevSeminorm 2 f + SmoothMap_Circle.sobolevSeminorm 2 f) * t ^ 2 / 2 := by apply div_le_div_of_nonneg_right _ (by norm_num : (0:ℝ) ≤ 2) exact mul_le_mul_of_nonneg_right (add_le_add hbd₁ hbd₂) (sq_nonneg t) _ = t ^ 2 * SmoothMap_Circle.sobolevSeminorm 2 f := by ring /-! ## Laplacian restriction bounds -/ /-- The Laplacian of a restricted test function is bounded. -/ theorem negLaplacian_restriction_bound (N : ℕ) [NeZero N] (f : SmoothMap_Circle L ℝ) (z : ZMod N) : |(circleSpacing L N)⁻¹ ^ 2 * (2 * circleRestriction L N f z - circleRestriction L N f (z + 1) - circleRestriction L N f (z - 1))| ≤ Real.sqrt (circleSpacing L N) * SmoothMap_Circle.sobolevSeminorm 2 f := by set a := circleSpacing L N with ha_def set x := circlePoint L N z with hx_def have ha_pos : 0 < a := circleSpacing_pos L N have hN_ne : (N : ℝ) ≠ 0 := Nat.cast_ne_zero.mpr (NeZero.ne N) simp only [circleRestriction_apply] -- Use L-periodicity to relate circlePoint at z±1 to x±a have hsucc : f (circlePoint L N (z + 1)) = f (x + a) := by simp only [x, a, circlePoint, circleSpacing] have hdvd : (N : ℤ) ∣ ((z.val : ℤ) + 1 - ((z + 1 : ZMod N).val : ℤ)) := by rw [← Int.modEq_iff_dvd, ← ZMod.intCast_eq_intCast_iff]; simp obtain ⟨q, hq⟩ := hdvd have hval : ((z + 1 : ZMod N).val : ℝ) = (z.val : ℝ) + 1 - (N : ℝ) * (q : ℝ) := by have := congr_arg (Int.cast (R := ℝ)) hq; push_cast at this ⊢; linarith conv_lhs => rw [show ((z + 1 : ZMod N).val : ℝ) * L / ↑N = (↑z.val * L / ↑N + L / ↑N) - ↑q * L from by rw [hval]; field_simp [hN_ne]] exact f.periodic.sub_int_mul_eq q have hpred : f (circlePoint L N (z - 1)) = f (x - a) := by simp only [x, a, circlePoint, circleSpacing] have hdvd : (N : ℤ) ∣ ((z.val : ℤ) - 1 - ((z - 1 : ZMod N).val : ℤ)) := by rw [← Int.modEq_iff_dvd, ← ZMod.intCast_eq_intCast_iff]; simp obtain ⟨q, hq⟩ := hdvd have hval : ((z - 1 : ZMod N).val : ℝ) = (z.val : ℝ) - 1 - (N : ℝ) * (q : ℝ) := by have := congr_arg (Int.cast (R := ℝ)) hq; push_cast at this ⊢; linarith conv_lhs => rw [show ((z - 1 : ZMod N).val : ℝ) * L / ↑N = (↑z.val * L / ↑N - L / ↑N) - ↑q * L from by rw [hval]; field_simp [hN_ne]] exact f.periodic.sub_int_mul_eq q rw [hsucc, hpred] -- Factor out √a, rearrange to isolate the second-difference rw [show 2 * (Real.sqrt a * f x) - Real.sqrt a * f (x + a) - Real.sqrt a * f (x - a) = Real.sqrt a * (-(f (x + a) + f (x - a) - 2 * f x)) from by ring, show a⁻¹ ^ 2 * (Real.sqrt a * -(f (x + a) + f (x - a) - 2 * f x)) = Real.sqrt a * (a⁻¹ ^ 2 * -(f (x + a) + f (x - a) - 2 * f x)) from by ring, abs_mul, abs_of_nonneg (Real.sqrt_nonneg _), abs_mul, abs_of_nonneg (by positivity : 0 ≤ a⁻¹ ^ 2), abs_neg] apply mul_le_mul_of_nonneg_left _ (Real.sqrt_nonneg _) -- a⁻² * |f(x+a) + f(x-a) - 2f(x)| ≤ a⁻² * a² * ‖f''‖ = ‖f''‖ calc a⁻¹ ^ 2 * |f (x + a) + f (x - a) - 2 * f x| ≤ a⁻¹ ^ 2 * (a ^ 2 * SmoothMap_Circle.sobolevSeminorm 2 f) := mul_le_mul_of_nonneg_left (symmetric_second_diff_bound L f x a) (by positivity) _ = SmoothMap_Circle.sobolevSeminorm 2 f := by rw [inv_pow, ← mul_assoc, inv_mul_cancel₀ (by positivity : a ^ 2 ≠ 0), one_mul] /-- Flat bound on the "Laplacian DFT coefficient." -/ theorem laplacianDFT_flat_bound (N : ℕ) [NeZero N] (f : SmoothMap_Circle L ℝ) (m : ℕ) : |∑ z : ZMod N, ((circleSpacing L N)⁻¹ ^ 2 * (2 * circleRestriction L N f z - circleRestriction L N f (z + 1) - circleRestriction L N f (z - 1))) * latticeFourierBasisFun N m z| ≤ Real.sqrt (2 * L) * SmoothMap_Circle.sobolevSeminorm 2 f := by set a := circleSpacing L N with ha_def have ha_pos : 0 < a := circleSpacing_pos L N have hN_pos : (0 : ℝ) < (N : ℝ) := Nat.cast_pos.mpr (NeZero.pos N) -- Fourier basis bound (inline since the original is private) have hφ_bound : ∀ z : ZMod N, |latticeFourierBasisFun N m z| ≤ Real.sqrt (2 / N) := by intro z; cases m with | zero => simp only [latticeFourierBasisFun] rw [abs_of_nonneg (by positivity)] calc 1 / Real.sqrt ↑N = Real.sqrt 1 / Real.sqrt ↑N := by rw [Real.sqrt_one] _ ≤ Real.sqrt 2 / Real.sqrt ↑N := div_le_div_of_nonneg_right (Real.sqrt_le_sqrt (by norm_num)) (Real.sqrt_nonneg _) _ = Real.sqrt (2 / ↑N) := by rw [Real.sqrt_div (by norm_num : (0:ℝ) ≤ 2)] | succ n => simp only [latticeFourierBasisFun]; split · rw [abs_mul, abs_of_nonneg (Real.sqrt_nonneg _)] exact mul_le_of_le_one_right (Real.sqrt_nonneg _) (Real.abs_cos_le_one _) · rw [abs_mul, abs_of_nonneg (Real.sqrt_nonneg _)] exact mul_le_of_le_one_right (Real.sqrt_nonneg _) (Real.abs_sin_le_one _) -- Per-term bound have h_term : ∀ z : ZMod N, |((circleSpacing L N)⁻¹ ^ 2 * (2 * circleRestriction L N f z - circleRestriction L N f (z + 1) - circleRestriction L N f (z - 1))) * latticeFourierBasisFun N m z| ≤ Real.sqrt a * SmoothMap_Circle.sobolevSeminorm 2 f * Real.sqrt (2 / N) := by intro z; rw [abs_mul] exact mul_le_mul (negLaplacian_restriction_bound L N f z) (hφ_bound z) (abs_nonneg _) (mul_nonneg (Real.sqrt_nonneg _) (SmoothMap_Circle.sobolevSeminorm_nonneg 2 f)) calc |∑ z : ZMod N, _| ≤ ∑ z : ZMod N, |((circleSpacing L N)⁻¹ ^ 2 * (2 * circleRestriction L N f z - circleRestriction L N f (z + 1) - circleRestriction L N f (z - 1))) * latticeFourierBasisFun N m z| := Finset.abs_sum_le_sum_abs _ _ _ ≤ ∑ _ : ZMod N, (Real.sqrt a * SmoothMap_Circle.sobolevSeminorm 2 f * Real.sqrt (2 / N)) := Finset.sum_le_sum (fun z _ => h_term z) _ = ↑N * (Real.sqrt a * SmoothMap_Circle.sobolevSeminorm 2 f * Real.sqrt (2 / N)) := by rw [Finset.sum_const, nsmul_eq_mul] congr 1; rw [show Finset.univ.card = (N : ℕ) from ZMod.card N] _ = ↑N * (Real.sqrt a * Real.sqrt (2 / N)) * SmoothMap_Circle.sobolevSeminorm 2 f := by ring _ = Real.sqrt (2 * L) * SmoothMap_Circle.sobolevSeminorm 2 f := by congr 1 rw [ha_def, circleSpacing_eq, ← Real.sqrt_mul (div_nonneg hL.out.le hN_pos.le)] rw [show L / ↑N * (2 / ↑N) = 2 * L / ↑N ^ 2 from by field_simp] rw [Real.sqrt_div (by linarith [hL.out] : (0 : ℝ) ≤ 2 * L)] rw [Real.sqrt_sq hN_pos.le]; field_simp /-! ## Eigenvalue lower bound (quadratic in frequency) -/ /-- **Eigenvalue lower bound**: `λ_m ≥ (1+m)²/L²` for m ≥ 1 and m < N. Uses: `λ_m = (4/a²)sin²(πk/N)` with `k = fourierFreq(m)` satisfying `2k ≥ m`, Jordan's inequality `sin(πk/N) ≥ 2k/N` for `k ≤ N/2`, and `a = L/N`. Combined: `λ_m ≥ 16k²/L² ≥ 4m²/L² ≥ (1+m)²/L²`. -/ theorem latticeEigenvalue1d_ge_quadratic (N m : ℕ) [NeZero N] (hm : 1 ≤ m) (hm_lt : m < N) : ((1 + (m : ℝ)) ^ 2) / L ^ 2 ≤ latticeEigenvalue1d N (circleSpacing L N) m := by unfold latticeEigenvalue1d push_cast [circleSpacing] set M := (N : ℝ) set j := (SmoothMap_Circle.fourierFreq m : ℝ) have hM_pos : 0 < M := Nat.cast_pos.mpr (NeZero.pos N) have hL' : 0 < L := hL.out have h_coeff : 4 / (L / M) ^ 2 = 4 * M ^ 2 / L ^ 2 := by field_simp rw [h_coeff] -- Key facts about fourierFreq have hj_pos : 0 < j := by show 0 < (SmoothMap_Circle.fourierFreq m : ℝ) have : 1 ≤ SmoothMap_Circle.fourierFreq m := by cases m with | zero => omega | succ n => simp only [SmoothMap_Circle.fourierFreq]; omega positivity have h2j_ge_m : (m : ℝ) ≤ 2 * j := by show (m : ℝ) ≤ 2 * (SmoothMap_Circle.fourierFreq m : ℝ) have : m ≤ 2 * SmoothMap_Circle.fourierFreq m := by cases m with | zero => omega | succ n => simp only [SmoothMap_Circle.fourierFreq]; omega exact_mod_cast this have hj_le_half : j ≤ M / 2 := by show (SmoothMap_Circle.fourierFreq m : ℝ) ≤ (N : ℝ) / 2 have h_nat : SmoothMap_Circle.fourierFreq m ≤ N / 2 := by cases m with | zero => omega | succ n => simp only [SmoothMap_Circle.fourierFreq]; omega calc (SmoothMap_Circle.fourierFreq m : ℝ) ≤ ((N / 2 : ℕ) : ℝ) := by exact_mod_cast h_nat _ ≤ (N : ℝ) / 2 := by have h := Nat.div_mul_le_self N 2 have : (↑(N / 2) : ℝ) * 2 ≤ ↑N := by exact_mod_cast h linarith -- Jordan's inequality: sin(πj/M) ≥ 2j/M have harg : 0 ≤ π * j / M := by positivity have harg_le : π * j / M ≤ π / 2 := by rw [div_le_div_iff₀ hM_pos (by norm_num : (0:ℝ) < 2)] nlinarith [pi_pos] have hjordan : 2 / π * (π * j / M) ≤ sin (π * j / M) := mul_le_sin harg harg_le -- sin²(πj/M) ≥ (2j/M)² have hsin_sq : (2 * j / M) ^ 2 ≤ sin (π * j / M) ^ 2 := by have : 2 / π * (π * j / M) = 2 * j / M := by field_simp rw [← this, sq, sq] exact mul_self_le_mul_self (by positivity) hjordan -- Chain: (1+m)²/L² ≤ 4m²/L² ≤ 16j²/L² ≤ (4M²/L²)·sin²(πj/M) have h_4m_ge : (1 + (m : ℝ)) ^ 2 ≤ 4 * (m : ℝ) ^ 2 := by have hm_real : (1 : ℝ) ≤ (m : ℝ) := by exact_mod_cast hm nlinarith calc (1 + (m : ℝ)) ^ 2 / L ^ 2 ≤ 4 * (m : ℝ) ^ 2 / L ^ 2 := by apply div_le_div_of_nonneg_right h_4m_ge (sq_nonneg L) _ ≤ 16 * j ^ 2 / L ^ 2 := by apply div_le_div_of_nonneg_right _ (sq_nonneg L) nlinarith _ = 4 * M ^ 2 / L ^ 2 * (2 * j / M) ^ 2 := by field_simp; ring _ ≤ 4 * M ^ 2 / L ^ 2 * sin (π * j / M) ^ 2 := by apply mul_le_mul_of_nonneg_left hsin_sq; positivity /-! ## Main theorem: Quadratic bound on DFT coefficients -/ /-- **Lattice DFT coefficients decay at least quadratically, uniformly in N.** `∃ C, 0 ≤ C ∧ ∀ N m, |c_m^{N+1}(f)| ≤ C / (1 + m)²` **Proof outline:** - m = 0: `|c₀| ≤ √(2L)‖f‖ ≤ C` - m ≥ 1, m < N+1: eigenvector transfer gives `λ_m · c_m = ⟨(-Δ)fN, φ_m⟩`, so `|c_m| ≤ √(2L)‖f''‖ / λ_m ≤ √(2L)‖f''‖L² / (1+m)²` - m ≥ N+1: `c_m = 0` -/ theorem latticeDFTCoeff1d_quadratic_bound' (f : SmoothMap_Circle L ℝ) : ∃ C : ℝ, 0 ≤ C ∧ ∀ N m, |latticeDFTCoeff1d L (N + 1) f m| ≤ C / (1 + (m : ℝ)) ^ 2 := by set C₀ := Real.sqrt (2 * L) * SmoothMap_Circle.sobolevSeminorm 0 f set C₂ := Real.sqrt (2 * L) * SmoothMap_Circle.sobolevSeminorm 2 f set Cd := C₂ * L ^ 2 have hC₀_nn : 0 ≤ C₀ := mul_nonneg (Real.sqrt_nonneg _) (SmoothMap_Circle.sobolevSeminorm_nonneg 0 f) have hC₂_nn : 0 ≤ C₂ := mul_nonneg (Real.sqrt_nonneg _) (SmoothMap_Circle.sobolevSeminorm_nonneg 2 f) refine ⟨max C₀ Cd, le_max_of_le_left hC₀_nn, ?_⟩ intro N m by_cases hm_lt : m < N + 1 · by_cases hm0 : m = 0 · -- m = 0: flat bound ≤ C₀ ≤ max C₀ Cd subst hm0; simp only [Nat.cast_zero, add_zero, one_pow, div_one] exact (latticeDFTCoeff1d_flat_bound L f N 0).trans (le_max_left _ _) · -- m ≥ 1: eigenvector transfer + eigenvalue lower bound have hm_pos : 1 ≤ m := Nat.one_le_iff_ne_zero.mpr hm0 haveI hN1 : NeZero (N + 1) := ⟨by omega⟩ have h_transfer := eigenvector_transfer L (N + 1) f m hm_lt have h_lap := laplacianDFT_flat_bound L (N + 1) f m have h_eig := latticeEigenvalue1d_ge_quadratic L (N + 1) m hm_pos hm_lt have h_eig_pos : 0 < latticeEigenvalue1d (N + 1) (circleSpacing L (N + 1)) m := lt_of_lt_of_le (by have := hL.out; positivity) h_eig -- |λ_m · c_m| = |laplacian coeff| ≤ C₂ rw [← h_transfer] at h_lap -- |c_m| ≤ C₂ / λ_m have h_abs : |latticeDFTCoeff1d L (N + 1) f m| ≤ C₂ / latticeEigenvalue1d (N + 1) (circleSpacing L (N + 1)) m := by rw [abs_mul, abs_of_pos h_eig_pos] at h_lap exact (le_div_iff₀ h_eig_pos).mpr (by linarith) -- C₂/λ_m ≤ Cd/(1+m)² have hL_pos := hL.out have h_denom_pos : (0 : ℝ) < (1 + ↑m) ^ 2 / L ^ 2 := by positivity calc |latticeDFTCoeff1d L (N + 1) f m| ≤ C₂ / latticeEigenvalue1d (N + 1) (circleSpacing L (N + 1)) m := h_abs _ ≤ C₂ / ((1 + ↑m) ^ 2 / L ^ 2) := div_le_div_of_nonneg_left hC₂_nn h_denom_pos h_eig _ = Cd / (1 + ↑m) ^ 2 := by simp only [Cd]; field_simp _ ≤ max C₀ Cd / (1 + ↑m) ^ 2 := div_le_div_of_nonneg_right (le_max_right _ _) (by positivity) · -- m ≥ N + 1: coefficient is 0 rw [latticeDFTCoeff1d_zero_of_ge L (N + 1) f m (by omega), abs_zero] exact div_nonneg (le_trans hC₀_nn (le_max_left _ _)) (by positivity) /-! ## Discrete trig sums The key orthogonality ingredient: sums of cos and sin over ZMod N vanish when the frequency is not a multiple of N. The proof uses shift-invariance (bijection z ↦ z+1) to get a homogeneous 2×2 system with nonzero determinant. -/ /-- Sum of cos over ZMod N vanishes when cos(2πk/N) ≠ 1 (i.e., N ∤ k). -/ theorem sum_cos_zmod_eq_zero (N : ℕ) [NeZero N] (k : ℕ) (hk : cos (2 * π * k / N) ≠ 1) : ∑ z : ZMod N, cos (2 * π * k * (z.val : ℝ) / N) = 0 := by set δ := 2 * π * (k : ℝ) / (N : ℝ) with hδ_def set S_c := ∑ z : ZMod N, cos (2 * π * k * (z.val : ℝ) / N) set S_s := ∑ z : ZMod N, sin (2 * π * k * (z.val : ℝ) / N) -- Shift invariance: Σ_z f(z) = Σ_z f(z+1) have h_shift_c : S_c = cos δ * S_c - sin δ * S_s := by simp only [S_c, S_s] rw [Finset.mul_sum, Finset.mul_sum, ← Finset.sum_sub_distrib] apply (Fintype.sum_equiv (Equiv.addRight (1 : ZMod N)) _ _ _).symm intro z show cos δ * cos (2 * π * ↑k * (z.val : ℝ) / ↑N) - sin δ * sin (2 * π * ↑k * (z.val : ℝ) / ↑N) = cos (2 * π * ↑k * ((z + 1 : ZMod N).val : ℝ) / ↑N) rw [cos_zmod_succ N k, show 2 * π * ↑k * ((z.val : ℝ) + 1) / ↑N = 2 * π * ↑k * (z.val : ℝ) / ↑N + δ from by simp only [δ]; ring, cos_add]; ring have h_shift_s : S_s = sin δ * S_c + cos δ * S_s := by simp only [S_c, S_s] rw [Finset.mul_sum, Finset.mul_sum, ← Finset.sum_add_distrib] apply (Fintype.sum_equiv (Equiv.addRight (1 : ZMod N)) _ _ _).symm intro z show sin δ * cos (2 * π * ↑k * (z.val : ℝ) / ↑N) + cos δ * sin (2 * π * ↑k * (z.val : ℝ) / ↑N) = sin (2 * π * ↑k * ((z + 1 : ZMod N).val : ℝ) / ↑N) rw [sin_zmod_succ N k, show 2 * π * ↑k * ((z.val : ℝ) + 1) / ↑N = 2 * π * ↑k * (z.val : ℝ) / ↑N + δ from by simp only [δ]; ring, sin_add]; ring -- From shifts: (1 - cos δ) S_c = -sin δ S_s ... (*) -- (1 - cos δ) S_s = sin δ S_c ... (**) have eq1 : (1 - cos δ) * S_c = -(sin δ * S_s) := by linarith [h_shift_c] -- Multiply (*) by (1 - cos δ): (1-cosδ)² S_c = -sinδ (1-cosδ) S_s -- Substitute (**): = -sinδ · sinδ · S_c = -sin²δ · S_c -- So [(1-cosδ)² + sin²δ] S_c = 0 have eq2 : (1 - cos δ) * S_s = sin δ * S_c := by linarith [h_shift_s] have hdet : (1 - cos δ) ^ 2 * S_c + sin δ ^ 2 * S_c = 0 := by have h1 : (1 - cos δ) ^ 2 * S_c = (1 - cos δ) * ((1 - cos δ) * S_c) := by ring rw [h1, eq1] have h2 : sin δ ^ 2 * S_c = sin δ * (sin δ * S_c) := by ring rw [h2, ← eq2]; ring -- (1-cosδ)² + sin²δ = 2(1-cosδ) have hsimp : ((1 - cos δ) ^ 2 + sin δ ^ 2) * S_c = 0 := by linarith [hdet] have h2 : (1 - cos δ) ^ 2 + sin δ ^ 2 = 2 * (1 - cos δ) := by have := sin_sq_add_cos_sq δ; nlinarith rw [h2] at hsimp have hne : 1 - cos δ ≠ 0 := sub_ne_zero.mpr (Ne.symm hk) exact (mul_eq_zero.mp hsimp).resolve_left (mul_ne_zero two_ne_zero hne) /-- Sum of sin over ZMod N vanishes for any integer frequency. -/ theorem sum_sin_zmod_eq_zero (N : ℕ) [NeZero N] (k : ℕ) : ∑ z : ZMod N, sin (2 * π * k * (z.val : ℝ) / N) = 0 := by by_cases hk : cos (2 * π * k / N) = 1 · -- cos δ = 1 means δ ∈ 2πℤ, so sin δ = 0 and each sin(δ·z) = sin(2πnz) = 0 -- More precisely: cos(δ) = 1 and sin²(δ) = 1 - cos²(δ) = 0 have hsin_δ : sin (2 * π * k / N) = 0 := by nlinarith [sin_sq_add_cos_sq (2 * π * k / N)] -- Each term: sin(2πk·z.val/N) = sin(z.val · δ) -- Since cos δ = 1 and sin δ = 0, by induction sin(nδ) = 0 for all n -- Use: sin(nδ) = sin((n-1)δ + δ) = sin((n-1)δ)cosδ + cos((n-1)δ)sinδ -- = sin((n-1)δ) · 1 + cos((n-1)δ) · 0 = sin((n-1)δ) -- So sin(nδ) = sin(0) = 0 by induction have hzero : ∀ z : ZMod N, sin (2 * π * k * (z.val : ℝ) / N) = 0 := by intro z have : 2 * π * (k : ℝ) * (z.val : ℝ) / (N : ℝ) = (z.val : ℝ) * (2 * π * k / N) := by ring rw [this] induction z.val with | zero => simp | succ n ih => rw [show ((n + 1 : ℕ) : ℝ) * (2 * π * ↑k / ↑N) = (n : ℝ) * (2 * π * ↑k / ↑N) + 2 * π * ↑k / ↑N from by push_cast; ring] rw [sin_add, hk, hsin_δ]; simp [ih] simp_rw [hzero, Finset.sum_const_zero] · -- cos δ ≠ 1: use the same 2×2 system as sum_cos_zmod_eq_zero set δ := 2 * π * (k : ℝ) / (N : ℝ) with hδ_def set S_c := ∑ z : ZMod N, cos (2 * π * k * (z.val : ℝ) / N) have hSc : S_c = 0 := sum_cos_zmod_eq_zero N k hk set S_s := ∑ z : ZMod N, sin (2 * π * k * (z.val : ℝ) / N) -- From the shift identity: (1-cosδ) S_s = sinδ · S_c = 0 have h_shift_s : S_s = sin δ * S_c + cos δ * S_s := by simp only [S_c, S_s] rw [Finset.mul_sum, Finset.mul_sum, ← Finset.sum_add_distrib] apply (Fintype.sum_equiv (Equiv.addRight (1 : ZMod N)) _ _ _).symm intro z show sin δ * cos (2 * π * ↑k * (z.val : ℝ) / ↑N) + cos δ * sin (2 * π * ↑k * (z.val : ℝ) / ↑N) = sin (2 * π * ↑k * ((z + 1 : ZMod N).val : ℝ) / ↑N) rw [sin_zmod_succ N k, show 2 * π * ↑k * ((z.val : ℝ) + 1) / ↑N = 2 * π * ↑k * (z.val : ℝ) / ↑N + δ from by simp only [δ]; ring, sin_add]; ring have eq2 : (1 - cos δ) * S_s = sin δ * S_c := by linarith [h_shift_s] rw [hSc, mul_zero] at eq2 have hne : 1 - cos δ ≠ 0 := sub_ne_zero.mpr (Ne.symm hk) exact (mul_eq_zero.mp eq2).resolve_left hne /-- Sum of cos over ZMod N when N divides k: equals N. -/ theorem sum_cos_zmod_eq_N (N : ℕ) [NeZero N] (k : ℕ) (hk : (N : ℤ) ∣ (k : ℤ)) : ∑ z : ZMod N, cos (2 * π * k * (z.val : ℝ) / N) = N := by obtain ⟨q, hq⟩ := hk have hN_ne : (N : ℝ) ≠ 0 := Nat.cast_ne_zero.mpr (NeZero.ne N) have : ∀ z : ZMod N, cos (2 * π * k * (z.val : ℝ) / N) = 1 := by intro z have hkR : (k : ℝ) = (N : ℝ) * (q : ℝ) := by exact_mod_cast hq rw [hkR, show 2 * π * ((N : ℝ) * (q : ℝ)) * (z.val : ℝ) / (N : ℝ) = ↑((z.val : ℤ) * q) * (2 * π) from by push_cast; field_simp] exact cos_int_mul_two_pi _ simp_rw [this, Finset.sum_const, Finset.card_univ, ZMod.card N, nsmul_eq_mul, mul_one] /-! ## Lattice Fourier basis norm squared -/ /-- The squared norm (sum of squares) of the m-th lattice Fourier basis function. -/ def latticeFourierNormSq (N m : ℕ) [NeZero N] : ℝ := ∑ z : ZMod N, latticeFourierBasisFun N m z ^ 2 /-- The constant mode has norm squared 1. -/ theorem latticeFourierNormSq_zero (N : ℕ) [NeZero N] : latticeFourierNormSq N 0 = 1 := by simp only [latticeFourierNormSq, latticeFourierBasisFun] have hN_pos : (0 : ℝ) < N := Nat.cast_pos.mpr (NeZero.pos N) simp_rw [div_pow, one_pow, sq_sqrt (Nat.cast_nonneg' N)] rw [Finset.sum_const, Finset.card_univ, ZMod.card N, nsmul_eq_mul] field_simp /-- For non-Nyquist modes m ≥ 1 with 2·fourierFreq(m) ≠ N, the norm squared is 1. -/ theorem latticeFourierNormSq_eq_one (N : ℕ) [NeZero N] (m : ℕ) (hm : 0 < m) (hm_lt : m < N) (hNotNyquist : 2 * SmoothMap_Circle.fourierFreq m ≠ N) : latticeFourierNormSq N m = 1 := by cases m with | zero => omega | succ n => simp only [latticeFourierNormSq, latticeFourierBasisFun] set k := n / 2 + 1 with hk_def have hN_pos : (0 : ℝ) < N := Nat.cast_pos.mpr (NeZero.pos N) have hN_ne : (N : ℝ) ≠ 0 := ne_of_gt hN_pos split · -- cosine mode: normSq = (2/N) Σ cos²(2πkz/N) = (2/N)(N/2) = 1 simp_rw [mul_pow, sq_sqrt (by positivity : (0:ℝ) ≤ 2 / N)] -- Reduce cos² to double-angle form and pull constant out of sum simp_rw [show ∀ z : ZMod N, 2 / ↑N * cos (2 * π * ↑k * (↑z.val : ℝ) / ↑N) ^ 2 = (1 / ↑N) * (1 + cos (2 * (2 * π * ↑k * (↑z.val : ℝ) / ↑N))) from fun z => by rw [cos_sq]; ring] rw [← Finset.mul_sum, Finset.sum_add_distrib, Finset.sum_const, Finset.card_univ, ZMod.card N, nsmul_eq_mul] -- Σ cos(4πkz/N) = 0 since N ∤ 2k simp_rw [show ∀ z : ZMod N, cos (2 * (2 * π * ↑k * (↑z.val : ℝ) / ↑N)) = cos (2 * π * ↑(2 * k) * (↑z.val : ℝ) / ↑N) from fun z => by congr 1; push_cast; ring] have hcos_sum : ∑ z : ZMod N, cos (2 * π * ↑(2 * k) * (↑z.val : ℝ) / ↑N) = 0 := by apply sum_cos_zmod_eq_zero intro heq apply hNotNyquist -- From heq: cos(2π(2k)/N) = 1 with 0 < 2k ≤ N forces 2k = N have hk_pos : 0 < k := by omega have h2k_le_N : 2 * k ≤ N := by omega rcases Nat.eq_or_lt_of_le h2k_le_N with h2k_eq | h2k_lt · -- 2k = N: directly gives fourierFreq(n+1) = k = N/2 simp only [SmoothMap_Circle.fourierFreq]; omega · -- 2k < N: cos = 1 is impossible since 0 < 2π(2k)/N < 2π exfalso have hx_pos : (0 : ℝ) < 2 * π * ↑(2 * k) / ↑N := by positivity have hx_lt : 2 * π * ↑(2 * k) / ↑N < 2 * π := by rw [div_lt_iff₀ hN_pos] nlinarith [show (↑(2 * k) : ℝ) < ↑N from Nat.cast_lt.mpr h2k_lt, pi_pos] have := (cos_eq_one_iff_of_lt_of_lt (by linarith) hx_lt).mp heq linarith rw [hcos_sum]; simp · -- sine mode: normSq = (2/N) Σ sin²(2πkz/N) = (2/N)(N/2) = 1 simp_rw [mul_pow, sq_sqrt (by positivity : (0:ℝ) ≤ 2 / N)] simp_rw [show ∀ z : ZMod N, 2 / ↑N * sin (2 * π * ↑k * (↑z.val : ℝ) / ↑N) ^ 2 = (1 / ↑N) * (1 - cos (2 * (2 * π * ↑k * (↑z.val : ℝ) / ↑N))) from fun z => by rw [sin_sq, cos_sq]; ring] rw [← Finset.mul_sum, Finset.sum_sub_distrib, Finset.sum_const, Finset.card_univ, ZMod.card N, nsmul_eq_mul] simp_rw [show ∀ z : ZMod N, cos (2 * (2 * π * ↑k * (↑z.val : ℝ) / ↑N)) = cos (2 * π * ↑(2 * k) * (↑z.val : ℝ) / ↑N) from fun z => by congr 1; push_cast; ring] have hcos_sum : ∑ z : ZMod N, cos (2 * π * ↑(2 * k) * (↑z.val : ℝ) / ↑N) = 0 := by apply sum_cos_zmod_eq_zero intro heq apply hNotNyquist have hk_pos : 0 < k := by omega have h2k_le_N : 2 * k ≤ N := by omega rcases Nat.eq_or_lt_of_le h2k_le_N with h2k_eq | h2k_lt · simp only [SmoothMap_Circle.fourierFreq]; omega · exfalso have hx_pos : (0 : ℝ) < 2 * π * ↑(2 * k) / ↑N := by positivity have hx_lt : 2 * π * ↑(2 * k) / ↑N < 2 * π := by rw [div_lt_iff₀ hN_pos] nlinarith [show (↑(2 * k) : ℝ) < ↑N from Nat.cast_lt.mpr h2k_lt, pi_pos] have := (cos_eq_one_iff_of_lt_of_lt (by linarith) hx_lt).mp heq linarith rw [hcos_sum]; simp /-! ## Lattice Fourier basis orthogonality -/ /-- Product-to-sum: cos a * cos b = (cos(a-b) + cos(a+b))/2. -/ private theorem cos_mul_cos (a b : ℝ) : cos a * cos b = (cos (a - b) + cos (a + b)) / 2 := by linarith [cos_add a b, cos_sub a b] /-- Product-to-sum: sin a * sin b = (cos(a-b) - cos(a+b))/2. -/ private theorem sin_mul_sin (a b : ℝ) : sin a * sin b = (cos (a - b) - cos (a + b)) / 2 := by linarith [cos_add a b, cos_sub a b] /-- Product-to-sum: cos a * sin b = (sin(a+b) - sin(a-b))/2. -/ private theorem cos_mul_sin (a b : ℝ) : cos a * sin b = (sin (a + b) - sin (a - b)) / 2 := by linarith [sin_add a b, sin_sub a b] /-- For 0 < m < N, cos(2πm/N) ≠ 1 (since 0 < 2πm/N < 2π). -/ theorem cos_ne_one_of_pos_lt (N : ℕ) [NeZero N] (m : ℕ) (hm_pos : 0 < m) (hm_lt : m < N) : cos (2 * π * ↑m / ↑N) ≠ 1 := by have hN_pos : (0 : ℝ) < N := Nat.cast_pos.mpr (NeZero.pos N) have hx_pos : (0 : ℝ) < 2 * π * ↑m / ↑N := by positivity have hx_lt : 2 * π * ↑m / ↑N < 2 * π := by rw [div_lt_iff₀ hN_pos] nlinarith [show (↑m : ℝ) < ↑N from Nat.cast_lt.mpr hm_lt, pi_pos] intro heq exact absurd ((cos_eq_one_iff_of_lt_of_lt (by linarith) hx_lt).mp heq) (by linarith) /-- Corollary: sum of cos vanishes for frequencies in (0, N). -/ theorem sum_cos_zmod_eq_zero_of_pos_lt (N : ℕ) [NeZero N] (m : ℕ) (hm_pos : 0 < m) (hm_lt : m < N) : ∑ z : ZMod N, cos (2 * π * ↑m * (z.val : ℝ) / ↑N) = 0 := sum_cos_zmod_eq_zero N m (cos_ne_one_of_pos_lt N m hm_pos hm_lt) /-- Sum of sin over ZMod N vanishes for any integer frequency. -/ theorem sum_sin_zmod_int_eq_zero (N : ℕ) [NeZero N] (m : ℤ) : ∑ z : ZMod N, sin (2 * π * ↑m * (z.val : ℝ) / ↑N) = 0 := by rcases le_or_gt 0 m with hm | hm · lift m to ℕ using hm exact_mod_cast sum_sin_zmod_eq_zero N m · -- m < 0: use sin(-x) = -sin(x) and reduce to (-m).toNat : ℕ set n := (-m).toNat have hmn : m = -(n : ℤ) := by simp [n, Int.toNat_of_nonneg (by omega : (0 : ℤ) ≤ -m)] simp_rw [show (m : ℝ) = -(n : ℝ) from by exact_mod_cast hmn, show ∀ z : ZMod N, 2 * π * (-(n : ℝ)) * (z.val : ℝ) / ↑N = -(2 * π * ↑n * (z.val : ℝ) / ↑N) from fun z => by ring, sin_neg, Finset.sum_neg_distrib, neg_eq_zero] exact sum_sin_zmod_eq_zero N n /-- Product of cos and sin over ZMod N sums to zero (any frequencies). -/ theorem sum_cos_mul_sin_zmod_eq_zero (N : ℕ) [NeZero N] (k l : ℕ) : ∑ z : ZMod N, cos (2 * π * ↑k * (z.val : ℝ) / ↑N) * sin (2 * π * ↑l * (z.val : ℝ) / ↑N) = 0 := by simp_rw [cos_mul_sin] rw [← Finset.sum_div, Finset.sum_sub_distrib] simp_rw [show ∀ z : ZMod N, 2 * π * ↑k * (z.val : ℝ) / ↑N + 2 * π * ↑l * (z.val : ℝ) / ↑N = 2 * π * ↑((k : ℤ) + ↑l) * (z.val : ℝ) / ↑N from fun z => by push_cast; ring] simp_rw [show ∀ z : ZMod N, 2 * π * ↑k * (z.val : ℝ) / ↑N - 2 * π * ↑l * (z.val : ℝ) / ↑N = 2 * π * ↑((k : ℤ) - ↑l) * (z.val : ℝ) / ↑N from fun z => by push_cast; ring] rw [sum_sin_zmod_int_eq_zero N ((k : ℤ) + l), sum_sin_zmod_int_eq_zero N ((k : ℤ) - l)] simp /-- Product of cos functions over ZMod N sums to zero when frequencies are distinct and both in (0, N/2]. -/ theorem sum_cos_mul_cos_zmod_eq_zero (N : ℕ) [NeZero N] (k l : ℕ) (hk_pos : 0 < k) (hl_pos : 0 < l) (hkl : k ≠ l) (hkl_sum : k + l < N) : ∑ z : ZMod N, cos (2 * π * ↑k * (z.val : ℝ) / ↑N) * cos (2 * π * ↑l * (z.val : ℝ) / ↑N) = 0 := by simp_rw [cos_mul_cos] rw [← Finset.sum_div, Finset.sum_add_distrib] -- Sum frequency: k + l, with 0 < k+l < N simp_rw [show ∀ z : ZMod N, 2 * π * ↑k * (z.val : ℝ) / ↑N + 2 * π * ↑l * (z.val : ℝ) / ↑N = 2 * π * ↑(k + l) * (z.val : ℝ) / ↑N from fun z => by push_cast; ring] -- Difference frequency: |k - l|, with 0 < |k-l| < N rcases le_or_gt l k with hlk | hkl' · -- k ≥ l: difference = k - l simp_rw [show ∀ z : ZMod N, 2 * π * ↑k * (z.val : ℝ) / ↑N - 2 * π * ↑l * (z.val : ℝ) / ↑N = 2 * π * ↑(k - l) * (z.val : ℝ) / ↑N from fun z => by rw [Nat.cast_sub hlk]; ring] rw [sum_cos_zmod_eq_zero_of_pos_lt N (k - l) (Nat.sub_pos_of_lt (lt_of_le_of_ne hlk (Ne.symm hkl))) (by omega), sum_cos_zmod_eq_zero_of_pos_lt N (k + l) (by omega) hkl_sum] simp · -- k < l: difference is negative, use cos(-x) = cos(x) simp_rw [show ∀ z : ZMod N, 2 * π * ↑k * (z.val : ℝ) / ↑N - 2 * π * ↑l * (z.val : ℝ) / ↑N = -(2 * π * ↑(l - k) * (z.val : ℝ) / ↑N) from fun z => by rw [Nat.cast_sub hkl'.le]; ring, cos_neg] rw [sum_cos_zmod_eq_zero_of_pos_lt N (l - k) (by omega) (by omega), sum_cos_zmod_eq_zero_of_pos_lt N (k + l) (by omega) hkl_sum] simp /-- Product of sin functions over ZMod N sums to zero when frequencies are distinct and both in (0, N/2]. -/ theorem sum_sin_mul_sin_zmod_eq_zero (N : ℕ) [NeZero N] (k l : ℕ) (hk_pos : 0 < k) (hl_pos : 0 < l) (hkl : k ≠ l) (hkl_sum : k + l < N) : ∑ z : ZMod N, sin (2 * π * ↑k * (z.val : ℝ) / ↑N) * sin (2 * π * ↑l * (z.val : ℝ) / ↑N) = 0 := by simp_rw [sin_mul_sin] rw [← Finset.sum_div, Finset.sum_sub_distrib] simp_rw [show ∀ z : ZMod N, 2 * π * ↑k * (z.val : ℝ) / ↑N + 2 * π * ↑l * (z.val : ℝ) / ↑N = 2 * π * ↑(k + l) * (z.val : ℝ) / ↑N from fun z => by push_cast; ring] rcases le_or_gt l k with hlk | hkl' · simp_rw [show ∀ z : ZMod N, 2 * π * ↑k * (z.val : ℝ) / ↑N - 2 * π * ↑l * (z.val : ℝ) / ↑N = 2 * π * ↑(k - l) * (z.val : ℝ) / ↑N from fun z => by rw [Nat.cast_sub hlk]; ring] rw [sum_cos_zmod_eq_zero_of_pos_lt N (k - l) (Nat.sub_pos_of_lt (lt_of_le_of_ne hlk (Ne.symm hkl))) (by omega), sum_cos_zmod_eq_zero_of_pos_lt N (k + l) (by omega) hkl_sum] simp · simp_rw [show ∀ z : ZMod N, 2 * π * ↑k * (z.val : ℝ) / ↑N - 2 * π * ↑l * (z.val : ℝ) / ↑N = -(2 * π * ↑(l - k) * (z.val : ℝ) / ↑N) from fun z => by rw [Nat.cast_sub hkl'.le]; ring, cos_neg] rw [sum_cos_zmod_eq_zero_of_pos_lt N (l - k) (by omega) (by omega), sum_cos_zmod_eq_zero_of_pos_lt N (k + l) (by omega) hkl_sum] simp /-! ## Eigenvector property of 1D lattice Laplacian The key structural result: cos(2πkz/N) and sin(2πkz/N) are eigenvectors of the negative 1D lattice Laplacian `(-Δ)` with eigenvalue `(4/a²)sin²(πk/N)`. This uses: cos(θ+δ) + cos(θ-δ) = 2cos(θ)cos(δ), so (-Δ cos_k)(z) = a⁻²(2cos(θ) - cos(θ+δ) - cos(θ-δ)) = 2a⁻²(1 - cos(δ))cos(θ) = (4/a²)sin²(πk/N)·cos(θ). -/ /-- Sum of cos at shifted argument: cos(k·(z+1)) + cos(k·(z-1)) = 2cos(kz)cos(k). -/ private theorem cos_shift_sum (N : ℕ) [NeZero N] (k : ℕ) (z : ZMod N) : cos (2 * π * ↑k * ((z + 1 : ZMod N).val : ℝ) / ↑N) + cos (2 * π * ↑k * ((z - 1 : ZMod N).val : ℝ) / ↑N) = 2 * cos (2 * π * ↑k * (z.val : ℝ) / ↑N) * cos (2 * π * ↑k / ↑N) := by rw [cos_zmod_succ, cos_zmod_pred] set θ := 2 * π * ↑k * (z.val : ℝ) / ↑N set δ := 2 * π * ↑k / ↑N rw [show 2 * π * ↑k * ((z.val : ℝ) + 1) / ↑N = θ + δ from by simp only [θ, δ]; ring, show 2 * π * ↑k * ((z.val : ℝ) - 1) / ↑N = θ - δ from by simp only [θ, δ]; ring] linarith [cos_add θ δ, cos_sub θ δ] /-- The negative 1D Laplacian acts on cos functions as eigenvalue multiplication. For `v(x) = cos(2πk·(x 0).val/N)`, we have `(-Δ) v = λ_k · v` where `λ_k = (2/a²)(1 - cos(2πk/N))`. -/ theorem negLaplacian1d_cos_eigenvalue (N : ℕ) [NeZero N] (a : ℝ) (_ha : a ≠ 0) (k : ℕ) : let v : FinLatticeField 1 N := fun x => cos (2 * π * ↑k * ((x 0).val : ℝ) / ↑N) ∀ x : FinLatticeSites 1 N, (negLaplacianMatrix 1 N a).mulVec v x = (2 * a⁻¹ ^ 2 * (1 - cos (2 * π * ↑k / ↑N))) * v x := by intro v x -- Bridge mulVec to operator action rw [show (negLaplacianMatrix 1 N a).mulVec v x = (massOperator 1 N a 0 v) x from by simp only [negLaplacianMatrix] rw [← massOperator_eq_matrix_mulVec]] -- Expand massOperator = -finiteLaplacian + 0²·id simp only [massOperator, add_apply, neg_apply, smul_apply, ContinuousLinearMap.id_apply, Pi.add_apply, Pi.neg_apply, Pi.smul_apply, smul_eq_mul] -- Unfold finiteLaplacian simp only [finiteLaplacian, ContinuousLinearMap.coe_mk', finiteLaplacianLM, LinearMap.coe_mk, AddHom.coe_mk, finiteLaplacianFun] -- For d=1, the sum over Fin 1 is a single term simp only [Fin.sum_univ_one] -- The shifted function arguments simplify via coordinate 0 have hv_succ : v (fun j : Fin 1 => if j = 0 then x j + 1 else x j) = cos (2 * π * ↑k * ((x 0 + 1 : ZMod N).val : ℝ) / ↑N) := by show cos (2 * π * ↑k * (((fun j : Fin 1 => if j = 0 then x j + 1 else x j) 0).val : ℝ) / ↑N) = _ simp have hv_pred : v (fun j : Fin 1 => if j = 0 then x j - 1 else x j) = cos (2 * π * ↑k * ((x 0 - 1 : ZMod N).val : ℝ) / ↑N) := by show cos (2 * π * ↑k * (((fun j : Fin 1 => if j = 0 then x j - 1 else x j) 0).val : ℝ) / ↑N) = _ simp rw [hv_succ, hv_pred, cos_shift_sum] ring /-- Sum of sin at shifted arguments: sin(k·(z+1)) + sin(k·(z-1)) = 2sin(kz)cos(k). -/ private theorem sin_shift_sum (N : ℕ) [NeZero N] (k : ℕ) (z : ZMod N) : sin (2 * π * ↑k * ((z + 1 : ZMod N).val : ℝ) / ↑N) + sin (2 * π * ↑k * ((z - 1 : ZMod N).val : ℝ) / ↑N) = 2 * sin (2 * π * ↑k * (z.val : ℝ) / ↑N) * cos (2 * π * ↑k / ↑N) := by rw [sin_zmod_succ, sin_zmod_pred] set θ := 2 * π * ↑k * (z.val : ℝ) / ↑N set δ := 2 * π * ↑k / ↑N rw [show 2 * π * ↑k * ((z.val : ℝ) + 1) / ↑N = θ + δ from by simp only [θ, δ]; ring, show 2 * π * ↑k * ((z.val : ℝ) - 1) / ↑N = θ - δ from by simp only [θ, δ]; ring] linarith [sin_add θ δ, sin_sub θ δ] /-- The negative 1D Laplacian acts on sin functions as eigenvalue multiplication. -/ theorem negLaplacian1d_sin_eigenvalue (N : ℕ) [NeZero N] (a : ℝ) (_ha : a ≠ 0) (k : ℕ) : let v : FinLatticeField 1 N := fun x => sin (2 * π * ↑k * ((x 0).val : ℝ) / ↑N) ∀ x : FinLatticeSites 1 N, (negLaplacianMatrix 1 N a).mulVec v x = (2 * a⁻¹ ^ 2 * (1 - cos (2 * π * ↑k / ↑N))) * v x := by intro v x rw [show (negLaplacianMatrix 1 N a).mulVec v x = (massOperator 1 N a 0 v) x from by simp only [negLaplacianMatrix] rw [← massOperator_eq_matrix_mulVec]] simp only [massOperator, add_apply, neg_apply, smul_apply, ContinuousLinearMap.id_apply, Pi.add_apply, Pi.neg_apply, Pi.smul_apply, smul_eq_mul] simp only [finiteLaplacian, ContinuousLinearMap.coe_mk', finiteLaplacianLM, LinearMap.coe_mk, AddHom.coe_mk, finiteLaplacianFun] simp only [Fin.sum_univ_one] have hv_succ : v (fun j : Fin 1 => if j = 0 then x j + 1 else x j) = sin (2 * π * ↑k * ((x 0 + 1 : ZMod N).val : ℝ) / ↑N) := by show sin (2 * π * ↑k * (((fun j : Fin 1 => if j = 0 then x j + 1 else x j) 0).val : ℝ) / ↑N) = _ simp have hv_pred : v (fun j : Fin 1 => if j = 0 then x j - 1 else x j) = sin (2 * π * ↑k * ((x 0 - 1 : ZMod N).val : ℝ) / ↑N) := by show sin (2 * π * ↑k * (((fun j : Fin 1 => if j = 0 then x j - 1 else x j) 0).val : ℝ) / ↑N) = _ simp rw [hv_succ, hv_pred, sin_shift_sum] ring /-! ## Full pairwise orthogonality of the DFT basis The lattice Fourier basis functions `latticeFourierBasisFun N m` for `m < N` are pairwise orthogonal over `ZMod N`. This uses the product-to-sum identities and the trig sum vanishing theorems. -/ /-- **Pairwise orthogonality of the lattice Fourier basis.** For distinct modes `m ≠ n` with `m, n < N`: `∑ z, φ_m(z) · φ_n(z) = 0` -/ theorem latticeFourierBasisFun_orthogonal (N : ℕ) [NeZero N] (m n : ℕ) (hm : m < N) (hn : n < N) (hmn : m ≠ n) : ∑ z : ZMod N, latticeFourierBasisFun N m z * latticeFourierBasisFun N n z = 0 := by cases m with | zero => cases n with | zero => exact absurd rfl hmn | succ n => simp only [latticeFourierBasisFun] set k := n / 2 + 1 have hk_pos : 0 < k := by omega have hk_lt : k < N := by omega split · -- 0 vs cos(k) simp_rw [show ∀ z : ZMod N, (1 / Real.sqrt ↑N) * (Real.sqrt (2 / ↑N) * cos (2 * π * ↑k * ↑z.val / ↑N)) = (Real.sqrt (2 / ↑N) / Real.sqrt ↑N) * cos (2 * π * ↑k * ↑z.val / ↑N) from fun z => by ring] rw [← Finset.mul_sum, sum_cos_zmod_eq_zero_of_pos_lt N k hk_pos hk_lt, mul_zero] · -- 0 vs sin(k) simp_rw [show ∀ z : ZMod N, (1 / Real.sqrt ↑N) * (Real.sqrt (2 / ↑N) * sin (2 * π * ↑k * ↑z.val / ↑N)) = (Real.sqrt (2 / ↑N) / Real.sqrt ↑N) * sin (2 * π * ↑k * ↑z.val / ↑N) from fun z => by ring] rw [← Finset.mul_sum, sum_sin_zmod_eq_zero N k, mul_zero] | succ m => cases n with | zero => -- m+1 vs 0: factor out constants, use trig sum vanishing simp only [latticeFourierBasisFun] set k := m / 2 + 1 have hk_pos : 0 < k := by omega have hk_lt : k < N := by omega split · -- cos(k) vs constant simp_rw [show ∀ z : ZMod N, (Real.sqrt (2 / ↑N) * cos (2 * π * ↑k * ↑z.val / ↑N)) * (1 / Real.sqrt ↑N) = (Real.sqrt (2 / ↑N) / Real.sqrt ↑N) * cos (2 * π * ↑k * ↑z.val / ↑N) from fun z => by ring] rw [← Finset.mul_sum, sum_cos_zmod_eq_zero_of_pos_lt N k hk_pos hk_lt, mul_zero] · -- sin(k) vs constant simp_rw [show ∀ z : ZMod N, (Real.sqrt (2 / ↑N) * sin (2 * π * ↑k * ↑z.val / ↑N)) * (1 / Real.sqrt ↑N) = (Real.sqrt (2 / ↑N) / Real.sqrt ↑N) * sin (2 * π * ↑k * ↑z.val / ↑N) from fun z => by ring] rw [← Finset.mul_sum, sum_sin_zmod_eq_zero N k, mul_zero] | succ n => -- Both ≥ 1: case split on mode types simp only [latticeFourierBasisFun] set km := m / 2 + 1 with hkm_def set kn := n / 2 + 1 with hkn_def have hkm_pos : 0 < km := by omega have hkn_pos : 0 < kn := by omega have hkm_lt : km ≤ N / 2 := by omega have hkn_lt : kn ≤ N / 2 := by omega -- Factor √(2/N) · a · (√(2/N) · b) = (2/N) · (a · b) have hsq : ∀ (a b : ℝ), Real.sqrt (2 / ↑N) * a * (Real.sqrt (2 / ↑N) * b) = (2 / ↑N) * (a * b) := by intro a b rw [mul_mul_mul_comm, Real.mul_self_sqrt (by positivity : (0 : ℝ) ≤ 2 / ↑N)] split <;> split · -- cos(km) vs cos(kn) have hkm_ne_kn : km ≠ kn := by intro h; apply hmn; omega have hkm_kn_lt : km + kn < N := by omega simp_rw [hsq] rw [← Finset.mul_sum, sum_cos_mul_cos_zmod_eq_zero N km kn hkm_pos hkn_pos hkm_ne_kn hkm_kn_lt, mul_zero] · -- cos(km) vs sin(kn) simp_rw [hsq] rw [← Finset.mul_sum, sum_cos_mul_sin_zmod_eq_zero N km kn, mul_zero] · -- sin(km) vs cos(kn) simp_rw [hsq, show ∀ z : ZMod N, sin (2 * π * ↑km * ↑z.val / ↑N) * cos (2 * π * ↑kn * ↑z.val / ↑N) = cos (2 * π * ↑kn * ↑z.val / ↑N) * sin (2 * π * ↑km * ↑z.val / ↑N) from fun z => mul_comm _ _] rw [← Finset.mul_sum, sum_cos_mul_sin_zmod_eq_zero N kn km, mul_zero] · -- sin(km) vs sin(kn) have hkm_ne_kn : km ≠ kn := by intro h; apply hmn; omega have hkm_kn_lt : km + kn < N := by omega simp_rw [hsq] rw [← Finset.mul_sum, sum_sin_mul_sin_zmod_eq_zero N km kn hkm_pos hkn_pos hkm_ne_kn hkm_kn_lt, mul_zero] /-- The lattice Fourier norm squared is positive for all modes m < N. -/ theorem latticeFourierNormSq_pos (N : ℕ) [NeZero N] (m : ℕ) (hm : m < N) : 0 < latticeFourierNormSq N m := by cases m with | zero => rw [latticeFourierNormSq_zero]; exact one_pos | succ n => by_cases hNyq : 2 * SmoothMap_Circle.fourierFreq (n + 1) = N · -- Nyquist: n must be even (n odd → fourierFreq = (n+1)/2, so N = n+1, contradicting hm) have hn_even : n % 2 = 0 := by by_contra h; push Not at h simp [SmoothMap_Circle.fourierFreq] at hNyq; omega -- φ(0) = √(2/N) * cos(0) = √(2/N) > 0, so φ(0)² > 0 have h0 : 0 < latticeFourierBasisFun N (n + 1) (0 : ZMod N) ^ 2 := by simp only [latticeFourierBasisFun, hn_even, ↓reduceIte] simp [ZMod.val_zero, mul_zero, zero_div, cos_zero, mul_one] exact pow_pos (div_pos (Real.sqrt_pos_of_pos (by norm_num : (0:ℝ) < 2)) (Real.sqrt_pos_of_pos (Nat.cast_pos.mpr (NeZero.pos N)))) 2 exact lt_of_lt_of_le h0 (Finset.single_le_sum (fun z _ => sq_nonneg _) (Finset.mem_univ (0 : ZMod N))) · rw [latticeFourierNormSq_eq_one N (n + 1) (by omega) hm hNyq]; exact one_pos /-- At the Nyquist mode, the norm squared is 2. -/ private theorem latticeFourierNormSq_nyquist (N : ℕ) [NeZero N] (n : ℕ) (_hn_lt : n + 1 < N) (hn_even : n % 2 = 0) (hNyq : 2 * SmoothMap_Circle.fourierFreq (n + 1) = N) : latticeFourierNormSq N (n + 1) = 2 := by have hN_pos : (0 : ℝ) < N := Nat.cast_pos.mpr (NeZero.pos N) have h2k : (2 : ℝ) * ↑(n / 2 + 1) = ↑N := by exact_mod_cast hNyq simp only [latticeFourierNormSq, latticeFourierBasisFun, hn_even, ↓reduceIte] simp_rw [mul_pow, sq_sqrt (by positivity : (0:ℝ) ≤ 2 / N)] -- cos(2π(n/2+1)·z/N)² = cos(π·z)² = 1 for all z (since 2(n/2+1) = N) have hcos_sq_one : ∀ z : ZMod N, cos (2 * π * ↑(n / 2 + 1) * ↑z.val / ↑N) ^ 2 = 1 := by intro z have harg : 2 * π * ↑(n / 2 + 1) * ↑z.val / ↑N = ↑z.val * π := by rw [show 2 * π * ↑(n / 2 + 1) * ↑z.val / ↑N = ↑z.val * π * (2 * ↑(n / 2 + 1) / ↑N) from by ring, h2k, div_self (ne_of_gt hN_pos), mul_one] rw [harg] nlinarith [Real.sin_nat_mul_pi z.val, Real.sin_sq_add_cos_sq (↑z.val * π)] simp_rw [hcos_sq_one, mul_one, Finset.sum_const, Finset.card_univ, ZMod.card, nsmul_eq_mul] rw [mul_div_cancel₀ _ (ne_of_gt hN_pos)] /-- The lattice Fourier norm squared is at least 1 for all modes m < N. -/ theorem latticeFourierNormSq_ge_one (N : ℕ) [NeZero N] (m : ℕ) (hm : m < N) : 1 ≤ latticeFourierNormSq N m := by cases m with | zero => rw [latticeFourierNormSq_zero] | succ n => by_cases hNyq : 2 * SmoothMap_Circle.fourierFreq (n + 1) = N · have hn_even : n % 2 = 0 := by by_contra h; push Not at h simp [SmoothMap_Circle.fourierFreq] at hNyq; omega rw [latticeFourierNormSq_nyquist N n hm hn_even hNyq]; norm_num · rw [latticeFourierNormSq_eq_one N (n + 1) (by omega) hm hNyq] /-- For fixed m, latticeFourierNormSq (N+1) m = 1 for all sufficiently large N. -/ theorem latticeFourierNormSq_eventually_one (m : ℕ) : ∀ᶠ N in Filter.atTop, latticeFourierNormSq (N + 1) m = 1 := by cases m with | zero => apply Filter.Eventually.of_forall; intro N exact latticeFourierNormSq_zero (N + 1) | succ n => -- For fixed n+1, the Nyquist condition 2*fourierFreq(n+1) = N+1 holds for at most one N. -- For N ≥ max(n, 2*fourierFreq(n+1)), both n+1 < N+1 and ¬Nyquist hold. set k := SmoothMap_Circle.fourierFreq (n + 1) apply Filter.eventually_atTop.mpr refine ⟨n + 1 + 2 * k, fun N hN => ?_⟩ have hm_lt : n + 1 < N + 1 := by omega have hNotNyq : 2 * k ≠ N + 1 := by omega exact latticeFourierNormSq_eq_one (N + 1) (n + 1) (by omega) hm_lt hNotNyq /-- The DFT basis functions are linearly independent over ℝ. -/ theorem latticeFourierBasisFun_linearIndependent (N : ℕ) [NeZero N] : LinearIndependent ℝ (fun m : Fin N => latticeFourierBasisFun N m.val) := by rw [linearIndependent_iff'] intro s c hsum m hms -- hsum : ∑ i ∈ s, c i • φ_i = 0 (as function ZMod N → ℝ) -- Take "dot product" with φ_m to isolate c_m have hpw : ∀ z : ZMod N, ∑ i ∈ s, c i * latticeFourierBasisFun N i.val z = 0 := by intro z; have := congr_fun hsum z; simpa [Finset.sum_apply, smul_eq_mul] -- Multiply each equation by φ_m(z) and sum over z have h_dot : ∑ z : ZMod N, ∑ i ∈ s, c i * (latticeFourierBasisFun N m.val z * latticeFourierBasisFun N i.val z) = 0 := by simp_rw [show ∀ (i : Fin N) (z : ZMod N), c i * (latticeFourierBasisFun N m.val z * latticeFourierBasisFun N i.val z) = latticeFourierBasisFun N m.val z * (c i * latticeFourierBasisFun N i.val z) from fun i z => by ring, ← Finset.mul_sum] simp [hpw] -- Swap sums: ∑_i c_i * (∑_z φ_m(z) * φ_i(z)) rw [Finset.sum_comm] at h_dot simp_rw [← Finset.mul_sum] at h_dot -- By orthogonality, ∑_z φ_m(z) * φ_i(z) = 0 for i ≠ m, so only i = m survives have h_ortho : ∀ i ∈ s, i ≠ m → ∑ z : ZMod N, latticeFourierBasisFun N m.val z * latticeFourierBasisFun N i.val z = 0 := by intro ⟨i, hi⟩ _ hne exact latticeFourierBasisFun_orthogonal N m.val i m.2 hi (fun h => hne (Fin.ext h.symm)) rw [Finset.sum_eq_single m (fun i hi hne => by rw [h_ortho i hi hne, mul_zero]) (fun hm_not => absurd hms hm_not)] at h_dot -- h_dot : c m * normSq(m) = 0 have h_normSq_pos := latticeFourierNormSq_pos N m.val m.2 rw [latticeFourierNormSq] at h_normSq_pos simp_rw [sq] at h_normSq_pos exact (mul_eq_zero.mp h_dot).resolve_right (ne_of_gt h_normSq_pos) /-- The DFT basis functions span all of `ZMod N → ℝ`. -/ theorem latticeFourierBasisFun_span_eq_top (N : ℕ) [NeZero N] : Submodule.span ℝ (Set.range (fun m : Fin N => (fun z : ZMod N => latticeFourierBasisFun N m.val z))) = ⊤ := (latticeFourierBasisFun_linearIndependent N).span_eq_top_of_card_eq_finrank' (by rw [Fintype.card_fin, Module.finrank_pi, ZMod.card]) /-- The DFT basis as a `Module.Basis`. -/ noncomputable def latticeFourierBasis (N : ℕ) [NeZero N] : Module.Basis (Fin N) ℝ (ZMod N → ℝ) := Module.Basis.mk (latticeFourierBasisFun_linearIndependent N) (latticeFourierBasisFun_span_eq_top N).ge /-- The DFT expansion formula: any `f : ZMod N → ℝ` equals its Fourier series with coefficients `(f · φ_m) / normSq(m)`. -/ theorem dft_expansion (N : ℕ) [NeZero N] (f : ZMod N → ℝ) : f = ∑ m : Fin N, ((∑ z : ZMod N, f z * latticeFourierBasisFun N m.val z) / latticeFourierNormSq N m.val) • (fun z => latticeFourierBasisFun N m.val z) := by -- Abbreviate the DFT basis functions set φ : Fin N → (ZMod N → ℝ) := fun m => latticeFourierBasisFun N m.val with hφ_def -- Construct a basis from linear independence + span = ⊤ set B : Module.Basis (Fin N) ℝ (ZMod N → ℝ) := Module.Basis.mk (latticeFourierBasisFun_linearIndependent N) (latticeFourierBasisFun_span_eq_top N).ge have hBφ : ∀ m : Fin N, B m = φ m := fun m => Module.Basis.mk_apply _ _ m -- Define the "error" g = f - expansion set g := f - ∑ m : Fin N, ((∑ z, f z * φ m z) / latticeFourierNormSq N m.val) • φ m -- Step 1: g · φ_j = 0 for every j (by orthogonality) have h_ortho : ∀ j : Fin N, ∑ z : ZMod N, g z * φ j z = 0 := by intro j simp only [g, Pi.sub_apply, sub_mul, Finset.sum_sub_distrib] rw [sub_eq_zero] simp_rw [Finset.sum_apply, Pi.smul_apply, smul_eq_mul, Finset.sum_mul] rw [Finset.sum_comm] simp_rw [show ∀ (m : Fin N) (z : ZMod N), (∑ w, f w * φ m w) / latticeFourierNormSq N m.val * φ m z * φ j z = ((∑ w, f w * φ m w) / latticeFourierNormSq N m.val) * (φ m z * φ j z) from fun m z => by ring] simp_rw [← Finset.mul_sum] rw [Finset.sum_eq_single j] · rw [show ∑ z : ZMod N, φ j z * φ j z = latticeFourierNormSq N j.val from by simp only [latticeFourierNormSq, sq, φ], div_mul_cancel₀] exact ne_of_gt (latticeFourierNormSq_pos N j.val j.2) · intro m _ hmj rw [latticeFourierBasisFun_orthogonal N m.val j.val m.2 j.2 (fun h => hmj (Fin.ext h)), mul_zero] · intro hj; exact absurd (Finset.mem_univ j) hj -- Step 2: B.repr g j = 0 for every j have hcoeff : ∀ j : Fin N, B.repr g j = 0 := by intro j -- Pointwise: g z = ∑ m, (B.repr g m) * φ m z have h_pw : ∀ z : ZMod N, g z = ∑ m : Fin N, B.repr g m * φ m z := by intro z have h := congr_fun (B.sum_repr g) z simp only [Finset.sum_apply, Pi.smul_apply, smul_eq_mul] at h rw [← h]; congr 1; ext m; congr 1; exact congr_fun (hBφ m) _ -- c_j * normSq(j) = g · φ_j = 0 have h_dot : B.repr g j * latticeFourierNormSq N j.val = 0 := by -- Unfold normSq: ∑ φ² = ∑ φ*φ have h_normSq : latticeFourierNormSq N j.val = ∑ z : ZMod N, φ j z * φ j z := by simp only [latticeFourierNormSq, sq, φ] rw [h_normSq, show B.repr g j * ∑ z, φ j z * φ j z = ∑ z : ZMod N, g z * φ j z from ?_, h_ortho j] -- Expand g, swap sums, use orthogonality simp_rw [h_pw, Finset.sum_mul] rw [Finset.sum_comm] simp_rw [show ∀ (m : Fin N) (z : ZMod N), B.repr g m * φ m z * φ j z = B.repr g m * (φ m z * φ j z) from fun m z => by ring] simp_rw [← Finset.mul_sum] rw [Finset.sum_eq_single j (fun m _ hmj => by rw [latticeFourierBasisFun_orthogonal N m.val j.val m.2 j.2 (fun h => hmj (Fin.ext h)), mul_zero]) (fun hj => absurd (Finset.mem_univ j) hj)] exact (mul_eq_zero.mp h_dot).resolve_right (ne_of_gt (latticeFourierNormSq_pos N j.val j.2)) -- Step 3: g = 0 have hg : g = 0 := B.ext_elem (fun j => by rw [hcoeff j, map_zero, Finsupp.zero_apply]) exact sub_eq_zero.mp hg /-- The normalized DFT basis functions are eigenvectors of the 1D negative Laplacian on `FinLatticeSites 1 N`. -/ theorem negLaplacian1d_dft_eigenvector (N : ℕ) [NeZero N] (a : ℝ) (ha : a ≠ 0) (m : ℕ) (_hm : m < N) : (negLaplacianMatrix 1 N a).mulVec (fun x : FinLatticeSites 1 N => latticeFourierBasisFun N m (x 0)) = latticeEigenvalue1d N a m • (fun x : FinLatticeSites 1 N => latticeFourierBasisFun N m (x 0)) := by -- Helper: eigenvalue formula connecting the cos form to latticeEigenvalue1d have hev : ∀ k : ℕ, ∀ n : ℕ, SmoothMap_Circle.fourierFreq (n + 1) = k → latticeEigenvalue1d N a (n + 1) = 2 * a⁻¹ ^ 2 * (1 - cos (2 * π * ↑k / ↑N)) := by intro k n hfk unfold latticeEigenvalue1d; rw [hfk] linarith [eigenvalue_formula a N k] funext x; simp only [Pi.smul_apply, smul_eq_mul] cases m with | zero => -- φ₀ = const 1/√N, λ₀ = 0 simp only [latticeFourierBasisFun, latticeEigenvalue1d_zero, zero_mul] -- M.mulVec (const c) = 0 because Laplacian of constant is 0 have h := negLaplacian1d_cos_eigenvalue N a ha 0 x simp only [Nat.cast_zero, zero_mul, zero_div, cos_zero, sub_self, mul_zero] at h -- h : M.mulVec (fun _ => 1) x = 0 conv_lhs => rw [show (fun (_ : FinLatticeSites 1 N) => (1 : ℝ) / Real.sqrt ↑N) = (1 / Real.sqrt ↑N) • (fun (_ : FinLatticeSites 1 N) => (1 : ℝ)) from by ext; simp] rw [Matrix.mulVec_smul, Pi.smul_apply, smul_eq_mul, h, mul_zero] | succ n => simp only [latticeFourierBasisFun] set k := n / 2 + 1 have hfk : SmoothMap_Circle.fourierFreq (n + 1) = k := rfl split · -- Cos mode rw [show (fun x : FinLatticeSites 1 N => Real.sqrt (2 / ↑N) * cos (2 * π * ↑k * ↑(x 0).val / ↑N)) = Real.sqrt (2 / ↑N) • (fun x : FinLatticeSites 1 N => cos (2 * π * ↑k * ↑(x 0).val / ↑N)) from rfl, Matrix.mulVec_smul]; simp only [Pi.smul_apply, smul_eq_mul] rw [negLaplacian1d_cos_eigenvalue N a ha k x, hev k n hfk]; ring · -- Sin mode rw [show (fun x : FinLatticeSites 1 N => Real.sqrt (2 / ↑N) * sin (2 * π * ↑k * ↑(x 0).val / ↑N)) = Real.sqrt (2 / ↑N) • (fun x : FinLatticeSites 1 N => sin (2 * π * ↑k * ↑(x 0).val / ↑N)) from rfl, Matrix.mulVec_smul]; simp only [Pi.smul_apply, smul_eq_mul] rw [negLaplacian1d_sin_eigenvalue N a ha k x, hev k n hfk]; ring /-- **Corrected spectral expansion of the 1D lattice heat kernel.** The bilinear form equals the sum over DFT modes of `exp(-t λ_m) * c_m(f) * c_m(g) / normSq(m)`. The `/normSq(m)` factor corrects the normalization at the Nyquist mode for even N. -/ theorem latticeHeatKernelBilinear1d_eq_spectral' (N : ℕ) [NeZero N] (t : ℝ) (f g : SmoothMap_Circle L ℝ) : latticeHeatKernelBilinear1d L N t f g = ∑ m ∈ Finset.range N, Real.exp (-t * latticeEigenvalue1d N (circleSpacing L N) m) * latticeDFTCoeff1d L N f m * latticeDFTCoeff1d L N g m / latticeFourierNormSq N m := by set a := circleSpacing L N set M := negLaplacianMatrix 1 N a set fN : FinLatticeSites 1 N → ℝ := fun x => circleRestriction L N f (x 0) set gN : FinLatticeSites 1 N → ℝ := fun x => circleRestriction L N g (x 0) set φ := latticeFourierBasisFun N set φL : ℕ → FinLatticeSites 1 N → ℝ := fun m x => φ m (x 0) have ha : a ≠ 0 := ne_of_gt (circleSpacing_pos L N) -- Step 1: Expand gN via DFT expansion, lifted to FinLatticeSites have h_gN_expand : gN = ∑ m : Fin N, ((∑ z, circleRestriction L N g z * φ m.val z) / latticeFourierNormSq N m.val) • φL m.val := by ext x have := congr_fun (dft_expansion N (fun z => circleRestriction L N g z)) (x 0) simp only [Finset.sum_apply, Pi.smul_apply, smul_eq_mul] at this ⊢ exact this -- Step 2: Apply exp(-tM) to the expansion have h_exp_gN : (latticeHeatKernelMatrix 1 N a t).mulVec gN = ∑ m : Fin N, (((∑ z, circleRestriction L N g z * φ m.val z) / latticeFourierNormSq N m.val) * Real.exp (-t * latticeEigenvalue1d N a m.val)) • φL m.val := by conv_lhs => rw [h_gN_expand] unfold latticeHeatKernelMatrix rw [Matrix.mulVec_sum] congr 1; ext m rw [Matrix.mulVec_smul] -- exp(-tM) φL_m = exp(-tλ_m) • φL_m have h_eig : (NormedSpace.exp ((-t) • M)).mulVec (φL m.val) = Real.exp (-t * latticeEigenvalue1d N a m.val) • φL m.val := Matrix.IsHermitian.mulVec_exp_of_eigenvector (negLaplacianMatrix_isHermitian 1 N a) t (negLaplacian1d_dft_eigenvector N a ha m.val m.2) rw [h_eig, smul_smul] -- Step 3: Rewrite bilinear form as a sum and apply h_exp_gN show (∑ x, fN x * ((latticeHeatKernelMatrix 1 N a t).mulVec gN) x) = ∑ m ∈ Finset.range N, Real.exp (-t * latticeEigenvalue1d N a m) * latticeDFTCoeff1d L N f m * latticeDFTCoeff1d L N g m / latticeFourierNormSq N m rw [h_exp_gN] simp_rw [Finset.sum_apply, Pi.smul_apply, smul_eq_mul, Finset.mul_sum] rw [Finset.sum_comm, ← Fin.sum_univ_eq_sum_range] congr 1; ext m -- Factor out the scalar coefficient have h_rearrange : ∀ (x : FinLatticeSites 1 N), fN x * ((∑ z, (circleRestriction L N) g z * φ (↑m) z) / latticeFourierNormSq N ↑m * rexp (-t * latticeEigenvalue1d N a ↑m) * φL (↑m) x) = ((∑ z, (circleRestriction L N) g z * φ (↑m) z) / latticeFourierNormSq N ↑m * rexp (-t * latticeEigenvalue1d N a ↑m)) * (fN x * φL (↑m) x) := fun x => by ring simp_rw [h_rearrange, ← Finset.mul_sum] -- Identify ∑ x, fN x * φL m x = latticeDFTCoeff1d L N f m have h_dot_f : ∑ x : FinLatticeSites 1 N, fN x * φL (↑m) x = latticeDFTCoeff1d L N f m.val := by rw [sum_fin1_eq_sum_zmod] simp only [latticeDFTCoeff1d, fN, φL, φ, m.2, ↓reduceIte] rw [h_dot_f] -- Identify the coefficient sum with latticeDFTCoeff1d have h_coeff_g : ∑ z, (circleRestriction L N) g z * φ (↑m) z = latticeDFTCoeff1d L N g m.val := by simp only [latticeDFTCoeff1d, φ, m.2, ↓reduceIte] rw [h_coeff_g]; ring /-! ## Full 1D heat kernel convergence -/ /-- The m-th corrected lattice heat kernel term at lattice size N. Includes the `/ latticeFourierNormSq` factor that corrects the Nyquist mode. -/ private def latticeHeatTerm1d (N : ℕ) [NeZero N] (t : ℝ) (f g : SmoothMap_Circle L ℝ) (m : ℕ) : ℝ := Real.exp (-t * latticeEigenvalue1d N (circleSpacing L N) m) * latticeDFTCoeff1d L N f m * latticeDFTCoeff1d L N g m / latticeFourierNormSq N m /-- Lattice heat kernel term vanishes for m ≥ N. -/ private theorem latticeHeatTerm1d_zero_of_ge (N : ℕ) [NeZero N] (t : ℝ) (f g : SmoothMap_Circle L ℝ) (m : ℕ) (hm : N ≤ m) : latticeHeatTerm1d L N t f g m = 0 := by unfold latticeHeatTerm1d rw [latticeDFTCoeff1d_zero_of_ge L N f m hm] ring /-- The lattice bilinear equals the tsum of corrected lattice heat terms. -/ private theorem latticeHeatKernelBilinear1d_eq_tsum (N : ℕ) [NeZero N] (t : ℝ) (f g : SmoothMap_Circle L ℝ) : latticeHeatKernelBilinear1d L N t f g = ∑' m, latticeHeatTerm1d L N t f g m := by rw [latticeHeatKernelBilinear1d_eq_spectral'] symm rw [tsum_eq_sum (s := Finset.range N)] · rfl · intro m hm rw [Finset.mem_range, not_lt] at hm exact latticeHeatTerm1d_zero_of_ge L N t f g m hm /-- **Full 1D heat kernel convergence: lattice → continuum.** Uses the flat DFT bound `|c_m| ≤ √(2L) · ‖f‖_C⁰` combined with the eigenvalue lower bound `λ_m ≥ 8m/L²` (from Jordan's inequality) to construct a summable dominating function `C · exp(-8tm/L²)` for Tannery's theorem. The `/normSq` correction vanishes for large N (normSq → 1 for fixed m). -/ theorem lattice_heatKernel_tendsto_continuum_1d (t : ℝ) (ht : 0 < t) (f g : SmoothMap_Circle L ℝ) : Tendsto (fun N : ℕ => latticeHeatKernelBilinear1d L (N + 1) t f g) atTop (nhds (heatKernelBilinear (E := SmoothMap_Circle L ℝ) t f g)) := by -- Step 1: Rewrite LHS as tsum of lattice heat terms simp_rw [latticeHeatKernelBilinear1d_eq_tsum] -- Step 2: RHS unfolds to a tsum have hRHS : heatKernelBilinear (E := SmoothMap_Circle L ℝ) t f g = ∑' m, Real.exp (-t * HasLaplacianEigenvalues.eigenvalue (E := SmoothMap_Circle L ℝ) m) * DyninMityaginSpace.coeff m f * DyninMityaginSpace.coeff m g := rfl rw [hRHS] -- Step 3: Set up flat bounds and eigenvalue decay set Cf := Real.sqrt (2 * L) * SmoothMap_Circle.sobolevSeminorm 0 f set Cg := Real.sqrt (2 * L) * SmoothMap_Circle.sobolevSeminorm 0 g have hCf_nn : 0 ≤ Cf := mul_nonneg (Real.sqrt_nonneg _) (SmoothMap_Circle.sobolevSeminorm_nonneg 0 f) have hCg_nn : 0 ≤ Cg := mul_nonneg (Real.sqrt_nonneg _) (SmoothMap_Circle.sobolevSeminorm_nonneg 0 g) set α := 8 * t / L ^ 2 with hα_def have hα_pos : 0 < α := div_pos (mul_pos (by norm_num : (0:ℝ) < 8) ht) (sq_pos_of_pos hL.out) -- Summable dominating function: Cf * Cg * exp(-α * m) have h_sum : Summable (fun m : ℕ => Cf * Cg * Real.exp (-α * (m : ℝ))) := by have h_exp_sum : Summable (fun m : ℕ => Real.exp (-α * (m : ℝ))) := by have : ∀ m : ℕ, Real.exp (-α * (m : ℝ)) = Real.exp ((m : ℝ) * (-α)) := by intro m; ring_nf simp_rw [this] exact Real.summable_exp_nat_mul_iff.mpr (by linarith) exact h_exp_sum.const_smul (Cf * Cg) |>.congr fun m => by simp [smul_eq_mul] -- Step 4: Apply Tannery's theorem apply tendsto_tsum_of_dominated_convergence (bound := fun m : ℕ => Cf * Cg * Real.exp (-α * (m : ℝ))) · exact h_sum · -- Pointwise convergence: each term converges intro m -- normSq(N+1, m) = 1 eventually, so latticeHeatTerm1d = exp * c_f * c_g eventually have h_ev : ∀ᶠ N in atTop, latticeHeatTerm1d L (N + 1) t f g m = Real.exp (-t * latticeEigenvalue1d (N + 1) (circleSpacing L (N + 1)) m) * latticeDFTCoeff1d L (N + 1) f m * latticeDFTCoeff1d L (N + 1) g m := (latticeFourierNormSq_eventually_one m).mono fun N hN => by unfold latticeHeatTerm1d; rw [hN, div_one] exact (Filter.Tendsto.mul (Filter.Tendsto.mul ((Real.continuous_exp.tendsto _).comp (tendsto_const_nhds.mul (latticeEigenvalue1d_tendsto_continuum L m))) (latticeDFTCoeff1d_tendsto L f m)) (latticeDFTCoeff1d_tendsto L g m)).congr' (Filter.EventuallyEq.symm h_ev) · -- Norm bound: |latticeHeatTerm(N,m)| ≤ Cf * Cg * exp(-α*m) apply Filter.Eventually.of_forall intro N m unfold latticeHeatTerm1d -- |exp * c_f * c_g / normSq| ≤ |exp * c_f * c_g| since normSq ≥ 1 rw [Real.norm_eq_abs] by_cases hm : m = 0 · subst hm simp only [latticeEigenvalue1d_zero, mul_zero, Real.exp_zero, one_mul, latticeFourierNormSq_zero, div_one, Nat.cast_zero, mul_one] rw [abs_mul] exact mul_le_mul (latticeDFTCoeff1d_flat_bound L f N 0) (latticeDFTCoeff1d_flat_bound L g N 0) (abs_nonneg _) hCf_nn · have hm' : 1 ≤ m := Nat.one_le_iff_ne_zero.mpr hm by_cases hm_lt : m < N + 1 · have h_eig_lb := latticeEigenvalue1d_ge_8m L N m hm' hm_lt have h_exp : Real.exp (-t * latticeEigenvalue1d (N + 1) (circleSpacing L (N + 1)) m) ≤ Real.exp (-α * (m : ℝ)) := by apply Real.exp_le_exp_of_le rw [hα_def, neg_mul, neg_mul, neg_le_neg_iff] calc 8 * t / L ^ 2 * (m : ℝ) = t * (8 * (m : ℝ) / L ^ 2) := by ring _ ≤ t * latticeEigenvalue1d (N + 1) (circleSpacing L (N + 1)) m := mul_le_mul_of_nonneg_left h_eig_lb ht.le -- |a / b| ≤ |a| when b ≥ 1 have h_normSq_ge : 1 ≤ latticeFourierNormSq (N + 1) m := latticeFourierNormSq_ge_one (N + 1) m hm_lt have h_abs_div : |Real.exp (-t * latticeEigenvalue1d (N + 1) (circleSpacing L (N + 1)) m) * latticeDFTCoeff1d L (N + 1) f m * latticeDFTCoeff1d L (N + 1) g m / latticeFourierNormSq (N + 1) m| ≤ |Real.exp (-t * latticeEigenvalue1d (N + 1) (circleSpacing L (N + 1)) m) * latticeDFTCoeff1d L (N + 1) f m * latticeDFTCoeff1d L (N + 1) g m| := by rw [abs_div] exact div_le_self (abs_nonneg _) (by rw [abs_of_nonneg (le_of_lt (latticeFourierNormSq_pos (N + 1) m hm_lt))] exact h_normSq_ge) calc |Real.exp _ * latticeDFTCoeff1d L (N + 1) f m * latticeDFTCoeff1d L (N + 1) g m / latticeFourierNormSq (N + 1) m| ≤ |Real.exp _ * latticeDFTCoeff1d L (N + 1) f m * latticeDFTCoeff1d L (N + 1) g m| := h_abs_div _ = Real.exp _ * |latticeDFTCoeff1d L (N + 1) f m| * |latticeDFTCoeff1d L (N + 1) g m| := by rw [abs_mul, abs_mul, abs_of_pos (Real.exp_pos _)] _ ≤ Real.exp (-α * (m : ℝ)) * Cf * Cg := by apply mul_le_mul · exact mul_le_mul h_exp (latticeDFTCoeff1d_flat_bound L f N m) (abs_nonneg _) (Real.exp_nonneg _) · exact latticeDFTCoeff1d_flat_bound L g N m · exact abs_nonneg _ · exact mul_nonneg (Real.exp_nonneg _) hCf_nn _ = Cf * Cg * Real.exp (-α * (m : ℝ)) := by ring · rw [latticeDFTCoeff1d_zero_of_ge L (N + 1) f m (by omega), latticeDFTCoeff1d_zero_of_ge L (N + 1) g m (by omega)] simp only [mul_zero, zero_div, abs_zero] exact mul_nonneg (mul_nonneg hCf_nn hCg_nn) (Real.exp_nonneg _) end GaussianField