phanerozoic commited on
Commit
6e6bb88
·
verified ·
1 Parent(s): 9970e78

Upload folder using huggingface_hub

Browse files
Files changed (4) hide show
  1. README.md +81 -81
  2. config.json +9 -9
  3. create_safetensors.py +169 -169
  4. model.py +52 -52
README.md CHANGED
@@ -1,81 +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
 
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 CHANGED
@@ -1,9 +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
- }
 
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 CHANGED
@@ -1,169 +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}")
 
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 CHANGED
@@ -1,52 +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}')
 
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}')