File size: 5,488 Bytes
ad474c9
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
import unittest

from hdc.hypervec import hvc, sim
from hdc.ring import (
    PHASES,
    HistoricLedger,
    LedgerEntry,
    RingController,
    drifting_world,
    evolve,
    periodic_world,
    report,
    rewind,
    tick_key,
)

D = 2000


class TestAlgebraicRing(unittest.TestCase):
    def test_evolution_is_invertible(self):
        key = hvc("ring/test-key", D)
        for i in range(4):
            state = hvc("ring/state/%d" % i, D)
            for k in (1, 2, 7):
                self.assertEqual(rewind(evolve(state, key, k, D), key, k, D), state)

    def test_evolution_actually_moves_the_state(self):
        key = hvc("ring/test-key", D)
        state = hvc("ring/state/0", D)
        self.assertLess(abs(sim(evolve(state, key, 1, D), state, D)), 0.1)

    def test_tick_keys_are_distinct(self):
        self.assertNotEqual(tick_key(0, D), tick_key(1, D))
        self.assertLess(abs(sim(tick_key(0, D), tick_key(1, D), D)), 0.1)


class TestLedger(unittest.TestCase):
    def test_append_and_lookup(self):
        ledger = HistoricLedger(d=D, capacity=4)
        state = hvc("ledger/0", D)
        ledger.append(LedgerEntry(0, "past", state, None, 0.5, 1.0))
        self.assertIs(ledger.at(0).state, state)
        self.assertEqual(ledger.recall_state(0)[0], state)
        self.assertIsNone(ledger.at(9))

    def test_eviction_recycles_into_the_phase_summary(self):
        ledger = HistoricLedger(d=D, capacity=2)
        for i in range(5):
            ledger.append(LedgerEntry(i, PHASES[i % 3], hvc("ledger/%d" % i, D), None, 0.0, 1.0))
        self.assertEqual(len(ledger), 2)
        self.assertEqual(ledger.recycled, 3)
        self.assertEqual(sum(1 for p in PHASES if ledger.summaries[p].keys), 3)

    def test_entropy_of_uniform_surprise_is_zero(self):
        ledger = HistoricLedger(d=D, capacity=8)
        for i in range(4):
            ledger.append(LedgerEntry(i, "past", hvc("l/%d" % i, D), None, 0.0, 1.0))
        self.assertEqual(ledger.entropy(), 0.0)

    def test_entropy_rises_when_surprise_spreads(self):
        ledger = HistoricLedger(d=D, capacity=8)
        for i, surprise in enumerate([0.0, 0.5, 1.0, 0.0]):
            ledger.append(LedgerEntry(i, "past", hvc("l/%d" % i, D), None, surprise, 1.0))
        self.assertGreater(ledger.entropy(), 1.0)

    def test_timeline_is_a_superposition_of_the_history(self):
        ledger = HistoricLedger(d=D, capacity=4)
        states = [hvc("timeline/%d" % i, D) for i in range(4)]
        for i, state in enumerate(states):
            ledger.append(LedgerEntry(i, PHASES[i % 3], state, None, 0.0, 1.0))
        record = ledger.timeline_record()
        self.assertNotEqual(record, 0)
        got, score = ledger.recall_timeline(2)
        self.assertEqual(got, states[2])
        self.assertGreater(score, 0.1)

    def test_empty_ledger_is_well_behaved(self):
        ledger = HistoricLedger(d=D, capacity=2)
        self.assertEqual(ledger.entropy(), 0.0)
        self.assertEqual(ledger.timeline_record(), 0)
        self.assertEqual(ledger.recall_timeline(0), (None, 0.0))

    def test_zero_capacity_rejected(self):
        with self.assertRaises(ValueError):
            HistoricLedger(d=D, capacity=0)


class TestRingController(unittest.TestCase):
    def test_converges_on_a_periodic_world(self):
        ring = RingController(d=D, capacity=8)
        ring.run(periodic_world(4, 12, D))
        tail = ring.ledger.entries[-4:]
        self.assertTrue(all(e.surprise < 1e-6 for e in tail), msg=[e.surprise for e in tail])
        self.assertEqual(ring.energy, 1.0)
        self.assertEqual(ring.mode(), "stable")
        self.assertEqual(ring.horizon(), 3)

    def test_stays_surprised_on_a_drifting_world(self):
        ring = RingController(d=D, capacity=8)
        ring.run(drifting_world(4, 12, D))
        self.assertGreater(ring.mean_surprise(), 0.9)
        self.assertEqual(ring.energy, 0.0)
        self.assertEqual(ring.mode(), "explore")
        self.assertEqual(ring.horizon(), 1)

    def test_energy_stays_in_range(self):
        ring = RingController(d=D, capacity=8)
        for entry in ring.run(periodic_world(4, 20, D)):
            self.assertGreaterEqual(entry.energy, 0.0)
            self.assertLessEqual(entry.energy, 1.0)

    def test_first_tick_has_no_prediction(self):
        ring = RingController(d=D, capacity=4)
        entry = ring.step(hvc("ring/first", D))
        self.assertEqual(entry.surprise, 0.5)

    def test_phases_rotate(self):
        ring = RingController(d=D, capacity=8)
        phases = [e.phase for e in ring.run(periodic_world(4, 6, D))]
        self.assertEqual(phases, [PHASES[i % 3] for i in range(6)])

    def test_horizon_follows_energy(self):
        ring = RingController(d=D, capacity=8)
        ring.energy = 0.0
        self.assertEqual(ring.horizon(), 1)
        ring.energy = 1.0
        self.assertEqual(ring.horizon(), 3)

    def test_predictions_become_exact(self):
        ring = RingController(d=D, capacity=8)
        ring.run(periodic_world(4, 10, D))
        predicted, _ = ring.model.predict(ring.observations[-1], "tick")
        self.assertIsNotNone(predicted)
        self.assertEqual(predicted, ring.observations[-4])

    def test_report_mentions_the_mode(self):
        ring = RingController(d=D, capacity=4)
        ring.run(periodic_world(4, 5, D))
        self.assertIn("mode", ring.report())
        self.assertIn("ring controller", report(D))


if __name__ == "__main__":
    unittest.main()