File size: 4,163 Bytes
22d1ad7 | 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 | """Tests for the Brain's central persistent memory."""
from __future__ import annotations
import pytest
from ensemble import Brain, Expert, BrainMemory
from palimseste.hv import random_hv, similarity
class TestBrainMemory:
def test_create_empty(self):
bm = BrainMemory(D=2000)
assert bm.n_concepts == 0
assert len(bm) == 0
def test_remember_and_recall(self):
bm = BrainMemory(D=2000)
addr = random_hv(2000, rng=__import__("numpy").random.default_rng(1))
val = random_hv(2000, rng=__import__("numpy").random.default_rng(2))
bm.remember(addr, val, weight=1.0, tag="test")
assert bm.n_concepts == 1
# recall with the exact address -> should retrieve the value
results = bm.recall(addr, top_k=1)
assert len(results) >= 1
assert results[0].value == val
def test_recall_empty_memory(self):
bm = BrainMemory(D=2000)
q = random_hv(2000)
assert bm.recall(q) == []
def test_dimension_mismatch_raises(self):
bm = BrainMemory(D=2000)
bad = random_hv(3000)
with pytest.raises(ValueError):
bm.remember(bad, bad)
def test_reconstruct(self):
bm = BrainMemory(D=2000)
rng = __import__("numpy").random.default_rng(5)
addr = random_hv(2000, rng=rng)
val = random_hv(2000, rng=rng)
bm.remember(addr, val)
recon = bm.reconstruct(addr)
assert recon is not None
assert similarity(recon, val) > 0.5
def test_save_load_round_trip(self, tmp_path):
bm = BrainMemory(D=2000)
rng = __import__("numpy").random.default_rng(7)
for i in range(5):
bm.remember(random_hv(2000, rng=rng), random_hv(2000, rng=rng))
assert bm.n_concepts == 5
bm.save(tmp_path / "mem.bin")
bm2 = BrainMemory.load(tmp_path / "mem.bin", D=2000)
assert bm2.n_concepts == 5
class TestBrainCentralMemory:
def test_brain_creates_memory_on_first_expert(self):
brain = Brain()
assert brain._brain_memory is None
e = Expert.from_text("hello world. " * 20, D=2000)
brain.add_expert(e)
assert brain._brain_memory is not None
assert brain._D == 2000
def test_brain_remember_recall(self):
brain = Brain()
e = Expert.from_text("hello world. " * 20, D=2000)
brain.add_expert(e)
rng = __import__("numpy").random.default_rng(3)
addr = random_hv(2000, rng=rng)
val = random_hv(2000, rng=rng)
brain.remember(addr, val, tag="manual")
assert brain.n_concepts == 1
results = brain.recall(addr)
assert len(results) >= 1
def test_brain_save_load_persists_concepts(self, tmp_path):
brain = Brain()
e = Expert.from_text("hello world. " * 20, D=2000)
brain.add_expert(e)
rng = __import__("numpy").random.default_rng(9)
for _ in range(3):
brain.remember(random_hv(2000, rng=rng), random_hv(2000, rng=rng))
n_before = brain.n_concepts
assert n_before == 3
brain.save(tmp_path / "brain")
brain2 = Brain.load(tmp_path / "brain")
assert brain2.n_concepts == n_before
def test_think_writes_to_central_memory(self):
"""thinking should write concepts into the central memory when they emerge."""
# use overlapping experts so cross-expert concepts can form
a = Expert.from_qa_pairs(
[("what is x", "x is a thing")] * 4, domain="a", D=3000)
b = Expert.from_qa_pairs(
[("what is x", "x is an object")] * 4, domain="b", D=3000)
brain = Brain()
brain.add_expert(a)
brain.add_expert(b)
n0 = brain.n_concepts
brain.think(n_cycles=4)
# thinking ran without crashing; concepts may or may not form
# (depends on coherence), but n_concepts must be accessible
assert brain.n_concepts >= n0
def test_brain_with_D_param(self):
brain = Brain(D=2500)
assert brain._D == 2500
assert brain._brain_memory is not None
assert brain._brain_memory.D == 2500
|