hdcstorage / tests /test_machine.py
AEUPH's picture
HDCSTORAGE: hyperdimensional computer, webOS and agentic MCP layer (stdlib-only Python)
ad474c9 verified
Raw History Blame Contribute Delete
7.89 kB
"""End-to-end tests for the HDC machine."""
import os
import sys
import unittest
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from hdc.assembler import assemble # noqa: E402
from hdc.interpreter import Machine # noqa: E402
from hdc.memory import HDCStorage # noqa: E402
D = 2000
def build(src, **kw):
prog = assemble(src, d=D)
machine = Machine(storage=HDCStorage(d=D), d=D, **kw)
machine.load(prog)
return machine
class TestArithmetic(unittest.TestCase):
def test_add_and_out(self):
m = build(
"""
ldi R0, 5
ldi R1, 37
add R2, R0, R1
out R2
halt
"""
)
result = m.run()
self.assertEqual(result["status"], "halted")
self.assertEqual(result["output"], ["42"])
self.assertEqual(m.get_int(2), 42)
def test_bitwise_ops(self):
m = build(
"""
ldi R0, 12
ldi R1, 10
and R2, R0, R1
or R3, R0, R1
xor R4, R0, R1
not R5, R0
shl R6, R0, 2
shr R7, R0, 2
halt
"""
)
m.run()
self.assertEqual(m.get_int(2), 12 & 10)
self.assertEqual(m.get_int(3), 12 | 10)
self.assertEqual(m.get_int(4), 12 ^ 10)
self.assertEqual(m.get_int(5), ~12)
self.assertEqual(m.get_int(6), 48)
self.assertEqual(m.get_int(7), 3)
def test_sub_negatives(self):
m = build("ldi R0, 3\n ldi R1, 10\n sub R2, R0, R1\n out R2\n halt")
self.assertEqual(m.run()["output"], ["-7"])
class TestControlFlow(unittest.TestCase):
def test_counting_loop_with_cmp_jnz(self):
m = build(
"""
ldi R0, 0
ldi R1, 1
ldi R2, 4
loop:
add R0, R0, R1
cmp R0, R2
jnz loop
out R0
halt
"""
)
result = m.run()
self.assertEqual(result["output"], ["4"])
self.assertTrue(result["flag_z"])
def test_call_ret_and_push_pop(self):
m = build(
"""
ldi R0, 7
push R0
call sub1
pop R2
out R2
out R3
halt
sub1:
ldi R3, 11
ret
"""
)
result = m.run()
self.assertEqual(result["output"], ["7", "11"])
self.assertEqual(result["stack_depth"], 0)
def test_jmp_skips_code(self):
m = build(
"""
ldi R0, 1
jmp past
ldi R0, 99
past:
out R0
halt
"""
)
self.assertEqual(m.run()["output"], ["1"])
def test_step_advances_one_cell(self):
m = build("ldi R0, 5\n ldi R1, 6\n halt")
m.step()
self.assertEqual(m.pc, 1)
self.assertEqual(m.steps, 1)
self.assertEqual(m.get_int(0), 5)
self.assertEqual(len(m.trace), 1)
self.assertEqual(m.trace[0].op, "LDI")
class TestHyperdimensionalOps(unittest.TestCase):
def test_bind_then_unbind_recovers_register(self):
m = build(
"""
ldi R0, 5
ldi R1, 37
bind R2, R0, R1
bind R3, R2, R1
sim R4, R3, R0
out R4
halt
"""
)
m.run()
# XOR binding is self-inverse, so the round trip is exact
self.assertEqual(m.regs[3], m.regs[0])
self.assertEqual(m.get_int(4), 1000)
self.assertEqual(m.output, ["1000"])
def test_bind_and_bundle_differ(self):
m = build(
"""
ldi R0, 5
ldi R1, 37
bind R2, R0, R1
bundle R3, R0, R1
cmp R2, R3
halt
"""
)
m.run()
self.assertNotEqual(m.regs[2], m.regs[3])
def test_permute_is_deterministic_and_decorrelating(self):
m = build(
"""
ldi R0, 5
ldi R1, 37
bind R2, R0, R1
permute R3, R2, 5
permute R4, R2, 5
sim R5, R3, R4
sim R6, R3, R2
halt
"""
)
m.run()
# same rotation of the same vector is bit-identical
self.assertEqual(m.regs[3], m.regs[4])
self.assertEqual(m.get_int(5), 1000)
# a rotation by 5 is near-orthogonal to its original
self.assertLess(abs(m.get_int(6)), 300)
def test_popcount(self):
m = build("ldi R0, 1\n popc R1, R0\n halt")
m.run()
from hdc.hypervec import popcount
self.assertEqual(m.get_int(1), popcount(m.regs[0]))
class TestStorageOps(unittest.TestCase):
def test_store_load_roundtrip(self):
m = build(
"""
.name box
ldi R0, 99
store box, R0
load R1, box
out R1
halt
"""
)
self.assertEqual(m.run()["output"], ["99"])
self.assertEqual(m.storage.read("box"), m.regs[0])
self.assertEqual(m.get_int(1), 99)
def test_recall_finds_stored_cell(self):
m = build(
"""
.name probe
ldi R0, 7
store probe, R0
recall R1, R0, 1
halt
"""
)
m.run()
self.assertTrue(m.last_recall)
self.assertEqual(m.last_recall[0][0], "probe")
self.assertEqual(m.last_recall[0][2], 1.0)
def test_memstat(self):
m = build("memstat R0\n halt")
m.run()
self.assertEqual(m.get_int(0), len(m.storage))
def test_program_cells_live_in_storage(self):
m = build("ldi R0, 1\n halt")
self.assertEqual(m.prog_len, 2)
for pc in range(m.prog_len):
self.assertIn("code/%d" % pc, m.storage)
class TestTraps(unittest.TestCase):
def test_break_pauses_then_resumes(self):
m = build(
"""
ldi R0, 1
break
ldi R0, 2
out R0
halt
"""
)
result = m.run()
self.assertEqual(result["status"], "break")
self.assertEqual(result["output"], [])
self.assertEqual(m.pc, 1)
m.skip_break = True
result = m.run()
self.assertEqual(result["status"], "halted")
self.assertEqual(result["output"], ["2"])
def test_tool_without_registry_faults_not_raises(self):
m = build(
"""
.tool hdc.similarity
ldi R0, 1
tool R1, R0, R0, hdc.similarity
halt
"""
)
result = m.run()
self.assertEqual(result["status"], "error")
self.assertTrue(result["halted"])
def test_ret_on_empty_stack_faults(self):
m = build("ret\n halt")
result = m.run()
self.assertEqual(result["status"], "error")
class TestInspection(unittest.TestCase):
def test_disasm_marks_current_cell(self):
m = build("ldi R0, 1\n out R0\n halt")
m.step()
text = m.disasm(0, 3)
self.assertIn("=>", text)
self.assertIn("OUT", text)
def test_regs_text_lists_registers(self):
m = build("ldi R0, 5\n halt")
m.run()
text = m.regs_text()
self.assertIn("R0", text)
self.assertIn("R15", text)
def test_reset_clears_state(self):
m = build("ldi R0, 5\n out R0\n halt")
m.run()
self.assertEqual(m.output, ["5"])
m.reset()
self.assertEqual(m.output, [])
self.assertEqual(m.pc, 0)
self.assertEqual(m.steps, 0)
self.assertEqual(m.get_int(0), 0)
if __name__ == "__main__":
unittest.main()