SilverSight/tests/quarantine/q16_roundtrip_test.legacy.py
allaun 4490dc28a7 feat(rrc): bare-minimum RRC refactor into SilverSight
- Move canonical FixedPoint to Core/SilverSight/FixedPoint.lean
- Add SilverSightRRC library: RRC logogram gates, receipt bridge, AVM ISA
- Add AVMIsa.Emit as the sole top-level JSON output boundary
- Add rrc-emit-fixture executable and Python I/O shims
- Update AGENTS.md, glossary, project map, and build baseline

Build: 2981 jobs, 0 errors (lake build)
2026-06-21 09:08:48 -05:00

426 lines
14 KiB
Python

"""Q16_16 Cross-Language Roundtrip Test
Tests that all three implementations (Lean spec, Python, C) agree on
Q16_16 conversions. This is the core correctness property of the rebuild.
Test Strategy:
1. 1,000 random floats: Python == C (both use banker's rounding)
2. Edge cases: 0.0, -0.0, min, max, half-LSB boundaries
3. Half-LSB tie cases: values exactly between two Q16_16 values
4. Integer roundtrip: exact for all in-range integers
DISAGREEMENT = BUG. All three implementations must produce identical results.
"""
import ctypes
import math
import os
import random
import struct
import subprocess
import sys
import tempfile
import unittest
# Import Python implementation
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', 'PythonBridge'))
from q16_canonical import (
float_to_q16 as py_float_to_q16,
q16_to_float as py_q16_to_float,
int_to_q16 as py_int_to_q16,
q16_to_int as py_q16_to_int,
Q16_SCALE,
Q16_MIN_RAW,
Q16_MAX_RAW,
Q16_RESOLUTION,
)
# ============================================================
# §1 C INTERFACE SETUP
# ============================================================
# Compile the C implementation
def _compile_c_lib():
"""Compile q16_canonical.c into a shared library."""
c_src = os.path.join(os.path.dirname(__file__), '..', 'CBride', 'q16_canonical.c')
c_dir = os.path.dirname(c_src)
# Try different library extensions
lib_name = 'libq16.so'
lib_path = os.path.join(c_dir, lib_name)
compile_cmd = ['gcc', '-shared', '-fPIC', '-O2', '-Wall', c_src, '-o', lib_path, '-lm']
try:
result = subprocess.run(compile_cmd, capture_output=True, text=True, cwd=c_dir)
if result.returncode != 0:
print(f"C compilation failed: {result.stderr}")
return None
return lib_path
except FileNotFoundError:
print("gcc not found, skipping C tests")
return None
C_LIB_PATH = _compile_c_lib()
C_AVAILABLE = C_LIB_PATH is not None and os.path.exists(C_LIB_PATH)
if C_AVAILABLE:
_lib = ctypes.CDLL(C_LIB_PATH)
# float_to_q16
_lib.float_to_q16_nearbyint.argtypes = [ctypes.c_double]
_lib.float_to_q16_nearbyint.restype = ctypes.c_int32
# q16_to_float
_lib.q16_to_float.argtypes = [ctypes.c_int32]
_lib.q16_to_float.restype = ctypes.c_double
# int_to_q16
_lib.int_to_q16.argtypes = [ctypes.c_int32]
_lib.int_to_q16.restype = ctypes.c_int32
# q16_to_int
_lib.q16_to_int.argtypes = [ctypes.c_int32]
_lib.q16_to_int.restype = ctypes.c_int32
def c_float_to_q16(f):
return _lib.float_to_q16_nearbyint(f)
def c_q16_to_float(q):
return _lib.q16_to_float(q)
def c_int_to_q16(i):
return _lib.int_to_q16(i)
def c_q16_to_int(q):
return _lib.q16_to_int(q)
else:
c_float_to_q16 = None
c_q16_to_float = None
c_int_to_q16 = None
c_q16_to_int = None
# ============================================================
# §2 TEST CASES
# ============================================================
class TestQ16Roundtrip(unittest.TestCase):
"""Test that Python and C implementations agree."""
def _check_agreement(self, label, py_val, c_val):
"""Check that Python and C values agree."""
self.assertEqual(
py_val, c_val,
f"MISMATCH on {label}: Python={py_val}, C={c_val}"
)
# ---- §2.1 Edge Cases ----------------------------------------
def test_zero(self):
"""0.0 converts exactly."""
py = py_float_to_q16(0.0)
self.assertEqual(py, 0)
self.assertEqual(py_q16_to_float(py), 0.0)
if C_AVAILABLE:
c = c_float_to_q16(0.0)
self._check_agreement("0.0", py, c)
def test_negative_zero(self):
"""-0.0 converts to 0 (same as 0.0)."""
py = py_float_to_q16(-0.0)
self.assertEqual(py, 0)
if C_AVAILABLE:
c = c_float_to_q16(-0.0)
self._check_agreement("-0.0", py, c)
def test_one(self):
"""1.0 converts exactly to 65536."""
py = py_float_to_q16(1.0)
self.assertEqual(py, 65536)
self.assertAlmostEqual(py_q16_to_float(py), 1.0, places=10)
if C_AVAILABLE:
c = c_float_to_q16(1.0)
self._check_agreement("1.0", py, c)
def test_minus_one(self):
"""-1.0 converts exactly to -65536."""
py = py_float_to_q16(-1.0)
self.assertEqual(py, -65536)
self.assertAlmostEqual(py_q16_to_float(py), -1.0, places=10)
if C_AVAILABLE:
c = c_float_to_q16(-1.0)
self._check_agreement("-1.0", py, c)
def test_min_value(self):
"""Minimum representable value: -32768.0"""
py = py_float_to_q16(-32768.0)
self.assertEqual(py, -32768 * 65536)
self.assertAlmostEqual(py_q16_to_float(py), -32768.0, places=5)
if C_AVAILABLE:
c = c_float_to_q16(-32768.0)
self._check_agreement("-32768.0", py, c)
def test_max_value(self):
"""Maximum representable value: 32767.9999847412109375"""
py = py_float_to_q16(32767.9999847412109375)
self.assertEqual(py, Q16_MAX_RAW)
self.assertAlmostEqual(py_q16_to_float(py), 32767.9999847412109375, places=5)
if C_AVAILABLE:
c = c_float_to_q16(32767.9999847412109375)
self._check_agreement("max_value", py, c)
def test_half_lsb_positive(self):
"""+0.5/65536 = +0.00000762939453125 (half LSB, should round to 0 = even)."""
half_lsb = 0.5 / Q16_SCALE # = 0.00000762939453125
py = py_float_to_q16(half_lsb)
# 0.5 * 65536 / 65536 = 0.5, tie case: round to even (0)
self.assertEqual(py, 0, f"half_lsb should round to 0 (even), got {py}")
if C_AVAILABLE:
c = c_float_to_q16(half_lsb)
self._check_agreement("half_lsb_positive", py, c)
def test_half_lsb_negative(self):
"""-0.5/65536 (half LSB negative, should round to 0 = even)."""
half_lsb = -0.5 / Q16_SCALE
py = py_float_to_q16(half_lsb)
# -0.5 * 65536 = -32768, scaled = -0.5, tie: round to even (0)
self.assertEqual(py, 0, f"-half_lsb should round to 0 (even), got {py}")
if C_AVAILABLE:
c = c_float_to_q16(half_lsb)
self._check_agreement("half_lsb_negative", py, c)
def test_three_half_lsb(self):
"""1.5/65536 (should round to 2 since 2 is even... wait: 1.5 rounds to 2).
Actually: 1.5 rounds to 2 (nearest even to 1.5 is 2).
"""
val = 1.5 / Q16_SCALE
py = py_float_to_q16(val)
# scaled = 1.5, tie at 1.5, nearest even of {1, 2} is 2
self.assertEqual(py, 2, f"1.5 LSB should round to 2 (even), got {py}")
if C_AVAILABLE:
c = c_float_to_q16(val)
self._check_agreement("1.5_lsb", py, c)
def test_two_and_half_lsb(self):
"""2.5/65536 (should round to 2 since 2 is even)."""
val = 2.5 / Q16_SCALE
py = py_float_to_q16(val)
# scaled = 2.5, tie at 2.5, nearest even of {2, 3} is 2
self.assertEqual(py, 2, f"2.5 LSB should round to 2 (even), got {py}")
if C_AVAILABLE:
c = c_float_to_q16(val)
self._check_agreement("2.5_lsb", py, c)
# ---- §2.2 Integer Roundtrip ----------------------------------
def test_int_roundtrip_all_small(self):
"""Integer roundtrip is exact for integers in [-1000, 1000]."""
for i in range(-1000, 1001):
py_q = py_int_to_q16(i)
py_i = py_q16_to_int(py_q)
self.assertEqual(py_i, i, f"int roundtrip failed for {i}: got {py_i}")
if C_AVAILABLE:
c_q = c_int_to_q16(i)
c_i = c_q16_to_int(c_q)
self._check_agreement(f"int_roundtrip({i})", py_i, c_i)
def test_int_roundtrip_boundary(self):
"""Integer roundtrip at range boundaries."""
boundaries = [-32768, -32767, -1, 0, 1, 32766, 32767]
for i in boundaries:
py_q = py_int_to_q16(i)
py_i = py_q16_to_int(py_q)
self.assertEqual(py_i, i, f"int roundtrip failed for {i}")
if C_AVAILABLE:
c_q = c_int_to_q16(i)
c_i = c_q16_to_int(c_q)
self._check_agreement(f"int_boundary({i})", py_i, c_i)
# ---- §2.3 Float Roundtrip ------------------------------------
def test_float_roundtrip_random(self):
"""Float roundtrip error < 1/65536 for random values."""
seed = 42
rng = random.Random(seed)
for trial in range(1000):
f = rng.uniform(-32768.0, 32767.9999)
py_q = py_float_to_q16(f)
py_f = py_q16_to_float(py_q)
err = abs(py_f - f)
self.assertLess(
err, Q16_RESOLUTION,
f"Roundtrip error too large for {f}: |{py_f} - {f}| = {err}"
)
def test_python_c_agreement_random(self):
"""Python and C agree on 1,000 random floats."""
if not C_AVAILABLE:
self.skipTest("C library not available")
seed = 42
rng = random.Random(seed)
mismatches = 0
for trial in range(1000):
f = rng.uniform(-32768.0, 32767.9999)
py_q = py_float_to_q16(f)
c_q = c_float_to_q16(f)
if py_q != c_q:
mismatches += 1
# Report first few mismatches in detail
if mismatches <= 5:
scaled = f * Q16_SCALE
print(f" MISMATCH #{mismatches}: f={f}")
print(f" scaled={scaled}, Python={py_q}, C={c_q}")
self.assertEqual(
mismatches, 0,
f"Python and C disagree on {mismatches}/1000 random values"
)
def test_python_c_agreement_tie_cases(self):
"""Python and C agree on half-LSB tie cases."""
if not C_AVAILABLE:
self.skipTest("C library not available")
# Generate tie cases: values where f * 65536 has fractional part = 0.5
# These are: (n + 0.5) / 65536 for integer n
mismatches = 0
for n in range(-100, 101):
f = (n + 0.5) / Q16_SCALE
py_q = py_float_to_q16(f)
c_q = c_float_to_q16(f)
if py_q != c_q:
mismatches += 1
if mismatches <= 5:
print(f" TIE MISMATCH: n={n}, f={f}, Python={py_q}, C={c_q}")
self.assertEqual(
mismatches, 0,
f"Python and C disagree on {mismatches} tie cases"
)
# ---- §2.4 Arithmetic Operations -------------------------------
def test_add_basic(self):
"""Q16_16 addition works."""
a = py_float_to_q16(1.5)
b = py_float_to_q16(2.25)
result_q = py_float_to_q16(1.5 + 2.25)
# Just verify no crash and result is reasonable
self.assertTrue(Q16_MIN_RAW <= a <= Q16_MAX_RAW)
self.assertTrue(Q16_MIN_RAW <= b <= Q16_MAX_RAW)
def test_saturation(self):
"""Addition saturates at max value."""
max_q = py_float_to_q16(30000.0)
big_q = py_float_to_q16(30000.0)
# In real add with saturation: max_q + big_q should clamp
# ---- §2.5 Precision Tests -------------------------------------
def test_pi(self):
"""π is represented within 1 LSB."""
py = py_float_to_q16(math.pi)
py_f = py_q16_to_float(py)
err = abs(py_f - math.pi)
self.assertLess(err, Q16_RESOLUTION)
def test_e(self):
"""e is represented within 1 LSB."""
py = py_float_to_q16(math.e)
py_f = py_q16_to_float(py)
err = abs(py_f - math.e)
self.assertLess(err, Q16_RESOLUTION)
def test_sqrt2(self):
"""√2 is represented within 1 LSB."""
py = py_float_to_q16(math.sqrt(2))
py_f = py_q16_to_float(py)
err = abs(py_f - math.sqrt(2))
self.assertLess(err, Q16_RESOLUTION)
# ---- §2.6 Stress Test -----------------------------------------
def test_stress_banker_rounding(self):
"""Stress test banker's rounding consistency."""
if not C_AVAILABLE:
self.skipTest("C library not available")
seed = 12345
rng = random.Random(seed)
mismatches = 0
# Focus on values near tie boundaries
for trial in range(5000):
# Mix of random and boundary-focused values
if trial % 10 == 0:
# Near tie boundary
n = rng.randint(-100000, 100000)
f = (n + 0.5 + rng.uniform(-0.01, 0.01)) / Q16_SCALE
else:
f = rng.uniform(-32768.0, 32767.9999)
py_q = py_float_to_q16(f)
c_q = c_float_to_q16(f)
if py_q != c_q:
mismatches += 1
self.assertEqual(
mismatches, 0,
f"Python and C disagree on {mismatches}/5000 stress test values"
)
def run_test_summary():
"""Run all tests and print a summary."""
print("=" * 60)
print("Q16_16 Cross-Language Roundtrip Test")
print("=" * 60)
print()
# Check C availability
if C_AVAILABLE:
print(f"[OK] C library loaded: {C_LIB_PATH}")
else:
print("[WARN] C library not available (gcc missing?)")
print()
# Run tests
loader = unittest.TestLoader()
suite = loader.loadTestsFromTestCase(TestQ16Roundtrip)
runner = unittest.TextTestRunner(verbosity=2)
result = runner.run(suite)
# Summary
print()
print("=" * 60)
print("TEST SUMMARY")
print("=" * 60)
print(f" Tests run: {result.testsRun}")
print(f" Failures: {len(result.failures)}")
print(f" Errors: {len(result.errors)}")
print(f" Skipped: {len(result.skipped)}")
print()
if result.wasSuccessful():
print(" STATUS: ALL TESTS PASSED ✓")
print()
print(" Q16_16 rounding is CANONICAL across Python and C.")
print(" Lean specification: CoreFormalism/FixedPoint.lean")
print(" Python implementation: PythonBridge/q16_canonical.py")
print(" C implementation: CBride/q16_canonical.c")
return 0
else:
print(" STATUS: SOME TESTS FAILED ✗")
return 1
if __name__ == '__main__':
sys.exit(run_test_summary())