CharlesCNorton commited on
Commit
b827152
·
0 Parent(s):

Add 4-bit count trailing zeros threshold circuit

Browse files

10 neurons, 3 layers, 37 parameters, magnitude 30.

Files changed (6) hide show
  1. .gitattributes +1 -0
  2. README.md +103 -0
  3. config.json +9 -0
  4. create_safetensors.py +134 -0
  5. model.py +37 -0
  6. model.safetensors +3 -0
.gitattributes ADDED
@@ -0,0 +1 @@
 
 
1
+ *.safetensors filter=lfs diff=lfs merge=lfs -text
README.md ADDED
@@ -0,0 +1,103 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ license: mit
3
+ tags:
4
+ - pytorch
5
+ - safetensors
6
+ - threshold-logic
7
+ - neuromorphic
8
+ - bit-manipulation
9
+ ---
10
+
11
+ # threshold-ctz4
12
+
13
+ 4-bit count trailing zeros. Returns the number of consecutive zero bits starting from the LSB.
14
+
15
+ ## Function
16
+
17
+ ctz4(x3, x2, x1, x0) -> count (0-4)
18
+
19
+ - CTZ = 0 if x0 = 1 (no trailing zeros)
20
+ - CTZ = 1 if x0 = 0, x1 = 1
21
+ - CTZ = 2 if x0 = x1 = 0, x2 = 1
22
+ - CTZ = 3 if x0 = x1 = x2 = 0, x3 = 1
23
+ - CTZ = 4 if all bits are zero
24
+
25
+ ## Truth Table (selected rows)
26
+
27
+ | Input | CTZ | Output |
28
+ |-------|-----|--------|
29
+ | 0001 | 0 | 000 |
30
+ | 0010 | 1 | 001 |
31
+ | 0100 | 2 | 010 |
32
+ | 1000 | 3 | 011 |
33
+ | 0000 | 4 | 100 |
34
+ | 0110 | 1 | 001 |
35
+ | 1100 | 2 | 010 |
36
+
37
+ ## Architecture
38
+
39
+ ```
40
+ x3 x2 x1 x0
41
+
42
+
43
+ ┌─────────────────────────────────┐
44
+ │ Layer 1: Prefix conditions │
45
+ │ p0 = (x0=0) │
46
+ │ p01 = (x0=x1=0) │
47
+ │ p012 = (x0=x1=x2=0) │
48
+ │ all_zero = (all=0) │
49
+ └─────────────────────────────────┘
50
+
51
+
52
+ ┌─────────────────────────────────┐
53
+ │ Layer 2: One-hot position │
54
+ │ z1 = p0 AND x1 │
55
+ │ z2 = p01 AND x2 │
56
+ │ z3 = p012 AND x3 │
57
+ └─────────────────────────────────┘
58
+
59
+
60
+ ┌─────────────────────────────────┐
61
+ │ Layer 3: Binary encoding │
62
+ │ y2 = all_zero │
63
+ │ y1 = z2 OR z3 │
64
+ │ y0 = z1 OR z3 │
65
+ └─────────────────────────────────┘
66
+
67
+
68
+ y2 y1 y0
69
+ ```
70
+
71
+ ## Parameters
72
+
73
+ | | |
74
+ |---|---|
75
+ | Inputs | 4 |
76
+ | Outputs | 3 |
77
+ | Neurons | 10 |
78
+ | Layers | 3 |
79
+ | Parameters | 37 |
80
+ | Magnitude | 30 |
81
+
82
+ ## Usage
83
+
84
+ ```python
85
+ from safetensors.torch import load_file
86
+ import torch
87
+
88
+ w = load_file('model.safetensors')
89
+
90
+ # ctz4(0,1,0,0) = 2 (binary 0100 has 2 trailing zeros)
91
+ # See model.py for full implementation
92
+ ```
93
+
94
+ ## Applications
95
+
96
+ - Finding the lowest set bit position
97
+ - Efficient division by powers of 2
98
+ - Bit manipulation in cryptography
99
+ - Hardware priority encoders
100
+
101
+ ## License
102
+
103
+ MIT
config.json ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "threshold-ctz4",
3
+ "description": "4-bit count trailing zeros",
4
+ "inputs": 4,
5
+ "outputs": 3,
6
+ "neurons": 10,
7
+ "layers": 3,
8
+ "parameters": 37
9
+ }
create_safetensors.py ADDED
@@ -0,0 +1,134 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+ from safetensors.torch import save_file
3
+
4
+ weights = {}
5
+
6
+ # CTZ4: Count Trailing Zeros (4-bit)
7
+ # Inputs: x3, x2, x1, x0 (x0 is LSB)
8
+ # Output: 3-bit count (0-4)
9
+ #
10
+ # CTZ = 0 if x0 = 1
11
+ # CTZ = 1 if x0 = 0, x1 = 1
12
+ # CTZ = 2 if x0 = x1 = 0, x2 = 1
13
+ # CTZ = 3 if x0 = x1 = x2 = 0, x3 = 1
14
+ # CTZ = 4 if all zeros
15
+ #
16
+ # Output encoding: y2 y1 y0
17
+ # 0 -> 000, 1 -> 001, 2 -> 010, 3 -> 011, 4 -> 100
18
+
19
+ # Layer 1: Compute prefix conditions (all lower bits are zero)
20
+ # p0 = (x0 = 0)
21
+ weights['p0.weight'] = torch.tensor([[0.0, 0.0, 0.0, -1.0]], dtype=torch.float32)
22
+ weights['p0.bias'] = torch.tensor([0.0], dtype=torch.float32)
23
+
24
+ # p01 = (x0 = 0 AND x1 = 0)
25
+ weights['p01.weight'] = torch.tensor([[0.0, 0.0, -1.0, -1.0]], dtype=torch.float32)
26
+ weights['p01.bias'] = torch.tensor([0.0], dtype=torch.float32)
27
+
28
+ # p012 = (x0 = 0 AND x1 = 0 AND x2 = 0)
29
+ weights['p012.weight'] = torch.tensor([[0.0, -1.0, -1.0, -1.0]], dtype=torch.float32)
30
+ weights['p012.bias'] = torch.tensor([0.0], dtype=torch.float32)
31
+
32
+ # all_zero = (all bits = 0)
33
+ weights['all_zero.weight'] = torch.tensor([[-1.0, -1.0, -1.0, -1.0]], dtype=torch.float32)
34
+ weights['all_zero.bias'] = torch.tensor([0.0], dtype=torch.float32)
35
+
36
+ # Layer 2: Compute one-hot position indicators
37
+ # z1 = p0 AND x1 (first 1 is at position 1)
38
+ weights['z1.weight'] = torch.tensor([[1.0, 1.0]], dtype=torch.float32)
39
+ weights['z1.bias'] = torch.tensor([-2.0], dtype=torch.float32)
40
+
41
+ # z2 = p01 AND x2 (first 1 is at position 2)
42
+ weights['z2.weight'] = torch.tensor([[1.0, 1.0]], dtype=torch.float32)
43
+ weights['z2.bias'] = torch.tensor([-2.0], dtype=torch.float32)
44
+
45
+ # z3 = p012 AND x3 (first 1 is at position 3)
46
+ weights['z3.weight'] = torch.tensor([[1.0, 1.0]], dtype=torch.float32)
47
+ weights['z3.bias'] = torch.tensor([-2.0], dtype=torch.float32)
48
+
49
+ # Layer 3: Encode to binary output
50
+ # y2 = all_zero (CTZ = 4)
51
+ weights['y2.weight'] = torch.tensor([[1.0]], dtype=torch.float32)
52
+ weights['y2.bias'] = torch.tensor([-1.0], dtype=torch.float32)
53
+
54
+ # y1 = z2 OR z3 (CTZ in {2, 3})
55
+ weights['y1.weight'] = torch.tensor([[1.0, 1.0]], dtype=torch.float32)
56
+ weights['y1.bias'] = torch.tensor([-1.0], dtype=torch.float32)
57
+
58
+ # y0 = z1 OR z3 (CTZ in {1, 3})
59
+ weights['y0.weight'] = torch.tensor([[1.0, 1.0]], dtype=torch.float32)
60
+ weights['y0.bias'] = torch.tensor([-1.0], dtype=torch.float32)
61
+
62
+ save_file(weights, 'model.safetensors')
63
+
64
+ def ctz4(x3, x2, x1, x0):
65
+ inp = torch.tensor([float(x3), float(x2), float(x1), float(x0)])
66
+
67
+ # Layer 1
68
+ p0 = int((inp @ weights['p0.weight'].T + weights['p0.bias'] >= 0).item())
69
+ p01 = int((inp @ weights['p01.weight'].T + weights['p01.bias'] >= 0).item())
70
+ p012 = int((inp @ weights['p012.weight'].T + weights['p012.bias'] >= 0).item())
71
+ all_zero = int((inp @ weights['all_zero.weight'].T + weights['all_zero.bias'] >= 0).item())
72
+
73
+ # Layer 2
74
+ l2_z1 = torch.tensor([float(p0), float(x1)])
75
+ z1 = int((l2_z1 @ weights['z1.weight'].T + weights['z1.bias'] >= 0).item())
76
+
77
+ l2_z2 = torch.tensor([float(p01), float(x2)])
78
+ z2 = int((l2_z2 @ weights['z2.weight'].T + weights['z2.bias'] >= 0).item())
79
+
80
+ l2_z3 = torch.tensor([float(p012), float(x3)])
81
+ z3 = int((l2_z3 @ weights['z3.weight'].T + weights['z3.bias'] >= 0).item())
82
+
83
+ # Layer 3
84
+ l3_y2 = torch.tensor([float(all_zero)])
85
+ y2 = int((l3_y2 @ weights['y2.weight'].T + weights['y2.bias'] >= 0).item())
86
+
87
+ l3_y1 = torch.tensor([float(z2), float(z3)])
88
+ y1 = int((l3_y1 @ weights['y1.weight'].T + weights['y1.bias'] >= 0).item())
89
+
90
+ l3_y0 = torch.tensor([float(z1), float(z3)])
91
+ y0 = int((l3_y0 @ weights['y0.weight'].T + weights['y0.bias'] >= 0).item())
92
+
93
+ return y2, y1, y0
94
+
95
+ def reference_ctz4(x3, x2, x1, x0):
96
+ if x0 == 1:
97
+ return 0
98
+ if x1 == 1:
99
+ return 1
100
+ if x2 == 1:
101
+ return 2
102
+ if x3 == 1:
103
+ return 3
104
+ return 4
105
+
106
+ print("Verifying CTZ4...")
107
+ errors = 0
108
+ for i in range(16):
109
+ x3, x2, x1, x0 = (i >> 3) & 1, (i >> 2) & 1, (i >> 1) & 1, i & 1
110
+ y2, y1, y0 = ctz4(x3, x2, x1, x0)
111
+ result = y2 * 4 + y1 * 2 + y0
112
+ expected = reference_ctz4(x3, x2, x1, x0)
113
+ if result != expected:
114
+ errors += 1
115
+ print(f"ERROR: {x3}{x2}{x1}{x0} -> {result}, expected {expected}")
116
+
117
+ if errors == 0:
118
+ print("All 16 test cases passed!")
119
+ else:
120
+ print(f"FAILED: {errors} errors")
121
+
122
+ print("\nTruth Table:")
123
+ print("x3 x2 x1 x0 | CTZ | y2 y1 y0")
124
+ print("-" * 30)
125
+ for i in range(16):
126
+ x3, x2, x1, x0 = (i >> 3) & 1, (i >> 2) & 1, (i >> 1) & 1, i & 1
127
+ y2, y1, y0 = ctz4(x3, x2, x1, x0)
128
+ result = y2 * 4 + y1 * 2 + y0
129
+ print(f" {x3} {x2} {x1} {x0} | {result} | {y2} {y1} {y0}")
130
+
131
+ mag = sum(t.abs().sum().item() for t in weights.values())
132
+ print(f"\nMagnitude: {mag:.0f}")
133
+ print(f"Parameters: {sum(t.numel() for t in weights.values())}")
134
+ print(f"Neurons: {len([k for k in weights.keys() if 'weight' in k])}")
model.py ADDED
@@ -0,0 +1,37 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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 ctz4(x3, x2, x1, x0, weights):
8
+ """4-bit count trailing zeros: returns number of trailing 0 bits (0-4)."""
9
+ inp = torch.tensor([float(x3), float(x2), float(x1), float(x0)])
10
+
11
+ # Layer 1: prefix conditions
12
+ p0 = int((inp @ weights['p0.weight'].T + weights['p0.bias'] >= 0).item())
13
+ p01 = int((inp @ weights['p01.weight'].T + weights['p01.bias'] >= 0).item())
14
+ p012 = int((inp @ weights['p012.weight'].T + weights['p012.bias'] >= 0).item())
15
+ all_zero = int((inp @ weights['all_zero.weight'].T + weights['all_zero.bias'] >= 0).item())
16
+
17
+ # Layer 2: one-hot position
18
+ z1 = int((torch.tensor([float(p0), float(x1)]) @ weights['z1.weight'].T + weights['z1.bias'] >= 0).item())
19
+ z2 = int((torch.tensor([float(p01), float(x2)]) @ weights['z2.weight'].T + weights['z2.bias'] >= 0).item())
20
+ z3 = int((torch.tensor([float(p012), float(x3)]) @ weights['z3.weight'].T + weights['z3.bias'] >= 0).item())
21
+
22
+ # Layer 3: binary encoding
23
+ y2 = int((torch.tensor([float(all_zero)]) @ weights['y2.weight'].T + weights['y2.bias'] >= 0).item())
24
+ y1 = int((torch.tensor([float(z2), float(z3)]) @ weights['y1.weight'].T + weights['y1.bias'] >= 0).item())
25
+ y0 = int((torch.tensor([float(z1), float(z3)]) @ weights['y0.weight'].T + weights['y0.bias'] >= 0).item())
26
+
27
+ return y2, y1, y0
28
+
29
+ if __name__ == '__main__':
30
+ w = load_model()
31
+ print('CTZ4 examples:')
32
+ examples = [0b0001, 0b0010, 0b0100, 0b1000, 0b0000, 0b0110, 0b1100]
33
+ for val in examples:
34
+ x3, x2, x1, x0 = (val >> 3) & 1, (val >> 2) & 1, (val >> 1) & 1, val & 1
35
+ y2, y1, y0 = ctz4(x3, x2, x1, x0, w)
36
+ count = y2 * 4 + y1 * 2 + y0
37
+ print(f' {val:04b} -> {count}')
model.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:966c1503a6657d088a8aa946e88255100a5b55172418f1fe13e3c1c640b44a57
3
+ size 1452