Fix sbc8bit test: pass borrow input to subtractor eval
Browse filesPROBLEM:
- arithmetic.sbc8bit was failing 67/134 tests
- Test had borrow variable but never passed it to eval_subtractor
- eval_subtractor defaulted to carry_in=1, which is correct for sub8bit
but wrong for sbc8bit which takes external borrow input
ANALYSIS:
- sbc8bit has $cin input (signal 2355) for borrow
- $cin goes directly into fa0.xor2 (first full adder's carry input)
- No carry_in gate like sub8bit has
FIX:
1. Modified eval_subtractor() to accept optional initial_carry parameter
2. Updated sbc8bit test to compute: initial_carry = 1.0 - borrow
- When borrow=0: carry_in=1, computes a - b
- When borrow=1: carry_in=0, computes a - b - 1
MATH:
- sbc computes: a - b - borrow
- Using two's complement: a + ~b + ~borrow
- So carry_in = ~borrow (inverted borrow)
RESULT:
- arithmetic.sbc8bit: 67/134 -> 134/134 PASS
- Overall: 206,124/206,124 (100.0000%)
- Fitness: 1.000000
All circuits now pass!
|
@@ -662,20 +662,25 @@ def test_clz(ctx: EvalContext) -> List[TestResult]:
|
|
| 662 |
# =============================================================================
|
| 663 |
|
| 664 |
def eval_subtractor(ctx: EvalContext, prefix: str, a_bits: List[float],
|
| 665 |
-
b_bits: List[float]) -> Tuple[List[float], float]:
|
| 666 |
-
"""Evaluate 8-bit subtractor (a - b) using full adders with b inverted + carry-in
|
| 667 |
|
| 668 |
The subtractor circuit has internal NOT gates (notb0-notb7) that invert b,
|
| 669 |
-
then uses full adders to compute a + ~b +
|
|
|
|
|
|
|
|
|
|
| 670 |
"""
|
| 671 |
n = len(a_bits)
|
| 672 |
result = []
|
| 673 |
|
| 674 |
-
# Get initial carry
|
| 675 |
-
if
|
|
|
|
|
|
|
| 676 |
carry = eval_gate_direct(ctx, f"{prefix}.carry_in", [1.0])
|
| 677 |
else:
|
| 678 |
-
carry = 1.0
|
| 679 |
|
| 680 |
# First, invert b bits using the circuit's NOT gates
|
| 681 |
notb_bits = []
|
|
@@ -856,20 +861,24 @@ def test_adders(ctx: EvalContext) -> List[TestResult]:
|
|
| 856 |
results.append(TestResult("arithmetic.adc8bit", passed, total))
|
| 857 |
|
| 858 |
# 8-bit subtract with borrow (sbc8bit)
|
|
|
|
|
|
|
| 859 |
if f"arithmetic.sbc8bit.fa0.xor1.layer1.or.weight" in ctx.tensors:
|
| 860 |
passed, total = 0, 0
|
| 861 |
test_cases = [(0, 0, 0), (0, 0, 1), (255, 1, 0), (255, 1, 1), (100, 50, 0), (100, 50, 1)]
|
| 862 |
if not ctx.quick:
|
| 863 |
test_cases.extend((a, b, c) for a in range(0, 256, 32) for b in range(0, 256, 32) for c in [0, 1])
|
| 864 |
|
| 865 |
-
for a, b,
|
| 866 |
a_bits = [float((a >> i) & 1) for i in range(8)]
|
| 867 |
b_bits = [float((b >> i) & 1) for i in range(8)]
|
| 868 |
|
| 869 |
-
|
|
|
|
|
|
|
| 870 |
result = sum(int(bit) << i for i, bit in enumerate(result_bits))
|
| 871 |
# sbc: a - b - borrow
|
| 872 |
-
expected = (a - b -
|
| 873 |
|
| 874 |
total += 1
|
| 875 |
if result == expected:
|
|
|
|
| 662 |
# =============================================================================
|
| 663 |
|
| 664 |
def eval_subtractor(ctx: EvalContext, prefix: str, a_bits: List[float],
|
| 665 |
+
b_bits: List[float], initial_carry: float = None) -> Tuple[List[float], float]:
|
| 666 |
+
"""Evaluate 8-bit subtractor (a - b) using full adders with b inverted + carry-in.
|
| 667 |
|
| 668 |
The subtractor circuit has internal NOT gates (notb0-notb7) that invert b,
|
| 669 |
+
then uses full adders to compute a + ~b + carry_in.
|
| 670 |
+
|
| 671 |
+
For sub8bit: carry_in = 1 (computes a - b)
|
| 672 |
+
For sbc8bit: carry_in = ~borrow (computes a - b - borrow)
|
| 673 |
"""
|
| 674 |
n = len(a_bits)
|
| 675 |
result = []
|
| 676 |
|
| 677 |
+
# Get initial carry
|
| 678 |
+
if initial_carry is not None:
|
| 679 |
+
carry = initial_carry
|
| 680 |
+
elif f"{prefix}.carry_in.weight" in ctx.tensors:
|
| 681 |
carry = eval_gate_direct(ctx, f"{prefix}.carry_in", [1.0])
|
| 682 |
else:
|
| 683 |
+
carry = 1.0 # Default for sub8bit
|
| 684 |
|
| 685 |
# First, invert b bits using the circuit's NOT gates
|
| 686 |
notb_bits = []
|
|
|
|
| 861 |
results.append(TestResult("arithmetic.adc8bit", passed, total))
|
| 862 |
|
| 863 |
# 8-bit subtract with borrow (sbc8bit)
|
| 864 |
+
# sbc computes: a - b - borrow = a + ~b + ~borrow
|
| 865 |
+
# So carry_in = ~borrow (1 when borrow=0, 0 when borrow=1)
|
| 866 |
if f"arithmetic.sbc8bit.fa0.xor1.layer1.or.weight" in ctx.tensors:
|
| 867 |
passed, total = 0, 0
|
| 868 |
test_cases = [(0, 0, 0), (0, 0, 1), (255, 1, 0), (255, 1, 1), (100, 50, 0), (100, 50, 1)]
|
| 869 |
if not ctx.quick:
|
| 870 |
test_cases.extend((a, b, c) for a in range(0, 256, 32) for b in range(0, 256, 32) for c in [0, 1])
|
| 871 |
|
| 872 |
+
for a, b, borrow in test_cases:
|
| 873 |
a_bits = [float((a >> i) & 1) for i in range(8)]
|
| 874 |
b_bits = [float((b >> i) & 1) for i in range(8)]
|
| 875 |
|
| 876 |
+
# carry_in = ~borrow for sbc (inverted borrow)
|
| 877 |
+
initial_carry = 1.0 - float(borrow)
|
| 878 |
+
result_bits, _ = eval_subtractor(ctx, "arithmetic.sbc8bit", a_bits, b_bits, initial_carry)
|
| 879 |
result = sum(int(bit) << i for i, bit in enumerate(result_bits))
|
| 880 |
# sbc: a - b - borrow
|
| 881 |
+
expected = (a - b - borrow) % 256
|
| 882 |
|
| 883 |
total += 1
|
| 884 |
if result == expected:
|