MnemoCore / tests /test_hdv.py
Granis87's picture
Initial upload of MnemoCore
dbb04e4 verified
"""
Tests for HDV module (deprecated) and BinaryHDV compatibility shims.
This test file verifies:
1. Legacy HDV class still works (with deprecation warnings)
2. BinaryHDV compatibility shims work correctly
3. Migration path is valid
"""
import pytest
import warnings
import numpy as np
# Test legacy HDV (deprecated)
from mnemocore.core.hdv import HDV
from mnemocore.core.exceptions import DimensionMismatchError
# Test BinaryHDV with compatibility shims
from mnemocore.core.binary_hdv import BinaryHDV
class TestLegacyHDV:
"""Tests for the deprecated HDV class."""
def test_initialization(self):
"""Test HDV initialization."""
with warnings.catch_warnings(record=True) as w:
warnings.simplefilter("always")
hdv = HDV(dimension=1000)
assert hdv.vector.shape[0] == 1000
assert hdv.dimension == 1000
# Import warning is emitted
assert len(w) >= 1
assert issubclass(w[0].category, DeprecationWarning)
def test_xor_binding(self):
"""Test existing XOR binding behavior."""
with warnings.catch_warnings(record=True):
warnings.simplefilter("always")
v1 = HDV(dimension=100)
v2 = HDV(dimension=100)
# Test that __xor__ works
with warnings.catch_warnings(record=True) as w:
warnings.simplefilter("always")
v3 = v1 ^ v2
assert isinstance(v3, HDV)
assert v3.dimension == 100
assert v3.vector is not None
# Deprecation warning should be emitted
assert any(issubclass(x.category, DeprecationWarning) for x in w)
# Test commutative property (circular convolution is commutative)
with warnings.catch_warnings(record=True):
warnings.simplefilter("always")
v4 = v2 ^ v1
np.testing.assert_allclose(v3.vector, v4.vector, atol=1e-8, err_msg="Binding should be commutative")
def test_bind_method(self):
"""Test the bind method."""
with warnings.catch_warnings(record=True):
warnings.simplefilter("always")
v1 = HDV(dimension=100)
v2 = HDV(dimension=100)
with warnings.catch_warnings(record=True) as w:
warnings.simplefilter("always")
v3 = v1.bind(v2)
assert isinstance(v3, HDV)
assert v3.dimension == 100
# Deprecation warning should be emitted
assert any(issubclass(x.category, DeprecationWarning) for x in w)
# Should be equivalent to XOR (which is an alias)
with warnings.catch_warnings(record=True):
warnings.simplefilter("always")
v_xor = v1 ^ v2
np.testing.assert_allclose(v3.vector, v_xor.vector)
def test_dimension_mismatch(self):
"""Test that dimension mismatch raises error."""
with warnings.catch_warnings(record=True):
warnings.simplefilter("always")
v1 = HDV(dimension=100)
v2 = HDV(dimension=200)
with pytest.raises(DimensionMismatchError, match="Dimension mismatch"):
_ = v1 ^ v2
class TestBinaryHDVCompatibilityShims:
"""Tests for BinaryHDV compatibility shims that match HDV API."""
def test_bind_shim(self):
"""Test that bind() works as alias for xor_bind()."""
v1 = BinaryHDV.random(dimension=1000)
v2 = BinaryHDV.random(dimension=1000)
# bind() should be equivalent to xor_bind()
v_bind = v1.bind(v2)
v_xor = v1.xor_bind(v2)
assert v_bind == v_xor
def test_unbind_shim(self):
"""Test that unbind() works (XOR is self-inverse)."""
v1 = BinaryHDV.random(dimension=1000)
v2 = BinaryHDV.random(dimension=1000)
# unbind() should be equivalent to xor_bind() for XOR
v_unbind = v1.unbind(v2)
v_xor = v1.xor_bind(v2)
assert v_unbind == v_xor
# Self-inverse property: (a XOR b) XOR b = a
recovered = v_unbind.xor_bind(v2)
assert recovered == v1
def test_cosine_similarity_shim(self):
"""Test that cosine_similarity() is an alias for similarity()."""
v1 = BinaryHDV.random(dimension=1000)
v2 = BinaryHDV.random(dimension=1000)
cosine_sim = v1.cosine_similarity(v2)
sim = v1.similarity(v2)
assert cosine_sim == sim
assert 0.0 <= cosine_sim <= 1.0
def test_normalize_shim(self):
"""Test that normalize() returns a copy."""
v1 = BinaryHDV.random(dimension=1000)
normalized = v1.normalize()
# For binary vectors, normalize is a no-op (returns copy)
assert normalized == v1
assert normalized is not v1 # Should be a different object
assert normalized.data is not v1.data # Data should be copied
def test_xor_operator(self):
"""Test that __xor__ operator works for binding."""
v1 = BinaryHDV.random(dimension=1000)
v2 = BinaryHDV.random(dimension=1000)
# v1 ^ v2 should use xor_bind
v_xor = v1 ^ v2
v_bind = v1.xor_bind(v2)
assert v_xor == v_bind
def test_full_roundtrip(self):
"""Test a full roundtrip: bind, unbind, similarity."""
v_a = BinaryHDV.random(dimension=1000)
v_b = BinaryHDV.random(dimension=1000)
# Bind A and B
v_ab = v_a.bind(v_b)
# Unbind to recover A
v_recovered = v_ab.unbind(v_b)
# Should be similar to original A
similarity = v_a.similarity(v_recovered)
assert similarity == 1.0 # XOR is exact, not approximate
class TestMigrationPath:
"""Tests verifying the migration path from HDV to BinaryHDV."""
def test_api_equivalence(self):
"""
Verify that BinaryHDV has all methods needed to replace HDV.
This test documents the migration path.
"""
# Create equivalent vectors
# HDV: hdv = HDV(dimension=10000)
# BinaryHDV: hdv = BinaryHDV.random(dimension=16384)
binary_hdv = BinaryHDV.random(dimension=16384)
# All these methods should exist on BinaryHDV:
assert hasattr(binary_hdv, 'bind')
assert hasattr(binary_hdv, 'unbind')
assert hasattr(binary_hdv, 'permute')
assert hasattr(binary_hdv, 'cosine_similarity')
assert hasattr(binary_hdv, 'normalize')
assert hasattr(binary_hdv, 'xor_bind')
assert hasattr(binary_hdv, 'similarity')
assert hasattr(binary_hdv, 'hamming_distance')
def test_xor_binding_self_inverse(self):
"""
Demonstrate that XOR binding is self-inverse (unlike HRR).
This is a key difference but makes the API simpler.
"""
v_a = BinaryHDV.random(dimension=1000)
v_b = BinaryHDV.random(dimension=1000)
# XOR binding
v_ab = v_a.xor_bind(v_b)
# XOR unbinding (same operation!)
v_recovered = v_ab.xor_bind(v_b)
# Exact recovery (not approximate like HRR)
assert v_recovered == v_a
# Same with bind/unbind shims
v_ab_shim = v_a.bind(v_b)
v_recovered_shim = v_ab_shim.unbind(v_b)
assert v_recovered_shim == v_a