File size: 7,144 Bytes
0bee73d 595d852 | 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 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 | """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'
|