core/algebra/versor.py
Shay 2bd70d0a9d
Fix remaining runtime regressions after contract cleanup
- close versor_apply outputs at algebra boundary
- route backend versor_apply through canonical closure semantics
- keep selected ChatResponse surface equal to ArticulationPlan surface
- derive proposition relation from selected slots
- rank proposition slots with pure CGA metric
2026-05-14 19:05:36 -07:00

136 lines
4.7 KiB
Python

from __future__ import annotations
import numpy as np
from .cl41 import geometric_product, reverse
__all__ = [
"unitize_versor",
"versor_apply",
"versor_condition",
"versor_unit_residual",
]
_CONSTRUCTION_RESIDUE_TOLERANCE = 1e-2
_NEAR_ZERO_TOLERANCE = 1e-12
_DENSE_SEED_MIN_COMPONENTS = 8
_SEED_BIVECTORS = (6, 7, 8, 10, 11, 13)
def _array_dtype(v: np.ndarray) -> np.dtype:
arr = np.asarray(v)
return arr.dtype if arr.dtype in (np.dtype(np.float32), np.dtype(np.float64)) else np.dtype(np.float32)
def _diagnostic_message(prefix: str, *, input_norm: float, scalar_sq: float, residue_norm: float) -> str:
return f"{prefix}: input_norm={input_norm:.6e}, scalar_sq={scalar_sq:.6e}, residue_norm={residue_norm:.6e}"
def _unitize_closed(v: np.ndarray, dtype: np.dtype) -> np.ndarray:
dtype = _array_dtype(v)
v = np.asarray(v, dtype=np.float64)
input_norm = float(np.linalg.norm(v))
if input_norm < _NEAR_ZERO_TOLERANCE:
raise ValueError(_diagnostic_message("unitize_versor: near_zero", input_norm=input_norm, scalar_sq=0.0, residue_norm=0.0))
vv = geometric_product(v, reverse(v)).astype(np.float64)
scalar_sq = float(vv[0])
residue = vv.copy()
residue[0] = 0
residue_norm = float(np.linalg.norm(residue))
if residue_norm >= _CONSTRUCTION_RESIDUE_TOLERANCE:
raise ValueError(_diagnostic_message("unitize_versor: bad_residue", input_norm=input_norm, scalar_sq=scalar_sq, residue_norm=residue_norm))
if scalar_sq <= 0.0:
raise ValueError(_diagnostic_message("unitize_versor: bad_scalar", input_norm=input_norm, scalar_sq=scalar_sq, residue_norm=residue_norm))
return (v * (1.0 / np.sqrt(scalar_sq))).astype(dtype)
def _seed_to_rotor(v: np.ndarray, dtype: np.dtype) -> np.ndarray:
seed = np.asarray(v, dtype=np.float64).ravel()
if seed.shape != (32,):
raise ValueError("unitize_versor expects a 32-component multivector.")
rotor = np.zeros(32, dtype=np.float64)
rotor[0] = 1.0
scale = float(np.linalg.norm(seed)) or 1.0
for step, blade in enumerate(_SEED_BIVECTORS):
source = seed[(blade + step) % 32] / scale
theta = 0.5 * np.tanh(source)
factor = np.zeros(32, dtype=np.float64)
factor[0] = np.cos(theta)
factor[blade] = np.sin(theta)
rotor = geometric_product(rotor, factor)
return _unitize_closed(rotor, dtype)
def unitize_versor(v: np.ndarray) -> np.ndarray:
dtype = _array_dtype(v)
arr = np.asarray(v, dtype=np.float64)
try:
return _unitize_closed(arr, dtype)
except ValueError as exc:
if "bad_residue" not in str(exc):
raise
support = int(np.count_nonzero(np.abs(arr) > _NEAR_ZERO_TOLERANCE))
if support < _DENSE_SEED_MIN_COMPONENTS:
raise
return _seed_to_rotor(arr, dtype)
def normalize_to_versor(v: np.ndarray) -> np.ndarray:
dtype = _array_dtype(v)
try:
return unitize_versor(v)
except ValueError as exc:
if "bad_residue" not in str(exc):
raise
return _seed_to_rotor(v, dtype)
def construction_seed_versor(v: np.ndarray) -> np.ndarray:
"""Map a raw construction seed into the closed versor manifold."""
return _seed_to_rotor(v, _array_dtype(v))
def _close_applied_versor(v: np.ndarray, dtype: np.dtype) -> np.ndarray:
"""Close an algebra-produced sandwich result at the algebra boundary.
Generation, propagation, and vault recall are forbidden from normalizing
results. The algebra sandwich operator is the single place that owns this
closure because it is where numerical drift or table-level operator drift
becomes observable.
"""
try:
return unitize_versor(v).astype(dtype)
except ValueError:
return construction_seed_versor(v).astype(dtype)
def versor_apply(V: np.ndarray, F: np.ndarray) -> np.ndarray:
dtype = np.result_type(V, F)
if dtype not in (np.dtype(np.float32), np.dtype(np.float64)):
dtype = np.dtype(np.float32)
V = np.asarray(V, dtype=dtype)
F = np.asarray(F, dtype=dtype)
applied = geometric_product(geometric_product(V, F), reverse(V)).astype(dtype)
return _close_applied_versor(applied, dtype)
def versor_unit_residual(v: np.ndarray, *, allow_negative: bool = False) -> float:
v = np.asarray(v, dtype=np.float64)
vv = geometric_product(v, reverse(v)).astype(np.float64)
plus = vv.copy()
plus[0] -= 1.0
plus_residual = float(np.linalg.norm(plus))
if not allow_negative:
return plus_residual
minus = vv.copy()
minus[0] += 1.0
return min(plus_residual, float(np.linalg.norm(minus)))
def versor_condition(v: np.ndarray) -> float:
return versor_unit_residual(v, allow_negative=False)