phanerozoic commited on
Commit
1be05b5
·
verified ·
1 Parent(s): 85c46f0

Rename from tiny-mod5-verified

Browse files
Files changed (4) hide show
  1. README.md +107 -0
  2. config.json +30 -0
  3. model.py +119 -0
  4. model.safetensors +3 -0
README.md ADDED
@@ -0,0 +1,107 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ license: mit
3
+ tags:
4
+ - pytorch
5
+ - safetensors
6
+ - threshold-logic
7
+ - neuromorphic
8
+ - modular-arithmetic
9
+ ---
10
+
11
+ # threshold-mod5
12
+
13
+ Computes Hamming weight mod 5 for 8-bit inputs. Multi-layer network with thermometer encoding.
14
+
15
+ ## Circuit
16
+
17
+ ```
18
+ x₀ x₁ x₂ x₃ x₄ x₅ x₆ x₇
19
+ │ │ │ │ │ │ │ │
20
+ └──┴──┴──┴──┼──┴──┴──┴──┘
21
+
22
+ ┌─────────────┐
23
+ │ Thermometer │ Layer 1: 9 neurons
24
+ │ Encoding │
25
+ └─────────────┘
26
+
27
+
28
+ ┌─────────────┐
29
+ │ MOD-5 │ Layer 2: 4 neurons
30
+ │ Detection │ Pattern (1,1,1,1,-4)
31
+ └─────────────┘
32
+
33
+
34
+ ┌─────────────┐
35
+ │ Classify │ Output: 5 classes
36
+ └─────────────┘
37
+
38
+
39
+ {0, 1, 2, 3, 4}
40
+ ```
41
+
42
+ ## Algebraic Insight
43
+
44
+ Pattern `(1, 1, 1, 1, -4)` causes cumulative sums to cycle mod 5:
45
+
46
+ ```
47
+ HW=0: sum=0 → 0 mod 5
48
+ HW=1: sum=1 → 1 mod 5
49
+ HW=2: sum=2 → 2 mod 5
50
+ HW=3: sum=3 → 3 mod 5
51
+ HW=4: sum=4 → 4 mod 5
52
+ HW=5: sum=0 → 0 mod 5 (reset: 1+1+1+1-4=0)
53
+ HW=6: sum=1 → 1 mod 5
54
+ HW=7: sum=2 → 2 mod 5
55
+ HW=8: sum=3 → 3 mod 5
56
+ ```
57
+
58
+ ## Architecture
59
+
60
+ | Layer | Neurons | Function |
61
+ |-------|---------|----------|
62
+ | Input | 8 | Binary bits |
63
+ | Hidden 1 | 9 | Thermometer encoding |
64
+ | Hidden 2 | 4 | MOD-5 detection |
65
+ | Output | 5 | One-hot classification |
66
+
67
+ **Total: 18 neurons, 146 parameters**
68
+
69
+ ## Output Distribution
70
+
71
+ | Class | HW values | Count/256 |
72
+ |-------|-----------|-----------|
73
+ | 0 | 0, 5 | 57 |
74
+ | 1 | 1, 6 | 36 |
75
+ | 2 | 2, 7 | 36 |
76
+ | 3 | 3, 8 | 57 |
77
+ | 4 | 4 | 70 |
78
+
79
+ ## Usage
80
+
81
+ ```python
82
+ from safetensors.torch import load_file
83
+ import torch
84
+
85
+ w = load_file('model.safetensors')
86
+
87
+ def forward(x):
88
+ x = x.float()
89
+ x = (x @ w['layer1.weight'].T + w['layer1.bias'] >= 0).float()
90
+ x = (x @ w['layer2.weight'].T + w['layer2.bias'] >= 0).float()
91
+ out = x @ w['output.weight'].T + w['output.bias']
92
+ return out.argmax(dim=-1)
93
+ ```
94
+
95
+ ## Files
96
+
97
+ ```
98
+ threshold-mod5/
99
+ ├── model.safetensors
100
+ ├── model.py
101
+ ├── config.json
102
+ └── README.md
103
+ ```
104
+
105
+ ## License
106
+
107
+ MIT
config.json ADDED
@@ -0,0 +1,30 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "model_type": "threshold_network",
3
+ "task": "mod5_classification",
4
+ "architecture": "8 -> 9 -> 4 -> 5",
5
+ "input_size": 8,
6
+ "hidden1_size": 9,
7
+ "hidden2_size": 4,
8
+ "output_size": 5,
9
+ "num_parameters": 146,
10
+ "num_neurons": 18,
11
+ "activation": "heaviside",
12
+ "weight_constraints": "integer",
13
+ "verification": {
14
+ "method": "coq_proof",
15
+ "exhaustive": true,
16
+ "constructive": true,
17
+ "algebraic": true,
18
+ "axiom_free": true
19
+ },
20
+ "accuracy": {
21
+ "all_inputs": "256/256",
22
+ "percentage": 100.0
23
+ },
24
+ "algebraic_insight": "Weights (1,1,1,1,-4) on thermometer encoding produce cumsum = HW mod 5",
25
+ "github": "https://github.com/CharlesCNorton/mod5-verified",
26
+ "related": {
27
+ "mod3_model": "https://huggingface.co/phanerozoic/tiny-mod3-prover",
28
+ "parity_model": "https://huggingface.co/phanerozoic/tiny-parity-prover"
29
+ }
30
+ }
model.py ADDED
@@ -0,0 +1,119 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Inference code for mod5-verified threshold network.
3
+
4
+ This network computes MOD-5 (Hamming weight mod 5) on 8-bit binary inputs.
5
+ """
6
+
7
+ import torch
8
+ import torch.nn as nn
9
+ from safetensors.torch import load_file
10
+
11
+
12
+ def heaviside(x):
13
+ """Heaviside step function: 1 if x >= 0, else 0."""
14
+ return (x >= 0).float()
15
+
16
+
17
+ class Mod5Network(nn.Module):
18
+ """
19
+ Verified threshold network for MOD-5 computation.
20
+
21
+ Architecture: 8 -> 9 -> 4 -> 5
22
+ - Layer 1: Thermometer encoding (9 neurons detect HW >= k)
23
+ - Layer 2: MOD-5 detection using (1,1,1,1,-4) weight pattern
24
+ - Output: 5-class classification
25
+ """
26
+
27
+ def __init__(self):
28
+ super().__init__()
29
+ self.layer1 = nn.Linear(8, 9)
30
+ self.layer2 = nn.Linear(9, 4)
31
+ self.output = nn.Linear(4, 5)
32
+
33
+ def forward(self, x):
34
+ """Forward pass with Heaviside activation."""
35
+ x = x.float()
36
+ x = heaviside(self.layer1(x))
37
+ x = heaviside(self.layer2(x))
38
+ x = self.output(x)
39
+ return x
40
+
41
+ def predict(self, x):
42
+ """Get predicted class (0, 1, 2, 3, or 4)."""
43
+ return self.forward(x).argmax(dim=-1)
44
+
45
+ @classmethod
46
+ def from_safetensors(cls, path):
47
+ """Load model from safetensors file."""
48
+ model = cls()
49
+ weights = load_file(path)
50
+
51
+ model.layer1.weight.data = weights['layer1.weight']
52
+ model.layer1.bias.data = weights['layer1.bias']
53
+ model.layer2.weight.data = weights['layer2.weight']
54
+ model.layer2.bias.data = weights['layer2.bias']
55
+ model.output.weight.data = weights['output.weight']
56
+ model.output.bias.data = weights['output.bias']
57
+
58
+ return model
59
+
60
+
61
+ def mod5_reference(x):
62
+ """Reference implementation: Hamming weight mod 5."""
63
+ return (x.sum(dim=-1) % 5).long()
64
+
65
+
66
+ def verify(model, verbose=True):
67
+ """Verify model on all 256 inputs."""
68
+ inputs = torch.zeros(256, 8)
69
+ for i in range(256):
70
+ for j in range(8):
71
+ inputs[i, j] = (i >> j) & 1
72
+
73
+ targets = mod5_reference(inputs)
74
+ predictions = model.predict(inputs)
75
+
76
+ correct = (predictions == targets).sum().item()
77
+
78
+ if verbose:
79
+ print(f"Verification: {correct}/256 ({100*correct/256:.1f}%)")
80
+
81
+ if correct < 256:
82
+ errors = (predictions != targets).nonzero(as_tuple=True)[0]
83
+ print(f"Errors at indices: {errors[:10].tolist()}")
84
+
85
+ return correct == 256
86
+
87
+
88
+ def demo():
89
+ """Demonstration of MOD-5 computation."""
90
+ print("Loading mod5-verified model...")
91
+ model = Mod5Network.from_safetensors('model.safetensors')
92
+
93
+ print("\nVerifying on all 256 inputs...")
94
+ verify(model)
95
+
96
+ print("\nExample predictions:")
97
+ test_cases = [
98
+ [0, 0, 0, 0, 0, 0, 0, 0],
99
+ [1, 0, 0, 0, 0, 0, 0, 0],
100
+ [1, 1, 0, 0, 0, 0, 0, 0],
101
+ [1, 1, 1, 0, 0, 0, 0, 0],
102
+ [1, 1, 1, 1, 0, 0, 0, 0],
103
+ [1, 1, 1, 1, 1, 0, 0, 0],
104
+ [1, 1, 1, 1, 1, 1, 0, 0],
105
+ [1, 1, 1, 1, 1, 1, 1, 0],
106
+ [1, 1, 1, 1, 1, 1, 1, 1],
107
+ ]
108
+
109
+ for bits in test_cases:
110
+ x = torch.tensor([bits], dtype=torch.float32)
111
+ hw = sum(bits)
112
+ pred = model.predict(x).item()
113
+ expected = hw % 5
114
+ status = "OK" if pred == expected else "ERROR"
115
+ print(f" {bits} -> HW={hw}, pred={pred}, expected={expected} [{status}]")
116
+
117
+
118
+ if __name__ == '__main__':
119
+ demo()
model.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:56840ae01951fd606bfc6aadb521629c1860752d8886efaad1ecd6bd2583707d
3
+ size 1008