File size: 7,554 Bytes
dbb04e4 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 | """
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
|