| """Tests for cortex features 4-7: multimodal, meta-learning, dual memory, analogies.""" |
|
|
| import pytest |
| import numpy as np |
|
|
| from palimseste.lm import PalimpsesteForCausalLM, PalimpsesteConfig |
| from palimseste.cortex import ( |
| MultimodalFusion, MetaLearner, DualMemory, Analogizer, |
| ModalityBinding, MetaLearningResult, MemoryRecord, AnalogyResult, |
| ) |
| from palimseste.hv_word2vec import HVWord2Vec, Word2VecConfig |
|
|
|
|
| def _build_model(D=3000, ctx=64, radius=100): |
| cfg = PalimpsesteConfig(D=D, context_window=ctx, kernel_radius=radius, temperature=0.0) |
| lm = PalimpsesteForCausalLM(config=cfg) |
| lm.build_tokenizer("hello world test memory image text analogical reasoning python france") |
| return lm |
|
|
|
|
| |
| class TestMultimodalFusion: |
| def test_learn_image(self): |
| lm = _build_model() |
| fusion = MultimodalFusion(mem=lm.mem, encoder=lm.encoder) |
| img = np.random.randint(0, 255, (16, 16, 3), dtype=np.uint8) |
| binding = fusion.learn_image("a red square", img) |
| assert isinstance(binding, ModalityBinding) |
| assert fusion.n_bindings == 1 |
|
|
| def test_find_image(self): |
| lm = _build_model() |
| fusion = MultimodalFusion(mem=lm.mem, encoder=lm.encoder) |
| img = np.random.randint(0, 255, (16, 16, 3), dtype=np.uint8) |
| fusion.learn_image("a red square", img) |
| results = fusion.find_image("red") |
| assert len(results) > 0 |
| assert "red square" in results[0][0] |
|
|
| def test_describe_image(self): |
| lm = _build_model() |
| fusion = MultimodalFusion(mem=lm.mem, encoder=lm.encoder) |
| img = np.random.randint(0, 255, (16, 16, 3), dtype=np.uint8) |
| fusion.learn_image("a blue circle", img) |
| results = fusion.describe_image(img) |
| assert len(results) > 0 |
| |
| assert results[0][1] > 0.9 |
|
|
| def test_multiple_bindings(self): |
| lm = _build_model() |
| fusion = MultimodalFusion(mem=lm.mem, encoder=lm.encoder) |
| for label in ["cat", "dog", "bird"]: |
| img = np.random.randint(0, 255, (16, 16, 3), dtype=np.uint8) |
| fusion.learn_image(label, img) |
| assert fusion.n_bindings == 3 |
|
|
|
|
| |
| class TestMetaLearner: |
| def test_adapt(self): |
| lm = _build_model() |
| |
| lm.train_on_qa_pairs([("hello", "hi there"), ("what is x", "x is y")]) |
| meta = MetaLearner(mem=lm.mem, phi=lm.phi) |
| result = meta.adapt() |
| assert isinstance(result, MetaLearningResult) |
|
|
| def test_current_config(self): |
| lm = _build_model() |
| meta = MetaLearner(mem=lm.mem, phi=lm.phi) |
| cfg = meta.current_config |
| assert "radius" in cfg |
| assert "min_weight" in cfg |
|
|
| def test_n_adaptations(self): |
| lm = _build_model() |
| meta = MetaLearner(mem=lm.mem, phi=lm.phi) |
| assert meta.n_adaptations >= 0 |
|
|
|
|
| |
| class TestDualMemory: |
| def test_store_episodic(self): |
| lm = _build_model() |
| dm = DualMemory(mem=lm.mem, encoder=lm.encoder) |
| record = dm.store_episodic("user said hello") |
| assert record.memory_type == "episodic" |
| assert dm.n_episodic == 1 |
|
|
| def test_store_semantic(self): |
| lm = _build_model() |
| dm = DualMemory(mem=lm.mem, encoder=lm.encoder) |
| record = dm.store_semantic("the sky is blue") |
| assert record.memory_type == "semantic" |
| assert dm.n_semantic == 1 |
|
|
| def test_recall(self): |
| lm = _build_model() |
| dm = DualMemory(mem=lm.mem, encoder=lm.encoder) |
| dm.store_semantic("python is a programming language") |
| dm.store_episodic("user asked about python") |
| results = dm.recall("python", top_k=3) |
| assert len(results) > 0 |
| assert all(isinstance(r[0], MemoryRecord) for r in results) |
|
|
| def test_forget_old(self): |
| lm = _build_model() |
| dm = DualMemory(mem=lm.mem, encoder=lm.encoder) |
| dm.store_episodic("old memory") |
| dm.store_semantic("persistent fact") |
| |
| n = dm.forget_old_episodic(max_age=0) |
| assert n >= 1 |
| assert dm.n_episodic == 0 |
| assert dm.n_semantic == 1 |
|
|
| def test_counts(self): |
| lm = _build_model() |
| dm = DualMemory(mem=lm.mem, encoder=lm.encoder) |
| dm.store_episodic("e1") |
| dm.store_episodic("e2") |
| dm.store_semantic("s1") |
| assert dm.n_episodic == 2 |
| assert dm.n_semantic == 1 |
| assert dm.total == 3 |
|
|
|
|
| |
| class TestAnalogizer: |
| def _build_w2v(self): |
| w2v = HVWord2Vec(config=Word2VecConfig(D=3000, n_epochs=3, min_word_freq=1)) |
| corpus = ("france paris england london germany berlin italy rome japan tokyo " |
| "france paris england london germany berlin italy rome japan tokyo " |
| "france england germany italy japan paris london berlin rome tokyo") |
| w2v.train(corpus) |
| return w2v |
|
|
| def test_analogy_returns_result(self): |
| w2v = self._build_w2v() |
| analogizer = Analogizer(word2vec=w2v) |
| result = analogizer.analogy("paris", "france", "tokyo") |
| assert result is not None |
| assert isinstance(result, AnalogyResult) |
| assert result.c == "tokyo" |
|
|
| def test_analogy_unknown_word(self): |
| w2v = self._build_w2v() |
| analogizer = Analogizer(word2vec=w2v) |
| result = analogizer.analogy("paris", "france", "xyznonexistent") |
| assert result is None |
|
|
| def test_analogy_no_w2v(self): |
| analogizer = Analogizer(word2vec=None) |
| result = analogizer.analogy("a", "b", "c") |
| assert result is None |
|
|
| def test_find_relations(self): |
| w2v = self._build_w2v() |
| analogizer = Analogizer(word2vec=w2v) |
| related = analogizer.find_relations("france", top_k=3) |
| assert len(related) > 0 |
| |
| assert related[0][0] in ["italy", "england", "germany", "japan"] |
|
|
| def test_find_relations_no_w2v(self): |
| analogizer = Analogizer(word2vec=None) |
| result = analogizer.find_relations("test") |
| assert result == [] |
|
|