From bacf1f4084be21b4b78fccebd078198769639cb8 Mon Sep 17 00:00:00 2001 From: Shay Date: Wed, 13 May 2026 12:55:20 -0700 Subject: [PATCH] Use true versor fixtures in closure tests --- tests/test_versor_closure.py | 47 ++++++++++++++++++++++++------------ 1 file changed, 32 insertions(+), 15 deletions(-) diff --git a/tests/test_versor_closure.py b/tests/test_versor_closure.py index 74674ede..5b1ac38a 100644 --- a/tests/test_versor_closure.py +++ b/tests/test_versor_closure.py @@ -4,41 +4,58 @@ 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, normalize_to_versor, versor_condition +from algebra.versor import versor_apply, unitize_versor, versor_condition -def _random_versor(seed=None) -> np.ndarray: +def _unit_reflector(seed=None) -> np.ndarray: + """ + Construct a true Cl(4,1) versor: a unit grade-1 vector. + + Scaling an arbitrary 32D multivector does not make it a versor. A unit + vector is a valid reflector by construction and satisfies v * ~v = ±1. + """ rng = np.random.default_rng(seed) - raw = rng.standard_normal(32).astype(np.float32) - return normalize_to_versor(raw) + vec = rng.standard_normal(5).astype(np.float32) + # Avoid accidentally producing an exactly null vector under (+,+,+,+,-). + if abs(float(np.dot(vec[:4], vec[:4]) - vec[4] * vec[4])) < 1e-4: + vec[0] += 1.0 + mv = np.zeros(32, dtype=np.float32) + mv[1:6] = vec + return unitize_versor(mv) @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 versors.""" - V = _random_versor(seed) - F = _random_versor(seed + 1000) + V = _unit_reflector(seed) + F = _unit_reflector(seed + 1000) result = versor_apply(V, F) cond = versor_condition(result) assert cond < 1e-4, f"versor_apply broke the manifold: condition={cond:.2e}" -def test_normalize_produces_versor(): - raw = np.random.randn(32).astype(np.float32) - V = normalize_to_versor(raw) - assert versor_condition(V) < 1e-6 +def test_unitize_random_multivector_is_not_claimed_to_create_versor(): + """ + Unitizing arbitrary 32D garbage is not versor construction. + + unitize_versor() scales a construction product; it does not project an + arbitrary multivector onto the versor manifold. This test prevents the + old false fixture from returning. + """ + raw = np.random.default_rng(0).standard_normal(32).astype(np.float32) + V = unitize_versor(raw) + assert versor_condition(V) > 1e-3 def test_composition_closed(): """Two sequential versor_apply calls stay on the manifold.""" - V1 = _random_versor(0) - V2 = _random_versor(1) - F = _random_versor(2) + V1 = _unit_reflector(0) + V2 = _unit_reflector(1) + F = _unit_reflector(2) F2 = versor_apply(V1, F) F3 = versor_apply(V2, F2) assert versor_condition(F3) < 1e-4 @@ -48,6 +65,6 @@ 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 = _random_versor(42) + F = _unit_reflector(42) result = versor_apply(identity, F) assert np.allclose(result, F, atol=1e-5)