CharlesCNorton
commited on
Commit
·
7ec35ca
1
Parent(s):
5d9ffc5
Add PUSH/POP/RET stack operation tests
Browse files- PUSH SP decrement: 96 tests (16-bit borrow chain)
- POP SP increment: 96 tests (16-bit carry chain)
- RET address buffer: 80 tests (16 identity gates)
- Tests: 6,169 -> 6,441
- Fitness: 1.000000
README.md
CHANGED
|
@@ -479,7 +479,7 @@ The interface generalizes to **all** 65,536 8-bit additions once trained—no me
|
|
| 479 |
|------|-------------|
|
| 480 |
| `neural_computer.safetensors` | 11,581 tensors, 8,290,134 parameters |
|
| 481 |
| `threshold_cpu.py` | CPU state, reference cycle, threshold runtime |
|
| 482 |
-
| `eval.py` | Unified evaluation suite (6,
|
| 483 |
| `build.py` | Build tools for memory, ALU, and .inputs tensors |
|
| 484 |
| `prune_weights.py` | Weight magnitude pruning |
|
| 485 |
|
|
|
|
| 479 |
|------|-------------|
|
| 480 |
| `neural_computer.safetensors` | 11,581 tensors, 8,290,134 parameters |
|
| 481 |
| `threshold_cpu.py` | CPU state, reference cycle, threshold runtime |
|
| 482 |
+
| `eval.py` | Unified evaluation suite (6,441 tests, GPU-batched) |
|
| 483 |
| `build.py` | Build tools for memory, ALU, and .inputs tensors |
|
| 484 |
| `prune_weights.py` | Weight magnitude pruning |
|
| 485 |
|
eval.py
CHANGED
|
@@ -1327,6 +1327,157 @@ class BatchedFitnessEvaluator:
|
|
| 1327 |
scores += s
|
| 1328 |
total += t
|
| 1329 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1330 |
return scores, total
|
| 1331 |
|
| 1332 |
# =========================================================================
|
|
|
|
| 1327 |
scores += s
|
| 1328 |
total += t
|
| 1329 |
|
| 1330 |
+
# Stack operations
|
| 1331 |
+
s, t = self._test_stack_ops(pop, debug)
|
| 1332 |
+
scores += s
|
| 1333 |
+
total += t
|
| 1334 |
+
|
| 1335 |
+
return scores, total
|
| 1336 |
+
|
| 1337 |
+
def _test_stack_ops(self, pop: Dict, debug: bool) -> Tuple[torch.Tensor, int]:
|
| 1338 |
+
"""Test PUSH/POP/RET stack operation circuits."""
|
| 1339 |
+
pop_size = next(iter(pop.values())).shape[0]
|
| 1340 |
+
scores = torch.zeros(pop_size, device=self.device)
|
| 1341 |
+
total = 0
|
| 1342 |
+
|
| 1343 |
+
if debug:
|
| 1344 |
+
print("\n=== STACK OPERATIONS ===")
|
| 1345 |
+
|
| 1346 |
+
# Test PUSH SP decrement (16-bit, borrow chain)
|
| 1347 |
+
try:
|
| 1348 |
+
sp_tests = [0x0000, 0x0001, 0x0100, 0x8000, 0xFFFF, 0x1234]
|
| 1349 |
+
op_scores = torch.zeros(pop_size, device=self.device)
|
| 1350 |
+
op_total = 0
|
| 1351 |
+
|
| 1352 |
+
for sp_val in sp_tests:
|
| 1353 |
+
expected_val = (sp_val - 1) & 0xFFFF
|
| 1354 |
+
sp_bits = [float((sp_val >> (15 - i)) & 1) for i in range(16)]
|
| 1355 |
+
|
| 1356 |
+
borrow = 1.0
|
| 1357 |
+
out_bits = []
|
| 1358 |
+
for bit in range(15, -1, -1): # LSB to MSB
|
| 1359 |
+
prefix = f'control.push.sp_dec.bit{bit}'
|
| 1360 |
+
|
| 1361 |
+
w_or = pop[f'{prefix}.xor.layer1.or.weight'].view(pop_size, 2)
|
| 1362 |
+
b_or = pop[f'{prefix}.xor.layer1.or.bias'].view(pop_size)
|
| 1363 |
+
w_nand = pop[f'{prefix}.xor.layer1.nand.weight'].view(pop_size, 2)
|
| 1364 |
+
b_nand = pop[f'{prefix}.xor.layer1.nand.bias'].view(pop_size)
|
| 1365 |
+
w2 = pop[f'{prefix}.xor.layer2.weight'].view(pop_size, 2)
|
| 1366 |
+
b2 = pop[f'{prefix}.xor.layer2.bias'].view(pop_size)
|
| 1367 |
+
|
| 1368 |
+
inp = torch.tensor([sp_bits[bit], borrow], device=self.device)
|
| 1369 |
+
h_or = heaviside((inp * w_or).sum(-1) + b_or)
|
| 1370 |
+
h_nand = heaviside((inp * w_nand).sum(-1) + b_nand)
|
| 1371 |
+
hidden = torch.stack([h_or, h_nand], dim=-1)
|
| 1372 |
+
diff_bit = heaviside((hidden * w2).sum(-1) + b2)
|
| 1373 |
+
out_bits.insert(0, diff_bit)
|
| 1374 |
+
|
| 1375 |
+
# Borrow: NOT(sp) AND borrow_in
|
| 1376 |
+
not_sp = 1.0 - sp_bits[bit]
|
| 1377 |
+
w_borrow = pop[f'{prefix}.borrow.weight'].view(pop_size, 2)
|
| 1378 |
+
b_borrow = pop[f'{prefix}.borrow.bias'].view(pop_size)
|
| 1379 |
+
borrow_inp = torch.tensor([not_sp, borrow], device=self.device)
|
| 1380 |
+
borrow = heaviside((borrow_inp * w_borrow).sum(-1) + b_borrow)[0].item()
|
| 1381 |
+
|
| 1382 |
+
out = torch.stack(out_bits, dim=-1)
|
| 1383 |
+
expected = torch.tensor([((expected_val >> (15 - i)) & 1) for i in range(16)],
|
| 1384 |
+
device=self.device, dtype=torch.float32)
|
| 1385 |
+
correct = (out == expected.unsqueeze(0)).float().sum(1)
|
| 1386 |
+
op_scores += correct
|
| 1387 |
+
op_total += 16
|
| 1388 |
+
|
| 1389 |
+
scores += op_scores
|
| 1390 |
+
total += op_total
|
| 1391 |
+
self._record('control.push.sp_dec', int(op_scores[0].item()), op_total, [])
|
| 1392 |
+
if debug:
|
| 1393 |
+
r = self.results[-1]
|
| 1394 |
+
print(f" {r.name}: {r.passed}/{r.total} {'PASS' if r.success else 'FAIL'}")
|
| 1395 |
+
except (KeyError, RuntimeError) as e:
|
| 1396 |
+
if debug:
|
| 1397 |
+
print(f" control.push.sp_dec: SKIP ({e})")
|
| 1398 |
+
|
| 1399 |
+
# Test POP SP increment (16-bit, carry chain)
|
| 1400 |
+
try:
|
| 1401 |
+
op_scores = torch.zeros(pop_size, device=self.device)
|
| 1402 |
+
op_total = 0
|
| 1403 |
+
|
| 1404 |
+
for sp_val in sp_tests:
|
| 1405 |
+
expected_val = (sp_val + 1) & 0xFFFF
|
| 1406 |
+
sp_bits = [float((sp_val >> (15 - i)) & 1) for i in range(16)]
|
| 1407 |
+
|
| 1408 |
+
carry = 1.0
|
| 1409 |
+
out_bits = []
|
| 1410 |
+
for bit in range(15, -1, -1): # LSB to MSB
|
| 1411 |
+
prefix = f'control.pop.sp_inc.bit{bit}'
|
| 1412 |
+
|
| 1413 |
+
w_or = pop[f'{prefix}.xor.layer1.or.weight'].view(pop_size, 2)
|
| 1414 |
+
b_or = pop[f'{prefix}.xor.layer1.or.bias'].view(pop_size)
|
| 1415 |
+
w_nand = pop[f'{prefix}.xor.layer1.nand.weight'].view(pop_size, 2)
|
| 1416 |
+
b_nand = pop[f'{prefix}.xor.layer1.nand.bias'].view(pop_size)
|
| 1417 |
+
w2 = pop[f'{prefix}.xor.layer2.weight'].view(pop_size, 2)
|
| 1418 |
+
b2 = pop[f'{prefix}.xor.layer2.bias'].view(pop_size)
|
| 1419 |
+
|
| 1420 |
+
inp = torch.tensor([sp_bits[bit], carry], device=self.device)
|
| 1421 |
+
h_or = heaviside((inp * w_or).sum(-1) + b_or)
|
| 1422 |
+
h_nand = heaviside((inp * w_nand).sum(-1) + b_nand)
|
| 1423 |
+
hidden = torch.stack([h_or, h_nand], dim=-1)
|
| 1424 |
+
sum_bit = heaviside((hidden * w2).sum(-1) + b2)
|
| 1425 |
+
out_bits.insert(0, sum_bit)
|
| 1426 |
+
|
| 1427 |
+
# Carry: sp AND carry_in
|
| 1428 |
+
w_carry = pop[f'{prefix}.carry.weight'].view(pop_size, 2)
|
| 1429 |
+
b_carry = pop[f'{prefix}.carry.bias'].view(pop_size)
|
| 1430 |
+
carry = heaviside((inp * w_carry).sum(-1) + b_carry)[0].item()
|
| 1431 |
+
|
| 1432 |
+
out = torch.stack(out_bits, dim=-1)
|
| 1433 |
+
expected = torch.tensor([((expected_val >> (15 - i)) & 1) for i in range(16)],
|
| 1434 |
+
device=self.device, dtype=torch.float32)
|
| 1435 |
+
correct = (out == expected.unsqueeze(0)).float().sum(1)
|
| 1436 |
+
op_scores += correct
|
| 1437 |
+
op_total += 16
|
| 1438 |
+
|
| 1439 |
+
scores += op_scores
|
| 1440 |
+
total += op_total
|
| 1441 |
+
self._record('control.pop.sp_inc', int(op_scores[0].item()), op_total, [])
|
| 1442 |
+
if debug:
|
| 1443 |
+
r = self.results[-1]
|
| 1444 |
+
print(f" {r.name}: {r.passed}/{r.total} {'PASS' if r.success else 'FAIL'}")
|
| 1445 |
+
except (KeyError, RuntimeError) as e:
|
| 1446 |
+
if debug:
|
| 1447 |
+
print(f" control.pop.sp_inc: SKIP ({e})")
|
| 1448 |
+
|
| 1449 |
+
# Test RET address buffer (16 identity gates)
|
| 1450 |
+
try:
|
| 1451 |
+
op_scores = torch.zeros(pop_size, device=self.device)
|
| 1452 |
+
op_total = 0
|
| 1453 |
+
|
| 1454 |
+
addr_tests = [0x0000, 0xFFFF, 0x1234, 0x8000, 0x00FF]
|
| 1455 |
+
for addr_val in addr_tests:
|
| 1456 |
+
addr_bits = torch.tensor([float((addr_val >> (15 - i)) & 1) for i in range(16)],
|
| 1457 |
+
device=self.device, dtype=torch.float32)
|
| 1458 |
+
|
| 1459 |
+
out_bits = []
|
| 1460 |
+
for bit in range(16):
|
| 1461 |
+
w = pop[f'control.ret.addr.bit{bit}.weight'].view(pop_size)
|
| 1462 |
+
b = pop[f'control.ret.addr.bit{bit}.bias'].view(pop_size)
|
| 1463 |
+
out = heaviside(addr_bits[bit] * w + b)
|
| 1464 |
+
out_bits.append(out)
|
| 1465 |
+
|
| 1466 |
+
out = torch.stack(out_bits, dim=-1)
|
| 1467 |
+
correct = (out == addr_bits.unsqueeze(0)).float().sum(1)
|
| 1468 |
+
op_scores += correct
|
| 1469 |
+
op_total += 16
|
| 1470 |
+
|
| 1471 |
+
scores += op_scores
|
| 1472 |
+
total += op_total
|
| 1473 |
+
self._record('control.ret.addr', int(op_scores[0].item()), op_total, [])
|
| 1474 |
+
if debug:
|
| 1475 |
+
r = self.results[-1]
|
| 1476 |
+
print(f" {r.name}: {r.passed}/{r.total} {'PASS' if r.success else 'FAIL'}")
|
| 1477 |
+
except (KeyError, RuntimeError) as e:
|
| 1478 |
+
if debug:
|
| 1479 |
+
print(f" control.ret.addr: SKIP ({e})")
|
| 1480 |
+
|
| 1481 |
return scores, total
|
| 1482 |
|
| 1483 |
# =========================================================================
|