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

Files changed (2) hide show
  1. README.md +1 -1
  2. eval.py +151 -0
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,169 tests, GPU-batched) |
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
  # =========================================================================