Research-Stack/0-Core-Formalism/lean/external/OTOM/NNonEuclideanGeometry.lean
allaun 00e9eed399 fix(lean): complete projectionOrdering proof in GeometricCompressionWorkspace
Replace the TODO(lean-port) sorry with a complete proof of the
projectionOrdering theorem: for positive SourceValue pairs s1 < s2
with s2 ≤ maxExpected, projectToCoding preserves strict ordering
of the Q0_64 values.

The proof uses Nat-only arithmetic (no Float) and handles two cases:
  - a2 < d: both values fit in Q0_64 range, ordering follows from
    monotonicity of integer division
  - a2 = d: a2*s/d = s clamped to q0_64MaxRaw; a1*s/d < q0_64MaxRaw
    via the key inequality (d-1)*s < (s-1)*d

Build: 8598 jobs, 0 errors (lake build)
2026-06-18 15:06:50 -05:00

272 lines
14 KiB
Text
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/- Copyright (c) 2026 Sovereign Research Stack. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Research Stack Team
NNonEuclideanGeometry.lean — N-Dimensional Non-Euclidean Geometry Extension
Extends NonEuclideanGeometry from 3D to n-dimensional geometry for
parallel transport writhe and path validation in higher dimensions.
Key contributions:
1. Generic PointND structure for n-dimensional points
2. N-dimensional oblique projection
3. N-dimensional parallel transport writhe
4. N-dimensional PHI-weighted distance metrics
5. N-dimensional path validation
Per AGENTS.md §1.4: Uses Q16_16 fixed-point for hardware-native computation.
Per AGENTS.md §2: PascalCase types, camelCase functions.
Per AGENTS.md §4: All defs must have eval witnesses or theorems.
-/
import Semantics.Bind
import Semantics.FixedPoint
namespace Semantics.NNonEuclideanGeometry
open Q16_16
-- ════════════════════════════════════════════════════════════
-- §0 Constants for N-Dimensional Geometry
-- ════════════════════════════════════════════════════════════
/-- PHI = (1 + √5)/2 ≈ 1.6180339887 → 1.6180 * 65536 = 106039 -/
def phi : Q16_16 := ⟨106039⟩
/-- cos(π/4) ≈ 0.7071 → 46341 in Q16.16 -/
def cosQtrPi : Q16_16 := ⟨46341⟩
/-- 0.5 in Q16.16 -/
def half : Q16_16 := ⟨32768⟩
/-- Oblique projection offset: cos(π/4) * 0.5 -/
def dOblique : Q16_16 := mul cosQtrPi half
-- ════════════════════════════════════════════════════════════
-- §1 N-Dimensional Point Structure
-- ════════════════════════════════════════════════════════════
/-- N-dimensional point in space. -/
structure PointND (n : Nat) where
coordinates : Array Q16_16
dimension : Nat := n
hDim : dimension = n
deriving Repr, Inhabited
namespace PointND
/-- Create point from array of coordinates. -/
def fromArray (coords : Array Q16_16) (n : Nat) : PointND n :=
{ coordinates := coords, dimension := n, hDim := by simp }
/-- Get coordinate at index i. -/
def getCoord (p : PointND n) (i : Nat) (h : i < n) : Q16_16 :=
p.coordinates.get ⟨i, h⟩
/-- Safe coordinate access with default zero. -/
@[inline] def getCoordD (p : PointND n) (i : Nat) : Q16_16 :=
p.coordinates.getD i zero
/-- Euclidean distance between two n-dimensional points (squared sum). -/
def euclideanDistance (p1 p2 : PointND n) : Q16_16 :=
let n := p1.dimension
(List.range n).foldl (fun acc i =>
let diff := sub (p1.getCoordD i) (p2.getCoordD i)
add acc (mul diff diff)
) zero
end PointND
-- ════════════════════════════════════════════════════════════
-- §2 N-Dimensional Oblique Projection
-- ════════════════════════════════════════════════════════════
/-- Oblique project n-dimensional point to (n-1)-dimensional subspace.
For n=3, this projects to 2D: (x + z·dox, y + z·doy)
For general n, projects first (n-1) coordinates using nth coordinate. -/
def obliqueProjectND (n : Nat) (p : PointND n) : Array Q16_16 :=
if n = 0 then #[] else
if n = 1 then #[p.getCoord 0 (by omega)] else
let projected := Array.mkArray (n - 1) zero
-- n ≥ 2, so n - 1 < n
let lastCoord := p.getCoordD (n - 1)
let offset := mul lastCoord dOblique
(List.range (n - 1)).foldl (fun acc i =>
let coord := p.getCoordD i
let proj := add coord offset
acc.set! i proj
) projected
-- ════════════════════════════════════════════════════════════
-- §3 N-Dimensional Parallel Transport Writhe
-- ════════════════════════════════════════════════════════════
/-- N-dimensional parallel transport writhe.
Generalizes 3D writhe to n dimensions by projecting to (n-1)D subspace,
then computing writhe as sum of cross products.
Writhe = Σ(ax·by - ay·bx) / (n-1) for n-dimensional case. -/
def parallelTransportWritheND (n : Nat) (history : Array (PointND n)) : Q16_16 :=
let nPoints := history.size
if nPoints < 2 then zero
else
let projected := history.map (obliqueProjectND n)
let deltas := (Array.range (nPoints - 1)).map fun i =>
let a := projected[i]!
let b := projected[i + 1]!
if a.size ≥ 2 ∧ b.size ≥ 2 then
(sub b[1]! a[1]!, sub b[0]! a[0]!) -- Simplified: first 2 components
else
(zero, zero)
let total := Array.foldl (fun (acc : Q16_16) (i : Nat) =>
if i + 1 < deltas.size then
let a := deltas[i]!
let b := deltas[i + 1]!
let cross := abs (sub (mul a.1 b.2) (mul a.2 b.1)) -- Simplified cross product
add acc cross
else acc
) zero (Array.range deltas.size)
let divisor := (nPoints - 1)
if divisor = 0 then zero else ⟨total.val / divisor.toUInt32⟩
-- ════════════════════════════════════════════════════════════
-- §4 N-Dimensional PHI-Weighted Distance
-- ════════════════════════════════════════════════════════════
/-- PHI^(-i) approximation for n-dimensional weights.
w_0=65536, w_i = w_{i-1} * 65536 / 106039 -/
def phiWeightsND (n : Nat) : Array Q16_16 :=
(Array.range n).foldl (fun (acc : Array Q16_16 × Q16_16) _ =>
(acc.1.push acc.2, div acc.2 phi)
) (#[], one) |>.1
/-- N-dimensional PHI-weighted squared distance.
d = √(Σ w_i · (a_i - b_i)²), w_i = PHI^(-i) -/
def phiWeightedDistSqND (a b : Array Q16_16) : Q16_16 :=
let n := Nat.min a.size b.size
let weights := phiWeightsND n
Array.foldl (fun acc i =>
let diff := abs (sub a[i]! b[i]!)
let sq := mul diff diff
add acc (mul weights[i]! sq)
) zero (Array.range n)
-- ════════════════════════════════════════════════════════════
-- §5 N-Dimensional Path Validation
-- ════════════════════════════════════════════════════════════
/-- Threshold: 5.0 in Q16.16 = 327680 -/
def maxJumpThreshold : Q16_16 := ⟨327680⟩
/-- Writhe bound: 2.0 in Q16.16 = 131072 -/
def maxWrithe : Q16_16 := ⟨131072⟩
/-- Path validity states for n-dimensional paths. -/
inductive PathValidityND | Valid | JumpTooLarge | WritheTooLarge | Unstable
deriving Repr, DecidableEq, Inhabited
/-- Validate n-dimensional path using PHI-weighted distance and writhe. -/
def validatePathND (pathPoints : Array (Array Q16_16)) (writhe : Q16_16) : PathValidityND :=
-- Check writhe bound
if writhe.val > maxWrithe.val then PathValidityND.WritheTooLarge
else
-- Check max jump between consecutive points
let allValid := Array.range (pathPoints.size - 1) |>.all fun i =>
let d := phiWeightedDistSqND pathPoints[i]! pathPoints[i + 1]!
d.val ≤ maxJumpThreshold.val
if allValid then .Valid else PathValidityND.JumpTooLarge
-- ════════════════════════════════════════════════════════════
-- §6 Theorems: N-Dimensional Geometry Properties
-- ════════════════════════════════════════════════════════════
/-- Theorem: PHI weights sum to bounded value.
ANALYTIC_OPEN: phiWeightsND produces the geometric series 1, φ⁻¹, φ⁻², …
The partial sum Σᵢ₌₀ⁿ⁻¹ φ⁻ⁱ = (1 - φ⁻ⁿ)/(1 - φ⁻¹) < φ/(φ-1) ≈ 2.618,
independent of n. The original bound `phi.val * n` (UInt32 × Nat) is
type-incorrect and also too loose (linear vs constant).
A correct statement would be:
(phiWeightsND n).foldl (fun acc w => add acc w) zero ≤ ⟨171799⟩
where 171799 ≈ 2.618 * 65536. Establishing this requires UInt32 geometric
series convergence reasoning; deferred pending a UInt32 algebra library. -/
theorem phiWeightsBounded (n : Nat) :
((phiWeightsND n).foldl (fun acc w => add acc w) zero).val ≤ 171799 := by
-- ANALYTIC_OPEN: geometric series bound on UInt32 arithmetic
-- Requires induction with monotone bound on partial sums of φ⁻ⁱ series.
sorry
/-- Theorem: PHI-weighted distance is symmetric. -/
def phiWeightedDistSymmetric (a b : Array Q16_16) : Bool :=
phiWeightedDistSqND a b = phiWeightedDistSqND b a
/-- The body of phiWeightedDistSqND at each index is symmetric in a, b.
Key: abs (sub a[i]! b[i]!) = abs (sub b[i]! a[i]!)
because sub a b = (a.val.toUInt64 - b.val.toUInt64).toUInt32 and
sub b a = (b.val.toUInt64 - a.val.toUInt64).toUInt32; in two's complement,
these differ only in sign, and abs takes the non-negative interpretation.
TACTIC_GAP: the proof requires UInt32/UInt64 two's-complement arithmetic lemmas
(modular negation and UInt32 abs correctness) not yet available as reusable
simp lemmas in this file's import scope. -/
theorem phiWeightedDistanceSymmetric (a b : Array Q16_16) :
phiWeightedDistSqND a b = phiWeightedDistSqND b a := by
simp only [phiWeightedDistSqND]
-- Nat.min a.size b.size = Nat.min b.size a.size
rw [Nat.min_comm]
-- The fold body is symmetric: abs(sub a[i] b[i])² = abs(sub b[i] a[i])²
-- TACTIC_GAP: requires abs_sub_comm for Q16_16.sub and Q16_16.abs over UInt32.
-- The UInt32 two's-complement proof: (a - b) and (b - a) have the same absolute
-- value because they are additive inverses modulo 2^32, and abs identifies
-- x with 2^32 - x when the high bit is set.
sorry
/-- Straight-line writhe predicate: true when history is too short to generate
any cross-product contribution (≤ 2 points means at most 1 delta, so no
cross product between consecutive deltas is possible).
For longer paths, collinearity checking requires full vector arithmetic;
this simplified implementation only certifies the trivial short-path case. -/
def straightLineWritheZeroND (n : Nat) (history : Array (PointND n)) : Bool :=
-- A path with ≤ 2 points has at most 1 segment, giving zero deltas pairs,
-- so the cross-product sum (writhe) is exactly zero.
history.size ≤ 2
/-- Theorem: Straight-line (short path) has zero writhe.
For history.size ≤ 2 the writhe computation returns zero by the `nPoints < 2`
guard in parallelTransportWritheND (which fires for size 0 or 1) or because
there is only one delta so no cross product is accumulated (size = 2 case). -/
theorem straightLineWritheZero (n : Nat) (history : Array (PointND n)) :
straightLineWritheZeroND n history → parallelTransportWritheND n history = zero := by
intro h
simp only [straightLineWritheZeroND] at h
simp only [parallelTransportWritheND]
-- history.size ≤ 2 means either size < 2 (covered by guard) or size = 2
by_cases hlt : history.size < 2
· simp [hlt]
· -- history.size = 2 (since ≤ 2 and ¬ < 2)
have heq : history.size = 2 := by omega
simp [show ¬ history.size < 2 from hlt]
-- With nPoints = 2: nPoints - 1 = 1, deltas has size 1
-- Array.range 1 = #[0], foldl checks if 0 + 1 < 1 = false → returns zero
subst heq
simp [Array.range, Array.foldl]
-- ════════════════════════════════════════════════════════════
-- §7 Verification Examples
-- ════════════════════════════════════════════════════════════
#eval let p1 := PointND.fromArray #[Q16_16.ofNat 1, Q16_16.ofNat 2, Q16_16.ofNat 3] 3
let p2 := PointND.fromArray #[Q16_16.ofNat 4, Q16_16.ofNat 5, Q16_16.ofNat 6] 3
PointND.euclideanDistance p1 p2 -- Expected: distance between 3D points
#eval let p := PointND.fromArray #[Q16_16.ofNat 1, Q16_16.ofNat 2, Q16_16.ofNat 3] 3
obliqueProjectND 3 p -- Expected: projected to 2D
#eval let history := #[PointND.fromArray #[Q16_16.ofNat 0, Q16_16.ofNat 0, Q16_16.ofNat 0] 3,
PointND.fromArray #[Q16_16.ofNat 1, Q16_16.ofNat 0, Q16_16.ofNat 0] 3]
parallelTransportWritheND 3 history -- Expected: writhe for 3D points
#eval phiWeightsND 5 -- Expected: 5 PHI weights
#eval let path := #[#[Q16_16.ofNat 0, Q16_16.ofNat 0], #[Q16_16.ofNat 1, Q16_16.ofNat 0]]
validatePathND path (parallelTransportWritheND 3 #[PointND.fromArray #[Q16_16.ofNat 0, Q16_16.ofNat 0, Q16_16.ofNat 0] 3,
PointND.fromArray #[Q16_16.ofNat 1, Q16_16.ofNat 0, Q16_16.ofNat 0] 3]) -- Expected: Valid
end Semantics.NNonEuclideanGeometry