Fail closed on invalid versor construction

Make versor construction fail closed instead of synthesizing hash-derived fallback rotors.

- remove pseudo-random construction fallback from unitize_versor
- add signed residual helper for +1 field states vs ±1 manifold entries
- validate vocab manifold entries with full residuals
- document antipodal transition rotor failure contract
- add focused invariant tests for versor closure and manifold validation
This commit is contained in:
Shay 2026-05-14 10:55:11 -07:00 committed by GitHub
parent aadaf11612
commit 9723941a38
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 197 additions and 145 deletions

View file

@ -25,12 +25,22 @@ def word_transition_rotor(A: np.ndarray, B: np.ndarray) -> np.ndarray:
encode a position. Call this from algebra-aware field logic; never
store the result on a vocabulary structure.
Antipodal or near-antipodal inputs can make 1 + B * reverse(A) null or
near-zero. That is an ill-conditioned transition construction, not a
case for synthetic fallback. unitize_versor() must fail closed, and the
caller must decide whether to skip, terminate, or choose another edge.
Args:
A: Source versor, shape (32,), grade-normed to ±1.
B: Target versor, shape (32,), grade-normed to ±1.
Returns:
R: Unitized rotor in Cl(4,1), shape (32,).
Raises:
ValueError: if the transition rotor is null, near-zero, non-scalar
after multiplication by its reverse, or otherwise cannot be
scaled into a clean +1 operator.
"""
R = geometric_product(B, reverse(A))
R = R.copy()

View file

@ -1,120 +1,55 @@
"""
algebra/versor.py Versor operations for Cl(4,1).
Normalization doctrine:
unitize_versor(v) CONSTRUCTION primitive.
Call this when building rotors, motors, or
manifold entries from raw arrays. It is the
algebra layer's legitimate construction operation.
May be called in: algebra/, persona/, vocab/ (pre-add).
normalize_to_versor(v) GATE primitive. Internal to ingest/gate.py.
Normalizes raw holonomy output to a versor at
the injection boundary. Do not call this anywhere
else in production code. It is NOT the same
operation as unitize_versor conceptually it is
the boundary crossing from raw data into the field.
FORBIDDEN: calling either function inside propagation, generation,
vault recall, or as a post-hoc repair for a supposedly
closed transition. If you need normalization there, the
algebra is not closed fix the operator, not the result.
"""
from __future__ import annotations
import hashlib
import numpy as np
from .cl41 import geometric_product, reverse, N_COMPONENTS
from .cl41 import geometric_product, reverse
__all__ = [
"unitize_versor",
"versor_apply",
"versor_condition",
# normalize_to_versor is intentionally NOT in __all__.
# Import it explicitly only if you are ingest/gate.py.
"versor_unit_residual",
]
_CONSTRUCTION_RESIDUE_TOLERANCE = 1e-2
_NEAR_ZERO_TOLERANCE = 1e-12
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_versor(v: np.ndarray) -> np.ndarray:
"""
Construction-time algebra primitive.
Scale v so that the scalar part of v * reverse(v) equals +1.
Use this when building rotors, motors, or vocabulary entries
from raw computed arrays.
This is not a repair operation. It is valid only during construction
of new algebraic objects, never as a correction inside propagation.
Args:
v: shape (N_COMPONENTS,) float32 multivector.
Returns:
Scaled copy of v satisfying |V * ~V|_scalar 1.
Raises:
ValueError: if v is a null, zero, or near-zero multivector.
"""
arr = np.asarray(v)
dtype = arr.dtype if arr.dtype in (np.dtype(np.float32), np.dtype(np.float64)) else np.dtype(np.float32)
dtype = _array_dtype(v)
v = np.asarray(v, dtype=dtype)
vv = geometric_product(v, reverse(v))
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(dtype)
scalar_sq = float(vv[0])
if float(np.linalg.norm(v)) < 1e-12:
raise ValueError(
"unitize_versor: null, zero, or near-zero multivector; cannot unitize."
)
residue = vv.copy()
residue[0] = 0
if float(np.linalg.norm(residue)) < 1e-7 and scalar_sq > 0:
scale = 1.0 / np.sqrt(scalar_sq)
return (v * scale).astype(dtype)
residue_norm = float(np.linalg.norm(residue))
digest = hashlib.sha256(np.ascontiguousarray(v).view(np.uint8)).digest()
flat_idx = digest[0]
theta_unit = int.from_bytes(digest[1:5], "big") / 2**32
theta = 0.05 + theta_unit * (np.pi - 0.1)
sign_idx = int(np.argmax(np.abs(v[1:]))) + 1
if float(v[sign_idx]) < 0:
theta = -theta
negative_bivectors = (6, 7, 9, 10, 12, 14)
rotor = np.zeros(N_COMPONENTS, dtype=dtype)
rotor[0] = np.cos(theta)
rotor[negative_bivectors[flat_idx % len(negative_bivectors)]] = np.sin(theta)
return rotor.astype(dtype)
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 normalize_to_versor(v: np.ndarray) -> np.ndarray:
"""
Gate-only injection primitive. Reserved for ingest/gate.py.
Do not call this function outside the injection gate.
For construction of algebraic objects, use unitize_versor() instead.
"""
# Implementation is identical to unitize_versor — the distinction
# is semantic and enforced by convention + docs + test rules.
return unitize_versor(v)
def versor_apply(V: np.ndarray, F: np.ndarray) -> np.ndarray:
"""
Apply versor V to field state F via the sandwich product.
F' = V * F * reverse(V)
This is the ONLY way field state changes in production code.
No normalization is applied here. The sandwich product of two
valid versors is always a valid versor algebraic closure is
the invariant, not runtime monitoring.
Args:
V: versor operator, shape (N_COMPONENTS,).
F: field state, shape (N_COMPONENTS,).
Returns:
F': transformed field state, shape (N_COMPONENTS,).
"""
dtype = np.result_type(V, F)
if dtype not in (np.dtype(np.float32), np.dtype(np.float64)):
dtype = np.dtype(np.float32)
@ -123,18 +58,19 @@ def versor_apply(V: np.ndarray, F: np.ndarray) -> np.ndarray:
return geometric_product(geometric_product(V, F), reverse(V)).astype(dtype)
def versor_condition(v: np.ndarray) -> float:
"""
Full residual distance from the unit-versor condition.
Computes ||v * reverse(v) - 1||_F, not a signed scalar shortcut.
Zero means v satisfies the unit-versor condition. Any non-scalar residue
or scalar drift contributes positively to the residual.
"""
v = np.asarray(v)
dtype = v.dtype if v.dtype in (np.dtype(np.float32), np.dtype(np.float64)) else np.dtype(np.float32)
def versor_unit_residual(v: np.ndarray, *, allow_negative: bool = False) -> float:
dtype = _array_dtype(v)
v = np.asarray(v, dtype=dtype)
vv = geometric_product(v, reverse(v)).astype(dtype)
vv = vv.copy()
vv[0] -= 1.0
return float(np.linalg.norm(vv))
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)

View file

@ -4,10 +4,17 @@ It verifies the core algebraic invariant of the entire system.
"""
import numpy as np
import pytest
from hypothesis import given, settings
from hypothesis import strategies as st
from algebra.versor import versor_apply, unitize_versor, versor_condition
from algebra.rotor import word_transition_rotor
from algebra.versor import (
unitize_versor,
versor_apply,
versor_condition,
versor_unit_residual,
)
def _positive_unit_reflector(seed=None) -> np.ndarray:
@ -16,8 +23,7 @@ def _positive_unit_reflector(seed=None) -> np.ndarray:
The current field action uses V * F * reverse(V), so the operator fixture
must satisfy V * reverse(V) = +1, not -1. We therefore keep the fifth
(negative-metric) basis component bounded below the positive four-space
norm before construction-unitizing.
basis component bounded below the positive four-space norm.
"""
rng = np.random.default_rng(seed)
vec4 = rng.standard_normal(4).astype(np.float32)
@ -38,7 +44,6 @@ def _positive_unit_reflector(seed=None) -> np.ndarray:
@given(st.integers(min_value=0, max_value=99))
@settings(max_examples=100)
def test_versor_apply_preserves_manifold(seed):
"""V*F*reverse(V) must be a versor if V and F are positive unit versors."""
V = _positive_unit_reflector(seed)
F = _positive_unit_reflector(seed + 1000)
result = versor_apply(V, F)
@ -46,18 +51,50 @@ def test_versor_apply_preserves_manifold(seed):
assert cond < 1e-4, f"versor_apply broke the manifold: condition={cond:.2e}"
def test_unitize_random_multivector_constructs_versor():
"""
unitize_versor() is the construction primitive for lifting raw
deterministic coordinates into a valid versor.
"""
raw = np.random.default_rng(0).standard_normal(32).astype(np.float32)
def test_unitize_clean_scalar_constructs_positive_unit_versor():
raw = np.zeros(32, dtype=np.float32)
raw[0] = 2.0
V = unitize_versor(raw)
assert versor_condition(V) < 1e-5
assert np.allclose(V[0], 1.0, atol=1e-7)
assert versor_condition(V) < 1e-7
def test_unitize_rejects_non_scalar_residue_instead_of_hash_fallback():
dirty = np.zeros(32, dtype=np.float32)
dirty[0] = np.sqrt(0.5)
dirty[1] = np.sqrt(0.5)
with pytest.raises(ValueError, match="bad_residue"):
unitize_versor(dirty)
def test_unitize_rejects_non_positive_scalar_norm():
negative_norm = np.zeros(32, dtype=np.float32)
negative_norm[5] = 1.0
with pytest.raises(ValueError, match="bad_scalar"):
unitize_versor(negative_norm)
def test_versor_unit_residual_can_accept_signed_manifold_versors():
negative_norm = np.zeros(32, dtype=np.float32)
negative_norm[5] = 1.0
assert versor_condition(negative_norm) > 1.0
assert versor_unit_residual(negative_norm, allow_negative=True) < 1e-7
def test_word_transition_rotor_fails_closed_for_antipodal_inputs():
A = np.zeros(32, dtype=np.float32)
A[0] = 1.0
B = np.zeros(32, dtype=np.float32)
B[0] = -1.0
with pytest.raises(ValueError, match="near_zero"):
word_transition_rotor(A, B)
def test_composition_closed():
"""Two sequential versor_apply calls stay on the manifold."""
V1 = _positive_unit_reflector(0)
V2 = _positive_unit_reflector(1)
F = _positive_unit_reflector(2)
@ -67,7 +104,6 @@ def test_composition_closed():
def test_identity_versor():
"""Scalar 1 is a valid versor and applies as identity."""
identity = np.zeros(32, dtype=np.float32)
identity[0] = 1.0
F = _positive_unit_reflector(42)

View file

@ -0,0 +1,52 @@
import numpy as np
import pytest
from vocab.manifold import VocabManifold
def _identity() -> np.ndarray:
v = np.zeros(32, dtype=np.float32)
v[0] = 1.0
return v
def _negative_unit_vector() -> np.ndarray:
v = np.zeros(32, dtype=np.float32)
v[5] = 1.0
return v
def _scalar_norm_one_with_non_scalar_residue() -> np.ndarray:
v = np.zeros(32, dtype=np.float32)
v[0] = np.sqrt(0.5)
v[1] = np.sqrt(0.5)
return v
def test_manifold_accepts_positive_unit_versor() -> None:
manifold = VocabManifold()
manifold.add("one", _identity())
assert manifold.index_of("one") == 0
def test_manifold_accepts_negative_unit_versor() -> None:
"""Vocabulary manifold entries follow the mathematical ±1 versor contract."""
manifold = VocabManifold()
manifold.add("negative", _negative_unit_vector())
assert manifold.index_of("negative") == 0
def test_manifold_rejects_scalar_norm_shortcut_with_non_scalar_residue() -> None:
"""Scalar grade-norm near one is insufficient when residue is non-scalar."""
manifold = VocabManifold()
with pytest.raises(ValueError, match="non_scalar_residue"):
manifold.add("dirty", _scalar_norm_one_with_non_scalar_residue())
def test_manifold_update_rejects_non_scalar_residue() -> None:
manifold = VocabManifold()
manifold.add("clean", _identity())
with pytest.raises(ValueError, match="replacement versor residual"):
manifold.update("clean", _scalar_norm_one_with_non_scalar_residue())

View file

@ -4,9 +4,10 @@ VocabManifold — the geometric vocabulary.
Each word is a versor in Cl(4,1). nearest(F) finds the closest word
by CGA inner product no cosine similarity, no ANN index.
Invariant: every stored versor must satisfy the Cl(4,1) grade-norm
condition |V * reverse(V)|_scalar ±1. This is enforced at insertion
time in add() and at replacement time in update().
Invariant: every stored versor must satisfy the full Cl(4,1) unit-versor
condition V * reverse(V) ±1. This rejects non-scalar construction residue,
not merely scalar grade-norm drift, and is enforced at insertion time in
add() and at replacement time in update().
Normalization doctrine for this module:
- Raw coordinate vectors (e.g. from external embeddings) must be
@ -30,13 +31,41 @@ dispatches to the Rust extension when available.
import numpy as np
from algebra.backend import cga_inner
from algebra.cl41 import geometric_product, reverse
from algebra.versor import versor_unit_residual
from language_packs.schema import MorphologyEntry
_MANIFOLD_RESIDUAL_TOLERANCE = 1e-5
def _versor_diagnostics(v: np.ndarray) -> tuple[float, float, float]:
product = geometric_product(v, reverse(v))
scalar = float(product[0])
residue = product.copy()
residue[0] = 0.0
residue_norm = float(np.linalg.norm(residue))
residual = versor_unit_residual(v, allow_negative=True)
return residual, scalar, residue_norm
def _assert_manifold_versor(word: str, versor: np.ndarray, *, replacement: bool = False) -> None:
residual, scalar, residue_norm = _versor_diagnostics(versor)
if residual > _MANIFOLD_RESIDUAL_TOLERANCE:
noun = "replacement versor" if replacement else "versor"
action = "Call algebra.versor.unitize_versor() before update()." if replacement else (
"If lifting from a raw array, call algebra.versor.unitize_versor() first."
)
raise ValueError(
f"Word '{word}': {noun} residual {residual:.4e} exceeds "
f"{_MANIFOLD_RESIDUAL_TOLERANCE:.1e}; scalar={scalar:.4f}, "
f"non_scalar_residue={residue_norm:.4e}. Pass a clean Cl(4,1) "
f"unit versor satisfying V*reverse(V)≈±1. {action}"
)
class VocabManifold:
def __init__(self):
self._words: list[str] = []
self._versors: list[np.ndarray] = [] # each shape (32,), grade-normed to ±1
self._versors: list[np.ndarray] = [] # each shape (32,), unit-versor ±1
self._morphology_by_word: dict[str, MorphologyEntry] = {}
self._language_by_word: dict[str, str] = {}
self._transient_words: set[str] = set()
@ -52,10 +81,10 @@ class VocabManifold:
"""
Register a word-versor pair.
Enforces the Cl(4,1) versor invariant: the scalar part of
V * reverse(V) must be ±1. This rejects any raw coordinate
vector or external embedding that has not been lifted into the
algebra.
Enforces the Cl(4,1) manifold invariant: V * reverse(V) must be
approximately +1 or -1 as a full multivector residual, not merely
in its scalar component. This rejects raw coordinates, external
embeddings, and dirty construction products.
If your source is a raw float array, call
algebra.versor.unitize_versor() first that is the construction-time
@ -63,16 +92,10 @@ class VocabManifold:
that function is reserved for the injection gate.
Raises:
ValueError: if the grade-norm condition is not satisfied.
ValueError: if the full unit-versor residual is not satisfied.
"""
v = np.asarray(versor, dtype=np.float32).copy()
grade_norm = float(geometric_product(v, reverse(v))[0])
if not (0.95 <= abs(grade_norm) <= 1.05):
raise ValueError(
f"Word '{word}': versor grade-norm {grade_norm:.4f} ≠ ±1. "
"Pass a valid Cl(4,1) versor. "
"If lifting from a raw array, call algebra.versor.unitize_versor() first."
)
_assert_manifold_versor(word, v)
self._words.append(word)
self._versors.append(v)
resolved_language = language or (morphology.language if morphology is not None else None)
@ -134,24 +157,19 @@ class VocabManifold:
Used by the alignment correction pass after compilation to nudge
cross-language aligned pairs toward each other without rebuilding
the full manifold. The grade-norm invariant is enforced identically
to add().
the full manifold. The full unit-versor residual is enforced
identically to add().
Raises:
KeyError: if the word is not already in the manifold.
ValueError: if the grade-norm condition is not satisfied.
ValueError: if the full unit-versor residual is not satisfied.
"""
try:
idx = self._words.index(word)
except ValueError:
raise KeyError(f"Word '{word}' not in vocabulary; use add() for new entries.")
v = np.asarray(versor, dtype=np.float32).copy()
grade_norm = float(geometric_product(v, reverse(v))[0])
if not (0.95 <= abs(grade_norm) <= 1.05):
raise ValueError(
f"Word '{word}': replacement versor grade-norm {grade_norm:.4f} ≠ ±1. "
"Call algebra.versor.unitize_versor() before update()."
)
_assert_manifold_versor(word, v, replacement=True)
self._versors[idx] = v
def get_versor(self, word: str) -> np.ndarray: