""" 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]}")