CharlesCNorton commited on
Commit
9970e78
·
0 Parent(s):

Minimum of two 2-bit integers, magnitude 95

Browse files
Files changed (5) hide show
  1. README.md +81 -0
  2. config.json +9 -0
  3. create_safetensors.py +169 -0
  4. model.py +52 -0
  5. model.safetensors +0 -0
README.md ADDED
@@ -0,0 +1,81 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ license: mit
3
+ tags:
4
+ - pytorch
5
+ - safetensors
6
+ - threshold-logic
7
+ - neuromorphic
8
+ ---
9
+
10
+ # threshold-min2
11
+
12
+ Minimum of two 2-bit unsigned integers.
13
+
14
+ ## Function
15
+
16
+ min2(a, b) = min(a, b) where a, b are 2-bit unsigned integers (0-3)
17
+
18
+ Inputs: a1, a0, b1, b0 (MSB first)
19
+ Outputs: m1, m0 = binary representation of min(a, b)
20
+
21
+ ## Truth Table
22
+
23
+ | a | b | min |
24
+ |---|---|-----|
25
+ | 0 | 0 | 0 |
26
+ | 0 | 1 | 0 |
27
+ | 0 | 2 | 0 |
28
+ | 0 | 3 | 0 |
29
+ | 1 | 0 | 0 |
30
+ | 1 | 1 | 1 |
31
+ | 1 | 2 | 1 |
32
+ | 1 | 3 | 1 |
33
+ | 2 | 0 | 0 |
34
+ | 2 | 1 | 1 |
35
+ | 2 | 2 | 2 |
36
+ | 2 | 3 | 2 |
37
+ | 3 | 0 | 0 |
38
+ | 3 | 1 | 1 |
39
+ | 3 | 2 | 2 |
40
+ | 3 | 3 | 3 |
41
+
42
+ ## Architecture
43
+
44
+ 7-layer circuit:
45
+ 1. Layer 1: Bit comparisons and passthrough (10 neurons)
46
+ 2. Layer 2: Equality detection (9 neurons)
47
+ 3. Layer 3: Partial comparison (8 neurons)
48
+ 4. Layer 4: Full a > b and a == b (6 neurons)
49
+ 5. Layer 5: a <= b computation (5 neurons)
50
+ 6. Layer 6: MUX selection (4 neurons)
51
+ 7. Layer 7: Final OR (2 neurons)
52
+
53
+ ## Parameters
54
+
55
+ | | |
56
+ |---|---|
57
+ | Inputs | 4 |
58
+ | Outputs | 2 |
59
+ | Neurons | 44 |
60
+ | Layers | 7 |
61
+ | Parameters | 186 |
62
+ | Magnitude | 95 |
63
+
64
+ ## Usage
65
+
66
+ ```python
67
+ from safetensors.torch import load_file
68
+ import torch
69
+
70
+ w = load_file('model.safetensors')
71
+
72
+ # Use model.py for full implementation
73
+ from model import min2, load_model
74
+ w = load_model()
75
+ m1, m0 = min2(1, 0, 0, 1, w) # min(2, 1) = 1
76
+ print(2*m1 + m0) # 1
77
+ ```
78
+
79
+ ## License
80
+
81
+ MIT
config.json ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "threshold-min2",
3
+ "description": "Minimum of two 2-bit unsigned integers",
4
+ "inputs": 4,
5
+ "outputs": 2,
6
+ "neurons": 44,
7
+ "layers": 7,
8
+ "parameters": 186
9
+ }
create_safetensors.py ADDED
@@ -0,0 +1,169 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+ from safetensors.torch import save_file
3
+
4
+ # Min of two 2-bit unsigned numbers
5
+ # Inputs: a1, a0, b1, b0
6
+ # Outputs: m1, m0 = min(a, b)
7
+ #
8
+ # Logic: if a <= b then output a, else output b
9
+ # Same comparison as max, but swap output selection
10
+
11
+ weights = {}
12
+
13
+ # Layer 1: Basic comparisons (same as max2)
14
+ weights['l1.a1_gt_b1.weight'] = torch.tensor([[1.0, 0.0, -1.0, 0.0]], dtype=torch.float32)
15
+ weights['l1.a1_gt_b1.bias'] = torch.tensor([-1.0], dtype=torch.float32)
16
+
17
+ weights['l1.b1_gt_a1.weight'] = torch.tensor([[-1.0, 0.0, 1.0, 0.0]], dtype=torch.float32)
18
+ weights['l1.b1_gt_a1.bias'] = torch.tensor([-1.0], dtype=torch.float32)
19
+
20
+ weights['l1.a0_gt_b0.weight'] = torch.tensor([[0.0, 1.0, 0.0, -1.0]], dtype=torch.float32)
21
+ weights['l1.a0_gt_b0.bias'] = torch.tensor([-1.0], dtype=torch.float32)
22
+
23
+ weights['l1.b0_gt_a0.weight'] = torch.tensor([[0.0, -1.0, 0.0, 1.0]], dtype=torch.float32)
24
+ weights['l1.b0_gt_a0.bias'] = torch.tensor([-1.0], dtype=torch.float32)
25
+
26
+ weights['l1.both1_high.weight'] = torch.tensor([[1.0, 0.0, 1.0, 0.0]], dtype=torch.float32)
27
+ weights['l1.both1_high.bias'] = torch.tensor([-2.0], dtype=torch.float32)
28
+
29
+ weights['l1.both1_low.weight'] = torch.tensor([[-1.0, 0.0, -1.0, 0.0]], dtype=torch.float32)
30
+ weights['l1.both1_low.bias'] = torch.tensor([0.0], dtype=torch.float32)
31
+
32
+ weights['l1.a1.weight'] = torch.tensor([[1.0, 0.0, 0.0, 0.0]], dtype=torch.float32)
33
+ weights['l1.a1.bias'] = torch.tensor([-0.5], dtype=torch.float32)
34
+ weights['l1.a0.weight'] = torch.tensor([[0.0, 1.0, 0.0, 0.0]], dtype=torch.float32)
35
+ weights['l1.a0.bias'] = torch.tensor([-0.5], dtype=torch.float32)
36
+ weights['l1.b1.weight'] = torch.tensor([[0.0, 0.0, 1.0, 0.0]], dtype=torch.float32)
37
+ weights['l1.b1.bias'] = torch.tensor([-0.5], dtype=torch.float32)
38
+ weights['l1.b0.weight'] = torch.tensor([[0.0, 0.0, 0.0, 1.0]], dtype=torch.float32)
39
+ weights['l1.b0.bias'] = torch.tensor([-0.5], dtype=torch.float32)
40
+
41
+ # Layer 2
42
+ weights['l2.a1_eq_b1.weight'] = torch.tensor([[0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 0.0, 0.0, 0.0, 0.0]], dtype=torch.float32)
43
+ weights['l2.a1_eq_b1.bias'] = torch.tensor([-1.0], dtype=torch.float32)
44
+
45
+ for v in ['a1_gt_b1', 'b1_gt_a1', 'a0_gt_b0', 'b0_gt_a0', 'a1', 'a0', 'b1', 'b0']:
46
+ idx = ['a1_gt_b1', 'b1_gt_a1', 'a0_gt_b0', 'b0_gt_a0', 'both1_high', 'both1_low', 'a1', 'a0', 'b1', 'b0'].index(v)
47
+ w = [0.0] * 10
48
+ w[idx] = 1.0
49
+ weights[f'l2.{v}.weight'] = torch.tensor([w], dtype=torch.float32)
50
+ weights[f'l2.{v}.bias'] = torch.tensor([-0.5], dtype=torch.float32)
51
+
52
+ # Layer 3
53
+ weights['l3.a_gt_b_part2.weight'] = torch.tensor([[1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0]], dtype=torch.float32)
54
+ weights['l3.a_gt_b_part2.bias'] = torch.tensor([-2.0], dtype=torch.float32)
55
+
56
+ weights['l3.a0_neq_b0.weight'] = torch.tensor([[0.0, 0.0, 0.0, 1.0, 1.0, 0.0, 0.0, 0.0, 0.0]], dtype=torch.float32)
57
+ weights['l3.a0_neq_b0.bias'] = torch.tensor([-1.0], dtype=torch.float32)
58
+
59
+ for v in ['a1_gt_b1', 'a1', 'a0', 'b1', 'b0', 'a1_eq_b1']:
60
+ if v == 'a1_eq_b1':
61
+ idx = 0
62
+ else:
63
+ idx = ['a1_eq_b1', 'a1_gt_b1', 'b1_gt_a1', 'a0_gt_b0', 'b0_gt_a0', 'a1', 'a0', 'b1', 'b0'].index(v)
64
+ w = [0.0] * 9
65
+ w[idx] = 1.0
66
+ weights[f'l3.{v}.weight'] = torch.tensor([w], dtype=torch.float32)
67
+ weights[f'l3.{v}.bias'] = torch.tensor([-0.5], dtype=torch.float32)
68
+
69
+ # Layer 4
70
+ weights['l4.a_gt_b.weight'] = torch.tensor([[1.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0]], dtype=torch.float32)
71
+ weights['l4.a_gt_b.bias'] = torch.tensor([-1.0], dtype=torch.float32)
72
+
73
+ weights['l4.a_eq_b.weight'] = torch.tensor([[0.0, -1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0]], dtype=torch.float32)
74
+ weights['l4.a_eq_b.bias'] = torch.tensor([-1.0], dtype=torch.float32)
75
+
76
+ for v in ['a1', 'a0', 'b1', 'b0']:
77
+ idx = ['a_gt_b_part2', 'a0_neq_b0', 'a1_gt_b1', 'a1', 'a0', 'b1', 'b0', 'a1_eq_b1'].index(v)
78
+ w = [0.0] * 8
79
+ w[idx] = 1.0
80
+ weights[f'l4.{v}.weight'] = torch.tensor([w], dtype=torch.float32)
81
+ weights[f'l4.{v}.bias'] = torch.tensor([-0.5], dtype=torch.float32)
82
+
83
+ # Layer 5: For MIN, we want a_le_b = NOT a_gt_b
84
+ weights['l5.a_le_b.weight'] = torch.tensor([[-1.0, 1.0, 0.0, 0.0, 0.0, 0.0]], dtype=torch.float32)
85
+ weights['l5.a_le_b.bias'] = torch.tensor([0.0], dtype=torch.float32) # fires when a_gt_b=0 OR a_eq_b=1
86
+
87
+ for v in ['a1', 'a0', 'b1', 'b0']:
88
+ idx = ['a_gt_b', 'a_eq_b', 'a1', 'a0', 'b1', 'b0'].index(v)
89
+ w = [0.0] * 6
90
+ w[idx] = 1.0
91
+ weights[f'l5.{v}.weight'] = torch.tensor([w], dtype=torch.float32)
92
+ weights[f'l5.{v}.bias'] = torch.tensor([-0.5], dtype=torch.float32)
93
+
94
+ # Layer 6: MUX - select a when a <= b, else b
95
+ # m1 = (a1 AND a_le_b) OR (b1 AND NOT a_le_b)
96
+ weights['l6.m1_a.weight'] = torch.tensor([[1.0, 1.0, 0.0, 0.0, 0.0]], dtype=torch.float32)
97
+ weights['l6.m1_a.bias'] = torch.tensor([-2.0], dtype=torch.float32)
98
+
99
+ weights['l6.m1_b.weight'] = torch.tensor([[-1.0, 0.0, 0.0, 1.0, 0.0]], dtype=torch.float32)
100
+ weights['l6.m1_b.bias'] = torch.tensor([-1.0], dtype=torch.float32)
101
+
102
+ weights['l6.m0_a.weight'] = torch.tensor([[1.0, 0.0, 1.0, 0.0, 0.0]], dtype=torch.float32)
103
+ weights['l6.m0_a.bias'] = torch.tensor([-2.0], dtype=torch.float32)
104
+
105
+ weights['l6.m0_b.weight'] = torch.tensor([[-1.0, 0.0, 0.0, 0.0, 1.0]], dtype=torch.float32)
106
+ weights['l6.m0_b.bias'] = torch.tensor([-1.0], dtype=torch.float32)
107
+
108
+ # Layer 7: Final OR
109
+ weights['l7.m1.weight'] = torch.tensor([[1.0, 1.0, 0.0, 0.0]], dtype=torch.float32)
110
+ weights['l7.m1.bias'] = torch.tensor([-1.0], dtype=torch.float32)
111
+
112
+ weights['l7.m0.weight'] = torch.tensor([[0.0, 0.0, 1.0, 1.0]], dtype=torch.float32)
113
+ weights['l7.m0.bias'] = torch.tensor([-1.0], dtype=torch.float32)
114
+
115
+ save_file(weights, 'model.safetensors')
116
+
117
+ # Verification
118
+ def min2(a1, a0, b1, b0):
119
+ inp = torch.tensor([float(a1), float(a0), float(b1), float(b0)])
120
+
121
+ l1_keys = ['a1_gt_b1', 'b1_gt_a1', 'a0_gt_b0', 'b0_gt_a0', 'both1_high', 'both1_low', 'a1', 'a0', 'b1', 'b0']
122
+ l1 = {k: int((inp @ weights[f'l1.{k}.weight'].T + weights[f'l1.{k}.bias'] >= 0).item()) for k in l1_keys}
123
+ l1_out = torch.tensor([float(l1[k]) for k in l1_keys])
124
+
125
+ l2_keys = ['a1_eq_b1', 'a1_gt_b1', 'b1_gt_a1', 'a0_gt_b0', 'b0_gt_a0', 'a1', 'a0', 'b1', 'b0']
126
+ l2 = {k: int((l1_out @ weights[f'l2.{k}.weight'].T + weights[f'l2.{k}.bias'] >= 0).item()) for k in l2_keys}
127
+ l2_out = torch.tensor([float(l2[k]) for k in l2_keys])
128
+
129
+ l3_keys = ['a_gt_b_part2', 'a0_neq_b0', 'a1_gt_b1', 'a1', 'a0', 'b1', 'b0', 'a1_eq_b1']
130
+ l3 = {k: int((l2_out @ weights[f'l3.{k}.weight'].T + weights[f'l3.{k}.bias'] >= 0).item()) for k in l3_keys}
131
+ l3_out = torch.tensor([float(l3[k]) for k in l3_keys])
132
+
133
+ l4_keys = ['a_gt_b', 'a_eq_b', 'a1', 'a0', 'b1', 'b0']
134
+ l4 = {k: int((l3_out @ weights[f'l4.{k}.weight'].T + weights[f'l4.{k}.bias'] >= 0).item()) for k in l4_keys}
135
+ l4_out = torch.tensor([float(l4[k]) for k in l4_keys])
136
+
137
+ l5_keys = ['a_le_b', 'a1', 'a0', 'b1', 'b0']
138
+ l5 = {k: int((l4_out @ weights[f'l5.{k}.weight'].T + weights[f'l5.{k}.bias'] >= 0).item()) for k in l5_keys}
139
+ l5_out = torch.tensor([float(l5[k]) for k in l5_keys])
140
+
141
+ l6_keys = ['m1_a', 'm1_b', 'm0_a', 'm0_b']
142
+ l6 = {k: int((l5_out @ weights[f'l6.{k}.weight'].T + weights[f'l6.{k}.bias'] >= 0).item()) for k in l6_keys}
143
+ l6_out = torch.tensor([float(l6[k]) for k in l6_keys])
144
+
145
+ m1 = int((l6_out @ weights['l7.m1.weight'].T + weights['l7.m1.bias'] >= 0).item())
146
+ m0 = int((l6_out @ weights['l7.m0.weight'].T + weights['l7.m0.bias'] >= 0).item())
147
+
148
+ return m1, m0
149
+
150
+ print("Verifying min2...")
151
+ errors = 0
152
+ for a in range(4):
153
+ for b in range(4):
154
+ a1, a0 = (a >> 1) & 1, a & 1
155
+ b1, b0 = (b >> 1) & 1, b & 1
156
+ m1, m0 = min2(a1, a0, b1, b0)
157
+ result = 2*m1 + m0
158
+ expected = min(a, b)
159
+ if result != expected:
160
+ errors += 1
161
+ print(f"ERROR: min({a}, {b}) = {result}, expected {expected}")
162
+
163
+ if errors == 0:
164
+ print("All 16 test cases passed!")
165
+ else:
166
+ print(f"FAILED: {errors} errors")
167
+
168
+ mag = sum(t.abs().sum().item() for t in weights.values())
169
+ print(f"Magnitude: {mag:.0f}")
model.py ADDED
@@ -0,0 +1,52 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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 min2(a1, a0, b1, b0, weights):
8
+ """Minimum of two 2-bit unsigned integers.
9
+ Returns (m1, m0) where m = min(a, b).
10
+ """
11
+ inp = torch.tensor([float(a1), float(a0), float(b1), float(b0)])
12
+
13
+ l1_keys = ['a1_gt_b1', 'b1_gt_a1', 'a0_gt_b0', 'b0_gt_a0', 'both1_high', 'both1_low', 'a1', 'a0', 'b1', 'b0']
14
+ l1 = {k: int((inp @ weights[f'l1.{k}.weight'].T + weights[f'l1.{k}.bias'] >= 0).item()) for k in l1_keys}
15
+ l1_out = torch.tensor([float(l1[k]) for k in l1_keys])
16
+
17
+ l2_keys = ['a1_eq_b1', 'a1_gt_b1', 'b1_gt_a1', 'a0_gt_b0', 'b0_gt_a0', 'a1', 'a0', 'b1', 'b0']
18
+ l2 = {k: int((l1_out @ weights[f'l2.{k}.weight'].T + weights[f'l2.{k}.bias'] >= 0).item()) for k in l2_keys}
19
+ l2_out = torch.tensor([float(l2[k]) for k in l2_keys])
20
+
21
+ l3_keys = ['a_gt_b_part2', 'a0_neq_b0', 'a1_gt_b1', 'a1', 'a0', 'b1', 'b0', 'a1_eq_b1']
22
+ l3 = {k: int((l2_out @ weights[f'l3.{k}.weight'].T + weights[f'l3.{k}.bias'] >= 0).item()) for k in l3_keys}
23
+ l3_out = torch.tensor([float(l3[k]) for k in l3_keys])
24
+
25
+ l4_keys = ['a_gt_b', 'a_eq_b', 'a1', 'a0', 'b1', 'b0']
26
+ l4 = {k: int((l3_out @ weights[f'l4.{k}.weight'].T + weights[f'l4.{k}.bias'] >= 0).item()) for k in l4_keys}
27
+ l4_out = torch.tensor([float(l4[k]) for k in l4_keys])
28
+
29
+ l5_keys = ['a_le_b', 'a1', 'a0', 'b1', 'b0']
30
+ l5 = {k: int((l4_out @ weights[f'l5.{k}.weight'].T + weights[f'l5.{k}.bias'] >= 0).item()) for k in l5_keys}
31
+ l5_out = torch.tensor([float(l5[k]) for k in l5_keys])
32
+
33
+ l6_keys = ['m1_a', 'm1_b', 'm0_a', 'm0_b']
34
+ l6 = {k: int((l5_out @ weights[f'l6.{k}.weight'].T + weights[f'l6.{k}.bias'] >= 0).item()) for k in l6_keys}
35
+ l6_out = torch.tensor([float(l6[k]) for k in l6_keys])
36
+
37
+ m1 = int((l6_out @ weights['l7.m1.weight'].T + weights['l7.m1.bias'] >= 0).item())
38
+ m0 = int((l6_out @ weights['l7.m0.weight'].T + weights['l7.m0.bias'] >= 0).item())
39
+
40
+ return m1, m0
41
+
42
+ if __name__ == '__main__':
43
+ w = load_model()
44
+ print('min2 truth table:')
45
+ print(' a b | min')
46
+ print('-------+----')
47
+ for a in range(4):
48
+ for b in range(4):
49
+ a1, a0 = (a >> 1) & 1, a & 1
50
+ b1, b0 = (b >> 1) & 1, b & 1
51
+ m1, m0 = min2(a1, a0, b1, b0, w)
52
+ print(f' {a} {b} | {2*m1 + m0}')
model.safetensors ADDED
Binary file (7.69 kB). View file