CharlesCNorton
commited on
Commit
·
ad8baa5
1
Parent(s):
8e161d0
Two's complement negation
Browse files- README.md +90 -0
- config.json +9 -0
- create_safetensors.py +139 -0
- model.safetensors +0 -0
README.md
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
license: mit
|
| 3 |
+
tags:
|
| 4 |
+
- pytorch
|
| 5 |
+
- safetensors
|
| 6 |
+
- threshold-logic
|
| 7 |
+
- neuromorphic
|
| 8 |
+
- arithmetic
|
| 9 |
+
- signed
|
| 10 |
+
---
|
| 11 |
+
|
| 12 |
+
# threshold-twos-complement
|
| 13 |
+
|
| 14 |
+
4-bit two's complement negation circuit. Computes -A for signed integers.
|
| 15 |
+
|
| 16 |
+
## Circuit
|
| 17 |
+
|
| 18 |
+
```
|
| 19 |
+
A[3:0] ──► Negate ──┬──► N[3:0] (-A)
|
| 20 |
+
└──► overflow (when A = -8)
|
| 21 |
+
```
|
| 22 |
+
|
| 23 |
+
## Algorithm
|
| 24 |
+
|
| 25 |
+
```
|
| 26 |
+
-A = ~A + 1
|
| 27 |
+
|
| 28 |
+
1. Invert all bits
|
| 29 |
+
2. Add 1
|
| 30 |
+
```
|
| 31 |
+
|
| 32 |
+
## Truth Table
|
| 33 |
+
|
| 34 |
+
| A (unsigned) | A (signed) | N (signed) | Overflow |
|
| 35 |
+
|--------------|------------|------------|----------|
|
| 36 |
+
| 0000 (0) | +0 | +0 | 0 |
|
| 37 |
+
| 0001 (1) | +1 | -1 | 0 |
|
| 38 |
+
| 0111 (7) | +7 | -7 | 0 |
|
| 39 |
+
| 1000 (8) | -8 | -8 | 1 |
|
| 40 |
+
| 1111 (15) | -1 | +1 | 0 |
|
| 41 |
+
|
| 42 |
+
## Overflow
|
| 43 |
+
|
| 44 |
+
The value -8 (1000) cannot be negated in 4-bit two's complement:
|
| 45 |
+
- Range: -8 to +7
|
| 46 |
+
- -(-8) = +8, which exceeds +7
|
| 47 |
+
- Result wraps to -8, overflow flag set
|
| 48 |
+
|
| 49 |
+
## Architecture
|
| 50 |
+
|
| 51 |
+
| Component | Count | Neurons |
|
| 52 |
+
|-----------|-------|---------|
|
| 53 |
+
| Bit inverters | 4 | 4 |
|
| 54 |
+
| Half-adder chain | 4 | 16 |
|
| 55 |
+
| Overflow detect | 1 | 2 |
|
| 56 |
+
|
| 57 |
+
**Total: 22 neurons, 63 parameters, 4 layers**
|
| 58 |
+
|
| 59 |
+
## Examples
|
| 60 |
+
|
| 61 |
+
```
|
| 62 |
+
-(+5) = ~(0101) + 1 = 1010 + 1 = 1011 = -5 ✓
|
| 63 |
+
-(-3) = ~(1101) + 1 = 0010 + 1 = 0011 = +3 ✓
|
| 64 |
+
-(-8) = ~(1000) + 1 = 0111 + 1 = 1000 = -8 (overflow!)
|
| 65 |
+
```
|
| 66 |
+
|
| 67 |
+
## Usage
|
| 68 |
+
|
| 69 |
+
```python
|
| 70 |
+
from safetensors.torch import load_file
|
| 71 |
+
|
| 72 |
+
w = load_file('model.safetensors')
|
| 73 |
+
|
| 74 |
+
# All 16 cases verified
|
| 75 |
+
# Overflow only when A = -8 (1000)
|
| 76 |
+
```
|
| 77 |
+
|
| 78 |
+
## Files
|
| 79 |
+
|
| 80 |
+
```
|
| 81 |
+
threshold-twos-complement/
|
| 82 |
+
├── model.safetensors
|
| 83 |
+
├── create_safetensors.py
|
| 84 |
+
├── config.json
|
| 85 |
+
└── README.md
|
| 86 |
+
```
|
| 87 |
+
|
| 88 |
+
## License
|
| 89 |
+
|
| 90 |
+
MIT
|
config.json
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"name": "threshold-twos-complement",
|
| 3 |
+
"description": "4-bit two's complement negation",
|
| 4 |
+
"inputs": 4,
|
| 5 |
+
"outputs": 5,
|
| 6 |
+
"neurons": 22,
|
| 7 |
+
"layers": 4,
|
| 8 |
+
"parameters": 63
|
| 9 |
+
}
|
create_safetensors.py
ADDED
|
@@ -0,0 +1,139 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import torch
|
| 2 |
+
from safetensors.torch import save_file
|
| 3 |
+
|
| 4 |
+
weights = {}
|
| 5 |
+
|
| 6 |
+
# 4-bit Two's Complement Negation
|
| 7 |
+
# Inputs: a3,a2,a1,a0 (4 inputs)
|
| 8 |
+
# Outputs: n3,n2,n1,n0, overflow (5 outputs)
|
| 9 |
+
#
|
| 10 |
+
# -A = ~A + 1
|
| 11 |
+
# Overflow when A = -8 (1000), since -(-8) = 8 can't be represented
|
| 12 |
+
|
| 13 |
+
# Invert inputs
|
| 14 |
+
for i in range(4):
|
| 15 |
+
weights[f'inv{i}.weight'] = torch.tensor([[-1.0]], dtype=torch.float32)
|
| 16 |
+
weights[f'inv{i}.bias'] = torch.tensor([0.0], dtype=torch.float32)
|
| 17 |
+
|
| 18 |
+
# Add 1 using half-adder chain
|
| 19 |
+
def add_xor(name):
|
| 20 |
+
weights[f'{name}.or.weight'] = torch.tensor([[1.0, 1.0]], dtype=torch.float32)
|
| 21 |
+
weights[f'{name}.or.bias'] = torch.tensor([-1.0], dtype=torch.float32)
|
| 22 |
+
weights[f'{name}.nand.weight'] = torch.tensor([[-1.0, -1.0]], dtype=torch.float32)
|
| 23 |
+
weights[f'{name}.nand.bias'] = torch.tensor([1.0], dtype=torch.float32)
|
| 24 |
+
weights[f'{name}.and.weight'] = torch.tensor([[1.0, 1.0]], dtype=torch.float32)
|
| 25 |
+
weights[f'{name}.and.bias'] = torch.tensor([-2.0], dtype=torch.float32)
|
| 26 |
+
|
| 27 |
+
def add_ha(name):
|
| 28 |
+
add_xor(f'{name}.sum')
|
| 29 |
+
weights[f'{name}.carry.weight'] = torch.tensor([[1.0, 1.0]], dtype=torch.float32)
|
| 30 |
+
weights[f'{name}.carry.bias'] = torch.tensor([-2.0], dtype=torch.float32)
|
| 31 |
+
|
| 32 |
+
# First bit: ~a0 XOR 1 = NOT(~a0) = a0... wait that's wrong
|
| 33 |
+
# Actually ~a0 + 1:
|
| 34 |
+
# bit 0: ~a0 XOR 1
|
| 35 |
+
# bit 1: ~a1 XOR carry0
|
| 36 |
+
# etc.
|
| 37 |
+
|
| 38 |
+
# ~a0 XOR 1 = ~(~a0) = a0 when carry_in=1
|
| 39 |
+
# But using threshold: we add 1 to ~A
|
| 40 |
+
# ~a0 + 1: n0 = ~a0 XOR 1 = NOT(~a0) = a0? No...
|
| 41 |
+
# Actually XOR with 1 flips the bit: ~a0 XOR 1 = NOT(~a0) = a0
|
| 42 |
+
# But we need the sum with carry...
|
| 43 |
+
|
| 44 |
+
# Let me think more carefully:
|
| 45 |
+
# ~A = [~a3, ~a2, ~a1, ~a0]
|
| 46 |
+
# (~A) + 1 starting with carry_in = 1:
|
| 47 |
+
# n0 = ~a0 XOR 1, c0 = ~a0 AND 1 = ~a0
|
| 48 |
+
# n1 = ~a1 XOR ~a0, c1 = ~a1 AND ~a0
|
| 49 |
+
# n2 = ~a2 XOR (~a1 AND ~a0), c2 = ~a2 AND ~a1 AND ~a0
|
| 50 |
+
# n3 = ~a3 XOR (~a2 AND ~a1 AND ~a0)
|
| 51 |
+
|
| 52 |
+
# Simplify: carry propagates as long as bits are 0 (after inversion, as long as original bits are 1)
|
| 53 |
+
# n0 = ~a0 XOR 1 = a0 XOR 0 = NOT(~a0) = a0... hmm
|
| 54 |
+
# Wait, ~a0 XOR 1:
|
| 55 |
+
# if ~a0=0 (a0=1): 0 XOR 1 = 1
|
| 56 |
+
# if ~a0=1 (a0=0): 1 XOR 1 = 0
|
| 57 |
+
# So n0 = ~(~a0) = a0? That's not right for negation.
|
| 58 |
+
|
| 59 |
+
# Let me trace through with example: A = 5 = 0101
|
| 60 |
+
# ~A = 1010
|
| 61 |
+
# ~A + 1 = 1010 + 0001 = 1011 = -5 in two's complement
|
| 62 |
+
# So: n0 = 0 XOR 1 = 1 ✓
|
| 63 |
+
# n1 = 1 XOR 0 = 1 ✓ (carry from bit 0 is 0)
|
| 64 |
+
# n2 = 0 XOR 0 = 0 ✓
|
| 65 |
+
# n3 = 1 XOR 0 = 1 ✓
|
| 66 |
+
|
| 67 |
+
# For A = 0 = 0000:
|
| 68 |
+
# ~A = 1111
|
| 69 |
+
# ~A + 1 = 1111 + 1 = 10000, but 4-bit gives 0000 ✓ (with overflow)
|
| 70 |
+
|
| 71 |
+
# OK so the formula is:
|
| 72 |
+
# Starting with cin = 1:
|
| 73 |
+
# n0 = ~a0 XOR cin = ~a0 XOR 1 = NOT(~a0) = a0...
|
| 74 |
+
# Wait that gives wrong answer.
|
| 75 |
+
# For A=5: a0=1, ~a0=0, ~a0 XOR 1 = 0 XOR 1 = 1 ✓
|
| 76 |
+
|
| 77 |
+
# Let me be more careful:
|
| 78 |
+
# A = 5 = 0101: a3=0, a2=1, a1=0, a0=1
|
| 79 |
+
# ~a3=1, ~a2=0, ~a1=1, ~a0=0
|
| 80 |
+
# Add 1:
|
| 81 |
+
# n0 = 0 + 1 = 1, c=0
|
| 82 |
+
# n1 = 1 + 0 = 1, c=0
|
| 83 |
+
# n2 = 0 + 0 = 0, c=0
|
| 84 |
+
# n3 = 1 + 0 = 1, c=0
|
| 85 |
+
# Result: 1011 = -5 ✓
|
| 86 |
+
|
| 87 |
+
# So it's just incrementing ~A.
|
| 88 |
+
|
| 89 |
+
for i in range(4):
|
| 90 |
+
add_ha(f'inc{i}')
|
| 91 |
+
|
| 92 |
+
# Overflow detection: A = 1000 (-8)
|
| 93 |
+
# NOT(a3 OR a2 OR a1 OR a0) AND... no wait
|
| 94 |
+
# Overflow when A = -8 = 1000, meaning a3=1 and a2=a1=a0=0
|
| 95 |
+
weights['ov_nora.weight'] = torch.tensor([[-1.0, -1.0, -1.0]], dtype=torch.float32)
|
| 96 |
+
weights['ov_nora.bias'] = torch.tensor([0.0], dtype=torch.float32)
|
| 97 |
+
|
| 98 |
+
weights['overflow.weight'] = torch.tensor([[1.0, 1.0]], dtype=torch.float32)
|
| 99 |
+
weights['overflow.bias'] = torch.tensor([-2.0], dtype=torch.float32)
|
| 100 |
+
|
| 101 |
+
save_file(weights, 'model.safetensors')
|
| 102 |
+
|
| 103 |
+
def twos_comp(a):
|
| 104 |
+
inv = (~a) & 0xF
|
| 105 |
+
neg = (inv + 1) & 0xF
|
| 106 |
+
overflow = 1 if a == 8 else 0
|
| 107 |
+
return neg, overflow
|
| 108 |
+
|
| 109 |
+
print("Verifying 4-bit Two's Complement...")
|
| 110 |
+
errors = 0
|
| 111 |
+
for a in range(16):
|
| 112 |
+
result, ov = twos_comp(a)
|
| 113 |
+
if a == 0:
|
| 114 |
+
expected = 0
|
| 115 |
+
else:
|
| 116 |
+
expected = (16 - a) & 0xF
|
| 117 |
+
exp_ov = 1 if a == 8 else 0
|
| 118 |
+
|
| 119 |
+
if result != expected or ov != exp_ov:
|
| 120 |
+
errors += 1
|
| 121 |
+
if errors <= 5:
|
| 122 |
+
print(f"ERROR: -({a}) = {result}, expected {expected}")
|
| 123 |
+
|
| 124 |
+
if errors == 0:
|
| 125 |
+
print("All 16 test cases passed!")
|
| 126 |
+
else:
|
| 127 |
+
print(f"FAILED: {errors} errors")
|
| 128 |
+
|
| 129 |
+
print("\nSigned interpretation:")
|
| 130 |
+
for a in range(16):
|
| 131 |
+
signed_a = a if a < 8 else a - 16
|
| 132 |
+
neg, ov = twos_comp(a)
|
| 133 |
+
signed_neg = neg if neg < 8 else neg - 16
|
| 134 |
+
ov_str = " (OVERFLOW)" if ov else ""
|
| 135 |
+
print(f" -({signed_a:+d}) = {signed_neg:+d}{ov_str}")
|
| 136 |
+
|
| 137 |
+
mag = sum(t.abs().sum().item() for t in weights.values())
|
| 138 |
+
print(f"\nMagnitude: {mag:.0f}")
|
| 139 |
+
print(f"Parameters: {sum(t.numel() for t in weights.values())}")
|
model.safetensors
ADDED
|
Binary file (3.43 kB). View file
|
|
|