mirror of
https://github.com/allaunthefox/SilverSight.git
synced 2026-07-31 01:25:21 +00:00
- Remove Float from Core/SilverSightCore.lean (Receipt.pathCost is now Option Nat) - Prove TIC theorems tic_never_decreases and computation_generates_time - Add lakefile.lean, lean-toolchain, and .gitignore for .lake/ - Port Research-Stack Semantics.FixedPoint.lean to formal/CoreFormalism/FixedPoint.lean - Delete thin Q16_16_Spec.lean; update Python/QUBO comments and docs - Create AGENTS.md distilled from Research Stack core bindings - Update REBASE_RULES.md to strip legacy hacks and reference AGENTS.md Build: 2978 jobs, 0 errors (lake build)
427 lines
14 KiB
Python
427 lines
14 KiB
Python
"""
|
||
finsler_metric.py -- Randers Metric F = α + β computation
|
||
|
||
Computes the Finsler-Randers metric on the Hachimoji 8-state simplex.
|
||
The metric is the UNIQUE geometry on Δ⁷ (proven by ChentsovFinite.lean):
|
||
|
||
F(a→b) = α(a,b) + β(a,b)
|
||
|
||
α: Fisher information metric (symmetric, from Chentsov uniqueness theorem)
|
||
β: Drift 1-form (asymmetric, encodes torsion / wind field)
|
||
|
||
The Fisher metric is canonical: Chentsov's theorem proves it is the ONLY
|
||
Riemannian metric on the probability simplex that is invariant under all
|
||
Markov embeddings (stochastic refinements).
|
||
|
||
Reference: CoreFormalism/ChentsovFinite.lean -- chentsov_hachimoji theorem
|
||
"""
|
||
|
||
from __future__ import annotations
|
||
|
||
import math
|
||
from dataclasses import dataclass, field
|
||
from typing import Any, Optional
|
||
|
||
import numpy as np
|
||
|
||
|
||
# =========================================================================
|
||
# Q16_16 Fixed-Point Constants (from CoreFormalism/FixedPoint.lean)
|
||
# =========================================================================
|
||
Q16_SCALE: int = 65536 # 2^16 -- number of subdivisions per unit
|
||
|
||
|
||
def to_q16(value: float) -> int:
|
||
"""Convert float to Q16_16 raw integer with canonical rounding."""
|
||
scaled = value * Q16_SCALE
|
||
rounded = round(scaled)
|
||
return max(-2147483648, min(2147483647, rounded))
|
||
|
||
|
||
def from_q16(raw: int) -> float:
|
||
"""Convert Q16_16 raw integer to float."""
|
||
return raw / Q16_SCALE
|
||
|
||
|
||
# =========================================================================
|
||
# Hachimoji 8-State System
|
||
# =========================================================================
|
||
|
||
# Greek states with phases (45° steps, from HachimojiBase.lean §5)
|
||
GREEK_STATES: list[str] = ["\u03a6", "\u039b", "\u03a1", "\u039a", "\u03a9", "\u03a3", "\u03a0", "\u0396"]
|
||
|
||
# Phase angles in degrees (0, 45, 90, 135, 180, 225, 270, 315)
|
||
GREEK_PHASE: dict[str, float] = {
|
||
"\u03a6": 0.0, "\u039b": 45.0, "\u03a1": 90.0, "\u039a": 135.0,
|
||
"\u03a9": 180.0, "\u03a3": 225.0, "\u03a0": 270.0, "\u0396": 315.0,
|
||
}
|
||
|
||
# Latin ↔ Greek bijection (from HachimojiBase.lean §2)
|
||
LATIN_TO_GREEK: dict[str, str] = {
|
||
"A": "\u03a6", "T": "\u039b", "G": "\u03a1", "C": "\u039a",
|
||
"B": "\u03a9", "S": "\u03a3", "P": "\u03a0", "Z": "\u0396",
|
||
}
|
||
|
||
GREEK_TO_LATIN: dict[str, str] = {v: k for k, v in LATIN_TO_GREEK.items()}
|
||
|
||
|
||
# =========================================================================
|
||
# State Descriptors (4D HachimojiState)
|
||
# =========================================================================
|
||
|
||
@dataclass
|
||
class HachimojiState4D:
|
||
"""4D descriptor for a Hachimoji state on the probability simplex.
|
||
|
||
Components:
|
||
- symbol: Greek letter (Φ Λ Ρ Κ Ω Σ Π Ζ)
|
||
- phase: angle on S¹ in degrees [0, 360)
|
||
- probability: point on Δ⁷ (8 probabilities, sum to 1)
|
||
- fisher_curvature: local Fisher information scalar at this state
|
||
- drift: β component (wind field) at this state
|
||
"""
|
||
symbol: str
|
||
phase: float
|
||
probability: np.ndarray # 8-element, sums to 1
|
||
fisher_curvature: float = 0.0
|
||
drift: np.ndarray = field(default_factory=lambda: np.zeros(8))
|
||
|
||
def __post_init__(self):
|
||
self.probability = np.asarray(self.probability, dtype=float)
|
||
self.drift = np.asarray(self.drift, dtype=float)
|
||
# Normalize probability
|
||
s = self.probability.sum()
|
||
if s > 0:
|
||
self.probability /= s
|
||
|
||
def to_dict(self) -> dict:
|
||
return {
|
||
"symbol": self.symbol,
|
||
"phase": self.phase,
|
||
"probability": self.probability.tolist(),
|
||
"fisher_curvature": self.fisher_curvature,
|
||
"drift": self.drift.tolist(),
|
||
}
|
||
|
||
@classmethod
|
||
def from_dict(cls, d: dict) -> "HachimojiState4D":
|
||
return cls(
|
||
symbol=d["symbol"],
|
||
phase=d["phase"],
|
||
probability=np.array(d["probability"]),
|
||
fisher_curvature=d.get("fisher_curvature", 0.0),
|
||
drift=np.array(d.get("drift", [0.0] * 8)),
|
||
)
|
||
|
||
|
||
def make_uniform_hachimoji_states() -> list[HachimojiState4D]:
|
||
"""Create the 8 canonical Hachimoji states at the uniform distribution.
|
||
|
||
Each state corresponds to one vertex of the Greek alphabet on Δ⁷.
|
||
At the uniform distribution p_i = 1/8, the Fisher metric is:
|
||
g_Fisher(X, X) = 8 * Σ_i X_i² (since 1/p_i = 8)
|
||
"""
|
||
states = []
|
||
for sym in GREEK_STATES:
|
||
phase = GREEK_PHASE[sym]
|
||
# Uniform distribution on Δ⁷
|
||
prob = np.ones(8) / 8.0
|
||
# Fisher curvature at uniform: g_ii = 1/p_i = 8
|
||
fisher_curvature = 8.0
|
||
# Drift (β) points in the direction of increasing phase
|
||
# This encodes the circular topology of the Hachimoji states
|
||
drift = np.zeros(8)
|
||
idx = GREEK_STATES.index(sym)
|
||
# Wind field: stronger drift toward adjacent states on the circle
|
||
drift[(idx + 1) % 8] = 0.3 # forward neighbor
|
||
drift[(idx - 1) % 8] = -0.1 # backward neighbor
|
||
states.append(HachimojiState4D(
|
||
symbol=sym,
|
||
phase=phase,
|
||
probability=prob,
|
||
fisher_curvature=fisher_curvature,
|
||
drift=drift,
|
||
))
|
||
return states
|
||
|
||
|
||
# =========================================================================
|
||
# Fisher Information Metric (α component -- symmetric, canonical)
|
||
# =========================================================================
|
||
|
||
def fisher_information_metric(
|
||
p: np.ndarray,
|
||
X: np.ndarray,
|
||
Y: np.ndarray,
|
||
) -> float:
|
||
"""Compute the Fisher information metric g_Fisher(X, Y) at point p.
|
||
|
||
g_Fisher(X, Y) = Σ_i X_i · Y_i / p_i
|
||
|
||
This is the UNIQUE Riemannian metric on the probability simplex
|
||
that is invariant under all Markov embeddings (Chentsov's theorem).
|
||
|
||
Args:
|
||
p: probability distribution (positive, sums to 1)
|
||
X, Y: tangent vectors (components sum to 0)
|
||
|
||
Returns:
|
||
Fisher inner product
|
||
"""
|
||
p = np.asarray(p, dtype=float)
|
||
X = np.asarray(X, dtype=float)
|
||
Y = np.asarray(Y, dtype=float)
|
||
eps = 1e-12
|
||
result = 0.0
|
||
for i in range(len(p)):
|
||
if p[i] > eps:
|
||
result += X[i] * Y[i] / p[i]
|
||
return float(result)
|
||
|
||
|
||
def fisher_metric_matrix(p: np.ndarray) -> np.ndarray:
|
||
"""Compute the Fisher metric matrix G_ij = δ_ij / p_i at point p.
|
||
|
||
Returns:
|
||
8×8 diagonal matrix with G_ii = 1/p_i
|
||
"""
|
||
p = np.asarray(p, dtype=float)
|
||
eps = 1e-12
|
||
G = np.zeros((len(p), len(p)))
|
||
for i in range(len(p)):
|
||
if p[i] > eps:
|
||
G[i, i] = 1.0 / p[i]
|
||
return G
|
||
|
||
|
||
# =========================================================================
|
||
# Randers Finsler Metric: F = α + β
|
||
# =========================================================================
|
||
|
||
def compute_alpha_component(
|
||
state_a: HachimojiState4D | dict,
|
||
state_b: HachimojiState4D | dict,
|
||
) -> float:
|
||
"""Compute the α (Fisher) component: symmetric Riemannian distance.
|
||
|
||
α(a,b) = arccosh(1 + ½ · g_Fisher(v, v))
|
||
where v = b - a (tangent vector at the midpoint)
|
||
|
||
For the uniform distribution, this simplifies to the
|
||
Fisher-Rao distance on Δ⁷.
|
||
"""
|
||
if isinstance(state_a, dict):
|
||
state_a = HachimojiState4D.from_dict(state_a)
|
||
if isinstance(state_b, dict):
|
||
state_b = HachimojiState4D.from_dict(state_b)
|
||
|
||
# Tangent vector: difference of probability distributions
|
||
v = state_b.probability - state_a.probability
|
||
|
||
# Use midpoint for metric evaluation
|
||
p_mid = (state_a.probability + state_b.probability) / 2.0
|
||
p_mid = np.maximum(p_mid, 1e-12)
|
||
p_mid /= p_mid.sum()
|
||
|
||
# Fisher norm: g_Fisher(v, v) = Σ_i v_i² / p_i
|
||
fisher_norm_sq = fisher_information_metric(p_mid, v, v)
|
||
|
||
# α = sqrt(g_Fisher(v, v)) -- the Riemannian length
|
||
alpha = math.sqrt(max(0.0, fisher_norm_sq))
|
||
return alpha
|
||
|
||
|
||
def compute_beta_component(
|
||
state_a: HachimojiState4D | dict,
|
||
state_b: HachimojiState4D | dict,
|
||
) -> float:
|
||
"""Compute the β (drift) component: antisymmetric 1-form.
|
||
|
||
β(a,b) = ½ · (drift_a + drift_b) · (b - a)
|
||
|
||
β is a 1-form: β(-v) = -β(v), so β(b,a) = -β(a,b).
|
||
This encodes the torsion / wind field on the Hachimoji manifold.
|
||
"""
|
||
if isinstance(state_a, dict):
|
||
state_a = HachimojiState4D.from_dict(state_a)
|
||
if isinstance(state_b, dict):
|
||
state_b = HachimojiState4D.from_dict(state_b)
|
||
|
||
# Average drift field
|
||
avg_drift = (state_a.drift + state_b.drift) / 2.0
|
||
|
||
# Displacement vector
|
||
v = state_b.probability - state_a.probability
|
||
|
||
# β = drift · v
|
||
beta = float(np.dot(avg_drift, v))
|
||
return beta
|
||
|
||
|
||
def compute_finsler_metric(
|
||
state_a: HachimojiState4D | dict,
|
||
state_b: HachimojiState4D | dict,
|
||
) -> float:
|
||
"""Compute Randers metric F(a→b) = α(a,b) + β(a,b).
|
||
|
||
α: Fisher information metric (symmetric, from Chentsov uniqueness)
|
||
β: Drift 1-form (asymmetric, encodes torsion)
|
||
|
||
The metric is the UNIQUE geometry on the Hachimoji simplex
|
||
(proven by ChentsovFinite.lean: chentsov_hachimoji theorem).
|
||
|
||
Args:
|
||
state_a: HachimojiState4D for source (or dict)
|
||
state_b: HachimojiState4D for target (or dict)
|
||
|
||
Returns:
|
||
F(a→b): positive float, direction-dependent distance
|
||
"""
|
||
alpha = compute_alpha_component(state_a, state_b)
|
||
beta = compute_beta_component(state_a, state_b)
|
||
|
||
# F = α + β, but ensure positivity
|
||
# For a valid Finsler metric, we need α > |β|
|
||
F = alpha + beta
|
||
return max(F, 1e-10) # clamp to positive
|
||
|
||
|
||
def compute_finsler_distance_matrix(
|
||
states: list[HachimojiState4D | dict],
|
||
) -> np.ndarray:
|
||
"""Compute the full 8×8 Finsler distance matrix.
|
||
|
||
D[i,j] = F(states[i] → states[j])
|
||
|
||
Note: D is NOT symmetric because β is antisymmetric:
|
||
D[i,j] ≠ D[j,i] when drift ≠ 0
|
||
"""
|
||
n = len(states)
|
||
D = np.zeros((n, n))
|
||
for i in range(n):
|
||
for j in range(n):
|
||
if i != j:
|
||
D[i, j] = compute_finsler_metric(states[i], states[j])
|
||
return D
|
||
|
||
|
||
# =========================================================================
|
||
# Phase Distance on S¹ (circular topology)
|
||
# =========================================================================
|
||
|
||
def phase_distance_s1(phase_a: float, phase_b: float) -> float:
|
||
"""Compute the shortest distance between two phases on S¹.
|
||
|
||
d_phase(a,b) = min(|a-b|, 360 - |a-b|) · π/180
|
||
|
||
The phases live on a circle (0° = 360°), so the distance
|
||
is the arc length along the shorter arc.
|
||
"""
|
||
diff = abs(phase_a - phase_b)
|
||
diff = min(diff, 360.0 - diff)
|
||
# Convert to radians for geometric distance
|
||
return diff * (math.pi / 180.0)
|
||
|
||
|
||
def circular_phase_matrix(states: list[HachimojiState4D | dict]) -> np.ndarray:
|
||
"""Compute the 8×8 phase distance matrix on S¹.
|
||
|
||
P[i,j] = d_phase(phase_i, phase_j)² -- squared circular distance
|
||
"""
|
||
n = len(states)
|
||
P = np.zeros((n, n))
|
||
for i in range(n):
|
||
pa = states[i].phase if hasattr(states[i], "phase") else states[i]["phase"]
|
||
for j in range(n):
|
||
pb = states[j].phase if hasattr(states[j], "phase") else states[j]["phase"]
|
||
d = phase_distance_s1(pa, pb)
|
||
P[i, j] = d * d
|
||
return P
|
||
|
||
|
||
# =========================================================================
|
||
# Fisher Metric (canonical) factory
|
||
# =========================================================================
|
||
|
||
def build_fisher_metric(states: list[HachimojiState4D]) -> dict:
|
||
"""Build the canonical Fisher metric dict for the state simplex.
|
||
|
||
Returns dict compatible with compute_finsler_metric's
|
||
fisher_metric parameter:
|
||
{
|
||
"type": "Fisher",
|
||
"metric_matrix": 8×8 array,
|
||
"chentsov_constant": c, # positive constant from theorem
|
||
"at_uniform": True, # at p_i = 1/8
|
||
}
|
||
"""
|
||
# At the uniform distribution, the Fisher metric is 8·I
|
||
G = fisher_metric_matrix(np.ones(8) / 8.0)
|
||
return {
|
||
"type": "Fisher",
|
||
"metric_matrix": G.tolist(),
|
||
"chentsov_constant": 1.0, # c = 1 at uniform
|
||
"at_uniform": True,
|
||
}
|
||
|
||
|
||
# =========================================================================
|
||
# Convenience: full pipeline from states to distance matrix
|
||
# =========================================================================
|
||
|
||
def build_full_finsler_pipeline(
|
||
states: Optional[list[HachimojiState4D]] = None,
|
||
) -> dict:
|
||
"""Run the full Finsler metric computation pipeline.
|
||
|
||
Returns:
|
||
{
|
||
"states": [state dicts],
|
||
"finsler_matrix": 8×8 distance matrix,
|
||
"alpha_matrix": 8×8 symmetric α component,
|
||
"beta_matrix": 8×8 antisymmetric β component,
|
||
"phase_matrix": 8×8 phase distance on S¹,
|
||
"fisher_metric": Fisher metric dict,
|
||
"is_anisotropic": bool, # True if β ≠ 0
|
||
}
|
||
"""
|
||
if states is None:
|
||
states = make_uniform_hachimoji_states()
|
||
|
||
n = len(states)
|
||
F_mat = np.zeros((n, n))
|
||
A_mat = np.zeros((n, n))
|
||
B_mat = np.zeros((n, n))
|
||
|
||
for i in range(n):
|
||
for j in range(n):
|
||
if i != j:
|
||
A_mat[i, j] = compute_alpha_component(states[i], states[j])
|
||
B_mat[i, j] = compute_beta_component(states[i], states[j])
|
||
F_mat[i, j] = A_mat[i, j] + B_mat[i, j]
|
||
|
||
P_mat = circular_phase_matrix(states)
|
||
fisher = build_fisher_metric(states)
|
||
|
||
# Check anisotropy: max |B_ij + B_ji| should be ~0 (antisymmetric)
|
||
# but the Finsler matrix has asymmetric off-diagonals
|
||
anisotropic = np.any(np.abs(B_mat) > 1e-9)
|
||
|
||
return {
|
||
"states": [s.to_dict() for s in states],
|
||
"finsler_matrix": F_mat.tolist(),
|
||
"alpha_matrix": A_mat.tolist(),
|
||
"beta_matrix": B_mat.tolist(),
|
||
"phase_matrix": P_mat.tolist(),
|
||
"fisher_metric": fisher,
|
||
"is_anisotropic": bool(anisotropic),
|
||
}
|
||
|
||
|
||
if __name__ == "__main__":
|
||
result = build_full_finsler_pipeline()
|
||
print(f"Finsler metric computed for 8 Hachimoji states")
|
||
print(f"Anisotropic: {result['is_anisotropic']}")
|
||
print(f"Finsler matrix (first row): {result['finsler_matrix'][0]}")
|
||
print(f"Alpha matrix (first row): {result['alpha_matrix'][0]}")
|
||
print(f"Beta matrix (first row): {result['beta_matrix'][0]}")
|