ensemble / tests /test_structural.py
thefinalboss's picture
v0.3: structural query encoding (patterns + slots) for generalization. Expert.from_qa_pairs now accepts patterns=[...]; questions matching a template are encoded as bind(pattern_hv, slot_hv). On a capitals holdout benchmark, char-level returns empty 100% of the time on unseen slots; structural returns a well-formed answer 100% of the time (+100 pts graceful degradation). Honest: format-generalized guesses by analogy, not factual correctness (no embeddings). 91 tests passing.
27038ac verified
Raw
History Blame Contribute Delete
5.73 kB
"""Tests for the structural query encoder (patterns + slots)."""
from __future__ import annotations
import pytest
from ensemble import Expert, Brain
from ensemble.structural import StructuralEncoder
from palimseste.learner import Encoder
from palimseste.hv import similarity
import numpy as np
@pytest.fixture
def encoder():
return Encoder(D=3000, rng=np.random.default_rng(0))
@pytest.fixture
def se(encoder):
s = StructuralEncoder(encoder, D=3000)
s.learn_pattern("what is the capital of {country}")
s.learn_pattern("what is the population of {country}")
return s
class TestStructuralEncoder:
def test_learn_pattern(self, se):
assert se.n_patterns == 2
def test_learn_invalid_template(self, encoder):
se = StructuralEncoder(encoder, D=3000)
with pytest.raises(ValueError):
se.learn_pattern("no placeholder here")
with pytest.raises(ValueError):
se.learn_pattern("two {a} placeholders {b}")
def test_learn_idempotent(self, se):
n_before = se.n_patterns
se.learn_pattern("what is the capital of {country}")
assert se.n_patterns == n_before # no duplicate
def test_encode_matches(self, se):
r = se.encode("what is the capital of france")
assert r.matched
assert r.pattern is not None
assert r.slot_value == "france"
assert r.slot_hv is not None
def test_encode_no_match(self, se):
r = se.encode("what color is the sky")
assert not r.matched
assert r.pattern is None
def test_slot_hv_stable(self, se):
r1 = se.encode("what is the capital of france")
r2 = se.encode("what is the capital of france")
assert r1.slot_hv == r2.slot_hv
def test_observe_and_nearest_slot(self, se):
p = se.patterns[0]
se.observe_slot(p, "france")
se.observe_slot(p, "germany")
name, sim = se.nearest_slot(p, se._slot_hv("france"))
assert name == "france"
assert sim > 0.99 # identical
def test_nearest_slot_empty(self, se):
p = se.patterns[0]
name, sim = se.nearest_slot(p, se._slot_hv("x"))
assert name is None
def test_to_from_dict_roundtrip(self, se, encoder):
d = se.to_dict()
se2 = StructuralEncoder.from_dict(d, encoder)
assert se2.n_patterns == se.n_patterns
assert [p.template for p in se2.patterns] == [p.template for p in se.patterns]
def test_bind_cancels_pattern(self, se):
"""bind(pattern, slot) means same-pattern states differ only by slot."""
r_fr = se.encode("what is the capital of france")
r_sp = se.encode("what is the capital of spain")
r_pop = se.encode("what is the population of france")
# different patterns: orthogonal (bind with different pattern_hv)
assert similarity(r_fr.state_hv, r_pop.state_hv) < 0.1
class TestExpertStructural:
QA = [
("what is the capital of france", "the capital of france is paris"),
("what is the capital of germany", "the capital of germany is berlin"),
("what is the capital of italy", "the capital of italy is rome"),
] * 3
PATTERNS = ["what is the capital of {country}"]
def test_expert_learns_patterns(self):
e = Expert.from_qa_pairs(self.QA, domain="geo", D=3000,
patterns=self.PATTERNS)
assert e.n_patterns == 1
assert e._structural is not None
def test_relevance_positive_for_pattern(self):
e = Expert.from_qa_pairs(self.QA, domain="geo", D=3000,
patterns=self.PATTERNS)
# known AND unknown slots both match the pattern -> positive relevance
assert e.relevance("what is the capital of france") > 0.3
assert e.relevance("what is the capital of spain") > 0.3
def test_unknown_slot_returns_nonempty(self):
"""THE generalization guarantee: unseen slot -> non-empty answer."""
e = Expert.from_qa_pairs(self.QA, domain="geo", D=3000,
patterns=self.PATTERNS)
# spain was never in training data
ans = e.answer("what is the capital of spain")
assert ans != "", "structural expert should return a non-empty answer for unseen slots"
def test_char_level_returns_empty_for_unknown(self):
"""Baseline: char-level (no patterns) returns empty for unseen slots."""
e = Expert.from_qa_pairs(self.QA, domain="geo", D=3000) # no patterns
ans = e.answer("what is the capital of spain")
# char-level generalizes poorly -> empty or near-empty
assert ans == "" or len(ans) < 5
def test_no_patterns_falls_back_to_char(self):
e = Expert.from_qa_pairs(self.QA, domain="geo", D=3000)
assert e._structural is None
# relevance uses char-level path, doesn't crash
r = e.relevance("what is the capital of france")
assert -1.0 <= r <= 1.0
class TestStructuralBrain:
QA = [
("what is the capital of france", "the capital of france is paris"),
("what is the capital of germany", "the capital of germany is berlin"),
("what is the capital of italy", "the capital of italy is rome"),
] * 3
def test_brain_routes_structural_expert(self):
e = Expert.from_qa_pairs(self.QA, domain="geo", D=3000,
patterns=["what is the capital of {country}"])
brain = Brain()
brain.add_expert(e)
res = brain.query("what is the capital of spain")
# the structural expert should be routed to (dominant) and answer non-empty
assert res.dominant_expert == "geo"
assert res.answer != ""