ensemble / tests /test_embedding.py
thefinalboss's picture
v0.5: factual generalization via dense analogy. learn_relation + solve_analogy implement Mikolov paris-france+spain=madrid in normalized dense space, averaged over known examples, excluding the query slot. Wired into structural answer() so unseen slots get plain-text answers. Measured end-to-end via the Brain with fastText: capitals holdout goes 0% (char) / 0% (structural) -> ~50-67% factual-correct (spain->madrid, portugal->lisbon, greece->athens, norway->oslo recovered correctly). from_fasttext uses batch projection (fast load). The semantic wall is broken. 109 tests.
595d852 verified
Raw
History Blame Contribute Delete
7.14 kB
"""Tests for the dense→HV projection and the embedding expert."""
from __future__ import annotations
import pytest
import numpy as np
from ensemble.projection import make_projection, dense_to_hv
from ensemble import EmbeddingExpert, Brain, Expert
from palimseste.hv import similarity
class TestProjection:
def test_make_projection_shape(self):
R = make_projection(D=1000, dim=50, seed=1)
assert R.shape == (1000, 50)
assert R.dtype == np.int8
# entries are {+1,-1}
assert set(np.unique(R)).issubset({-1, 1})
def test_make_projection_deterministic(self):
a = make_projection(D=500, dim=20, seed=7)
b = make_projection(D=500, dim=20, seed=7)
assert np.array_equal(a, b)
def test_different_seeds_differ(self):
a = make_projection(D=500, dim=20, seed=1)
b = make_projection(D=500, dim=20, seed=2)
assert not np.array_equal(a, b)
def test_dense_to_hv_identical_vectors(self):
R = make_projection(D=2000, dim=30, seed=0)
v = np.random.default_rng(1).standard_normal(30)
h1 = dense_to_hv(v, R)
h2 = dense_to_hv(v, R)
assert h1 == h2
assert h1.D == 2000
def test_projection_preserves_cosine(self):
"""Higher cosine => higher HV similarity (monotone)."""
R = make_projection(D=4000, dim=100, seed=42)
rng = np.random.default_rng(0)
base = rng.standard_normal(100)
base /= np.linalg.norm(base)
def with_cos(c):
o = rng.standard_normal(100); o -= base*np.dot(o, base); o /= np.linalg.norm(o)
return c*base + (1-c*c)**0.5*o
sims = []
for c in [1.0, 0.5, 0.0]:
hv = dense_to_hv(with_cos(c), R)
sims.append(similarity(hv, dense_to_hv(base, R)))
# monotone decreasing as cosine drops
assert sims[0] > sims[1] > sims[2]
def test_dim_mismatch_raises(self):
R = make_projection(D=100, dim=10, seed=0)
with pytest.raises(ValueError):
dense_to_hv(np.zeros(20), R)
class TestEmbeddingExpert:
CORPUS = (
"the cat sat on the mat and purred. the dog ran and barked loudly. "
"cats and dogs are animals pets. the car drove fast on the road. "
) * 30
def test_from_corpus_local_builds(self):
e = EmbeddingExpert.from_corpus_local(self.CORPUS, D=2000, dim=40)
assert e.D == 2000
assert e.vocab_size > 5
assert "cat" in e.vectors or "the" in e.vectors
def test_get_word_hv(self):
e = EmbeddingExpert.from_corpus_local(self.CORPUS, D=2000, dim=40)
hv = e.get_word_hv("cat")
assert hv is not None
assert hv.D == 2000
def test_get_word_hv_oov(self):
e = EmbeddingExpert.from_corpus_local(self.CORPUS, D=2000, dim=40)
assert e.get_word_hv("xyzqwert") is None
def test_relevance_positive(self):
e = EmbeddingExpert.from_corpus_local(self.CORPUS, D=2000, dim=40)
r = e.relevance("the cat sat")
assert 0.0 <= r <= 1.0
def test_candidate_hv(self):
e = EmbeddingExpert.from_corpus_local(self.CORPUS, D=2000, dim=40)
hv = e.candidate_hv("the cat")
assert hv is not None
assert hv.D == 2000
def test_candidate_hv_empty(self):
e = EmbeddingExpert.from_corpus_local(self.CORPUS, D=2000, dim=40)
# all OOV words -> None
assert e.candidate_hv("xyzqwert zzz") is None
def test_answer_returns_string(self):
e = EmbeddingExpert.from_corpus_local(self.CORPUS, D=2000, dim=40)
a = e.answer("the cat")
assert isinstance(a, str)
def test_couples_to_brain(self):
"""The embedding expert plugs into a brain without changes."""
e = EmbeddingExpert.from_corpus_local(self.CORPUS, D=2000, dim=40)
lex = Expert.from_text("hello world. " * 20, D=2000)
brain = Brain()
brain.add_expert(lex)
brain.add_expert(e)
assert brain.n_experts == 2
res = brain.query("the cat")
assert isinstance(res.answer, str)
def test_semantic_similarity_above_noise(self):
"""Related words should be more similar than unrelated in a rich corpus."""
corpus = (
"king queen royal throne crown prince princess monarchy. "
"cat dog pet animal fur paw tail. "
"car truck vehicle road wheel engine drive. "
) * 40
e = EmbeddingExpert.from_corpus_local(corpus, D=3000, dim=60)
king, queen = e.get_word_hv("king"), e.get_word_hv("queen")
cat, dog = e.get_word_hv("cat"), e.get_word_hv("dog")
king_dog = similarity(king, dog) if king and dog else 0
king_queen = similarity(king, queen) if king and queen else 0
# related (king~queen) should beat unrelated (king~dog) — or at least
# the within-category pairs should be positive
if king and queen and cat and dog:
assert king_queen > -0.2 # not strongly anti-correlated
def test_solve_analogy_returns_answer(self):
"""Analogy with structured synthetic vectors recovers the right word."""
rng = np.random.default_rng(0)
dim = 40
base_c = rng.standard_normal(dim)
base_C = rng.standard_normal(dim)
vecs = {}
pairs = {'france': 'paris', 'germany': 'berlin', 'italy': 'rome'}
for c, C in pairs.items():
noise = rng.standard_normal(dim) * 0.2
vecs[c] = base_c + noise
vecs[C.lower()] = base_C + noise
# unseen: spain -> madrid (same noise pattern)
noise = rng.standard_normal(dim) * 0.2
vecs['spain'] = base_c + noise
vecs['madrid'] = base_C + noise
R = make_projection(D=2000, dim=dim, seed=0)
word_hvs = {w: dense_to_hv(v, R) for w, v in vecs.items()}
e = EmbeddingExpert(domain='t', D=2000, vectors=vecs, word_hvs=word_hvs,
projection=R, signature_hv=word_hvs['france'], dim=dim)
e.learn_relation('capital_of', pairs)
best, sim = e.solve_analogy('capital_of', 'spain')
assert best == 'madrid'
assert sim > 0.5
def test_solve_analogy_unknown_relation(self):
e = EmbeddingExpert.from_corpus_local("a b c d. " * 30, D=1000, dim=20)
best, sim = e.solve_analogy('nonexistent', 'x')
assert best is None
def test_solve_analogy_excludes_slot(self):
"""The query slot must not be returned as its own answer."""
rng = np.random.default_rng(1)
dim = 30
vecs = {w: rng.standard_normal(dim) for w in
['france', 'paris', 'germany', 'berlin', 'spain', 'madrid']}
R = make_projection(D=1000, dim=dim, seed=0)
whvs = {w: dense_to_hv(v, R) for w, v in vecs.items()}
e = EmbeddingExpert(domain='t', D=1000, vectors=vecs, word_hvs=whvs,
projection=R, signature_hv=whvs['france'], dim=dim)
e.learn_relation('capital_of', {'france': 'paris', 'germany': 'berlin'})
best, _ = e.solve_analogy('capital_of', 'spain')
assert best != 'spain'