CharlesCNorton commited on
Commit ·
84973bf
1
Parent(s): 14b3211
4-bit carry-save adder
Browse files- README.md +93 -0
- config.json +9 -0
- create_safetensors.py +104 -0
- model.safetensors +0 -0
README.md
ADDED
|
@@ -0,0 +1,93 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
license: mit
|
| 3 |
+
tags:
|
| 4 |
+
- pytorch
|
| 5 |
+
- safetensors
|
| 6 |
+
- threshold-logic
|
| 7 |
+
- neuromorphic
|
| 8 |
+
- arithmetic
|
| 9 |
+
- adder
|
| 10 |
+
---
|
| 11 |
+
|
| 12 |
+
# threshold-carrysave-adder
|
| 13 |
+
|
| 14 |
+
4-bit carry-save adder (CSA) as threshold circuit. Adds three 4-bit numbers producing a sum and carry vector, with no carry propagation delay.
|
| 15 |
+
|
| 16 |
+
## Circuit
|
| 17 |
+
|
| 18 |
+
```
|
| 19 |
+
A[3:0] ──┐
|
| 20 |
+
B[3:0] ──┼──► CSA ──┬──► S[3:0] (sum)
|
| 21 |
+
C[3:0] ──┘ └──► Cout[3:0] (carry)
|
| 22 |
+
|
| 23 |
+
Final result: A + B + C = S + (Cout << 1)
|
| 24 |
+
```
|
| 25 |
+
|
| 26 |
+
## How It Works
|
| 27 |
+
|
| 28 |
+
Each bit position computed independently (no ripple):
|
| 29 |
+
|
| 30 |
+
```
|
| 31 |
+
S[i] = A[i] XOR B[i] XOR C[i]
|
| 32 |
+
Cout[i] = MAJ(A[i], B[i], C[i])
|
| 33 |
+
```
|
| 34 |
+
|
| 35 |
+
The carry vector is shifted left by 1 before final addition.
|
| 36 |
+
|
| 37 |
+
## Truth Table (per bit)
|
| 38 |
+
|
| 39 |
+
| A | B | C | S | Cout |
|
| 40 |
+
|---|---|---|---|------|
|
| 41 |
+
| 0 | 0 | 0 | 0 | 0 |
|
| 42 |
+
| 0 | 0 | 1 | 1 | 0 |
|
| 43 |
+
| 0 | 1 | 0 | 1 | 0 |
|
| 44 |
+
| 0 | 1 | 1 | 0 | 1 |
|
| 45 |
+
| 1 | 0 | 0 | 1 | 0 |
|
| 46 |
+
| 1 | 0 | 1 | 0 | 1 |
|
| 47 |
+
| 1 | 1 | 0 | 0 | 1 |
|
| 48 |
+
| 1 | 1 | 1 | 1 | 1 |
|
| 49 |
+
|
| 50 |
+
## Architecture
|
| 51 |
+
|
| 52 |
+
| Component | Count | Neurons |
|
| 53 |
+
|-----------|-------|---------|
|
| 54 |
+
| XOR3 (sum) | 4 | 28 |
|
| 55 |
+
| MAJ3 (carry) | 4 | 4 |
|
| 56 |
+
|
| 57 |
+
**Total: 32 neurons, 256 parameters, 3 layers**
|
| 58 |
+
|
| 59 |
+
## Applications
|
| 60 |
+
|
| 61 |
+
CSAs are fundamental building blocks in:
|
| 62 |
+
- Fast multipliers (Wallace trees, Dadda trees)
|
| 63 |
+
- Multi-operand addition
|
| 64 |
+
- DSP circuits
|
| 65 |
+
- Reducing 3 operands to 2 without carry propagation
|
| 66 |
+
|
| 67 |
+
## Usage
|
| 68 |
+
|
| 69 |
+
```python
|
| 70 |
+
from safetensors.torch import load_file
|
| 71 |
+
|
| 72 |
+
w = load_file('model.safetensors')
|
| 73 |
+
|
| 74 |
+
# Example: 15 + 15 + 15 = 45
|
| 75 |
+
# S = 13 (1101), Cout = 7 (0111)
|
| 76 |
+
# Result = 13 + (7 << 1) = 13 + 14 = 27... wait
|
| 77 |
+
# Actually: S + 2*Cout = 13 + 14 = 27, but need final adder
|
| 78 |
+
# CSA output needs one final ripple-carry add
|
| 79 |
+
```
|
| 80 |
+
|
| 81 |
+
## Files
|
| 82 |
+
|
| 83 |
+
```
|
| 84 |
+
threshold-carrysave-adder/
|
| 85 |
+
├── model.safetensors
|
| 86 |
+
├── create_safetensors.py
|
| 87 |
+
├── config.json
|
| 88 |
+
└── README.md
|
| 89 |
+
```
|
| 90 |
+
|
| 91 |
+
## License
|
| 92 |
+
|
| 93 |
+
MIT
|
config.json
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"name": "threshold-carrysave-adder",
|
| 3 |
+
"description": "4-bit carry-save adder as threshold circuit",
|
| 4 |
+
"inputs": 12,
|
| 5 |
+
"outputs": 8,
|
| 6 |
+
"neurons": 32,
|
| 7 |
+
"layers": 3,
|
| 8 |
+
"parameters": 256
|
| 9 |
+
}
|
create_safetensors.py
ADDED
|
@@ -0,0 +1,104 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import torch
|
| 2 |
+
from safetensors.torch import save_file
|
| 3 |
+
|
| 4 |
+
weights = {}
|
| 5 |
+
|
| 6 |
+
# 4-bit Carry-Save Adder
|
| 7 |
+
# Inputs: a3,a2,a1,a0, b3,b2,b1,b0, c3,c2,c1,c0 (12 inputs)
|
| 8 |
+
# Outputs: s3,s2,s1,s0, cout3,cout2,cout1,cout0 (8 outputs)
|
| 9 |
+
#
|
| 10 |
+
# For each bit i:
|
| 11 |
+
# s[i] = a[i] XOR b[i] XOR c[i]
|
| 12 |
+
# cout[i] = MAJ(a[i], b[i], c[i])
|
| 13 |
+
#
|
| 14 |
+
# No carry propagation - all bits computed in parallel
|
| 15 |
+
|
| 16 |
+
def add_xor3(name, a_idx, b_idx, c_idx, total_inputs):
|
| 17 |
+
w1 = [0.0] * total_inputs
|
| 18 |
+
w1[a_idx] = 1.0
|
| 19 |
+
w1[b_idx] = 1.0
|
| 20 |
+
weights[f'{name}.xor1.or.weight'] = torch.tensor([w1], dtype=torch.float32)
|
| 21 |
+
weights[f'{name}.xor1.or.bias'] = torch.tensor([-1.0], dtype=torch.float32)
|
| 22 |
+
w2 = [0.0] * total_inputs
|
| 23 |
+
w2[a_idx] = -1.0
|
| 24 |
+
w2[b_idx] = -1.0
|
| 25 |
+
weights[f'{name}.xor1.nand.weight'] = torch.tensor([w2], dtype=torch.float32)
|
| 26 |
+
weights[f'{name}.xor1.nand.bias'] = torch.tensor([1.0], dtype=torch.float32)
|
| 27 |
+
weights[f'{name}.xor1.and.weight'] = torch.tensor([[1.0, 1.0]], dtype=torch.float32)
|
| 28 |
+
weights[f'{name}.xor1.and.bias'] = torch.tensor([-2.0], dtype=torch.float32)
|
| 29 |
+
weights[f'{name}.xor2.or.weight'] = torch.tensor([[1.0, 1.0]], dtype=torch.float32)
|
| 30 |
+
weights[f'{name}.xor2.or.bias'] = torch.tensor([-1.0], dtype=torch.float32)
|
| 31 |
+
weights[f'{name}.xor2.nand.weight'] = torch.tensor([[-1.0, -1.0]], dtype=torch.float32)
|
| 32 |
+
weights[f'{name}.xor2.nand.bias'] = torch.tensor([1.0], dtype=torch.float32)
|
| 33 |
+
weights[f'{name}.xor2.and.weight'] = torch.tensor([[1.0, 1.0]], dtype=torch.float32)
|
| 34 |
+
weights[f'{name}.xor2.and.bias'] = torch.tensor([-2.0], dtype=torch.float32)
|
| 35 |
+
wc = [0.0] * total_inputs
|
| 36 |
+
wc[c_idx] = 1.0
|
| 37 |
+
weights[f'{name}.c_input.weight'] = torch.tensor([wc], dtype=torch.float32)
|
| 38 |
+
weights[f'{name}.c_input.bias'] = torch.tensor([0.0], dtype=torch.float32)
|
| 39 |
+
|
| 40 |
+
def add_maj3(name, a_idx, b_idx, c_idx, total_inputs):
|
| 41 |
+
w = [0.0] * total_inputs
|
| 42 |
+
w[a_idx] = 1.0
|
| 43 |
+
w[b_idx] = 1.0
|
| 44 |
+
w[c_idx] = 1.0
|
| 45 |
+
weights[f'{name}.weight'] = torch.tensor([w], dtype=torch.float32)
|
| 46 |
+
weights[f'{name}.bias'] = torch.tensor([-2.0], dtype=torch.float32)
|
| 47 |
+
|
| 48 |
+
# Input indices: a3=0,a2=1,a1=2,a0=3, b3=4,b2=5,b1=6,b0=7, c3=8,c2=9,c1=10,c0=11
|
| 49 |
+
|
| 50 |
+
for i in range(4):
|
| 51 |
+
a_idx = 3 - i
|
| 52 |
+
b_idx = 7 - i
|
| 53 |
+
c_idx = 11 - i
|
| 54 |
+
add_xor3(f's{i}', a_idx, b_idx, c_idx, 12)
|
| 55 |
+
add_maj3(f'cout{i}', a_idx, b_idx, c_idx, 12)
|
| 56 |
+
|
| 57 |
+
save_file(weights, 'model.safetensors')
|
| 58 |
+
|
| 59 |
+
def eval_xor(a, b):
|
| 60 |
+
or_out = int(a + b >= 1)
|
| 61 |
+
nand_out = int(-a - b + 1 >= 0)
|
| 62 |
+
return int(or_out + nand_out >= 2)
|
| 63 |
+
|
| 64 |
+
def eval_xor3(a, b, c):
|
| 65 |
+
return eval_xor(eval_xor(a, b), c)
|
| 66 |
+
|
| 67 |
+
def eval_maj3(a, b, c):
|
| 68 |
+
return int(a + b + c >= 2)
|
| 69 |
+
|
| 70 |
+
def csa_4bit(a3, a2, a1, a0, b3, b2, b1, b0, c3, c2, c1, c0):
|
| 71 |
+
a = [a0, a1, a2, a3]
|
| 72 |
+
b = [b0, b1, b2, b3]
|
| 73 |
+
c = [c0, c1, c2, c3]
|
| 74 |
+
s = [eval_xor3(a[i], b[i], c[i]) for i in range(4)]
|
| 75 |
+
cout = [eval_maj3(a[i], b[i], c[i]) for i in range(4)]
|
| 76 |
+
return s[0], s[1], s[2], s[3], cout[0], cout[1], cout[2], cout[3]
|
| 77 |
+
|
| 78 |
+
print("Verifying 4-bit Carry-Save Adder...")
|
| 79 |
+
errors = 0
|
| 80 |
+
for a in range(16):
|
| 81 |
+
for b in range(16):
|
| 82 |
+
for c in range(16):
|
| 83 |
+
a3, a2, a1, a0 = (a>>3)&1, (a>>2)&1, (a>>1)&1, a&1
|
| 84 |
+
b3, b2, b1, b0 = (b>>3)&1, (b>>2)&1, (b>>1)&1, b&1
|
| 85 |
+
c3, c2, c1, c0 = (c>>3)&1, (c>>2)&1, (c>>1)&1, c&1
|
| 86 |
+
s0, s1, s2, s3, cout0, cout1, cout2, cout3 = csa_4bit(
|
| 87 |
+
a3, a2, a1, a0, b3, b2, b1, b0, c3, c2, c1, c0)
|
| 88 |
+
s_val = s0 + (s1 << 1) + (s2 << 2) + (s3 << 3)
|
| 89 |
+
cout_val = cout0 + (cout1 << 1) + (cout2 << 2) + (cout3 << 3)
|
| 90 |
+
result = s_val + (cout_val << 1)
|
| 91 |
+
expected = a + b + c
|
| 92 |
+
if result != expected:
|
| 93 |
+
errors += 1
|
| 94 |
+
if errors <= 5:
|
| 95 |
+
print(f"ERROR: {a}+{b}+{c} = {result}, expected {expected}")
|
| 96 |
+
|
| 97 |
+
if errors == 0:
|
| 98 |
+
print("All 4096 test cases passed!")
|
| 99 |
+
else:
|
| 100 |
+
print(f"FAILED: {errors} errors")
|
| 101 |
+
|
| 102 |
+
mag = sum(t.abs().sum().item() for t in weights.values())
|
| 103 |
+
print(f"Magnitude: {mag:.0f}")
|
| 104 |
+
print(f"Parameters: {sum(t.numel() for t in weights.values())}")
|
model.safetensors
ADDED
|
Binary file (5.73 kB). View file
|
|
|