PortfolioAI commited on
Commit ·
e6a7a67
1
Parent(s): d0106fe
Add build functions for float16 sub/mul/div/toint/fromint
Browse filesBuild functions define gate structures for:
- float16.sub: ~34 tensors (sign flip + passthrough)
- float16.mul: ~1532 tensors (11x11 multiplier + exp add)
- float16.div: ~3708 tensors (iterative division)
- float16.toint: ~788 tensors (shift + sign handling)
- float16.fromint: ~956 tensors (CLZ + normalize)
Inference functions still needed to wire gate inputs.
- TODO.md +5 -5
- __pycache__/eval.cpython-311.pyc +0 -0
- arithmetic.safetensors +2 -2
- convert_to_explicit_inputs.py +930 -0
TODO.md
CHANGED
|
@@ -8,13 +8,13 @@
|
|
| 8 |
- [x] `float16.cmp` -- comparison a > b (14 gates, 113/113 tests)
|
| 9 |
- [x] `float16.normalize` -- CLZ-based shift calculator (51 gates, 14/14 tests)
|
| 10 |
- [x] `float16.add` -- IEEE 754 addition (~998 gates, 125/125 tests)
|
| 11 |
-
- [ ] `float16.sub` -- subtraction (
|
| 12 |
-
- [ ] `float16.mul` -- multiplication
|
| 13 |
-
- [ ] `float16.div` -- division
|
|
|
|
|
|
|
| 14 |
- [x] `float16.neg` -- sign flip (16 gates, 58/58 tests)
|
| 15 |
- [x] `float16.abs` -- clear sign bit (16 gates, 58/58 tests)
|
| 16 |
-
- [ ] `float16.toint` -- convert to integer
|
| 17 |
-
- [ ] `float16.fromint` -- convert from integer
|
| 18 |
|
| 19 |
### Supporting Infrastructure
|
| 20 |
- [x] `arithmetic.clz8bit` -- 8-bit count leading zeros (30 gates, 256/256 tests)
|
|
|
|
| 8 |
- [x] `float16.cmp` -- comparison a > b (14 gates, 113/113 tests)
|
| 9 |
- [x] `float16.normalize` -- CLZ-based shift calculator (51 gates, 14/14 tests)
|
| 10 |
- [x] `float16.add` -- IEEE 754 addition (~998 gates, 125/125 tests)
|
| 11 |
+
- [ ] `float16.sub` -- IEEE 754 subtraction (build: done, inference: pending)
|
| 12 |
+
- [ ] `float16.mul` -- IEEE 754 multiplication (build: done, inference: pending)
|
| 13 |
+
- [ ] `float16.div` -- IEEE 754 division (build: done, inference: pending)
|
| 14 |
+
- [ ] `float16.toint` -- float16 to int16 (build: done, inference: pending)
|
| 15 |
+
- [ ] `float16.fromint` -- int16 to float16 (build: done, inference: pending)
|
| 16 |
- [x] `float16.neg` -- sign flip (16 gates, 58/58 tests)
|
| 17 |
- [x] `float16.abs` -- clear sign bit (16 gates, 58/58 tests)
|
|
|
|
|
|
|
| 18 |
|
| 19 |
### Supporting Infrastructure
|
| 20 |
- [x] `arithmetic.clz8bit` -- 8-bit count leading zeros (30 gates, 256/256 tests)
|
__pycache__/eval.cpython-311.pyc
CHANGED
|
Binary files a/__pycache__/eval.cpython-311.pyc and b/__pycache__/eval.cpython-311.pyc differ
|
|
|
arithmetic.safetensors
CHANGED
|
@@ -1,3 +1,3 @@
|
|
| 1 |
version https://git-lfs.github.com/spec/v1
|
| 2 |
-
oid sha256:
|
| 3 |
-
size
|
|
|
|
| 1 |
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:2a352227117a245ace401841a7ed6be4533c991d87b1d24bcd56476c3c857b3f
|
| 3 |
+
size 2259880
|
convert_to_explicit_inputs.py
CHANGED
|
@@ -3830,6 +3830,916 @@ def build_float16_add_tensors() -> Dict[str, torch.Tensor]:
|
|
| 3830 |
return tensors
|
| 3831 |
|
| 3832 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3833 |
def build_clz8bit_tensors() -> Dict[str, torch.Tensor]:
|
| 3834 |
"""Build tensors for arithmetic.clz8bit circuit.
|
| 3835 |
|
|
@@ -3943,6 +4853,26 @@ def main():
|
|
| 3943 |
tensors.update(add_tensors)
|
| 3944 |
print(f" float16.add: {len(add_tensors)} tensors")
|
| 3945 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3946 |
print(f"Total tensors: {len(tensors)}")
|
| 3947 |
|
| 3948 |
# Load routing for complex circuits
|
|
|
|
| 3830 |
return tensors
|
| 3831 |
|
| 3832 |
|
| 3833 |
+
def build_float16_sub_tensors() -> Dict[str, torch.Tensor]:
|
| 3834 |
+
"""Build tensors for float16.sub circuit.
|
| 3835 |
+
|
| 3836 |
+
Subtraction: a - b = a + (-b)
|
| 3837 |
+
Just flip the sign bit of b and use the add circuit.
|
| 3838 |
+
"""
|
| 3839 |
+
tensors = {}
|
| 3840 |
+
prefix = "float16.sub"
|
| 3841 |
+
|
| 3842 |
+
# Flip sign bit of b: b_neg_sign = NOT b[15]
|
| 3843 |
+
tensors[f"{prefix}.b_neg_sign.weight"] = torch.tensor([-1.0])
|
| 3844 |
+
tensors[f"{prefix}.b_neg_sign.bias"] = torch.tensor([0.0])
|
| 3845 |
+
|
| 3846 |
+
# Output bits: passthrough from float16.add with modified b
|
| 3847 |
+
for i in range(16):
|
| 3848 |
+
tensors[f"{prefix}.out{i}.weight"] = torch.tensor([1.0])
|
| 3849 |
+
tensors[f"{prefix}.out{i}.bias"] = torch.tensor([-0.5])
|
| 3850 |
+
|
| 3851 |
+
return tensors
|
| 3852 |
+
|
| 3853 |
+
|
| 3854 |
+
def build_float16_mul_tensors() -> Dict[str, torch.Tensor]:
|
| 3855 |
+
"""Build tensors for float16.mul circuit.
|
| 3856 |
+
|
| 3857 |
+
IEEE 754 float16 multiplication:
|
| 3858 |
+
- Result sign = a_sign XOR b_sign
|
| 3859 |
+
- Result exponent = a_exp + b_exp - bias (15)
|
| 3860 |
+
- Result mantissa = a_mant * b_mant (22-bit product, normalize to 11)
|
| 3861 |
+
|
| 3862 |
+
Special cases:
|
| 3863 |
+
- NaN * anything = NaN
|
| 3864 |
+
- Inf * 0 = NaN
|
| 3865 |
+
- Inf * finite = Inf
|
| 3866 |
+
- 0 * finite = 0
|
| 3867 |
+
"""
|
| 3868 |
+
tensors = {}
|
| 3869 |
+
prefix = "float16.mul"
|
| 3870 |
+
|
| 3871 |
+
# =========================================================================
|
| 3872 |
+
# STAGE 1: UNPACK AND DETECT SPECIAL CASES
|
| 3873 |
+
# =========================================================================
|
| 3874 |
+
|
| 3875 |
+
# exp_a_all_ones: exponent = 31 (inf or nan)
|
| 3876 |
+
tensors[f"{prefix}.exp_a_all_ones.weight"] = torch.tensor([1.0] * 5)
|
| 3877 |
+
tensors[f"{prefix}.exp_a_all_ones.bias"] = torch.tensor([-5.0])
|
| 3878 |
+
|
| 3879 |
+
tensors[f"{prefix}.exp_b_all_ones.weight"] = torch.tensor([1.0] * 5)
|
| 3880 |
+
tensors[f"{prefix}.exp_b_all_ones.bias"] = torch.tensor([-5.0])
|
| 3881 |
+
|
| 3882 |
+
# exp_a_zero: exponent = 0 (zero or subnormal)
|
| 3883 |
+
tensors[f"{prefix}.exp_a_zero.weight"] = torch.tensor([-1.0] * 5)
|
| 3884 |
+
tensors[f"{prefix}.exp_a_zero.bias"] = torch.tensor([0.0])
|
| 3885 |
+
|
| 3886 |
+
tensors[f"{prefix}.exp_b_zero.weight"] = torch.tensor([-1.0] * 5)
|
| 3887 |
+
tensors[f"{prefix}.exp_b_zero.bias"] = torch.tensor([0.0])
|
| 3888 |
+
|
| 3889 |
+
# mant_a_nonzero / mant_b_nonzero
|
| 3890 |
+
tensors[f"{prefix}.mant_a_nonzero.weight"] = torch.tensor([1.0] * 10)
|
| 3891 |
+
tensors[f"{prefix}.mant_a_nonzero.bias"] = torch.tensor([-1.0])
|
| 3892 |
+
|
| 3893 |
+
tensors[f"{prefix}.mant_b_nonzero.weight"] = torch.tensor([1.0] * 10)
|
| 3894 |
+
tensors[f"{prefix}.mant_b_nonzero.bias"] = torch.tensor([-1.0])
|
| 3895 |
+
|
| 3896 |
+
# mant_a_zero = NOT mant_a_nonzero
|
| 3897 |
+
tensors[f"{prefix}.mant_a_zero.weight"] = torch.tensor([-1.0])
|
| 3898 |
+
tensors[f"{prefix}.mant_a_zero.bias"] = torch.tensor([0.0])
|
| 3899 |
+
|
| 3900 |
+
tensors[f"{prefix}.mant_b_zero.weight"] = torch.tensor([-1.0])
|
| 3901 |
+
tensors[f"{prefix}.mant_b_zero.bias"] = torch.tensor([0.0])
|
| 3902 |
+
|
| 3903 |
+
# a_is_nan = exp_all_ones AND mant_nonzero
|
| 3904 |
+
tensors[f"{prefix}.a_is_nan.weight"] = torch.tensor([1.0, 1.0])
|
| 3905 |
+
tensors[f"{prefix}.a_is_nan.bias"] = torch.tensor([-2.0])
|
| 3906 |
+
|
| 3907 |
+
tensors[f"{prefix}.b_is_nan.weight"] = torch.tensor([1.0, 1.0])
|
| 3908 |
+
tensors[f"{prefix}.b_is_nan.bias"] = torch.tensor([-2.0])
|
| 3909 |
+
|
| 3910 |
+
# a_is_inf = exp_all_ones AND mant_zero
|
| 3911 |
+
tensors[f"{prefix}.a_is_inf.weight"] = torch.tensor([1.0, 1.0])
|
| 3912 |
+
tensors[f"{prefix}.a_is_inf.bias"] = torch.tensor([-2.0])
|
| 3913 |
+
|
| 3914 |
+
tensors[f"{prefix}.b_is_inf.weight"] = torch.tensor([1.0, 1.0])
|
| 3915 |
+
tensors[f"{prefix}.b_is_inf.bias"] = torch.tensor([-2.0])
|
| 3916 |
+
|
| 3917 |
+
# a_is_zero = exp_zero AND mant_zero
|
| 3918 |
+
tensors[f"{prefix}.a_is_zero.weight"] = torch.tensor([1.0, 1.0])
|
| 3919 |
+
tensors[f"{prefix}.a_is_zero.bias"] = torch.tensor([-2.0])
|
| 3920 |
+
|
| 3921 |
+
tensors[f"{prefix}.b_is_zero.weight"] = torch.tensor([1.0, 1.0])
|
| 3922 |
+
tensors[f"{prefix}.b_is_zero.bias"] = torch.tensor([-2.0])
|
| 3923 |
+
|
| 3924 |
+
# either_is_nan = a_is_nan OR b_is_nan
|
| 3925 |
+
tensors[f"{prefix}.either_is_nan.weight"] = torch.tensor([1.0, 1.0])
|
| 3926 |
+
tensors[f"{prefix}.either_is_nan.bias"] = torch.tensor([-1.0])
|
| 3927 |
+
|
| 3928 |
+
# either_is_inf = a_is_inf OR b_is_inf
|
| 3929 |
+
tensors[f"{prefix}.either_is_inf.weight"] = torch.tensor([1.0, 1.0])
|
| 3930 |
+
tensors[f"{prefix}.either_is_inf.bias"] = torch.tensor([-1.0])
|
| 3931 |
+
|
| 3932 |
+
# either_is_zero = a_is_zero OR b_is_zero
|
| 3933 |
+
tensors[f"{prefix}.either_is_zero.weight"] = torch.tensor([1.0, 1.0])
|
| 3934 |
+
tensors[f"{prefix}.either_is_zero.bias"] = torch.tensor([-1.0])
|
| 3935 |
+
|
| 3936 |
+
# inf_times_zero = either_is_inf AND either_is_zero (produces NaN)
|
| 3937 |
+
tensors[f"{prefix}.inf_times_zero.weight"] = torch.tensor([1.0, 1.0])
|
| 3938 |
+
tensors[f"{prefix}.inf_times_zero.bias"] = torch.tensor([-2.0])
|
| 3939 |
+
|
| 3940 |
+
# result_is_nan = either_is_nan OR inf_times_zero
|
| 3941 |
+
tensors[f"{prefix}.result_is_nan.weight"] = torch.tensor([1.0, 1.0])
|
| 3942 |
+
tensors[f"{prefix}.result_is_nan.bias"] = torch.tensor([-1.0])
|
| 3943 |
+
|
| 3944 |
+
# result_is_inf = either_is_inf AND NOT(result_is_nan) AND NOT(either_is_zero)
|
| 3945 |
+
tensors[f"{prefix}.not_result_is_nan.weight"] = torch.tensor([-1.0])
|
| 3946 |
+
tensors[f"{prefix}.not_result_is_nan.bias"] = torch.tensor([0.0])
|
| 3947 |
+
|
| 3948 |
+
tensors[f"{prefix}.not_either_is_zero.weight"] = torch.tensor([-1.0])
|
| 3949 |
+
tensors[f"{prefix}.not_either_is_zero.bias"] = torch.tensor([0.0])
|
| 3950 |
+
|
| 3951 |
+
tensors[f"{prefix}.result_is_inf.weight"] = torch.tensor([1.0, 1.0, 1.0])
|
| 3952 |
+
tensors[f"{prefix}.result_is_inf.bias"] = torch.tensor([-3.0])
|
| 3953 |
+
|
| 3954 |
+
# result_is_zero = either_is_zero AND NOT(result_is_nan)
|
| 3955 |
+
tensors[f"{prefix}.result_is_zero.weight"] = torch.tensor([1.0, 1.0])
|
| 3956 |
+
tensors[f"{prefix}.result_is_zero.bias"] = torch.tensor([-2.0])
|
| 3957 |
+
|
| 3958 |
+
# =========================================================================
|
| 3959 |
+
# STAGE 2: RESULT SIGN (XOR of input signs)
|
| 3960 |
+
# =========================================================================
|
| 3961 |
+
|
| 3962 |
+
tensors[f"{prefix}.result_sign.layer1.or.weight"] = torch.tensor([1.0, 1.0])
|
| 3963 |
+
tensors[f"{prefix}.result_sign.layer1.or.bias"] = torch.tensor([-1.0])
|
| 3964 |
+
tensors[f"{prefix}.result_sign.layer1.nand.weight"] = torch.tensor([-1.0, -1.0])
|
| 3965 |
+
tensors[f"{prefix}.result_sign.layer1.nand.bias"] = torch.tensor([1.0])
|
| 3966 |
+
tensors[f"{prefix}.result_sign.layer2.weight"] = torch.tensor([1.0, 1.0])
|
| 3967 |
+
tensors[f"{prefix}.result_sign.layer2.bias"] = torch.tensor([-2.0])
|
| 3968 |
+
|
| 3969 |
+
# =========================================================================
|
| 3970 |
+
# STAGE 3: MANTISSA MULTIPLICATION (11x11 -> 22 bits)
|
| 3971 |
+
# =========================================================================
|
| 3972 |
+
|
| 3973 |
+
# Implicit bits for mantissas
|
| 3974 |
+
tensors[f"{prefix}.implicit_a.weight"] = torch.tensor([-1.0])
|
| 3975 |
+
tensors[f"{prefix}.implicit_a.bias"] = torch.tensor([0.0])
|
| 3976 |
+
|
| 3977 |
+
tensors[f"{prefix}.implicit_b.weight"] = torch.tensor([-1.0])
|
| 3978 |
+
tensors[f"{prefix}.implicit_b.bias"] = torch.tensor([0.0])
|
| 3979 |
+
|
| 3980 |
+
# Full mantissas: mant_a[10:0] = {implicit_a, a[9:0]}
|
| 3981 |
+
# mant_b[10:0] = {implicit_b, b[9:0]}
|
| 3982 |
+
|
| 3983 |
+
# 11x11 array multiplier produces 22-bit product
|
| 3984 |
+
# Partial products: pp[i][j] = mant_a[i] AND mant_b[j]
|
| 3985 |
+
for i in range(11):
|
| 3986 |
+
for j in range(11):
|
| 3987 |
+
tensors[f"{prefix}.pp{i}_{j}.weight"] = torch.tensor([1.0, 1.0])
|
| 3988 |
+
tensors[f"{prefix}.pp{i}_{j}.bias"] = torch.tensor([-2.0])
|
| 3989 |
+
|
| 3990 |
+
# Reduction tree using carry-save adders
|
| 3991 |
+
# Level 0: 11 partial product rows
|
| 3992 |
+
# We'll use a Wallace tree reduction
|
| 3993 |
+
|
| 3994 |
+
# For simplicity, use a ripple reduction approach
|
| 3995 |
+
# Row i contributes to columns i through i+10
|
| 3996 |
+
# Sum columns using full adder chains
|
| 3997 |
+
|
| 3998 |
+
# Column sums using compressor tree
|
| 3999 |
+
for col in range(22):
|
| 4000 |
+
# Count how many partial products contribute to this column
|
| 4001 |
+
count = 0
|
| 4002 |
+
for i in range(11):
|
| 4003 |
+
j = col - i
|
| 4004 |
+
if 0 <= j < 11:
|
| 4005 |
+
count += 1
|
| 4006 |
+
|
| 4007 |
+
if count == 0:
|
| 4008 |
+
continue
|
| 4009 |
+
elif count == 1:
|
| 4010 |
+
# Single bit, just pass through
|
| 4011 |
+
tensors[f"{prefix}.col{col}.weight"] = torch.tensor([1.0])
|
| 4012 |
+
tensors[f"{prefix}.col{col}.bias"] = torch.tensor([-0.5])
|
| 4013 |
+
else:
|
| 4014 |
+
# Multi-bit column: use threshold gate to count
|
| 4015 |
+
# For proper addition we need full adder trees
|
| 4016 |
+
# Simplified: use weighted sum approach
|
| 4017 |
+
tensors[f"{prefix}.col{col}_sum.weight"] = torch.tensor([1.0] * count)
|
| 4018 |
+
tensors[f"{prefix}.col{col}_sum.bias"] = torch.tensor([-0.5])
|
| 4019 |
+
|
| 4020 |
+
# Carry generation for each threshold level
|
| 4021 |
+
for t in range(1, count):
|
| 4022 |
+
tensors[f"{prefix}.col{col}_ge{t}.weight"] = torch.tensor([1.0] * count)
|
| 4023 |
+
tensors[f"{prefix}.col{col}_ge{t}.bias"] = torch.tensor([-float(t)])
|
| 4024 |
+
|
| 4025 |
+
# Final product assembly using ripple carry
|
| 4026 |
+
for i in range(22):
|
| 4027 |
+
p = f"{prefix}.prod_fa{i}"
|
| 4028 |
+
tensors[f"{p}.xor1.layer1.or.weight"] = torch.tensor([1.0, 1.0])
|
| 4029 |
+
tensors[f"{p}.xor1.layer1.or.bias"] = torch.tensor([-1.0])
|
| 4030 |
+
tensors[f"{p}.xor1.layer1.nand.weight"] = torch.tensor([-1.0, -1.0])
|
| 4031 |
+
tensors[f"{p}.xor1.layer1.nand.bias"] = torch.tensor([1.0])
|
| 4032 |
+
tensors[f"{p}.xor1.layer2.weight"] = torch.tensor([1.0, 1.0])
|
| 4033 |
+
tensors[f"{p}.xor1.layer2.bias"] = torch.tensor([-2.0])
|
| 4034 |
+
|
| 4035 |
+
tensors[f"{p}.xor2.layer1.or.weight"] = torch.tensor([1.0, 1.0])
|
| 4036 |
+
tensors[f"{p}.xor2.layer1.or.bias"] = torch.tensor([-1.0])
|
| 4037 |
+
tensors[f"{p}.xor2.layer1.nand.weight"] = torch.tensor([-1.0, -1.0])
|
| 4038 |
+
tensors[f"{p}.xor2.layer1.nand.bias"] = torch.tensor([1.0])
|
| 4039 |
+
tensors[f"{p}.xor2.layer2.weight"] = torch.tensor([1.0, 1.0])
|
| 4040 |
+
tensors[f"{p}.xor2.layer2.bias"] = torch.tensor([-2.0])
|
| 4041 |
+
|
| 4042 |
+
tensors[f"{p}.and1.weight"] = torch.tensor([1.0, 1.0])
|
| 4043 |
+
tensors[f"{p}.and1.bias"] = torch.tensor([-2.0])
|
| 4044 |
+
tensors[f"{p}.and2.weight"] = torch.tensor([1.0, 1.0])
|
| 4045 |
+
tensors[f"{p}.and2.bias"] = torch.tensor([-2.0])
|
| 4046 |
+
tensors[f"{p}.cout.weight"] = torch.tensor([1.0, 1.0])
|
| 4047 |
+
tensors[f"{p}.cout.bias"] = torch.tensor([-1.0])
|
| 4048 |
+
|
| 4049 |
+
# =========================================================================
|
| 4050 |
+
# STAGE 4: EXPONENT ADDITION
|
| 4051 |
+
# =========================================================================
|
| 4052 |
+
|
| 4053 |
+
# Result exponent = exp_a + exp_b - 15 (bias adjustment)
|
| 4054 |
+
# First add exp_a + exp_b using 6-bit adder (max 31+31=62, need 6 bits)
|
| 4055 |
+
for i in range(6):
|
| 4056 |
+
p = f"{prefix}.exp_add.fa{i}"
|
| 4057 |
+
tensors[f"{p}.xor1.layer1.or.weight"] = torch.tensor([1.0, 1.0])
|
| 4058 |
+
tensors[f"{p}.xor1.layer1.or.bias"] = torch.tensor([-1.0])
|
| 4059 |
+
tensors[f"{p}.xor1.layer1.nand.weight"] = torch.tensor([-1.0, -1.0])
|
| 4060 |
+
tensors[f"{p}.xor1.layer1.nand.bias"] = torch.tensor([1.0])
|
| 4061 |
+
tensors[f"{p}.xor1.layer2.weight"] = torch.tensor([1.0, 1.0])
|
| 4062 |
+
tensors[f"{p}.xor1.layer2.bias"] = torch.tensor([-2.0])
|
| 4063 |
+
|
| 4064 |
+
tensors[f"{p}.xor2.layer1.or.weight"] = torch.tensor([1.0, 1.0])
|
| 4065 |
+
tensors[f"{p}.xor2.layer1.or.bias"] = torch.tensor([-1.0])
|
| 4066 |
+
tensors[f"{p}.xor2.layer1.nand.weight"] = torch.tensor([-1.0, -1.0])
|
| 4067 |
+
tensors[f"{p}.xor2.layer1.nand.bias"] = torch.tensor([1.0])
|
| 4068 |
+
tensors[f"{p}.xor2.layer2.weight"] = torch.tensor([1.0, 1.0])
|
| 4069 |
+
tensors[f"{p}.xor2.layer2.bias"] = torch.tensor([-2.0])
|
| 4070 |
+
|
| 4071 |
+
tensors[f"{p}.and1.weight"] = torch.tensor([1.0, 1.0])
|
| 4072 |
+
tensors[f"{p}.and1.bias"] = torch.tensor([-2.0])
|
| 4073 |
+
tensors[f"{p}.and2.weight"] = torch.tensor([1.0, 1.0])
|
| 4074 |
+
tensors[f"{p}.and2.bias"] = torch.tensor([-2.0])
|
| 4075 |
+
tensors[f"{p}.cout.weight"] = torch.tensor([1.0, 1.0])
|
| 4076 |
+
tensors[f"{p}.cout.bias"] = torch.tensor([-1.0])
|
| 4077 |
+
|
| 4078 |
+
# Subtract 15: exp_sum - 15 using subtractor
|
| 4079 |
+
# NOT of 15 = NOT(01111) = 10000
|
| 4080 |
+
for i in range(6):
|
| 4081 |
+
p = f"{prefix}.exp_sub.fa{i}"
|
| 4082 |
+
tensors[f"{p}.xor1.layer1.or.weight"] = torch.tensor([1.0, 1.0])
|
| 4083 |
+
tensors[f"{p}.xor1.layer1.or.bias"] = torch.tensor([-1.0])
|
| 4084 |
+
tensors[f"{p}.xor1.layer1.nand.weight"] = torch.tensor([-1.0, -1.0])
|
| 4085 |
+
tensors[f"{p}.xor1.layer1.nand.bias"] = torch.tensor([1.0])
|
| 4086 |
+
tensors[f"{p}.xor1.layer2.weight"] = torch.tensor([1.0, 1.0])
|
| 4087 |
+
tensors[f"{p}.xor1.layer2.bias"] = torch.tensor([-2.0])
|
| 4088 |
+
|
| 4089 |
+
tensors[f"{p}.xor2.layer1.or.weight"] = torch.tensor([1.0, 1.0])
|
| 4090 |
+
tensors[f"{p}.xor2.layer1.or.bias"] = torch.tensor([-1.0])
|
| 4091 |
+
tensors[f"{p}.xor2.layer1.nand.weight"] = torch.tensor([-1.0, -1.0])
|
| 4092 |
+
tensors[f"{p}.xor2.layer1.nand.bias"] = torch.tensor([1.0])
|
| 4093 |
+
tensors[f"{p}.xor2.layer2.weight"] = torch.tensor([1.0, 1.0])
|
| 4094 |
+
tensors[f"{p}.xor2.layer2.bias"] = torch.tensor([-2.0])
|
| 4095 |
+
|
| 4096 |
+
tensors[f"{p}.and1.weight"] = torch.tensor([1.0, 1.0])
|
| 4097 |
+
tensors[f"{p}.and1.bias"] = torch.tensor([-2.0])
|
| 4098 |
+
tensors[f"{p}.and2.weight"] = torch.tensor([1.0, 1.0])
|
| 4099 |
+
tensors[f"{p}.and2.bias"] = torch.tensor([-2.0])
|
| 4100 |
+
tensors[f"{p}.cout.weight"] = torch.tensor([1.0, 1.0])
|
| 4101 |
+
tensors[f"{p}.cout.bias"] = torch.tensor([-1.0])
|
| 4102 |
+
|
| 4103 |
+
# =========================================================================
|
| 4104 |
+
# STAGE 5: NORMALIZE PRODUCT
|
| 4105 |
+
# =========================================================================
|
| 4106 |
+
|
| 4107 |
+
# Product is in range [1.0, 4.0) since each mantissa is [1.0, 2.0)
|
| 4108 |
+
# If product >= 2.0, right shift by 1 and increment exponent
|
| 4109 |
+
# Check if bit 21 is set (product >= 2.0)
|
| 4110 |
+
tensors[f"{prefix}.prod_overflow.weight"] = torch.tensor([1.0])
|
| 4111 |
+
tensors[f"{prefix}.prod_overflow.bias"] = torch.tensor([-0.5])
|
| 4112 |
+
|
| 4113 |
+
tensors[f"{prefix}.not_prod_overflow.weight"] = torch.tensor([-1.0])
|
| 4114 |
+
tensors[f"{prefix}.not_prod_overflow.bias"] = torch.tensor([0.0])
|
| 4115 |
+
|
| 4116 |
+
# Normalized mantissa selection (10 bits)
|
| 4117 |
+
# If overflow: take bits 20:11, else take bits 19:10
|
| 4118 |
+
for i in range(10):
|
| 4119 |
+
tensors[f"{prefix}.norm_mant{i}.overflow_path.weight"] = torch.tensor([1.0, 1.0])
|
| 4120 |
+
tensors[f"{prefix}.norm_mant{i}.overflow_path.bias"] = torch.tensor([-2.0])
|
| 4121 |
+
tensors[f"{prefix}.norm_mant{i}.normal_path.weight"] = torch.tensor([1.0, 1.0])
|
| 4122 |
+
tensors[f"{prefix}.norm_mant{i}.normal_path.bias"] = torch.tensor([-2.0])
|
| 4123 |
+
tensors[f"{prefix}.norm_mant{i}.weight"] = torch.tensor([1.0, 1.0])
|
| 4124 |
+
tensors[f"{prefix}.norm_mant{i}.bias"] = torch.tensor([-1.0])
|
| 4125 |
+
|
| 4126 |
+
# Exponent adjustment: if overflow, add 1
|
| 4127 |
+
for i in range(5):
|
| 4128 |
+
p = f"{prefix}.result_exp_fa{i}"
|
| 4129 |
+
tensors[f"{p}.xor1.layer1.or.weight"] = torch.tensor([1.0, 1.0])
|
| 4130 |
+
tensors[f"{p}.xor1.layer1.or.bias"] = torch.tensor([-1.0])
|
| 4131 |
+
tensors[f"{p}.xor1.layer1.nand.weight"] = torch.tensor([-1.0, -1.0])
|
| 4132 |
+
tensors[f"{p}.xor1.layer1.nand.bias"] = torch.tensor([1.0])
|
| 4133 |
+
tensors[f"{p}.xor1.layer2.weight"] = torch.tensor([1.0, 1.0])
|
| 4134 |
+
tensors[f"{p}.xor1.layer2.bias"] = torch.tensor([-2.0])
|
| 4135 |
+
|
| 4136 |
+
tensors[f"{p}.xor2.layer1.or.weight"] = torch.tensor([1.0, 1.0])
|
| 4137 |
+
tensors[f"{p}.xor2.layer1.or.bias"] = torch.tensor([-1.0])
|
| 4138 |
+
tensors[f"{p}.xor2.layer1.nand.weight"] = torch.tensor([-1.0, -1.0])
|
| 4139 |
+
tensors[f"{p}.xor2.layer1.nand.bias"] = torch.tensor([1.0])
|
| 4140 |
+
tensors[f"{p}.xor2.layer2.weight"] = torch.tensor([1.0, 1.0])
|
| 4141 |
+
tensors[f"{p}.xor2.layer2.bias"] = torch.tensor([-2.0])
|
| 4142 |
+
|
| 4143 |
+
tensors[f"{p}.and1.weight"] = torch.tensor([1.0, 1.0])
|
| 4144 |
+
tensors[f"{p}.and1.bias"] = torch.tensor([-2.0])
|
| 4145 |
+
tensors[f"{p}.and2.weight"] = torch.tensor([1.0, 1.0])
|
| 4146 |
+
tensors[f"{p}.and2.bias"] = torch.tensor([-2.0])
|
| 4147 |
+
tensors[f"{p}.cout.weight"] = torch.tensor([1.0, 1.0])
|
| 4148 |
+
tensors[f"{p}.cout.bias"] = torch.tensor([-1.0])
|
| 4149 |
+
|
| 4150 |
+
# Overflow/underflow detection
|
| 4151 |
+
tensors[f"{prefix}.exp_overflow.weight"] = torch.tensor([1.0])
|
| 4152 |
+
tensors[f"{prefix}.exp_overflow.bias"] = torch.tensor([-0.5])
|
| 4153 |
+
|
| 4154 |
+
tensors[f"{prefix}.exp_underflow.weight"] = torch.tensor([1.0])
|
| 4155 |
+
tensors[f"{prefix}.exp_underflow.bias"] = torch.tensor([-0.5])
|
| 4156 |
+
|
| 4157 |
+
# =========================================================================
|
| 4158 |
+
# STAGE 6: OUTPUT ASSEMBLY
|
| 4159 |
+
# =========================================================================
|
| 4160 |
+
|
| 4161 |
+
tensors[f"{prefix}.is_normal_result.weight"] = torch.tensor([-1.0, -1.0, -1.0])
|
| 4162 |
+
tensors[f"{prefix}.is_normal_result.bias"] = torch.tensor([0.0])
|
| 4163 |
+
|
| 4164 |
+
for i in range(16):
|
| 4165 |
+
if i < 10:
|
| 4166 |
+
tensors[f"{prefix}.out_nan{i}.weight"] = torch.tensor([1.0])
|
| 4167 |
+
tensors[f"{prefix}.out_nan{i}.bias"] = torch.tensor([-0.5])
|
| 4168 |
+
elif i < 15:
|
| 4169 |
+
tensors[f"{prefix}.out_nan{i}.weight"] = torch.tensor([1.0])
|
| 4170 |
+
tensors[f"{prefix}.out_nan{i}.bias"] = torch.tensor([-0.5])
|
| 4171 |
+
|
| 4172 |
+
if i < 10:
|
| 4173 |
+
tensors[f"{prefix}.out_normal{i}.weight"] = torch.tensor([1.0])
|
| 4174 |
+
tensors[f"{prefix}.out_normal{i}.bias"] = torch.tensor([-0.5])
|
| 4175 |
+
elif i < 15:
|
| 4176 |
+
tensors[f"{prefix}.out_normal{i}.weight"] = torch.tensor([1.0])
|
| 4177 |
+
tensors[f"{prefix}.out_normal{i}.bias"] = torch.tensor([-0.5])
|
| 4178 |
+
|
| 4179 |
+
tensors[f"{prefix}.out{i}.nan_gate.weight"] = torch.tensor([1.0, 1.0])
|
| 4180 |
+
tensors[f"{prefix}.out{i}.nan_gate.bias"] = torch.tensor([-2.0])
|
| 4181 |
+
tensors[f"{prefix}.out{i}.inf_gate.weight"] = torch.tensor([1.0, 1.0])
|
| 4182 |
+
tensors[f"{prefix}.out{i}.inf_gate.bias"] = torch.tensor([-2.0])
|
| 4183 |
+
tensors[f"{prefix}.out{i}.zero_gate.weight"] = torch.tensor([1.0, 1.0])
|
| 4184 |
+
tensors[f"{prefix}.out{i}.zero_gate.bias"] = torch.tensor([-2.0])
|
| 4185 |
+
tensors[f"{prefix}.out{i}.normal_gate.weight"] = torch.tensor([1.0, 1.0])
|
| 4186 |
+
tensors[f"{prefix}.out{i}.normal_gate.bias"] = torch.tensor([-2.0])
|
| 4187 |
+
|
| 4188 |
+
tensors[f"{prefix}.out{i}.weight"] = torch.tensor([1.0, 1.0, 1.0, 1.0])
|
| 4189 |
+
tensors[f"{prefix}.out{i}.bias"] = torch.tensor([-1.0])
|
| 4190 |
+
|
| 4191 |
+
return tensors
|
| 4192 |
+
|
| 4193 |
+
|
| 4194 |
+
def build_float16_div_tensors() -> Dict[str, torch.Tensor]:
|
| 4195 |
+
"""Build tensors for float16.div circuit.
|
| 4196 |
+
|
| 4197 |
+
IEEE 754 float16 division:
|
| 4198 |
+
- Result sign = a_sign XOR b_sign
|
| 4199 |
+
- Result exponent = a_exp - b_exp + bias (15)
|
| 4200 |
+
- Result mantissa = a_mant / b_mant (using non-restoring division)
|
| 4201 |
+
|
| 4202 |
+
Special cases:
|
| 4203 |
+
- NaN / anything = NaN
|
| 4204 |
+
- anything / NaN = NaN
|
| 4205 |
+
- Inf / Inf = NaN
|
| 4206 |
+
- 0 / 0 = NaN
|
| 4207 |
+
- Inf / finite = Inf
|
| 4208 |
+
- finite / Inf = 0
|
| 4209 |
+
- finite / 0 = Inf
|
| 4210 |
+
- 0 / finite = 0
|
| 4211 |
+
"""
|
| 4212 |
+
tensors = {}
|
| 4213 |
+
prefix = "float16.div"
|
| 4214 |
+
|
| 4215 |
+
# Similar structure to mul but with subtraction for exponents
|
| 4216 |
+
# and iterative division for mantissas
|
| 4217 |
+
|
| 4218 |
+
# Special case detection (same as mul)
|
| 4219 |
+
tensors[f"{prefix}.exp_a_all_ones.weight"] = torch.tensor([1.0] * 5)
|
| 4220 |
+
tensors[f"{prefix}.exp_a_all_ones.bias"] = torch.tensor([-5.0])
|
| 4221 |
+
|
| 4222 |
+
tensors[f"{prefix}.exp_b_all_ones.weight"] = torch.tensor([1.0] * 5)
|
| 4223 |
+
tensors[f"{prefix}.exp_b_all_ones.bias"] = torch.tensor([-5.0])
|
| 4224 |
+
|
| 4225 |
+
tensors[f"{prefix}.exp_a_zero.weight"] = torch.tensor([-1.0] * 5)
|
| 4226 |
+
tensors[f"{prefix}.exp_a_zero.bias"] = torch.tensor([0.0])
|
| 4227 |
+
|
| 4228 |
+
tensors[f"{prefix}.exp_b_zero.weight"] = torch.tensor([-1.0] * 5)
|
| 4229 |
+
tensors[f"{prefix}.exp_b_zero.bias"] = torch.tensor([0.0])
|
| 4230 |
+
|
| 4231 |
+
tensors[f"{prefix}.mant_a_nonzero.weight"] = torch.tensor([1.0] * 10)
|
| 4232 |
+
tensors[f"{prefix}.mant_a_nonzero.bias"] = torch.tensor([-1.0])
|
| 4233 |
+
|
| 4234 |
+
tensors[f"{prefix}.mant_b_nonzero.weight"] = torch.tensor([1.0] * 10)
|
| 4235 |
+
tensors[f"{prefix}.mant_b_nonzero.bias"] = torch.tensor([-1.0])
|
| 4236 |
+
|
| 4237 |
+
tensors[f"{prefix}.mant_a_zero.weight"] = torch.tensor([-1.0])
|
| 4238 |
+
tensors[f"{prefix}.mant_a_zero.bias"] = torch.tensor([0.0])
|
| 4239 |
+
|
| 4240 |
+
tensors[f"{prefix}.mant_b_zero.weight"] = torch.tensor([-1.0])
|
| 4241 |
+
tensors[f"{prefix}.mant_b_zero.bias"] = torch.tensor([0.0])
|
| 4242 |
+
|
| 4243 |
+
tensors[f"{prefix}.a_is_nan.weight"] = torch.tensor([1.0, 1.0])
|
| 4244 |
+
tensors[f"{prefix}.a_is_nan.bias"] = torch.tensor([-2.0])
|
| 4245 |
+
|
| 4246 |
+
tensors[f"{prefix}.b_is_nan.weight"] = torch.tensor([1.0, 1.0])
|
| 4247 |
+
tensors[f"{prefix}.b_is_nan.bias"] = torch.tensor([-2.0])
|
| 4248 |
+
|
| 4249 |
+
tensors[f"{prefix}.a_is_inf.weight"] = torch.tensor([1.0, 1.0])
|
| 4250 |
+
tensors[f"{prefix}.a_is_inf.bias"] = torch.tensor([-2.0])
|
| 4251 |
+
|
| 4252 |
+
tensors[f"{prefix}.b_is_inf.weight"] = torch.tensor([1.0, 1.0])
|
| 4253 |
+
tensors[f"{prefix}.b_is_inf.bias"] = torch.tensor([-2.0])
|
| 4254 |
+
|
| 4255 |
+
tensors[f"{prefix}.a_is_zero.weight"] = torch.tensor([1.0, 1.0])
|
| 4256 |
+
tensors[f"{prefix}.a_is_zero.bias"] = torch.tensor([-2.0])
|
| 4257 |
+
|
| 4258 |
+
tensors[f"{prefix}.b_is_zero.weight"] = torch.tensor([1.0, 1.0])
|
| 4259 |
+
tensors[f"{prefix}.b_is_zero.bias"] = torch.tensor([-2.0])
|
| 4260 |
+
|
| 4261 |
+
tensors[f"{prefix}.either_is_nan.weight"] = torch.tensor([1.0, 1.0])
|
| 4262 |
+
tensors[f"{prefix}.either_is_nan.bias"] = torch.tensor([-1.0])
|
| 4263 |
+
|
| 4264 |
+
# inf/inf = NaN, 0/0 = NaN
|
| 4265 |
+
tensors[f"{prefix}.both_inf.weight"] = torch.tensor([1.0, 1.0])
|
| 4266 |
+
tensors[f"{prefix}.both_inf.bias"] = torch.tensor([-2.0])
|
| 4267 |
+
|
| 4268 |
+
tensors[f"{prefix}.both_zero.weight"] = torch.tensor([1.0, 1.0])
|
| 4269 |
+
tensors[f"{prefix}.both_zero.bias"] = torch.tensor([-2.0])
|
| 4270 |
+
|
| 4271 |
+
tensors[f"{prefix}.result_is_nan.weight"] = torch.tensor([1.0, 1.0, 1.0])
|
| 4272 |
+
tensors[f"{prefix}.result_is_nan.bias"] = torch.tensor([-1.0])
|
| 4273 |
+
|
| 4274 |
+
# finite/0 = inf, inf/finite = inf
|
| 4275 |
+
tensors[f"{prefix}.not_a_is_zero.weight"] = torch.tensor([-1.0])
|
| 4276 |
+
tensors[f"{prefix}.not_a_is_zero.bias"] = torch.tensor([0.0])
|
| 4277 |
+
|
| 4278 |
+
tensors[f"{prefix}.finite_div_zero.weight"] = torch.tensor([1.0, 1.0])
|
| 4279 |
+
tensors[f"{prefix}.finite_div_zero.bias"] = torch.tensor([-2.0])
|
| 4280 |
+
|
| 4281 |
+
tensors[f"{prefix}.not_b_is_inf.weight"] = torch.tensor([-1.0])
|
| 4282 |
+
tensors[f"{prefix}.not_b_is_inf.bias"] = torch.tensor([0.0])
|
| 4283 |
+
|
| 4284 |
+
tensors[f"{prefix}.inf_div_finite.weight"] = torch.tensor([1.0, 1.0])
|
| 4285 |
+
tensors[f"{prefix}.inf_div_finite.bias"] = torch.tensor([-2.0])
|
| 4286 |
+
|
| 4287 |
+
tensors[f"{prefix}.not_result_is_nan.weight"] = torch.tensor([-1.0])
|
| 4288 |
+
tensors[f"{prefix}.not_result_is_nan.bias"] = torch.tensor([0.0])
|
| 4289 |
+
|
| 4290 |
+
tensors[f"{prefix}.result_is_inf.weight"] = torch.tensor([1.0, 1.0, 1.0])
|
| 4291 |
+
tensors[f"{prefix}.result_is_inf.bias"] = torch.tensor([-1.0])
|
| 4292 |
+
|
| 4293 |
+
# 0/finite = 0, finite/inf = 0
|
| 4294 |
+
tensors[f"{prefix}.not_b_is_zero.weight"] = torch.tensor([-1.0])
|
| 4295 |
+
tensors[f"{prefix}.not_b_is_zero.bias"] = torch.tensor([0.0])
|
| 4296 |
+
|
| 4297 |
+
tensors[f"{prefix}.zero_div_finite.weight"] = torch.tensor([1.0, 1.0])
|
| 4298 |
+
tensors[f"{prefix}.zero_div_finite.bias"] = torch.tensor([-2.0])
|
| 4299 |
+
|
| 4300 |
+
tensors[f"{prefix}.not_a_is_inf.weight"] = torch.tensor([-1.0])
|
| 4301 |
+
tensors[f"{prefix}.not_a_is_inf.bias"] = torch.tensor([0.0])
|
| 4302 |
+
|
| 4303 |
+
tensors[f"{prefix}.finite_div_inf.weight"] = torch.tensor([1.0, 1.0])
|
| 4304 |
+
tensors[f"{prefix}.finite_div_inf.bias"] = torch.tensor([-2.0])
|
| 4305 |
+
|
| 4306 |
+
tensors[f"{prefix}.result_is_zero.weight"] = torch.tensor([1.0, 1.0, 1.0])
|
| 4307 |
+
tensors[f"{prefix}.result_is_zero.bias"] = torch.tensor([-1.0])
|
| 4308 |
+
|
| 4309 |
+
# Result sign (XOR)
|
| 4310 |
+
tensors[f"{prefix}.result_sign.layer1.or.weight"] = torch.tensor([1.0, 1.0])
|
| 4311 |
+
tensors[f"{prefix}.result_sign.layer1.or.bias"] = torch.tensor([-1.0])
|
| 4312 |
+
tensors[f"{prefix}.result_sign.layer1.nand.weight"] = torch.tensor([-1.0, -1.0])
|
| 4313 |
+
tensors[f"{prefix}.result_sign.layer1.nand.bias"] = torch.tensor([1.0])
|
| 4314 |
+
tensors[f"{prefix}.result_sign.layer2.weight"] = torch.tensor([1.0, 1.0])
|
| 4315 |
+
tensors[f"{prefix}.result_sign.layer2.bias"] = torch.tensor([-2.0])
|
| 4316 |
+
|
| 4317 |
+
# Exponent subtraction: exp_a - exp_b + 15
|
| 4318 |
+
# NOT gates for exp_b
|
| 4319 |
+
for i in range(5):
|
| 4320 |
+
tensors[f"{prefix}.not_exp_b{i}.weight"] = torch.tensor([-1.0])
|
| 4321 |
+
tensors[f"{prefix}.not_exp_b{i}.bias"] = torch.tensor([0.0])
|
| 4322 |
+
|
| 4323 |
+
# 6-bit subtractor for exp_a - exp_b
|
| 4324 |
+
for i in range(6):
|
| 4325 |
+
p = f"{prefix}.exp_sub.fa{i}"
|
| 4326 |
+
tensors[f"{p}.xor1.layer1.or.weight"] = torch.tensor([1.0, 1.0])
|
| 4327 |
+
tensors[f"{p}.xor1.layer1.or.bias"] = torch.tensor([-1.0])
|
| 4328 |
+
tensors[f"{p}.xor1.layer1.nand.weight"] = torch.tensor([-1.0, -1.0])
|
| 4329 |
+
tensors[f"{p}.xor1.layer1.nand.bias"] = torch.tensor([1.0])
|
| 4330 |
+
tensors[f"{p}.xor1.layer2.weight"] = torch.tensor([1.0, 1.0])
|
| 4331 |
+
tensors[f"{p}.xor1.layer2.bias"] = torch.tensor([-2.0])
|
| 4332 |
+
|
| 4333 |
+
tensors[f"{p}.xor2.layer1.or.weight"] = torch.tensor([1.0, 1.0])
|
| 4334 |
+
tensors[f"{p}.xor2.layer1.or.bias"] = torch.tensor([-1.0])
|
| 4335 |
+
tensors[f"{p}.xor2.layer1.nand.weight"] = torch.tensor([-1.0, -1.0])
|
| 4336 |
+
tensors[f"{p}.xor2.layer1.nand.bias"] = torch.tensor([1.0])
|
| 4337 |
+
tensors[f"{p}.xor2.layer2.weight"] = torch.tensor([1.0, 1.0])
|
| 4338 |
+
tensors[f"{p}.xor2.layer2.bias"] = torch.tensor([-2.0])
|
| 4339 |
+
|
| 4340 |
+
tensors[f"{p}.and1.weight"] = torch.tensor([1.0, 1.0])
|
| 4341 |
+
tensors[f"{p}.and1.bias"] = torch.tensor([-2.0])
|
| 4342 |
+
tensors[f"{p}.and2.weight"] = torch.tensor([1.0, 1.0])
|
| 4343 |
+
tensors[f"{p}.and2.bias"] = torch.tensor([-2.0])
|
| 4344 |
+
tensors[f"{p}.cout.weight"] = torch.tensor([1.0, 1.0])
|
| 4345 |
+
tensors[f"{p}.cout.bias"] = torch.tensor([-1.0])
|
| 4346 |
+
|
| 4347 |
+
# Add 15 to get biased result
|
| 4348 |
+
for i in range(6):
|
| 4349 |
+
p = f"{prefix}.exp_add15.fa{i}"
|
| 4350 |
+
tensors[f"{p}.xor1.layer1.or.weight"] = torch.tensor([1.0, 1.0])
|
| 4351 |
+
tensors[f"{p}.xor1.layer1.or.bias"] = torch.tensor([-1.0])
|
| 4352 |
+
tensors[f"{p}.xor1.layer1.nand.weight"] = torch.tensor([-1.0, -1.0])
|
| 4353 |
+
tensors[f"{p}.xor1.layer1.nand.bias"] = torch.tensor([1.0])
|
| 4354 |
+
tensors[f"{p}.xor1.layer2.weight"] = torch.tensor([1.0, 1.0])
|
| 4355 |
+
tensors[f"{p}.xor1.layer2.bias"] = torch.tensor([-2.0])
|
| 4356 |
+
|
| 4357 |
+
tensors[f"{p}.xor2.layer1.or.weight"] = torch.tensor([1.0, 1.0])
|
| 4358 |
+
tensors[f"{p}.xor2.layer1.or.bias"] = torch.tensor([-1.0])
|
| 4359 |
+
tensors[f"{p}.xor2.layer1.nand.weight"] = torch.tensor([-1.0, -1.0])
|
| 4360 |
+
tensors[f"{p}.xor2.layer1.nand.bias"] = torch.tensor([1.0])
|
| 4361 |
+
tensors[f"{p}.xor2.layer2.weight"] = torch.tensor([1.0, 1.0])
|
| 4362 |
+
tensors[f"{p}.xor2.layer2.bias"] = torch.tensor([-2.0])
|
| 4363 |
+
|
| 4364 |
+
tensors[f"{p}.and1.weight"] = torch.tensor([1.0, 1.0])
|
| 4365 |
+
tensors[f"{p}.and1.bias"] = torch.tensor([-2.0])
|
| 4366 |
+
tensors[f"{p}.and2.weight"] = torch.tensor([1.0, 1.0])
|
| 4367 |
+
tensors[f"{p}.and2.bias"] = torch.tensor([-2.0])
|
| 4368 |
+
tensors[f"{p}.cout.weight"] = torch.tensor([1.0, 1.0])
|
| 4369 |
+
tensors[f"{p}.cout.bias"] = torch.tensor([-1.0])
|
| 4370 |
+
|
| 4371 |
+
# Mantissa division: 11-bit non-restoring division
|
| 4372 |
+
# Produces 11-bit quotient
|
| 4373 |
+
tensors[f"{prefix}.implicit_a.weight"] = torch.tensor([-1.0])
|
| 4374 |
+
tensors[f"{prefix}.implicit_a.bias"] = torch.tensor([0.0])
|
| 4375 |
+
|
| 4376 |
+
tensors[f"{prefix}.implicit_b.weight"] = torch.tensor([-1.0])
|
| 4377 |
+
tensors[f"{prefix}.implicit_b.bias"] = torch.tensor([0.0])
|
| 4378 |
+
|
| 4379 |
+
# Division iterations (11 steps for 11-bit quotient)
|
| 4380 |
+
for step in range(11):
|
| 4381 |
+
p = f"{prefix}.div_step{step}"
|
| 4382 |
+
|
| 4383 |
+
# Compare and subtract
|
| 4384 |
+
for i in range(12):
|
| 4385 |
+
tensors[f"{p}.sub.fa{i}.xor1.layer1.or.weight"] = torch.tensor([1.0, 1.0])
|
| 4386 |
+
tensors[f"{p}.sub.fa{i}.xor1.layer1.or.bias"] = torch.tensor([-1.0])
|
| 4387 |
+
tensors[f"{p}.sub.fa{i}.xor1.layer1.nand.weight"] = torch.tensor([-1.0, -1.0])
|
| 4388 |
+
tensors[f"{p}.sub.fa{i}.xor1.layer1.nand.bias"] = torch.tensor([1.0])
|
| 4389 |
+
tensors[f"{p}.sub.fa{i}.xor1.layer2.weight"] = torch.tensor([1.0, 1.0])
|
| 4390 |
+
tensors[f"{p}.sub.fa{i}.xor1.layer2.bias"] = torch.tensor([-2.0])
|
| 4391 |
+
|
| 4392 |
+
tensors[f"{p}.sub.fa{i}.xor2.layer1.or.weight"] = torch.tensor([1.0, 1.0])
|
| 4393 |
+
tensors[f"{p}.sub.fa{i}.xor2.layer1.or.bias"] = torch.tensor([-1.0])
|
| 4394 |
+
tensors[f"{p}.sub.fa{i}.xor2.layer1.nand.weight"] = torch.tensor([-1.0, -1.0])
|
| 4395 |
+
tensors[f"{p}.sub.fa{i}.xor2.layer1.nand.bias"] = torch.tensor([1.0])
|
| 4396 |
+
tensors[f"{p}.sub.fa{i}.xor2.layer2.weight"] = torch.tensor([1.0, 1.0])
|
| 4397 |
+
tensors[f"{p}.sub.fa{i}.xor2.layer2.bias"] = torch.tensor([-2.0])
|
| 4398 |
+
|
| 4399 |
+
tensors[f"{p}.sub.fa{i}.and1.weight"] = torch.tensor([1.0, 1.0])
|
| 4400 |
+
tensors[f"{p}.sub.fa{i}.and1.bias"] = torch.tensor([-2.0])
|
| 4401 |
+
tensors[f"{p}.sub.fa{i}.and2.weight"] = torch.tensor([1.0, 1.0])
|
| 4402 |
+
tensors[f"{p}.sub.fa{i}.and2.bias"] = torch.tensor([-2.0])
|
| 4403 |
+
tensors[f"{p}.sub.fa{i}.cout.weight"] = torch.tensor([1.0, 1.0])
|
| 4404 |
+
tensors[f"{p}.sub.fa{i}.cout.bias"] = torch.tensor([-1.0])
|
| 4405 |
+
|
| 4406 |
+
# Quotient bit
|
| 4407 |
+
tensors[f"{p}.q_bit.weight"] = torch.tensor([1.0])
|
| 4408 |
+
tensors[f"{p}.q_bit.bias"] = torch.tensor([-0.5])
|
| 4409 |
+
|
| 4410 |
+
# Remainder selection
|
| 4411 |
+
for i in range(12):
|
| 4412 |
+
tensors[f"{p}.rem{i}.sub_path.weight"] = torch.tensor([1.0, 1.0])
|
| 4413 |
+
tensors[f"{p}.rem{i}.sub_path.bias"] = torch.tensor([-2.0])
|
| 4414 |
+
tensors[f"{p}.rem{i}.shift_path.weight"] = torch.tensor([1.0, 1.0])
|
| 4415 |
+
tensors[f"{p}.rem{i}.shift_path.bias"] = torch.tensor([-2.0])
|
| 4416 |
+
tensors[f"{p}.rem{i}.weight"] = torch.tensor([1.0, 1.0])
|
| 4417 |
+
tensors[f"{p}.rem{i}.bias"] = torch.tensor([-1.0])
|
| 4418 |
+
|
| 4419 |
+
# Normalization
|
| 4420 |
+
tensors[f"{prefix}.need_norm.weight"] = torch.tensor([-1.0])
|
| 4421 |
+
tensors[f"{prefix}.need_norm.bias"] = torch.tensor([0.0])
|
| 4422 |
+
|
| 4423 |
+
for i in range(10):
|
| 4424 |
+
tensors[f"{prefix}.norm_mant{i}.norm_path.weight"] = torch.tensor([1.0, 1.0])
|
| 4425 |
+
tensors[f"{prefix}.norm_mant{i}.norm_path.bias"] = torch.tensor([-2.0])
|
| 4426 |
+
tensors[f"{prefix}.norm_mant{i}.direct_path.weight"] = torch.tensor([1.0, 1.0])
|
| 4427 |
+
tensors[f"{prefix}.norm_mant{i}.direct_path.bias"] = torch.tensor([-2.0])
|
| 4428 |
+
tensors[f"{prefix}.norm_mant{i}.weight"] = torch.tensor([1.0, 1.0])
|
| 4429 |
+
tensors[f"{prefix}.norm_mant{i}.bias"] = torch.tensor([-1.0])
|
| 4430 |
+
|
| 4431 |
+
# Output assembly
|
| 4432 |
+
tensors[f"{prefix}.is_normal_result.weight"] = torch.tensor([-1.0, -1.0, -1.0])
|
| 4433 |
+
tensors[f"{prefix}.is_normal_result.bias"] = torch.tensor([0.0])
|
| 4434 |
+
|
| 4435 |
+
for i in range(16):
|
| 4436 |
+
tensors[f"{prefix}.out{i}.nan_gate.weight"] = torch.tensor([1.0, 1.0])
|
| 4437 |
+
tensors[f"{prefix}.out{i}.nan_gate.bias"] = torch.tensor([-2.0])
|
| 4438 |
+
tensors[f"{prefix}.out{i}.inf_gate.weight"] = torch.tensor([1.0, 1.0])
|
| 4439 |
+
tensors[f"{prefix}.out{i}.inf_gate.bias"] = torch.tensor([-2.0])
|
| 4440 |
+
tensors[f"{prefix}.out{i}.zero_gate.weight"] = torch.tensor([1.0, 1.0])
|
| 4441 |
+
tensors[f"{prefix}.out{i}.zero_gate.bias"] = torch.tensor([-2.0])
|
| 4442 |
+
tensors[f"{prefix}.out{i}.normal_gate.weight"] = torch.tensor([1.0, 1.0])
|
| 4443 |
+
tensors[f"{prefix}.out{i}.normal_gate.bias"] = torch.tensor([-2.0])
|
| 4444 |
+
|
| 4445 |
+
tensors[f"{prefix}.out{i}.weight"] = torch.tensor([1.0, 1.0, 1.0, 1.0])
|
| 4446 |
+
tensors[f"{prefix}.out{i}.bias"] = torch.tensor([-1.0])
|
| 4447 |
+
|
| 4448 |
+
return tensors
|
| 4449 |
+
|
| 4450 |
+
|
| 4451 |
+
def build_float16_toint_tensors() -> Dict[str, torch.Tensor]:
|
| 4452 |
+
"""Build tensors for float16.toint circuit.
|
| 4453 |
+
|
| 4454 |
+
Convert float16 to signed 16-bit integer (truncate toward zero).
|
| 4455 |
+
|
| 4456 |
+
Algorithm:
|
| 4457 |
+
1. If NaN or Inf, return 0 or max/min int
|
| 4458 |
+
2. If |value| < 1, return 0
|
| 4459 |
+
3. Shift mantissa by (exponent - 15 - 10) positions
|
| 4460 |
+
4. Apply sign
|
| 4461 |
+
"""
|
| 4462 |
+
tensors = {}
|
| 4463 |
+
prefix = "float16.toint"
|
| 4464 |
+
|
| 4465 |
+
# Special case detection
|
| 4466 |
+
tensors[f"{prefix}.exp_all_ones.weight"] = torch.tensor([1.0] * 5)
|
| 4467 |
+
tensors[f"{prefix}.exp_all_ones.bias"] = torch.tensor([-5.0])
|
| 4468 |
+
|
| 4469 |
+
tensors[f"{prefix}.exp_zero.weight"] = torch.tensor([-1.0] * 5)
|
| 4470 |
+
tensors[f"{prefix}.exp_zero.bias"] = torch.tensor([0.0])
|
| 4471 |
+
|
| 4472 |
+
tensors[f"{prefix}.mant_nonzero.weight"] = torch.tensor([1.0] * 10)
|
| 4473 |
+
tensors[f"{prefix}.mant_nonzero.bias"] = torch.tensor([-1.0])
|
| 4474 |
+
|
| 4475 |
+
tensors[f"{prefix}.is_nan.weight"] = torch.tensor([1.0, 1.0])
|
| 4476 |
+
tensors[f"{prefix}.is_nan.bias"] = torch.tensor([-2.0])
|
| 4477 |
+
|
| 4478 |
+
tensors[f"{prefix}.mant_zero.weight"] = torch.tensor([-1.0])
|
| 4479 |
+
tensors[f"{prefix}.mant_zero.bias"] = torch.tensor([0.0])
|
| 4480 |
+
|
| 4481 |
+
tensors[f"{prefix}.is_inf.weight"] = torch.tensor([1.0, 1.0])
|
| 4482 |
+
tensors[f"{prefix}.is_inf.bias"] = torch.tensor([-2.0])
|
| 4483 |
+
|
| 4484 |
+
tensors[f"{prefix}.is_zero.weight"] = torch.tensor([1.0, 1.0])
|
| 4485 |
+
tensors[f"{prefix}.is_zero.bias"] = torch.tensor([-2.0])
|
| 4486 |
+
|
| 4487 |
+
# Check if exponent < 15 (|value| < 1)
|
| 4488 |
+
# exp < 15 means unbiased exp < 0, so result is 0
|
| 4489 |
+
weights = [-float(2**i) for i in range(5)]
|
| 4490 |
+
tensors[f"{prefix}.exp_lt_15.weight"] = torch.tensor(weights)
|
| 4491 |
+
tensors[f"{prefix}.exp_lt_15.bias"] = torch.tensor([14.0])
|
| 4492 |
+
|
| 4493 |
+
tensors[f"{prefix}.result_is_zero.weight"] = torch.tensor([1.0, 1.0, 1.0])
|
| 4494 |
+
tensors[f"{prefix}.result_is_zero.bias"] = torch.tensor([-1.0])
|
| 4495 |
+
|
| 4496 |
+
# Compute shift amount: exp - 15 - 10 = exp - 25
|
| 4497 |
+
# If positive, left shift mantissa; if negative, right shift
|
| 4498 |
+
# For int16, max shift is 15 (for values up to 32767)
|
| 4499 |
+
|
| 4500 |
+
# exp - 25 subtractor
|
| 4501 |
+
for i in range(5):
|
| 4502 |
+
tensors[f"{prefix}.not_25_{i}.weight"] = torch.tensor([1.0])
|
| 4503 |
+
tensors[f"{prefix}.not_25_{i}.bias"] = torch.tensor([-0.5])
|
| 4504 |
+
|
| 4505 |
+
for i in range(6):
|
| 4506 |
+
p = f"{prefix}.shift_calc.fa{i}"
|
| 4507 |
+
tensors[f"{p}.xor1.layer1.or.weight"] = torch.tensor([1.0, 1.0])
|
| 4508 |
+
tensors[f"{p}.xor1.layer1.or.bias"] = torch.tensor([-1.0])
|
| 4509 |
+
tensors[f"{p}.xor1.layer1.nand.weight"] = torch.tensor([-1.0, -1.0])
|
| 4510 |
+
tensors[f"{p}.xor1.layer1.nand.bias"] = torch.tensor([1.0])
|
| 4511 |
+
tensors[f"{p}.xor1.layer2.weight"] = torch.tensor([1.0, 1.0])
|
| 4512 |
+
tensors[f"{p}.xor1.layer2.bias"] = torch.tensor([-2.0])
|
| 4513 |
+
|
| 4514 |
+
tensors[f"{p}.xor2.layer1.or.weight"] = torch.tensor([1.0, 1.0])
|
| 4515 |
+
tensors[f"{p}.xor2.layer1.or.bias"] = torch.tensor([-1.0])
|
| 4516 |
+
tensors[f"{p}.xor2.layer1.nand.weight"] = torch.tensor([-1.0, -1.0])
|
| 4517 |
+
tensors[f"{p}.xor2.layer1.nand.bias"] = torch.tensor([1.0])
|
| 4518 |
+
tensors[f"{p}.xor2.layer2.weight"] = torch.tensor([1.0, 1.0])
|
| 4519 |
+
tensors[f"{p}.xor2.layer2.bias"] = torch.tensor([-2.0])
|
| 4520 |
+
|
| 4521 |
+
tensors[f"{p}.and1.weight"] = torch.tensor([1.0, 1.0])
|
| 4522 |
+
tensors[f"{p}.and1.bias"] = torch.tensor([-2.0])
|
| 4523 |
+
tensors[f"{p}.and2.weight"] = torch.tensor([1.0, 1.0])
|
| 4524 |
+
tensors[f"{p}.and2.bias"] = torch.tensor([-2.0])
|
| 4525 |
+
tensors[f"{p}.cout.weight"] = torch.tensor([1.0, 1.0])
|
| 4526 |
+
tensors[f"{p}.cout.bias"] = torch.tensor([-1.0])
|
| 4527 |
+
|
| 4528 |
+
# Barrel shifter (left shift mantissa)
|
| 4529 |
+
tensors[f"{prefix}.implicit_bit.weight"] = torch.tensor([-1.0])
|
| 4530 |
+
tensors[f"{prefix}.implicit_bit.bias"] = torch.tensor([0.0])
|
| 4531 |
+
|
| 4532 |
+
for stage in range(4):
|
| 4533 |
+
shift_amt = 1 << stage
|
| 4534 |
+
tensors[f"{prefix}.not_shift{stage}.weight"] = torch.tensor([-1.0])
|
| 4535 |
+
tensors[f"{prefix}.not_shift{stage}.bias"] = torch.tensor([0.0])
|
| 4536 |
+
|
| 4537 |
+
for i in range(16):
|
| 4538 |
+
tensors[f"{prefix}.lshift_s{stage}_{i}.pass.weight"] = torch.tensor([1.0, 1.0])
|
| 4539 |
+
tensors[f"{prefix}.lshift_s{stage}_{i}.pass.bias"] = torch.tensor([-2.0])
|
| 4540 |
+
if i >= shift_amt:
|
| 4541 |
+
tensors[f"{prefix}.lshift_s{stage}_{i}.shift.weight"] = torch.tensor([1.0, 1.0])
|
| 4542 |
+
tensors[f"{prefix}.lshift_s{stage}_{i}.shift.bias"] = torch.tensor([-2.0])
|
| 4543 |
+
tensors[f"{prefix}.lshift_s{stage}_{i}.weight"] = torch.tensor([1.0, 1.0])
|
| 4544 |
+
else:
|
| 4545 |
+
tensors[f"{prefix}.lshift_s{stage}_{i}.weight"] = torch.tensor([1.0])
|
| 4546 |
+
tensors[f"{prefix}.lshift_s{stage}_{i}.bias"] = torch.tensor([-1.0])
|
| 4547 |
+
|
| 4548 |
+
# Apply sign (negate if negative)
|
| 4549 |
+
for i in range(16):
|
| 4550 |
+
tensors[f"{prefix}.not_mag{i}.weight"] = torch.tensor([-1.0])
|
| 4551 |
+
tensors[f"{prefix}.not_mag{i}.bias"] = torch.tensor([0.0])
|
| 4552 |
+
|
| 4553 |
+
# Two's complement negation
|
| 4554 |
+
for i in range(16):
|
| 4555 |
+
p = f"{prefix}.neg.fa{i}"
|
| 4556 |
+
tensors[f"{p}.xor.layer1.or.weight"] = torch.tensor([1.0, 1.0])
|
| 4557 |
+
tensors[f"{p}.xor.layer1.or.bias"] = torch.tensor([-1.0])
|
| 4558 |
+
tensors[f"{p}.xor.layer1.nand.weight"] = torch.tensor([-1.0, -1.0])
|
| 4559 |
+
tensors[f"{p}.xor.layer1.nand.bias"] = torch.tensor([1.0])
|
| 4560 |
+
tensors[f"{p}.xor.layer2.weight"] = torch.tensor([1.0, 1.0])
|
| 4561 |
+
tensors[f"{p}.xor.layer2.bias"] = torch.tensor([-2.0])
|
| 4562 |
+
|
| 4563 |
+
tensors[f"{p}.and.weight"] = torch.tensor([1.0, 1.0])
|
| 4564 |
+
tensors[f"{p}.and.bias"] = torch.tensor([-2.0])
|
| 4565 |
+
tensors[f"{p}.cout.weight"] = torch.tensor([1.0, 1.0])
|
| 4566 |
+
tensors[f"{p}.cout.bias"] = torch.tensor([-1.0])
|
| 4567 |
+
|
| 4568 |
+
# Output selection
|
| 4569 |
+
for i in range(16):
|
| 4570 |
+
tensors[f"{prefix}.out{i}.pos_path.weight"] = torch.tensor([1.0, 1.0])
|
| 4571 |
+
tensors[f"{prefix}.out{i}.pos_path.bias"] = torch.tensor([-2.0])
|
| 4572 |
+
tensors[f"{prefix}.out{i}.neg_path.weight"] = torch.tensor([1.0, 1.0])
|
| 4573 |
+
tensors[f"{prefix}.out{i}.neg_path.bias"] = torch.tensor([-2.0])
|
| 4574 |
+
tensors[f"{prefix}.out{i}.weight"] = torch.tensor([1.0, 1.0])
|
| 4575 |
+
tensors[f"{prefix}.out{i}.bias"] = torch.tensor([-1.0])
|
| 4576 |
+
|
| 4577 |
+
return tensors
|
| 4578 |
+
|
| 4579 |
+
|
| 4580 |
+
def build_float16_fromint_tensors() -> Dict[str, torch.Tensor]:
|
| 4581 |
+
"""Build tensors for float16.fromint circuit.
|
| 4582 |
+
|
| 4583 |
+
Convert signed 16-bit integer to float16.
|
| 4584 |
+
|
| 4585 |
+
Algorithm:
|
| 4586 |
+
1. Take absolute value
|
| 4587 |
+
2. Count leading zeros to find exponent
|
| 4588 |
+
3. Shift to normalize mantissa
|
| 4589 |
+
4. Apply sign
|
| 4590 |
+
"""
|
| 4591 |
+
tensors = {}
|
| 4592 |
+
prefix = "float16.fromint"
|
| 4593 |
+
|
| 4594 |
+
# Check if zero
|
| 4595 |
+
tensors[f"{prefix}.is_zero.weight"] = torch.tensor([-1.0] * 16)
|
| 4596 |
+
tensors[f"{prefix}.is_zero.bias"] = torch.tensor([0.0])
|
| 4597 |
+
|
| 4598 |
+
# Check if negative (sign bit)
|
| 4599 |
+
tensors[f"{prefix}.is_negative.weight"] = torch.tensor([1.0])
|
| 4600 |
+
tensors[f"{prefix}.is_negative.bias"] = torch.tensor([-0.5])
|
| 4601 |
+
|
| 4602 |
+
tensors[f"{prefix}.not_negative.weight"] = torch.tensor([-1.0])
|
| 4603 |
+
tensors[f"{prefix}.not_negative.bias"] = torch.tensor([0.0])
|
| 4604 |
+
|
| 4605 |
+
# Absolute value: if negative, negate
|
| 4606 |
+
for i in range(16):
|
| 4607 |
+
tensors[f"{prefix}.not_in{i}.weight"] = torch.tensor([-1.0])
|
| 4608 |
+
tensors[f"{prefix}.not_in{i}.bias"] = torch.tensor([0.0])
|
| 4609 |
+
|
| 4610 |
+
for i in range(16):
|
| 4611 |
+
p = f"{prefix}.abs.fa{i}"
|
| 4612 |
+
tensors[f"{p}.xor.layer1.or.weight"] = torch.tensor([1.0, 1.0])
|
| 4613 |
+
tensors[f"{p}.xor.layer1.or.bias"] = torch.tensor([-1.0])
|
| 4614 |
+
tensors[f"{p}.xor.layer1.nand.weight"] = torch.tensor([-1.0, -1.0])
|
| 4615 |
+
tensors[f"{p}.xor.layer1.nand.bias"] = torch.tensor([1.0])
|
| 4616 |
+
tensors[f"{p}.xor.layer2.weight"] = torch.tensor([1.0, 1.0])
|
| 4617 |
+
tensors[f"{p}.xor.layer2.bias"] = torch.tensor([-2.0])
|
| 4618 |
+
|
| 4619 |
+
tensors[f"{p}.and.weight"] = torch.tensor([1.0, 1.0])
|
| 4620 |
+
tensors[f"{p}.and.bias"] = torch.tensor([-2.0])
|
| 4621 |
+
tensors[f"{p}.cout.weight"] = torch.tensor([1.0, 1.0])
|
| 4622 |
+
tensors[f"{p}.cout.bias"] = torch.tensor([-1.0])
|
| 4623 |
+
|
| 4624 |
+
# Absolute value mux
|
| 4625 |
+
for i in range(16):
|
| 4626 |
+
tensors[f"{prefix}.abs{i}.neg_path.weight"] = torch.tensor([1.0, 1.0])
|
| 4627 |
+
tensors[f"{prefix}.abs{i}.neg_path.bias"] = torch.tensor([-2.0])
|
| 4628 |
+
tensors[f"{prefix}.abs{i}.pos_path.weight"] = torch.tensor([1.0, 1.0])
|
| 4629 |
+
tensors[f"{prefix}.abs{i}.pos_path.bias"] = torch.tensor([-2.0])
|
| 4630 |
+
tensors[f"{prefix}.abs{i}.weight"] = torch.tensor([1.0, 1.0])
|
| 4631 |
+
tensors[f"{prefix}.abs{i}.bias"] = torch.tensor([-1.0])
|
| 4632 |
+
|
| 4633 |
+
# CLZ on 16-bit absolute value
|
| 4634 |
+
for k in range(1, 17):
|
| 4635 |
+
tensors[f"{prefix}.pz{k}.weight"] = torch.tensor([-1.0] * k)
|
| 4636 |
+
tensors[f"{prefix}.pz{k}.bias"] = torch.tensor([0.0])
|
| 4637 |
+
|
| 4638 |
+
for k in range(1, 17):
|
| 4639 |
+
tensors[f"{prefix}.ge{k}.weight"] = torch.tensor([1.0] * 16)
|
| 4640 |
+
tensors[f"{prefix}.ge{k}.bias"] = torch.tensor([-float(k)])
|
| 4641 |
+
|
| 4642 |
+
# CLZ binary encoding
|
| 4643 |
+
for k in [2, 4, 8, 16]:
|
| 4644 |
+
tensors[f"{prefix}.not_ge{k}.weight"] = torch.tensor([-1.0])
|
| 4645 |
+
tensors[f"{prefix}.not_ge{k}.bias"] = torch.tensor([0.0])
|
| 4646 |
+
|
| 4647 |
+
tensors[f"{prefix}.clz3.weight"] = torch.tensor([1.0])
|
| 4648 |
+
tensors[f"{prefix}.clz3.bias"] = torch.tensor([-0.5])
|
| 4649 |
+
|
| 4650 |
+
tensors[f"{prefix}.clz_and_4_7.weight"] = torch.tensor([1.0, 1.0])
|
| 4651 |
+
tensors[f"{prefix}.clz_and_4_7.bias"] = torch.tensor([-2.0])
|
| 4652 |
+
tensors[f"{prefix}.clz_and_12_15.weight"] = torch.tensor([1.0, 1.0])
|
| 4653 |
+
tensors[f"{prefix}.clz_and_12_15.bias"] = torch.tensor([-2.0])
|
| 4654 |
+
tensors[f"{prefix}.clz2.weight"] = torch.tensor([1.0, 1.0])
|
| 4655 |
+
tensors[f"{prefix}.clz2.bias"] = torch.tensor([-1.0])
|
| 4656 |
+
|
| 4657 |
+
tensors[f"{prefix}.clz_and_2_3.weight"] = torch.tensor([1.0, 1.0])
|
| 4658 |
+
tensors[f"{prefix}.clz_and_2_3.bias"] = torch.tensor([-2.0])
|
| 4659 |
+
tensors[f"{prefix}.clz_and_6_7.weight"] = torch.tensor([1.0, 1.0])
|
| 4660 |
+
tensors[f"{prefix}.clz_and_6_7.bias"] = torch.tensor([-2.0])
|
| 4661 |
+
tensors[f"{prefix}.clz_and_10_11.weight"] = torch.tensor([1.0, 1.0])
|
| 4662 |
+
tensors[f"{prefix}.clz_and_10_11.bias"] = torch.tensor([-2.0])
|
| 4663 |
+
tensors[f"{prefix}.clz_and_14_15.weight"] = torch.tensor([1.0, 1.0])
|
| 4664 |
+
tensors[f"{prefix}.clz_and_14_15.bias"] = torch.tensor([-2.0])
|
| 4665 |
+
tensors[f"{prefix}.clz1.weight"] = torch.tensor([1.0, 1.0, 1.0, 1.0])
|
| 4666 |
+
tensors[f"{prefix}.clz1.bias"] = torch.tensor([-1.0])
|
| 4667 |
+
|
| 4668 |
+
for i in [1, 3, 5, 7, 9, 11, 13, 15]:
|
| 4669 |
+
tensors[f"{prefix}.clz_and_{i}.weight"] = torch.tensor([1.0, 1.0])
|
| 4670 |
+
tensors[f"{prefix}.clz_and_{i}.bias"] = torch.tensor([-2.0])
|
| 4671 |
+
tensors[f"{prefix}.clz0.weight"] = torch.tensor([1.0] * 8)
|
| 4672 |
+
tensors[f"{prefix}.clz0.bias"] = torch.tensor([-1.0])
|
| 4673 |
+
|
| 4674 |
+
# Exponent = 15 + 15 - CLZ = 30 - CLZ (biased)
|
| 4675 |
+
# Actually: exponent = bias + (15 - CLZ) = 15 + 15 - CLZ
|
| 4676 |
+
for i in range(5):
|
| 4677 |
+
tensors[f"{prefix}.not_clz{i}.weight"] = torch.tensor([-1.0])
|
| 4678 |
+
tensors[f"{prefix}.not_clz{i}.bias"] = torch.tensor([0.0])
|
| 4679 |
+
|
| 4680 |
+
for i in range(5):
|
| 4681 |
+
p = f"{prefix}.exp_calc.fa{i}"
|
| 4682 |
+
tensors[f"{p}.xor1.layer1.or.weight"] = torch.tensor([1.0, 1.0])
|
| 4683 |
+
tensors[f"{p}.xor1.layer1.or.bias"] = torch.tensor([-1.0])
|
| 4684 |
+
tensors[f"{p}.xor1.layer1.nand.weight"] = torch.tensor([-1.0, -1.0])
|
| 4685 |
+
tensors[f"{p}.xor1.layer1.nand.bias"] = torch.tensor([1.0])
|
| 4686 |
+
tensors[f"{p}.xor1.layer2.weight"] = torch.tensor([1.0, 1.0])
|
| 4687 |
+
tensors[f"{p}.xor1.layer2.bias"] = torch.tensor([-2.0])
|
| 4688 |
+
|
| 4689 |
+
tensors[f"{p}.xor2.layer1.or.weight"] = torch.tensor([1.0, 1.0])
|
| 4690 |
+
tensors[f"{p}.xor2.layer1.or.bias"] = torch.tensor([-1.0])
|
| 4691 |
+
tensors[f"{p}.xor2.layer1.nand.weight"] = torch.tensor([-1.0, -1.0])
|
| 4692 |
+
tensors[f"{p}.xor2.layer1.nand.bias"] = torch.tensor([1.0])
|
| 4693 |
+
tensors[f"{p}.xor2.layer2.weight"] = torch.tensor([1.0, 1.0])
|
| 4694 |
+
tensors[f"{p}.xor2.layer2.bias"] = torch.tensor([-2.0])
|
| 4695 |
+
|
| 4696 |
+
tensors[f"{p}.and1.weight"] = torch.tensor([1.0, 1.0])
|
| 4697 |
+
tensors[f"{p}.and1.bias"] = torch.tensor([-2.0])
|
| 4698 |
+
tensors[f"{p}.and2.weight"] = torch.tensor([1.0, 1.0])
|
| 4699 |
+
tensors[f"{p}.and2.bias"] = torch.tensor([-2.0])
|
| 4700 |
+
tensors[f"{p}.cout.weight"] = torch.tensor([1.0, 1.0])
|
| 4701 |
+
tensors[f"{p}.cout.bias"] = torch.tensor([-1.0])
|
| 4702 |
+
|
| 4703 |
+
# Left barrel shifter to normalize mantissa
|
| 4704 |
+
for stage in range(4):
|
| 4705 |
+
shift_amt = 1 << stage
|
| 4706 |
+
tensors[f"{prefix}.not_norm_shift{stage}.weight"] = torch.tensor([-1.0])
|
| 4707 |
+
tensors[f"{prefix}.not_norm_shift{stage}.bias"] = torch.tensor([0.0])
|
| 4708 |
+
|
| 4709 |
+
for i in range(16):
|
| 4710 |
+
tensors[f"{prefix}.norm_s{stage}_{i}.pass.weight"] = torch.tensor([1.0, 1.0])
|
| 4711 |
+
tensors[f"{prefix}.norm_s{stage}_{i}.pass.bias"] = torch.tensor([-2.0])
|
| 4712 |
+
if i >= shift_amt:
|
| 4713 |
+
tensors[f"{prefix}.norm_s{stage}_{i}.shift.weight"] = torch.tensor([1.0, 1.0])
|
| 4714 |
+
tensors[f"{prefix}.norm_s{stage}_{i}.shift.bias"] = torch.tensor([-2.0])
|
| 4715 |
+
tensors[f"{prefix}.norm_s{stage}_{i}.weight"] = torch.tensor([1.0, 1.0])
|
| 4716 |
+
else:
|
| 4717 |
+
tensors[f"{prefix}.norm_s{stage}_{i}.weight"] = torch.tensor([1.0])
|
| 4718 |
+
tensors[f"{prefix}.norm_s{stage}_{i}.bias"] = torch.tensor([-1.0])
|
| 4719 |
+
|
| 4720 |
+
# Output assembly
|
| 4721 |
+
for i in range(16):
|
| 4722 |
+
if i < 10:
|
| 4723 |
+
tensors[f"{prefix}.out{i}.zero_gate.weight"] = torch.tensor([1.0, 1.0])
|
| 4724 |
+
tensors[f"{prefix}.out{i}.zero_gate.bias"] = torch.tensor([-2.0])
|
| 4725 |
+
tensors[f"{prefix}.out{i}.normal_gate.weight"] = torch.tensor([1.0, 1.0])
|
| 4726 |
+
tensors[f"{prefix}.out{i}.normal_gate.bias"] = torch.tensor([-2.0])
|
| 4727 |
+
tensors[f"{prefix}.out{i}.weight"] = torch.tensor([1.0, 1.0])
|
| 4728 |
+
tensors[f"{prefix}.out{i}.bias"] = torch.tensor([-1.0])
|
| 4729 |
+
elif i < 15:
|
| 4730 |
+
tensors[f"{prefix}.out{i}.zero_gate.weight"] = torch.tensor([1.0, 1.0])
|
| 4731 |
+
tensors[f"{prefix}.out{i}.zero_gate.bias"] = torch.tensor([-2.0])
|
| 4732 |
+
tensors[f"{prefix}.out{i}.normal_gate.weight"] = torch.tensor([1.0, 1.0])
|
| 4733 |
+
tensors[f"{prefix}.out{i}.normal_gate.bias"] = torch.tensor([-2.0])
|
| 4734 |
+
tensors[f"{prefix}.out{i}.weight"] = torch.tensor([1.0, 1.0])
|
| 4735 |
+
tensors[f"{prefix}.out{i}.bias"] = torch.tensor([-1.0])
|
| 4736 |
+
else:
|
| 4737 |
+
tensors[f"{prefix}.out{i}.weight"] = torch.tensor([1.0])
|
| 4738 |
+
tensors[f"{prefix}.out{i}.bias"] = torch.tensor([-0.5])
|
| 4739 |
+
|
| 4740 |
+
return tensors
|
| 4741 |
+
|
| 4742 |
+
|
| 4743 |
def build_clz8bit_tensors() -> Dict[str, torch.Tensor]:
|
| 4744 |
"""Build tensors for arithmetic.clz8bit circuit.
|
| 4745 |
|
|
|
|
| 4853 |
tensors.update(add_tensors)
|
| 4854 |
print(f" float16.add: {len(add_tensors)} tensors")
|
| 4855 |
|
| 4856 |
+
sub_tensors = build_float16_sub_tensors()
|
| 4857 |
+
tensors.update(sub_tensors)
|
| 4858 |
+
print(f" float16.sub: {len(sub_tensors)} tensors")
|
| 4859 |
+
|
| 4860 |
+
mul_tensors = build_float16_mul_tensors()
|
| 4861 |
+
tensors.update(mul_tensors)
|
| 4862 |
+
print(f" float16.mul: {len(mul_tensors)} tensors")
|
| 4863 |
+
|
| 4864 |
+
div_tensors = build_float16_div_tensors()
|
| 4865 |
+
tensors.update(div_tensors)
|
| 4866 |
+
print(f" float16.div: {len(div_tensors)} tensors")
|
| 4867 |
+
|
| 4868 |
+
toint_tensors = build_float16_toint_tensors()
|
| 4869 |
+
tensors.update(toint_tensors)
|
| 4870 |
+
print(f" float16.toint: {len(toint_tensors)} tensors")
|
| 4871 |
+
|
| 4872 |
+
fromint_tensors = build_float16_fromint_tensors()
|
| 4873 |
+
tensors.update(fromint_tensors)
|
| 4874 |
+
print(f" float16.fromint: {len(fromint_tensors)} tensors")
|
| 4875 |
+
|
| 4876 |
print(f"Total tensors: {len(tensors)}")
|
| 4877 |
|
| 4878 |
# Load routing for complex circuits
|