fix(algebra): make versor_apply close runtime field results

Remove the implicit null-vector bypass from the runtime-facing versor_apply closure boundary.

FieldState.F is treated throughout the runtime and cognitive pipeline as a unit versor field. Returning null-like raw sandwich results from versor_apply created a contract mismatch and allowed multi-turn closure drift to escape into session state.

- make _close_applied_versor always close runtime field results
- keep unitize-first semantics and construction-seed fallback
- add regression proving null-like sandwich output is closed for the runtime contract

Null-vector preservation should return later behind an explicit geometry API, not the generic runtime field propagation path.

No GitHub CI/status checks were exposed for this PR.
This commit is contained in:
Shay 2026-05-15 23:12:26 -07:00 committed by GitHub
parent 9b7310c07c
commit 7aa8626ec4
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 17 additions and 13 deletions

View file

@ -14,7 +14,6 @@ _CONSTRUCTION_RESIDUE_TOLERANCE = 1e-2
_NEAR_ZERO_TOLERANCE = 1e-12
_DENSE_SEED_MIN_COMPONENTS = 8
_SEED_BIVECTORS = (6, 7, 8, 10, 11, 13)
_NULL_SCALAR_TOLERANCE = 1e-9
def _array_dtype(v: np.ndarray) -> np.dtype:
@ -95,22 +94,15 @@ def construction_seed_versor(v: np.ndarray) -> np.ndarray:
return _seed_to_rotor(v, _array_dtype(v))
def _is_null_vector(v: np.ndarray) -> bool:
product = geometric_product(v, v).astype(np.float64)
return float(np.linalg.norm(product)) < _NULL_SCALAR_TOLERANCE
def _close_applied_versor(v: np.ndarray, dtype: np.dtype) -> np.ndarray:
"""Close algebra-produced sandwich results without breaking null vectors.
"""Close algebra-produced sandwich results for runtime field states.
CGA sandwiching must preserve null vectors as null vectors. Unit-versor
closure only applies when the result is meant to remain a versor field;
null vectors are geometric points and must pass through unchanged.
``versor_apply`` is the field-propagation API: callers expect the returned
multivector to satisfy ``versor_condition(result)``. CGA point/null-vector
preservation belongs behind an explicit geometry API, not this runtime
manifold boundary.
"""
arr = np.asarray(v, dtype=dtype)
if _is_null_vector(arr):
return arr.astype(dtype)
try:
return unitize_versor(arr).astype(dtype)
except ValueError:

View file

@ -103,6 +103,18 @@ def test_composition_closed():
assert versor_condition(F3) < 1e-4
def test_versor_apply_closes_null_like_field_results_for_runtime_contract():
identity = np.zeros(32, dtype=np.float32)
identity[0] = 1.0
null_like = np.zeros(32, dtype=np.float32)
null_like[1] = 1.0
null_like[5] = 1.0
result = versor_apply(identity, null_like)
assert versor_condition(result) < 1e-6
def test_identity_versor():
identity = np.zeros(32, dtype=np.float32)
identity[0] = 1.0