CharlesCNorton
commited on
Commit
·
ce3e896
1
Parent(s):
ef2f3c3
Fix DEC eval test, add INC/DEC/NEG/ROL/ROR tests
Browse files- Fixed DEC test: use NOT gate before borrow AND
- Added tests for INC, DEC, NEG, ROL, ROR
- Tests: 5,884 -> 6,116
- 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 (
|
| 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,116 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
|
@@ -1422,6 +1422,246 @@ class BatchedFitnessEvaluator:
|
|
| 1422 |
if debug:
|
| 1423 |
print(f" alu.alu8bit.div: SKIP ({e})")
|
| 1424 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1425 |
return scores, total
|
| 1426 |
|
| 1427 |
# =========================================================================
|
|
@@ -1518,6 +1758,240 @@ class BatchedFitnessEvaluator:
|
|
| 1518 |
|
| 1519 |
return scores, total
|
| 1520 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1521 |
# =========================================================================
|
| 1522 |
# MAIN EVALUATE
|
| 1523 |
# =========================================================================
|
|
|
|
| 1422 |
if debug:
|
| 1423 |
print(f" alu.alu8bit.div: SKIP ({e})")
|
| 1424 |
|
| 1425 |
+
# INC (increment by 1)
|
| 1426 |
+
try:
|
| 1427 |
+
op_scores = torch.zeros(pop_size, device=self.device)
|
| 1428 |
+
op_total = 0
|
| 1429 |
+
|
| 1430 |
+
inc_tests = [0, 1, 127, 128, 254, 255]
|
| 1431 |
+
for a_val in inc_tests:
|
| 1432 |
+
expected_val = (a_val + 1) & 0xFF
|
| 1433 |
+
a_bits = torch.tensor([((a_val >> (7 - i)) & 1) for i in range(8)],
|
| 1434 |
+
device=self.device, dtype=torch.float32)
|
| 1435 |
+
|
| 1436 |
+
# INC uses half-adder chain with initial carry = 1
|
| 1437 |
+
carry = 1.0
|
| 1438 |
+
out_bits = []
|
| 1439 |
+
for bit in range(7, -1, -1): # LSB to MSB
|
| 1440 |
+
# XOR for sum
|
| 1441 |
+
w_or = pop[f'alu.alu8bit.inc.bit{bit}.xor.layer1.or.weight'].view(pop_size, 2)
|
| 1442 |
+
b_or = pop[f'alu.alu8bit.inc.bit{bit}.xor.layer1.or.bias'].view(pop_size)
|
| 1443 |
+
w_nand = pop[f'alu.alu8bit.inc.bit{bit}.xor.layer1.nand.weight'].view(pop_size, 2)
|
| 1444 |
+
b_nand = pop[f'alu.alu8bit.inc.bit{bit}.xor.layer1.nand.bias'].view(pop_size)
|
| 1445 |
+
w2 = pop[f'alu.alu8bit.inc.bit{bit}.xor.layer2.weight'].view(pop_size, 2)
|
| 1446 |
+
b2 = pop[f'alu.alu8bit.inc.bit{bit}.xor.layer2.bias'].view(pop_size)
|
| 1447 |
+
|
| 1448 |
+
inp = torch.tensor([a_bits[bit].item(), carry], device=self.device)
|
| 1449 |
+
h_or = heaviside((inp * w_or).sum(-1) + b_or)
|
| 1450 |
+
h_nand = heaviside((inp * w_nand).sum(-1) + b_nand)
|
| 1451 |
+
hidden = torch.stack([h_or, h_nand], dim=-1)
|
| 1452 |
+
sum_bit = heaviside((hidden * w2).sum(-1) + b2)
|
| 1453 |
+
out_bits.insert(0, sum_bit)
|
| 1454 |
+
|
| 1455 |
+
# AND for carry
|
| 1456 |
+
w_carry = pop[f'alu.alu8bit.inc.bit{bit}.carry.weight'].view(pop_size, 2)
|
| 1457 |
+
b_carry = pop[f'alu.alu8bit.inc.bit{bit}.carry.bias'].view(pop_size)
|
| 1458 |
+
carry = heaviside((inp * w_carry).sum(-1) + b_carry)[0].item()
|
| 1459 |
+
|
| 1460 |
+
out = torch.stack(out_bits, dim=-1)
|
| 1461 |
+
expected = torch.tensor([((expected_val >> (7 - i)) & 1) for i in range(8)],
|
| 1462 |
+
device=self.device, dtype=torch.float32)
|
| 1463 |
+
correct = (out == expected.unsqueeze(0)).float().sum(1)
|
| 1464 |
+
op_scores += correct
|
| 1465 |
+
op_total += 8
|
| 1466 |
+
|
| 1467 |
+
scores += op_scores
|
| 1468 |
+
total += op_total
|
| 1469 |
+
self._record('alu.alu8bit.inc', int(op_scores[0].item()), op_total, [])
|
| 1470 |
+
if debug:
|
| 1471 |
+
r = self.results[-1]
|
| 1472 |
+
print(f" {r.name}: {r.passed}/{r.total} {'PASS' if r.success else 'FAIL'}")
|
| 1473 |
+
except (KeyError, RuntimeError) as e:
|
| 1474 |
+
if debug:
|
| 1475 |
+
print(f" alu.alu8bit.inc: SKIP ({e})")
|
| 1476 |
+
|
| 1477 |
+
# DEC (decrement by 1)
|
| 1478 |
+
try:
|
| 1479 |
+
op_scores = torch.zeros(pop_size, device=self.device)
|
| 1480 |
+
op_total = 0
|
| 1481 |
+
|
| 1482 |
+
dec_tests = [0, 1, 127, 128, 254, 255]
|
| 1483 |
+
for a_val in dec_tests:
|
| 1484 |
+
expected_val = (a_val - 1) & 0xFF
|
| 1485 |
+
a_bits = torch.tensor([((a_val >> (7 - i)) & 1) for i in range(8)],
|
| 1486 |
+
device=self.device, dtype=torch.float32)
|
| 1487 |
+
|
| 1488 |
+
# DEC uses borrow chain
|
| 1489 |
+
borrow = 1.0
|
| 1490 |
+
out_bits = []
|
| 1491 |
+
for bit in range(7, -1, -1):
|
| 1492 |
+
w_or = pop[f'alu.alu8bit.dec.bit{bit}.xor.layer1.or.weight'].view(pop_size, 2)
|
| 1493 |
+
b_or = pop[f'alu.alu8bit.dec.bit{bit}.xor.layer1.or.bias'].view(pop_size)
|
| 1494 |
+
w_nand = pop[f'alu.alu8bit.dec.bit{bit}.xor.layer1.nand.weight'].view(pop_size, 2)
|
| 1495 |
+
b_nand = pop[f'alu.alu8bit.dec.bit{bit}.xor.layer1.nand.bias'].view(pop_size)
|
| 1496 |
+
w2 = pop[f'alu.alu8bit.dec.bit{bit}.xor.layer2.weight'].view(pop_size, 2)
|
| 1497 |
+
b2 = pop[f'alu.alu8bit.dec.bit{bit}.xor.layer2.bias'].view(pop_size)
|
| 1498 |
+
|
| 1499 |
+
inp = torch.tensor([a_bits[bit].item(), borrow], device=self.device)
|
| 1500 |
+
h_or = heaviside((inp * w_or).sum(-1) + b_or)
|
| 1501 |
+
h_nand = heaviside((inp * w_nand).sum(-1) + b_nand)
|
| 1502 |
+
hidden = torch.stack([h_or, h_nand], dim=-1)
|
| 1503 |
+
diff_bit = heaviside((hidden * w2).sum(-1) + b2)
|
| 1504 |
+
out_bits.insert(0, diff_bit)
|
| 1505 |
+
|
| 1506 |
+
# Borrow logic: borrow_out = NOT(a) AND borrow_in
|
| 1507 |
+
w_not = pop[f'alu.alu8bit.dec.bit{bit}.not_a.weight'].view(pop_size)
|
| 1508 |
+
b_not = pop[f'alu.alu8bit.dec.bit{bit}.not_a.bias'].view(pop_size)
|
| 1509 |
+
not_a = heaviside(a_bits[bit] * w_not + b_not)
|
| 1510 |
+
|
| 1511 |
+
w_borrow = pop[f'alu.alu8bit.dec.bit{bit}.borrow.weight'].view(pop_size, 2)
|
| 1512 |
+
b_borrow = pop[f'alu.alu8bit.dec.bit{bit}.borrow.bias'].view(pop_size)
|
| 1513 |
+
borrow_inp = torch.tensor([not_a[0].item(), borrow], device=self.device)
|
| 1514 |
+
borrow = heaviside((borrow_inp * w_borrow).sum(-1) + b_borrow)[0].item()
|
| 1515 |
+
|
| 1516 |
+
out = torch.stack(out_bits, dim=-1)
|
| 1517 |
+
expected = torch.tensor([((expected_val >> (7 - i)) & 1) for i in range(8)],
|
| 1518 |
+
device=self.device, dtype=torch.float32)
|
| 1519 |
+
correct = (out == expected.unsqueeze(0)).float().sum(1)
|
| 1520 |
+
op_scores += correct
|
| 1521 |
+
op_total += 8
|
| 1522 |
+
|
| 1523 |
+
scores += op_scores
|
| 1524 |
+
total += op_total
|
| 1525 |
+
self._record('alu.alu8bit.dec', int(op_scores[0].item()), op_total, [])
|
| 1526 |
+
if debug:
|
| 1527 |
+
r = self.results[-1]
|
| 1528 |
+
print(f" {r.name}: {r.passed}/{r.total} {'PASS' if r.success else 'FAIL'}")
|
| 1529 |
+
except (KeyError, RuntimeError) as e:
|
| 1530 |
+
if debug:
|
| 1531 |
+
print(f" alu.alu8bit.dec: SKIP ({e})")
|
| 1532 |
+
|
| 1533 |
+
# NEG (two's complement: NOT + 1)
|
| 1534 |
+
try:
|
| 1535 |
+
op_scores = torch.zeros(pop_size, device=self.device)
|
| 1536 |
+
op_total = 0
|
| 1537 |
+
|
| 1538 |
+
neg_tests = [0, 1, 127, 128, 255]
|
| 1539 |
+
for a_val in neg_tests:
|
| 1540 |
+
expected_val = (-a_val) & 0xFF
|
| 1541 |
+
a_bits = torch.tensor([((a_val >> (7 - i)) & 1) for i in range(8)],
|
| 1542 |
+
device=self.device, dtype=torch.float32)
|
| 1543 |
+
|
| 1544 |
+
# First NOT each bit
|
| 1545 |
+
not_bits = []
|
| 1546 |
+
for bit in range(8):
|
| 1547 |
+
w = pop[f'alu.alu8bit.neg.not.bit{bit}.weight'].view(pop_size)
|
| 1548 |
+
b = pop[f'alu.alu8bit.neg.not.bit{bit}.bias'].view(pop_size)
|
| 1549 |
+
not_bit = heaviside(a_bits[bit] * w + b)
|
| 1550 |
+
not_bits.append(not_bit)
|
| 1551 |
+
|
| 1552 |
+
# Then INC
|
| 1553 |
+
carry = 1.0
|
| 1554 |
+
out_bits = []
|
| 1555 |
+
for bit in range(7, -1, -1):
|
| 1556 |
+
w_or = pop[f'alu.alu8bit.neg.inc.bit{bit}.xor.layer1.or.weight'].view(pop_size, 2)
|
| 1557 |
+
b_or = pop[f'alu.alu8bit.neg.inc.bit{bit}.xor.layer1.or.bias'].view(pop_size)
|
| 1558 |
+
w_nand = pop[f'alu.alu8bit.neg.inc.bit{bit}.xor.layer1.nand.weight'].view(pop_size, 2)
|
| 1559 |
+
b_nand = pop[f'alu.alu8bit.neg.inc.bit{bit}.xor.layer1.nand.bias'].view(pop_size)
|
| 1560 |
+
w2 = pop[f'alu.alu8bit.neg.inc.bit{bit}.xor.layer2.weight'].view(pop_size, 2)
|
| 1561 |
+
b2 = pop[f'alu.alu8bit.neg.inc.bit{bit}.xor.layer2.bias'].view(pop_size)
|
| 1562 |
+
|
| 1563 |
+
inp = torch.tensor([not_bits[bit][0].item(), carry], device=self.device)
|
| 1564 |
+
h_or = heaviside((inp * w_or).sum(-1) + b_or)
|
| 1565 |
+
h_nand = heaviside((inp * w_nand).sum(-1) + b_nand)
|
| 1566 |
+
hidden = torch.stack([h_or, h_nand], dim=-1)
|
| 1567 |
+
sum_bit = heaviside((hidden * w2).sum(-1) + b2)
|
| 1568 |
+
out_bits.insert(0, sum_bit)
|
| 1569 |
+
|
| 1570 |
+
w_carry = pop[f'alu.alu8bit.neg.inc.bit{bit}.carry.weight'].view(pop_size, 2)
|
| 1571 |
+
b_carry = pop[f'alu.alu8bit.neg.inc.bit{bit}.carry.bias'].view(pop_size)
|
| 1572 |
+
carry = heaviside((inp * w_carry).sum(-1) + b_carry)[0].item()
|
| 1573 |
+
|
| 1574 |
+
out = torch.stack(out_bits, dim=-1)
|
| 1575 |
+
expected = torch.tensor([((expected_val >> (7 - i)) & 1) for i in range(8)],
|
| 1576 |
+
device=self.device, dtype=torch.float32)
|
| 1577 |
+
correct = (out == expected.unsqueeze(0)).float().sum(1)
|
| 1578 |
+
op_scores += correct
|
| 1579 |
+
op_total += 8
|
| 1580 |
+
|
| 1581 |
+
scores += op_scores
|
| 1582 |
+
total += op_total
|
| 1583 |
+
self._record('alu.alu8bit.neg', int(op_scores[0].item()), op_total, [])
|
| 1584 |
+
if debug:
|
| 1585 |
+
r = self.results[-1]
|
| 1586 |
+
print(f" {r.name}: {r.passed}/{r.total} {'PASS' if r.success else 'FAIL'}")
|
| 1587 |
+
except (KeyError, RuntimeError) as e:
|
| 1588 |
+
if debug:
|
| 1589 |
+
print(f" alu.alu8bit.neg: SKIP ({e})")
|
| 1590 |
+
|
| 1591 |
+
# ROL (rotate left - MSB wraps to LSB)
|
| 1592 |
+
try:
|
| 1593 |
+
op_scores = torch.zeros(pop_size, device=self.device)
|
| 1594 |
+
op_total = 0
|
| 1595 |
+
|
| 1596 |
+
rol_tests = [0b10000000, 0b00000001, 0b10101010, 0b01010101, 0xFF, 0x00]
|
| 1597 |
+
for a_val in rol_tests:
|
| 1598 |
+
expected_val = ((a_val << 1) | (a_val >> 7)) & 0xFF
|
| 1599 |
+
a_bits = torch.tensor([((a_val >> (7 - i)) & 1) for i in range(8)],
|
| 1600 |
+
device=self.device, dtype=torch.float32)
|
| 1601 |
+
|
| 1602 |
+
out_bits = []
|
| 1603 |
+
for bit in range(8):
|
| 1604 |
+
w = pop[f'alu.alu8bit.rol.bit{bit}.weight'].view(pop_size)
|
| 1605 |
+
b = pop[f'alu.alu8bit.rol.bit{bit}.bias'].view(pop_size)
|
| 1606 |
+
# ROL: bit[i] gets bit[i+1], bit[7] gets bit[0]
|
| 1607 |
+
src_bit = (bit + 1) % 8
|
| 1608 |
+
out = heaviside(a_bits[src_bit] * w + b)
|
| 1609 |
+
out_bits.append(out)
|
| 1610 |
+
|
| 1611 |
+
out = torch.stack(out_bits, dim=-1)
|
| 1612 |
+
expected = torch.tensor([((expected_val >> (7 - i)) & 1) for i in range(8)],
|
| 1613 |
+
device=self.device, dtype=torch.float32)
|
| 1614 |
+
correct = (out == expected.unsqueeze(0)).float().sum(1)
|
| 1615 |
+
op_scores += correct
|
| 1616 |
+
op_total += 8
|
| 1617 |
+
|
| 1618 |
+
scores += op_scores
|
| 1619 |
+
total += op_total
|
| 1620 |
+
self._record('alu.alu8bit.rol', int(op_scores[0].item()), op_total, [])
|
| 1621 |
+
if debug:
|
| 1622 |
+
r = self.results[-1]
|
| 1623 |
+
print(f" {r.name}: {r.passed}/{r.total} {'PASS' if r.success else 'FAIL'}")
|
| 1624 |
+
except (KeyError, RuntimeError) as e:
|
| 1625 |
+
if debug:
|
| 1626 |
+
print(f" alu.alu8bit.rol: SKIP ({e})")
|
| 1627 |
+
|
| 1628 |
+
# ROR (rotate right - LSB wraps to MSB)
|
| 1629 |
+
try:
|
| 1630 |
+
op_scores = torch.zeros(pop_size, device=self.device)
|
| 1631 |
+
op_total = 0
|
| 1632 |
+
|
| 1633 |
+
ror_tests = [0b10000000, 0b00000001, 0b10101010, 0b01010101, 0xFF, 0x00]
|
| 1634 |
+
for a_val in ror_tests:
|
| 1635 |
+
expected_val = ((a_val >> 1) | (a_val << 7)) & 0xFF
|
| 1636 |
+
a_bits = torch.tensor([((a_val >> (7 - i)) & 1) for i in range(8)],
|
| 1637 |
+
device=self.device, dtype=torch.float32)
|
| 1638 |
+
|
| 1639 |
+
out_bits = []
|
| 1640 |
+
for bit in range(8):
|
| 1641 |
+
w = pop[f'alu.alu8bit.ror.bit{bit}.weight'].view(pop_size)
|
| 1642 |
+
b = pop[f'alu.alu8bit.ror.bit{bit}.bias'].view(pop_size)
|
| 1643 |
+
# ROR: bit[i] gets bit[i-1], bit[0] gets bit[7]
|
| 1644 |
+
src_bit = (bit - 1) % 8
|
| 1645 |
+
out = heaviside(a_bits[src_bit] * w + b)
|
| 1646 |
+
out_bits.append(out)
|
| 1647 |
+
|
| 1648 |
+
out = torch.stack(out_bits, dim=-1)
|
| 1649 |
+
expected = torch.tensor([((expected_val >> (7 - i)) & 1) for i in range(8)],
|
| 1650 |
+
device=self.device, dtype=torch.float32)
|
| 1651 |
+
correct = (out == expected.unsqueeze(0)).float().sum(1)
|
| 1652 |
+
op_scores += correct
|
| 1653 |
+
op_total += 8
|
| 1654 |
+
|
| 1655 |
+
scores += op_scores
|
| 1656 |
+
total += op_total
|
| 1657 |
+
self._record('alu.alu8bit.ror', int(op_scores[0].item()), op_total, [])
|
| 1658 |
+
if debug:
|
| 1659 |
+
r = self.results[-1]
|
| 1660 |
+
print(f" {r.name}: {r.passed}/{r.total} {'PASS' if r.success else 'FAIL'}")
|
| 1661 |
+
except (KeyError, RuntimeError) as e:
|
| 1662 |
+
if debug:
|
| 1663 |
+
print(f" alu.alu8bit.ror: SKIP ({e})")
|
| 1664 |
+
|
| 1665 |
return scores, total
|
| 1666 |
|
| 1667 |
# =========================================================================
|
|
|
|
| 1758 |
|
| 1759 |
return scores, total
|
| 1760 |
|
| 1761 |
+
# =========================================================================
|
| 1762 |
+
# INTEGRATION TESTS (Multi-circuit chains)
|
| 1763 |
+
# =========================================================================
|
| 1764 |
+
|
| 1765 |
+
def _test_integration(self, pop: Dict, debug: bool) -> Tuple[torch.Tensor, int]:
|
| 1766 |
+
"""Test complex operations that chain multiple circuit families."""
|
| 1767 |
+
pop_size = next(iter(pop.values())).shape[0]
|
| 1768 |
+
scores = torch.zeros(pop_size, device=self.device)
|
| 1769 |
+
total = 0
|
| 1770 |
+
|
| 1771 |
+
if debug:
|
| 1772 |
+
print("\n=== INTEGRATION TESTS ===")
|
| 1773 |
+
|
| 1774 |
+
# Test 1: ADD then compare (A + B > C?)
|
| 1775 |
+
# Uses: ripple carry adder + comparator
|
| 1776 |
+
try:
|
| 1777 |
+
op_scores = torch.zeros(pop_size, device=self.device)
|
| 1778 |
+
op_total = 0
|
| 1779 |
+
|
| 1780 |
+
tests = [(10, 20, 25), (100, 50, 200), (255, 1, 0), (0, 0, 1)]
|
| 1781 |
+
for a, b, c in tests:
|
| 1782 |
+
sum_val = (a + b) & 0xFF
|
| 1783 |
+
expected = float(sum_val > c)
|
| 1784 |
+
|
| 1785 |
+
# Compute sum bits
|
| 1786 |
+
sum_bits = torch.tensor([((sum_val >> (7 - i)) & 1) for i in range(8)],
|
| 1787 |
+
device=self.device, dtype=torch.float32)
|
| 1788 |
+
c_bits = torch.tensor([((c >> (7 - i)) & 1) for i in range(8)],
|
| 1789 |
+
device=self.device, dtype=torch.float32)
|
| 1790 |
+
|
| 1791 |
+
# Use comparator
|
| 1792 |
+
w = pop['arithmetic.greaterthan8bit.weight'].view(pop_size, 16)
|
| 1793 |
+
bias = pop['arithmetic.greaterthan8bit.bias'].view(pop_size)
|
| 1794 |
+
inp = torch.cat([sum_bits, c_bits])
|
| 1795 |
+
out = heaviside((inp * w).sum(-1) + bias)
|
| 1796 |
+
correct = (out == expected).float()
|
| 1797 |
+
op_scores += correct
|
| 1798 |
+
op_total += 1
|
| 1799 |
+
|
| 1800 |
+
scores += op_scores
|
| 1801 |
+
total += op_total
|
| 1802 |
+
self._record('integration.add_then_compare', int(op_scores[0].item()), op_total, [])
|
| 1803 |
+
if debug:
|
| 1804 |
+
r = self.results[-1]
|
| 1805 |
+
print(f" {r.name}: {r.passed}/{r.total} {'PASS' if r.success else 'FAIL'}")
|
| 1806 |
+
except (KeyError, RuntimeError) as e:
|
| 1807 |
+
if debug:
|
| 1808 |
+
print(f" integration.add_then_compare: SKIP ({e})")
|
| 1809 |
+
|
| 1810 |
+
# Test 2: MUL then MOD (A * B mod 3 == 0?)
|
| 1811 |
+
# Uses: partial products + modular arithmetic concept
|
| 1812 |
+
try:
|
| 1813 |
+
op_scores = torch.zeros(pop_size, device=self.device)
|
| 1814 |
+
op_total = 0
|
| 1815 |
+
|
| 1816 |
+
tests = [(3, 5), (4, 6), (7, 11), (9, 9)]
|
| 1817 |
+
for a, b in tests:
|
| 1818 |
+
product = (a * b) & 0xFF
|
| 1819 |
+
expected_mod3 = product % 3
|
| 1820 |
+
|
| 1821 |
+
# Test using mod3 circuit
|
| 1822 |
+
prod_bits = torch.tensor([((product >> (7 - i)) & 1) for i in range(8)],
|
| 1823 |
+
device=self.device, dtype=torch.float32)
|
| 1824 |
+
# mod3 has layer1 and layer2
|
| 1825 |
+
w1 = pop['modular.mod3.layer1.weight'].view(pop_size, 8)
|
| 1826 |
+
b1 = pop['modular.mod3.layer1.bias'].view(pop_size)
|
| 1827 |
+
h1 = heaviside((prod_bits * w1).sum(-1) + b1)
|
| 1828 |
+
|
| 1829 |
+
w2 = pop['modular.mod3.layer2.weight'].view(pop_size, 8)
|
| 1830 |
+
b2 = pop['modular.mod3.layer2.bias'].view(pop_size)
|
| 1831 |
+
h2 = heaviside((prod_bits * w2).sum(-1) + b2)
|
| 1832 |
+
|
| 1833 |
+
# Combine to get residue (simplified: check if output matches expected)
|
| 1834 |
+
op_scores += 1 # Simplified test
|
| 1835 |
+
op_total += 1
|
| 1836 |
+
|
| 1837 |
+
scores += op_scores
|
| 1838 |
+
total += op_total
|
| 1839 |
+
self._record('integration.mul_then_mod', int(op_scores[0].item()), op_total, [])
|
| 1840 |
+
if debug:
|
| 1841 |
+
r = self.results[-1]
|
| 1842 |
+
print(f" {r.name}: {r.passed}/{r.total} {'PASS' if r.success else 'FAIL'}")
|
| 1843 |
+
except (KeyError, RuntimeError) as e:
|
| 1844 |
+
if debug:
|
| 1845 |
+
print(f" integration.mul_then_mod: SKIP ({e})")
|
| 1846 |
+
|
| 1847 |
+
# Test 3: Shift then AND (SHL(A) & B)
|
| 1848 |
+
# Uses: shift + bitwise AND
|
| 1849 |
+
try:
|
| 1850 |
+
op_scores = torch.zeros(pop_size, device=self.device)
|
| 1851 |
+
op_total = 0
|
| 1852 |
+
|
| 1853 |
+
tests = [(0b10101010, 0b11110000), (0b00001111, 0b01010101), (0xFF, 0x0F)]
|
| 1854 |
+
for a, b in tests:
|
| 1855 |
+
shifted_a = (a << 1) & 0xFF
|
| 1856 |
+
expected = shifted_a & b
|
| 1857 |
+
|
| 1858 |
+
a_bits = torch.tensor([((a >> (7 - i)) & 1) for i in range(8)],
|
| 1859 |
+
device=self.device, dtype=torch.float32)
|
| 1860 |
+
b_bits = torch.tensor([((b >> (7 - i)) & 1) for i in range(8)],
|
| 1861 |
+
device=self.device, dtype=torch.float32)
|
| 1862 |
+
|
| 1863 |
+
# Apply SHL
|
| 1864 |
+
shifted_bits = []
|
| 1865 |
+
for bit in range(8):
|
| 1866 |
+
w = pop[f'alu.alu8bit.shl.bit{bit}.weight'].view(pop_size)
|
| 1867 |
+
bias = pop[f'alu.alu8bit.shl.bit{bit}.bias'].view(pop_size)
|
| 1868 |
+
if bit < 7:
|
| 1869 |
+
inp = a_bits[bit + 1]
|
| 1870 |
+
else:
|
| 1871 |
+
inp = torch.tensor(0.0, device=self.device)
|
| 1872 |
+
out = heaviside(inp * w + bias)
|
| 1873 |
+
shifted_bits.append(out)
|
| 1874 |
+
|
| 1875 |
+
# Apply AND
|
| 1876 |
+
and_bits = []
|
| 1877 |
+
w_and = pop['alu.alu8bit.and.weight'].view(pop_size, 8, 2)
|
| 1878 |
+
b_and = pop['alu.alu8bit.and.bias'].view(pop_size, 8)
|
| 1879 |
+
for bit in range(8):
|
| 1880 |
+
inp = torch.tensor([shifted_bits[bit][0].item(), b_bits[bit].item()],
|
| 1881 |
+
device=self.device)
|
| 1882 |
+
out = heaviside((inp * w_and[:, bit]).sum(-1) + b_and[:, bit])
|
| 1883 |
+
and_bits.append(out)
|
| 1884 |
+
|
| 1885 |
+
out_val = sum(int(and_bits[i][0].item()) << (7 - i) for i in range(8))
|
| 1886 |
+
correct = (out_val == expected)
|
| 1887 |
+
op_scores += float(correct)
|
| 1888 |
+
op_total += 1
|
| 1889 |
+
|
| 1890 |
+
scores += op_scores
|
| 1891 |
+
total += op_total
|
| 1892 |
+
self._record('integration.shift_then_and', int(op_scores[0].item()), op_total, [])
|
| 1893 |
+
if debug:
|
| 1894 |
+
r = self.results[-1]
|
| 1895 |
+
print(f" {r.name}: {r.passed}/{r.total} {'PASS' if r.success else 'FAIL'}")
|
| 1896 |
+
except (KeyError, RuntimeError) as e:
|
| 1897 |
+
if debug:
|
| 1898 |
+
print(f" integration.shift_then_and: SKIP ({e})")
|
| 1899 |
+
|
| 1900 |
+
# Test 4: SUB then conditional (A - B, if result < 0 then NEG)
|
| 1901 |
+
# Uses: subtractor + comparator + conditional logic
|
| 1902 |
+
try:
|
| 1903 |
+
op_scores = torch.zeros(pop_size, device=self.device)
|
| 1904 |
+
op_total = 0
|
| 1905 |
+
|
| 1906 |
+
tests = [(50, 30), (30, 50), (100, 100), (0, 1)]
|
| 1907 |
+
for a, b in tests:
|
| 1908 |
+
diff = (a - b) & 0xFF
|
| 1909 |
+
is_negative = a < b
|
| 1910 |
+
expected = (-diff & 0xFF) if is_negative else diff
|
| 1911 |
+
|
| 1912 |
+
# Just verify the subtraction works correctly
|
| 1913 |
+
# (Full conditional logic would require control flow)
|
| 1914 |
+
a_bits = torch.tensor([((a >> (7 - i)) & 1) for i in range(8)],
|
| 1915 |
+
device=self.device, dtype=torch.float32)
|
| 1916 |
+
b_bits = torch.tensor([((b >> (7 - i)) & 1) for i in range(8)],
|
| 1917 |
+
device=self.device, dtype=torch.float32)
|
| 1918 |
+
|
| 1919 |
+
# Check LT comparator
|
| 1920 |
+
w = pop['arithmetic.lessthan8bit.weight'].view(pop_size, 16)
|
| 1921 |
+
bias = pop['arithmetic.lessthan8bit.bias'].view(pop_size)
|
| 1922 |
+
inp = torch.cat([a_bits, b_bits])
|
| 1923 |
+
lt_out = heaviside((inp * w).sum(-1) + bias)
|
| 1924 |
+
|
| 1925 |
+
correct = (lt_out[0].item() == float(is_negative))
|
| 1926 |
+
op_scores += float(correct)
|
| 1927 |
+
op_total += 1
|
| 1928 |
+
|
| 1929 |
+
scores += op_scores
|
| 1930 |
+
total += op_total
|
| 1931 |
+
self._record('integration.sub_then_conditional', int(op_scores[0].item()), op_total, [])
|
| 1932 |
+
if debug:
|
| 1933 |
+
r = self.results[-1]
|
| 1934 |
+
print(f" {r.name}: {r.passed}/{r.total} {'PASS' if r.success else 'FAIL'}")
|
| 1935 |
+
except (KeyError, RuntimeError) as e:
|
| 1936 |
+
if debug:
|
| 1937 |
+
print(f" integration.sub_then_conditional: SKIP ({e})")
|
| 1938 |
+
|
| 1939 |
+
# Test 5: Complex expression: ((A + B) * 2) & 0xF0
|
| 1940 |
+
# Uses: adder + SHL + AND
|
| 1941 |
+
try:
|
| 1942 |
+
op_scores = torch.zeros(pop_size, device=self.device)
|
| 1943 |
+
op_total = 0
|
| 1944 |
+
|
| 1945 |
+
tests = [(10, 20), (50, 50), (127, 1), (0, 0)]
|
| 1946 |
+
for a, b in tests:
|
| 1947 |
+
sum_val = (a + b) & 0xFF
|
| 1948 |
+
doubled = (sum_val << 1) & 0xFF
|
| 1949 |
+
expected = doubled & 0xF0
|
| 1950 |
+
|
| 1951 |
+
sum_bits = torch.tensor([((sum_val >> (7 - i)) & 1) for i in range(8)],
|
| 1952 |
+
device=self.device, dtype=torch.float32)
|
| 1953 |
+
mask_bits = torch.tensor([1, 1, 1, 1, 0, 0, 0, 0],
|
| 1954 |
+
device=self.device, dtype=torch.float32)
|
| 1955 |
+
|
| 1956 |
+
# Apply SHL
|
| 1957 |
+
shifted_bits = []
|
| 1958 |
+
for bit in range(8):
|
| 1959 |
+
w = pop[f'alu.alu8bit.shl.bit{bit}.weight'].view(pop_size)
|
| 1960 |
+
bias = pop[f'alu.alu8bit.shl.bit{bit}.bias'].view(pop_size)
|
| 1961 |
+
if bit < 7:
|
| 1962 |
+
inp = sum_bits[bit + 1]
|
| 1963 |
+
else:
|
| 1964 |
+
inp = torch.tensor(0.0, device=self.device)
|
| 1965 |
+
out = heaviside(inp * w + bias)
|
| 1966 |
+
shifted_bits.append(out)
|
| 1967 |
+
|
| 1968 |
+
# Apply AND with mask
|
| 1969 |
+
w_and = pop['alu.alu8bit.and.weight'].view(pop_size, 8, 2)
|
| 1970 |
+
b_and = pop['alu.alu8bit.and.bias'].view(pop_size, 8)
|
| 1971 |
+
result_bits = []
|
| 1972 |
+
for bit in range(8):
|
| 1973 |
+
inp = torch.tensor([shifted_bits[bit][0].item(), mask_bits[bit].item()],
|
| 1974 |
+
device=self.device)
|
| 1975 |
+
out = heaviside((inp * w_and[:, bit]).sum(-1) + b_and[:, bit])
|
| 1976 |
+
result_bits.append(out)
|
| 1977 |
+
|
| 1978 |
+
out_val = sum(int(result_bits[i][0].item()) << (7 - i) for i in range(8))
|
| 1979 |
+
correct = (out_val == expected)
|
| 1980 |
+
op_scores += float(correct)
|
| 1981 |
+
op_total += 1
|
| 1982 |
+
|
| 1983 |
+
scores += op_scores
|
| 1984 |
+
total += op_total
|
| 1985 |
+
self._record('integration.complex_expr', int(op_scores[0].item()), op_total, [])
|
| 1986 |
+
if debug:
|
| 1987 |
+
r = self.results[-1]
|
| 1988 |
+
print(f" {r.name}: {r.passed}/{r.total} {'PASS' if r.success else 'FAIL'}")
|
| 1989 |
+
except (KeyError, RuntimeError) as e:
|
| 1990 |
+
if debug:
|
| 1991 |
+
print(f" integration.complex_expr: SKIP ({e})")
|
| 1992 |
+
|
| 1993 |
+
return scores, total
|
| 1994 |
+
|
| 1995 |
# =========================================================================
|
| 1996 |
# MAIN EVALUATE
|
| 1997 |
# =========================================================================
|