core/algebra/versor.py

50 lines
1.6 KiB
Python

"""
The three versor primitives.
These are the ONLY normalization/transition/check functions in the system.
Do not add correction, monitoring, or grade-guard functions here.
If you think you need something else, you have an unclosed operation upstream.
"""
import numpy as np
from .cl41 import geometric_product, reverse, scalar_part, norm_squared
def versor_apply(V: np.ndarray, F: np.ndarray) -> np.ndarray:
"""
Sandwich product: V * F * reverse(V).
The ONLY allowed field transition in the system.
Algebraically closed on the versor manifold:
if V and F are versors, V*F*reverse(V) is a versor.
No pre/post normalization. No grade projection. No guards.
"""
return geometric_product(V, geometric_product(F, reverse(V)))
def normalize_to_versor(F: np.ndarray) -> np.ndarray:
"""
Project F onto the versor manifold: F / sqrt(|F * reverse(F)|).
Call this ONCE per input at the injection gate (ingest/gate.py).
Never call mid-propagation, mid-generation, or in the vault.
If you feel the urge to call this elsewhere, fix the upstream operation.
"""
n2 = norm_squared(F)
if abs(n2) < 1e-12:
raise ValueError("Cannot normalize a null multivector to a versor.")
return F / np.sqrt(abs(n2))
def versor_condition(F: np.ndarray) -> float:
"""
Returns ||F * reverse(F) - 1||_F.
Zero means F is on the versor manifold.
Use in tests and at the injection gate only.
Never call in the generation hot path.
"""
product = geometric_product(F, reverse(F))
product = product.copy()
product[0] -= 1.0
return float(np.linalg.norm(product))