File size: 5,768 Bytes
ad474c9
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
import unittest

from hdc.hypervec import hvc, sim
from hdc.memory_modes import (
    HolographicMemory,
    HybridMemory,
    PhotographicMemory,
    capacity_report,
    compare_modes,
)

D = 2000
SYMBOLS = [hvc("test/symbol/%d" % i, D) for i in range(8)]


class TestPhotographic(unittest.TestCase):
    def test_recall_is_bit_exact(self):
        mem = PhotographicMemory(d=D)
        value = hvc("pixel/42", D)
        mem.capture("frame/42", value)
        got, score = mem.recall("frame/42")
        self.assertEqual(got, value & ((1 << D) - 1))
        self.assertEqual(score, 1.0)

    def test_miss_is_none(self):
        mem = PhotographicMemory(d=D)
        self.assertEqual(mem.recall("absent"), (None, 0.0))

    def test_capacity_evicts_oldest(self):
        mem = PhotographicMemory(d=D, capacity=2)
        for i in range(3):
            mem.capture("k/%d" % i, SYMBOLS[i])
        self.assertEqual(len(mem), 2)
        self.assertNotIn("k/0", mem)
        self.assertIn("k/2", mem)
        self.assertEqual(mem.stats()["evicted"], 1)

    def test_overwrite_does_not_evict(self):
        mem = PhotographicMemory(d=D, capacity=1)
        mem.capture("k", SYMBOLS[0])
        mem.capture("k", SYMBOLS[1])
        self.assertEqual(mem.recall("k")[0], SYMBOLS[1])
        self.assertEqual(mem.stats()["evicted"], 0)


class TestHolographic(unittest.TestCase):
    def test_majority_superposition_recalls_exactly_at_small_k(self):
        mem = HolographicMemory(d=D, alphabet=SYMBOLS)
        for i in range(4):
            mem.record("slot/%d" % i, SYMBOLS[i])
        for i in range(4):
            got, _ = mem.recall("slot/%d" % i)
            self.assertEqual(got, SYMBOLS[i], msg="slot %d" % i)

    def test_xor_accumulation_is_the_wrong_rule(self):
        # Regression: XOR crosstalk is itself random at every bit, so unbinding
        # is chance at any k.  Majority is what makes recall work at all.
        # Measured: XOR recalls 0/2, 0/4, 2/8 (chance); majority 8/8.
        from hdc.hypervec import bind

        for k in (2, 4, 8):
            trace = 0
            for i in range(k):
                trace ^= bind(hvc("xor/%d" % i, D), SYMBOLS[i], D)
            xor_hits = 0
            for i in range(k):
                probe = bind(trace, hvc("xor/%d" % i, D), D)
                best = max(SYMBOLS, key=lambda s: sim(probe, s, D))
                xor_hits += best == SYMBOLS[i]
            self.assertLessEqual(xor_hits, k // 2, msg="k=%d" % k)

            majority = HolographicMemory(d=D, alphabet=SYMBOLS)
            for i in range(k):
                majority.record("xor/%d" % i, SYMBOLS[i])
            self.assertEqual(
                sum(1 for i in range(k) if majority.recall("xor/%d" % i)[0] == SYMBOLS[i]),
                k,
                msg="k=%d" % k,
            )

    def test_confidence_falls_as_the_trace_fills(self):
        mem = HolographicMemory(d=D, alphabet=SYMBOLS)
        mem.record("slot/0", SYMBOLS[0])
        alone = mem.confidence("slot/0")
        for i in range(1, 6):
            mem.record("slot/%d" % i, SYMBOLS[i])
        crowded = mem.confidence("slot/0")
        self.assertLess(crowded, alone)
        self.assertGreater(alone, 0.9)

    def test_empty_memory_returns_nothing(self):
        mem = HolographicMemory(d=D, alphabet=SYMBOLS)
        self.assertEqual(mem.recall("anything"), (None, 0.0))
        self.assertEqual(mem.confidence("anything"), 0.0)

    def test_trace_is_bounded_by_dimension(self):
        mem = HolographicMemory(d=D, alphabet=SYMBOLS)
        for i in range(8):
            mem.record("slot/%d" % i, SYMBOLS[i])
        self.assertLess(mem.trace.bit_length(), D + 1)
        self.assertEqual(mem.stats()["items"], 8)

    def test_capacity_report_is_monotone_in_similarity(self):
        rows = capacity_report(D, sizes=(2, 8, 16), symbols=8)
        self.assertEqual([r["items"] for r in rows], [2, 8, 16])
        for earlier, later in zip(rows, rows[1:]):
            self.assertLessEqual(later["mean_similarity"], earlier["mean_similarity"])


class TestHybrid(unittest.TestCase):
    def test_exact_path_wins_when_present(self):
        mem = HybridMemory(d=D, buckets=4, alphabet=SYMBOLS)
        mem.capture("key/1", SYMBOLS[1])
        value, score, path = mem.recall("key/1")
        self.assertEqual((value, score, path), (SYMBOLS[1], 1.0, "exact"))

    def test_trace_path_answers_after_eviction(self):
        mem = HybridMemory(d=D, buckets=1, per_bucket=1, alphabet=SYMBOLS)
        mem.capture("first", SYMBOLS[0])
        mem.capture("second", SYMBOLS[1])  # evicts "first" from the exact index
        self.assertNotIn("first", mem.exact[0])
        value, score, path = mem.recall("first")
        self.assertEqual(path, "trace")
        self.assertIsNotNone(value)

    def test_buckets_are_disjoint(self):
        mem = HybridMemory(d=D, buckets=8, alphabet=SYMBOLS)
        mem.capture("key/1", SYMBOLS[1])
        filled = [i for i, cell in enumerate(mem.exact) if cell]
        self.assertEqual(len(filled), 1)

    def test_consolidate_matches_a_fresh_trace(self):
        mem = HybridMemory(d=D, buckets=2, alphabet=SYMBOLS)
        for i in range(4):
            mem.capture("k/%d" % i, SYMBOLS[i])
        self.assertEqual(mem.consolidate(), 0)
        for i in range(4):
            self.assertEqual(mem.recall("k/%d" % i)[0], SYMBOLS[i])

    def test_compare_modes_photographic_is_perfect(self):
        result = compare_modes(D, k=8, symbols=8)
        self.assertEqual(result["photographic"], 1.0)
        self.assertEqual(result["items"], 8)

    def test_zero_buckets_rejected(self):
        with self.assertRaises(ValueError):
            HybridMemory(d=D, buckets=0)


if __name__ == "__main__":
    unittest.main()