CharlesCNorton
commited on
Commit
·
7310771
0
Parent(s):
Add Hamming(15,11) encoder threshold circuit
Browse files86 neurons, 6 layers, 591 parameters, magnitude 272.
- .gitattributes +1 -0
- README.md +82 -0
- config.json +9 -0
- create_safetensors.py +128 -0
- model.py +34 -0
- model.safetensors +3 -0
.gitattributes
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
*.safetensors filter=lfs diff=lfs merge=lfs -text
|
README.md
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
license: mit
|
| 3 |
+
tags:
|
| 4 |
+
- pytorch
|
| 5 |
+
- safetensors
|
| 6 |
+
- threshold-logic
|
| 7 |
+
- neuromorphic
|
| 8 |
+
- error-correction
|
| 9 |
+
---
|
| 10 |
+
|
| 11 |
+
# threshold-hamming1511-encoder
|
| 12 |
+
|
| 13 |
+
Hamming(15,11) encoder. Adds 4 parity bits to 11 data bits for single-error correction.
|
| 14 |
+
|
| 15 |
+
## Function
|
| 16 |
+
|
| 17 |
+
encode(d1..d11) -> [p1, p2, d1, p4, d2, d3, d4, p8, d5, d6, d7, d8, d9, d10, d11]
|
| 18 |
+
|
| 19 |
+
## Bit Positions
|
| 20 |
+
|
| 21 |
+
| Position | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
|
| 22 |
+
|----------|---|---|---|---|---|---|---|---|---|----|----|----|----|----|----|
|
| 23 |
+
| Bit | p1 | p2 | d1 | p4 | d2 | d3 | d4 | p8 | d5 | d6 | d7 | d8 | d9 | d10 | d11 |
|
| 24 |
+
|
| 25 |
+
## Parity Equations
|
| 26 |
+
|
| 27 |
+
Each parity bit covers positions where its position number (in binary) has a 1 in that bit:
|
| 28 |
+
|
| 29 |
+
- **p1** (bit 0): positions 1,3,5,7,9,11,13,15 -> XOR(d1,d2,d4,d5,d7,d9,d11)
|
| 30 |
+
- **p2** (bit 1): positions 2,3,6,7,10,11,14,15 -> XOR(d1,d3,d4,d6,d7,d10,d11)
|
| 31 |
+
- **p4** (bit 2): positions 4,5,6,7,12,13,14,15 -> XOR(d2,d3,d4,d8,d9,d10,d11)
|
| 32 |
+
- **p8** (bit 3): positions 8,9,10,11,12,13,14,15 -> XOR(d5,d6,d7,d8,d9,d10,d11)
|
| 33 |
+
|
| 34 |
+
## Architecture
|
| 35 |
+
|
| 36 |
+
Each parity bit requires a 7-way XOR, implemented as a tree of 2-way XORs:
|
| 37 |
+
|
| 38 |
+
```
|
| 39 |
+
XOR7(a,b,c,d,e,f,g) = XOR(XOR4(a,b,c,d), XOR3(e,f,g))
|
| 40 |
+
|
| 41 |
+
XOR4(a,b,c,d) = XOR(XOR(a,b), XOR(c,d))
|
| 42 |
+
XOR3(e,f,g) = XOR(XOR(e,f), g)
|
| 43 |
+
```
|
| 44 |
+
|
| 45 |
+
Each XOR2 requires 3 neurons (OR, NAND, AND).
|
| 46 |
+
|
| 47 |
+
## Parameters
|
| 48 |
+
|
| 49 |
+
| | |
|
| 50 |
+
|---|---|
|
| 51 |
+
| Inputs | 11 |
|
| 52 |
+
| Outputs | 15 |
|
| 53 |
+
| Neurons | 86 |
|
| 54 |
+
| Layers | 6 |
|
| 55 |
+
| Parameters | 591 |
|
| 56 |
+
| Magnitude | 272 |
|
| 57 |
+
|
| 58 |
+
## Error Correction
|
| 59 |
+
|
| 60 |
+
When decoded, the receiver computes syndrome bits by XORing received bits at parity positions. The syndrome directly indicates the bit position of any single-bit error (0 = no error).
|
| 61 |
+
|
| 62 |
+
## Comparison to Hamming(7,4)
|
| 63 |
+
|
| 64 |
+
| Code | Data | Parity | Total | Efficiency |
|
| 65 |
+
|------|------|--------|-------|------------|
|
| 66 |
+
| Hamming(7,4) | 4 | 3 | 7 | 57% |
|
| 67 |
+
| Hamming(15,11) | 11 | 4 | 15 | 73% |
|
| 68 |
+
|
| 69 |
+
Larger Hamming codes are more efficient but require more complex circuits.
|
| 70 |
+
|
| 71 |
+
## Usage
|
| 72 |
+
|
| 73 |
+
```python
|
| 74 |
+
from safetensors.torch import load_file
|
| 75 |
+
|
| 76 |
+
w = load_file('model.safetensors')
|
| 77 |
+
# See model.py for reference implementation
|
| 78 |
+
```
|
| 79 |
+
|
| 80 |
+
## License
|
| 81 |
+
|
| 82 |
+
MIT
|
config.json
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"name": "threshold-hamming1511-encoder",
|
| 3 |
+
"description": "Hamming(15,11) encoder",
|
| 4 |
+
"inputs": 11,
|
| 5 |
+
"outputs": 15,
|
| 6 |
+
"neurons": 86,
|
| 7 |
+
"layers": 6,
|
| 8 |
+
"parameters": 591
|
| 9 |
+
}
|
create_safetensors.py
ADDED
|
@@ -0,0 +1,128 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import torch
|
| 2 |
+
from safetensors.torch import save_file
|
| 3 |
+
|
| 4 |
+
weights = {}
|
| 5 |
+
|
| 6 |
+
# Hamming(15,11) Encoder
|
| 7 |
+
# 11 data bits (d1-d11) -> 15 coded bits (4 parity + 11 data)
|
| 8 |
+
#
|
| 9 |
+
# Bit positions:
|
| 10 |
+
# 1=p1, 2=p2, 3=d1, 4=p4, 5=d2, 6=d3, 7=d4, 8=p8, 9=d5, 10=d6, 11=d7, 12=d8, 13=d9, 14=d10, 15=d11
|
| 11 |
+
#
|
| 12 |
+
# Parity equations (XOR of data bits where position has that parity bit set):
|
| 13 |
+
# p1 covers positions with bit 0 set: 1,3,5,7,9,11,13,15 -> d1,d2,d4,d5,d7,d9,d11
|
| 14 |
+
# p2 covers positions with bit 1 set: 2,3,6,7,10,11,14,15 -> d1,d3,d4,d6,d7,d10,d11
|
| 15 |
+
# p4 covers positions with bit 2 set: 4,5,6,7,12,13,14,15 -> d2,d3,d4,d8,d9,d10,d11
|
| 16 |
+
# p8 covers positions with bit 3 set: 8,9,10,11,12,13,14,15 -> d5,d6,d7,d8,d9,d10,d11
|
| 17 |
+
#
|
| 18 |
+
# Each parity bit is a 7-way XOR, implemented as a tree of 2-way XORs.
|
| 19 |
+
# XOR7 = XOR(XOR4(a,b,c,d), XOR3(e,f,g))
|
| 20 |
+
# XOR4(a,b,c,d) = XOR(XOR(a,b), XOR(c,d))
|
| 21 |
+
# XOR3(e,f,g) = XOR(XOR(e,f), g)
|
| 22 |
+
#
|
| 23 |
+
# Input indices: d1=0, d2=1, d3=2, d4=3, d5=4, d6=5, d7=6, d8=7, d9=8, d10=9, d11=10
|
| 24 |
+
|
| 25 |
+
def add_xor2_weights(prefix, idx_a, idx_b, total_inputs):
|
| 26 |
+
"""Add weights for XOR(a, b) from direct inputs."""
|
| 27 |
+
w_or = [0.0] * total_inputs
|
| 28 |
+
w_or[idx_a] = 1.0
|
| 29 |
+
w_or[idx_b] = 1.0
|
| 30 |
+
weights[f'{prefix}.or.weight'] = torch.tensor([w_or], dtype=torch.float32)
|
| 31 |
+
weights[f'{prefix}.or.bias'] = torch.tensor([-1.0], dtype=torch.float32)
|
| 32 |
+
|
| 33 |
+
w_nand = [0.0] * total_inputs
|
| 34 |
+
w_nand[idx_a] = -1.0
|
| 35 |
+
w_nand[idx_b] = -1.0
|
| 36 |
+
weights[f'{prefix}.nand.weight'] = torch.tensor([w_nand], dtype=torch.float32)
|
| 37 |
+
weights[f'{prefix}.nand.bias'] = torch.tensor([1.0], dtype=torch.float32)
|
| 38 |
+
|
| 39 |
+
weights[f'{prefix}.and.weight'] = torch.tensor([[1.0, 1.0]], dtype=torch.float32)
|
| 40 |
+
weights[f'{prefix}.and.bias'] = torch.tensor([-2.0], dtype=torch.float32)
|
| 41 |
+
|
| 42 |
+
def add_xor2_stage_weights(prefix):
|
| 43 |
+
"""Add weights for XOR of two intermediate signals."""
|
| 44 |
+
weights[f'{prefix}.or.weight'] = torch.tensor([[1.0, 1.0]], dtype=torch.float32)
|
| 45 |
+
weights[f'{prefix}.or.bias'] = torch.tensor([-1.0], dtype=torch.float32)
|
| 46 |
+
weights[f'{prefix}.nand.weight'] = torch.tensor([[-1.0, -1.0]], dtype=torch.float32)
|
| 47 |
+
weights[f'{prefix}.nand.bias'] = torch.tensor([1.0], dtype=torch.float32)
|
| 48 |
+
weights[f'{prefix}.and.weight'] = torch.tensor([[1.0, 1.0]], dtype=torch.float32)
|
| 49 |
+
weights[f'{prefix}.and.bias'] = torch.tensor([-2.0], dtype=torch.float32)
|
| 50 |
+
|
| 51 |
+
# p1 = d1 XOR d2 XOR d4 XOR d5 XOR d7 XOR d9 XOR d11 (indices: 0,1,3,4,6,8,10)
|
| 52 |
+
# Tree: ((d1 XOR d2) XOR (d4 XOR d5)) XOR ((d7 XOR d9) XOR d11)
|
| 53 |
+
add_xor2_weights('p1.x12', 0, 1, 11) # d1 XOR d2
|
| 54 |
+
add_xor2_weights('p1.x45', 3, 4, 11) # d4 XOR d5
|
| 55 |
+
add_xor2_weights('p1.x79', 6, 8, 11) # d7 XOR d9
|
| 56 |
+
add_xor2_stage_weights('p1.x1245') # (d1^d2) XOR (d4^d5)
|
| 57 |
+
add_xor2_stage_weights('p1.x79_11') # (d7^d9) XOR d11 (d11 needs special handling)
|
| 58 |
+
add_xor2_stage_weights('p1.final') # combine
|
| 59 |
+
|
| 60 |
+
# p2 = d1 XOR d3 XOR d4 XOR d6 XOR d7 XOR d10 XOR d11 (indices: 0,2,3,5,6,9,10)
|
| 61 |
+
add_xor2_weights('p2.x13', 0, 2, 11)
|
| 62 |
+
add_xor2_weights('p2.x46', 3, 5, 11)
|
| 63 |
+
add_xor2_weights('p2.x7_10', 6, 9, 11)
|
| 64 |
+
add_xor2_stage_weights('p2.x1346')
|
| 65 |
+
add_xor2_stage_weights('p2.x7_10_11')
|
| 66 |
+
add_xor2_stage_weights('p2.final')
|
| 67 |
+
|
| 68 |
+
# p4 = d2 XOR d3 XOR d4 XOR d8 XOR d9 XOR d10 XOR d11 (indices: 1,2,3,7,8,9,10)
|
| 69 |
+
add_xor2_weights('p4.x23', 1, 2, 11)
|
| 70 |
+
add_xor2_weights('p4.x34', 2, 3, 11) # Wait, this should be different pairs
|
| 71 |
+
# Let me redo: ((d2 XOR d3) XOR (d4 XOR d8)) XOR ((d9 XOR d10) XOR d11)
|
| 72 |
+
add_xor2_weights('p4.x23', 1, 2, 11) # d2 XOR d3
|
| 73 |
+
add_xor2_weights('p4.x48', 3, 7, 11) # d4 XOR d8
|
| 74 |
+
add_xor2_weights('p4.x9_10', 8, 9, 11) # d9 XOR d10
|
| 75 |
+
add_xor2_stage_weights('p4.x2348')
|
| 76 |
+
add_xor2_stage_weights('p4.x9_10_11')
|
| 77 |
+
add_xor2_stage_weights('p4.final')
|
| 78 |
+
|
| 79 |
+
# p8 = d5 XOR d6 XOR d7 XOR d8 XOR d9 XOR d10 XOR d11 (indices: 4,5,6,7,8,9,10)
|
| 80 |
+
add_xor2_weights('p8.x56', 4, 5, 11)
|
| 81 |
+
add_xor2_weights('p8.x78', 6, 7, 11)
|
| 82 |
+
add_xor2_weights('p8.x9_10', 8, 9, 11)
|
| 83 |
+
add_xor2_stage_weights('p8.x5678')
|
| 84 |
+
add_xor2_stage_weights('p8.x9_10_11')
|
| 85 |
+
add_xor2_stage_weights('p8.final')
|
| 86 |
+
|
| 87 |
+
# Data pass-through (11 neurons)
|
| 88 |
+
for i in range(11):
|
| 89 |
+
w = [0.0] * 11
|
| 90 |
+
w[i] = 1.0
|
| 91 |
+
weights[f'd{i+1}.weight'] = torch.tensor([w], dtype=torch.float32)
|
| 92 |
+
weights[f'd{i+1}.bias'] = torch.tensor([-1.0], dtype=torch.float32)
|
| 93 |
+
|
| 94 |
+
save_file(weights, 'model.safetensors')
|
| 95 |
+
|
| 96 |
+
def xor2(a, b):
|
| 97 |
+
return a ^ b
|
| 98 |
+
|
| 99 |
+
def parity7(bits):
|
| 100 |
+
result = 0
|
| 101 |
+
for b in bits:
|
| 102 |
+
result ^= b
|
| 103 |
+
return result
|
| 104 |
+
|
| 105 |
+
def hamming1511_encode_ref(d):
|
| 106 |
+
"""Reference implementation."""
|
| 107 |
+
d1, d2, d3, d4, d5, d6, d7, d8, d9, d10, d11 = d
|
| 108 |
+
p1 = parity7([d1, d2, d4, d5, d7, d9, d11])
|
| 109 |
+
p2 = parity7([d1, d3, d4, d6, d7, d10, d11])
|
| 110 |
+
p4 = parity7([d2, d3, d4, d8, d9, d10, d11])
|
| 111 |
+
p8 = parity7([d5, d6, d7, d8, d9, d10, d11])
|
| 112 |
+
return [p1, p2, d1, p4, d2, d3, d4, p8, d5, d6, d7, d8, d9, d10, d11]
|
| 113 |
+
|
| 114 |
+
print("Verifying Hamming(15,11) Encoder reference...")
|
| 115 |
+
print("Data bits -> Encoded (15 bits)")
|
| 116 |
+
print("-" * 50)
|
| 117 |
+
|
| 118 |
+
for i in range(32): # Test first 32 patterns
|
| 119 |
+
bits = [(i >> j) & 1 for j in range(11)]
|
| 120 |
+
encoded = hamming1511_encode_ref(bits)
|
| 121 |
+
data_str = ''.join(map(str, bits))
|
| 122 |
+
enc_str = ''.join(map(str, encoded))
|
| 123 |
+
print(f"{data_str} -> {enc_str}")
|
| 124 |
+
|
| 125 |
+
mag = sum(t.abs().sum().item() for t in weights.values())
|
| 126 |
+
print(f"\nMagnitude: {mag:.0f}")
|
| 127 |
+
print(f"Parameters: {sum(t.numel() for t in weights.values())}")
|
| 128 |
+
print(f"Neurons: {len([k for k in weights.keys() if 'weight' in k])}")
|
model.py
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import torch
|
| 2 |
+
from safetensors.torch import load_file
|
| 3 |
+
|
| 4 |
+
def load_model(path='model.safetensors'):
|
| 5 |
+
return load_file(path)
|
| 6 |
+
|
| 7 |
+
def parity7(bits):
|
| 8 |
+
"""7-bit XOR (parity)."""
|
| 9 |
+
result = 0
|
| 10 |
+
for b in bits:
|
| 11 |
+
result ^= b
|
| 12 |
+
return result
|
| 13 |
+
|
| 14 |
+
def hamming1511_encode_ref(d1, d2, d3, d4, d5, d6, d7, d8, d9, d10, d11):
|
| 15 |
+
"""Reference encoder (not using threshold weights)."""
|
| 16 |
+
p1 = parity7([d1, d2, d4, d5, d7, d9, d11])
|
| 17 |
+
p2 = parity7([d1, d3, d4, d6, d7, d10, d11])
|
| 18 |
+
p4 = parity7([d2, d3, d4, d8, d9, d10, d11])
|
| 19 |
+
p8 = parity7([d5, d6, d7, d8, d9, d10, d11])
|
| 20 |
+
return [p1, p2, d1, p4, d2, d3, d4, p8, d5, d6, d7, d8, d9, d10, d11]
|
| 21 |
+
|
| 22 |
+
if __name__ == '__main__':
|
| 23 |
+
print('Hamming(15,11) Encoder')
|
| 24 |
+
print('11 data bits -> 15 coded bits (4 parity + 11 data)')
|
| 25 |
+
print()
|
| 26 |
+
print('Bit positions: p1,p2,d1,p4,d2,d3,d4,p8,d5,d6,d7,d8,d9,d10,d11')
|
| 27 |
+
print()
|
| 28 |
+
print('Examples:')
|
| 29 |
+
for i in range(8):
|
| 30 |
+
bits = [(i >> j) & 1 for j in range(11)]
|
| 31 |
+
encoded = hamming1511_encode_ref(*bits)
|
| 32 |
+
data_str = ''.join(map(str, bits))
|
| 33 |
+
enc_str = ''.join(map(str, encoded))
|
| 34 |
+
print(f' {data_str} -> {enc_str}')
|
model.safetensors
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:e20a735ba1438f1617ff5b2afcc866c1fe213e6360bb77fdcfefb44fdbd3fe81
|
| 3 |
+
size 15244
|