fractus-vorax / tests /test_brain_v2.py
thefinalboss's picture
Fractus-Vorax v1.0.0 — the takeover: sealed CTE brain + ingestion organs + mechanical speech (199 tests, honest floors)
1da7ac7 verified
Raw
History Blame Contribute Delete
7.34 kB
# tests/test_brain_v2.py
import numpy as np
from fractus_vorax.brain import ANALOGY_TRIGGER_SIM, Brain
from fractus_vorax.cards import Card
from fractus_vorax.compiler.atoms import Atom
from fractus_vorax.compiler.encode import text_hv
from fractus_vorax.hv import bind
CAPITALS = [
Atom("what is the capital of france", "paris", "cap.csv:2"),
Atom("what is the capital of spain", "madrid", "cap.csv:3"),
Atom("what is the capital of japan", "tokyo", "cap.csv:4"),
Atom("what is the capital of italy", "rome", "cap.csv:5"),
]
BOOKS = [Atom("who wrote hamlet", "william shakespeare", "books.csv:2")]
def _brain():
brain = Brain(D=2048)
brain.ingest_source("capitals", CAPITALS)
brain.ingest_source("books", BOOKS)
return brain
def test_ingest_source_spawns_expert_and_relations():
brain = _brain()
assert len(brain.experts) == 2
assert brain.experts.get("capitals") is not None
assert any("capital" in " ".join(p) for p in brain.relations)
assert brain.hebbian.n_writes >= 5
def test_ask_cards_fact():
brain = _brain()
cards = brain.ask_cards("what is the capital of japan", k=2)
facts = [c for c in cards if c.kind == "FAIT"]
assert facts and "tokyo" in facts[0].text
assert facts[0].sim > 0.5
def test_ask_cards_analogy_on_typo():
brain = _brain()
cards = brain.ask_cards("what is the capital of franc", k=3)
ana = [c for c in cards if c.kind == "ANALOGIE"]
assert ana and "paris" in ana[0].text
def test_consolidate_creates_composite():
brain = _brain()
for _ in range(3):
brain.ask_cards("what is the capital of france", k=2)
n0 = len(brain)
promoted = brain.consolidate(threshold=3)
assert promoted >= 1
assert len(brain) == n0 + promoted
comp = brain.atoms[-1]
assert comp.provenance == "consolidated"
def test_consolidate_is_idempotent():
brain = _brain()
for _ in range(3):
brain.ask_cards("what is the capital of france", k=2)
n1 = brain.consolidate(threshold=3)
n2 = brain.consolidate(threshold=3)
assert n1 >= 1 and n2 == 0 # plus de re-promotion
def test_plan1_compat_ask_still_works():
brain = _brain()
hits = brain.ask("what is the capital of spain", k=1)
assert hits[0][0].context == "madrid"
def test_save_load_roundtrip_v2(tmp_path):
brain = _brain()
brain.save(tmp_path / "brain")
loaded = Brain.load(tmp_path / "brain")
assert len(loaded.experts) == 2
assert loaded.hebbian.n_writes == brain.hebbian.n_writes
assert loaded.ask("who wrote hamlet", k=1)[0][0].context == "william shakespeare"
def test_load_plan1_brain_still_works(tmp_path):
"""Un brain Plan 1 (sans organes persistés) doit se charger."""
brain = Brain(D=2048)
brain.ingest_atoms(CAPITALS)
brain.save(tmp_path / "old")
# simuler l'absence des fichiers v2
import shutil
for sub in ("experts", "hebbian"):
shutil.rmtree(tmp_path / "old" / sub, ignore_errors=True)
(tmp_path / "old" / "coactivation.json").unlink(missing_ok=True)
(tmp_path / "old" / "relations.json").unlink(missing_ok=True)
loaded = Brain.load(tmp_path / "old")
assert len(loaded) == 4
assert len(loaded.experts) == 0
def test_analogy_trigger_is_named_constant():
"""Le seuil d'analogie est nommé et calibré : typos ~0.63-0.69, exact à 1.0."""
assert 0.6 < ANALOGY_TRIGGER_SIM < 0.9
def test_consolidate_roundtrip_preserves_composite_addr(tmp_path):
"""save/load doit rejouer addr = bind(addr_a, addr_b), pas text_hv(statement)."""
brain = _brain()
for _ in range(3):
brain.ask_cards("what is the capital of france", k=2)
assert brain.consolidate(threshold=3) >= 1
comp_id = len(brain) - 1
a_id, b_id = brain._composite_parents[comp_id]
addr_before = brain.traces.addrs[comp_id].copy()
expected = bind(brain.traces.addrs[a_id], brain.traces.addrs[b_id])
assert np.array_equal(addr_before, expected)
query = text_hv("what is the capital of france", brain.D, brain.seed)
top_before = brain.traces.retrieve(query, k=3)[0][0]
brain.save(tmp_path / "brain")
loaded = Brain.load(tmp_path / "brain")
assert np.array_equal(loaded.traces.addrs[-1], addr_before)
assert loaded._composite_parents[comp_id] == (a_id, b_id)
assert np.array_equal(
loaded.traces.addrs[comp_id],
bind(loaded.traces.addrs[a_id], loaded.traces.addrs[b_id]),
)
# le retrieval retrace pareil : même top-1 qu'avant la sauvegarde
assert loaded.traces.retrieve(query, k=3)[0][0] == top_before
def _force_depth2_composite(brain: Brain, comp_id: int, threshold: int = 3) -> None:
"""Fait entrer le composite (profondeur 1) dans une paire co-activée.
Les addr bind étant quasi orthogonales à tout, une requête hors-domaine
les classe dans le bruit proche de zéro : le top-k déterministe peut
inclure le composite. On essaie quelques (requête, k) ; sinon fallback
white-box : brain.coact.record() direct — même compteur qu'alimente
ask_cards(), acceptable et documenté.
"""
for query, k in [("zzz quantum lorem ipsum", 2), ("zzz quantum lorem ipsum", 3),
("xyzw quokka blorb", 2), ("xyzw quokka blorb", 3)]:
for _ in range(threshold):
brain.ask_cards(query, k=k)
if any(c >= threshold and comp_id in pair for pair, c in brain.coact.counts.items()):
return
for _ in range(threshold):
brain.coact.record([comp_id, 0])
def test_nested_composite_roundtrip_preserves_addr(tmp_path):
"""Composites imbriqués (profondeur ≥ 2) : addr bit-identique au save/load.
consolidate() lie les addr STOCKÉES des parents (le bind d'un parent
composite n'est pas re-productible par encode_atom), donc
addr = bind(addr_a_stockée, addr_b_stockée) à toute profondeur —
exactement le replay de Brain.load(). Avant le fix, le ré-encodage
divergeait dès la profondeur 2.
Étape « force top-k » : voir _force_depth2_composite — white-box fallback
documenté si la requête hors-domaine ne classe pas le composite.
"""
brain = _brain()
# profondeur 1 : co-activation répétée sur une question du domaine
for _ in range(3):
brain.ask_cards("what is the capital of france", k=2)
assert brain.consolidate(threshold=3) >= 1
comp_id = len(brain) - 1
assert comp_id in brain._composite_parents
# profondeur 2 : le composite doit co-activer avec un autre atome
_force_depth2_composite(brain, comp_id)
n_before = len(brain)
brain.consolidate(threshold=3)
assert len(brain) > n_before, "la consolidation de profondeur 2 n'a pas eu lieu"
d2 = [aid for aid, (pa, pb) in brain._composite_parents.items()
if aid > comp_id and comp_id in (pa, pb)]
assert d2, "aucun composite de profondeur 2 créé (parents ne contiennent pas comp_id)"
d2_id = d2[0]
p_a, p_b = brain._composite_parents[d2_id]
addr_before = brain.traces.addrs[d2_id].copy()
brain.save(tmp_path / "brain")
loaded = Brain.load(tmp_path / "brain")
assert np.array_equal(loaded.traces.addrs[d2_id], addr_before)
assert np.array_equal(
loaded.traces.addrs[d2_id],
bind(loaded.traces.addrs[p_a], loaded.traces.addrs[p_b]),
)