mirror of
https://github.com/allaunthefox/Research-Stack.git
synced 2026-07-30 18:56:16 +00:00
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)
272 lines
14 KiB
Text
272 lines
14 KiB
Text
/- 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
|