phanerozoic commited on
Commit
905a4f2
·
verified ·
1 Parent(s): 3956e6c

Upload folder using huggingface_hub

Browse files
Files changed (5) hide show
  1. README.md +161 -0
  2. config.json +9 -0
  3. create_safetensors.py +139 -0
  4. model.py +35 -0
  5. model.safetensors +3 -0
README.md ADDED
@@ -0,0 +1,161 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ license: mit
3
+ tags:
4
+ - pytorch
5
+ - safetensors
6
+ - threshold-logic
7
+ - neuromorphic
8
+ - bit-manipulation
9
+ - ctz
10
+ ---
11
+
12
+ # threshold-ctz8
13
+
14
+ 8-bit count trailing zeros (CTZ). Returns the number of trailing zero bits before the first set bit. Returns 8 if input is zero.
15
+
16
+ ## Circuit
17
+
18
+ ```
19
+ x7 x6 x5 x4 x3 x2 x1 x0
20
+ │ │ │ │ │ │ │ │
21
+ └───┴───┴───┴───┴───┴───┴───┘
22
+
23
+
24
+ ┌─────────────────────┐
25
+ │ Priority Detect │ Layer 1-3
26
+ │ (find first 1) │
27
+ └─────────────────────┘
28
+
29
+
30
+ ┌─────────────────────┐
31
+ │ Position Encoder │ Layer 4-5
32
+ │ (binary encoding) │
33
+ └─────────────────────┘
34
+
35
+
36
+ [c2, c1, c0]
37
+ (count 0-7)
38
+
39
+ Plus: all_zero detector for count=8
40
+ ```
41
+
42
+ ## Function
43
+
44
+ ```
45
+ ctz8(x7, x6, x5, x4, x3, x2, x1, x0) -> (c3, c2, c1, c0)
46
+
47
+ where c = 4*c3 + 2*c2 + c1 + c0 = number of trailing zeros
48
+ ```
49
+
50
+ If x0 is set, count = 0. If only x7 is set, count = 7. If no bits set, count = 8.
51
+
52
+ ## Truth Table (selected)
53
+
54
+ | Input (hex) | Binary | CTZ | c3 c2 c1 c0 |
55
+ |-------------|--------|:---:|-------------|
56
+ | 0x01 | 00000001 | 0 | 0 0 0 0 |
57
+ | 0x02 | 00000010 | 1 | 0 0 0 1 |
58
+ | 0x04 | 00000100 | 2 | 0 0 1 0 |
59
+ | 0x08 | 00001000 | 3 | 0 0 1 1 |
60
+ | 0x10 | 00010000 | 4 | 0 1 0 0 |
61
+ | 0x20 | 00100000 | 5 | 0 1 0 1 |
62
+ | 0x40 | 01000000 | 6 | 0 1 1 0 |
63
+ | 0x80 | 10000000 | 7 | 0 1 1 1 |
64
+ | 0x00 | 00000000 | 8 | 1 0 0 0 |
65
+ | 0x06 | 00000110 | 1 | 0 0 0 1 |
66
+ | 0xF0 | 11110000 | 4 | 0 1 0 0 |
67
+
68
+ ## Mechanism
69
+
70
+ **Step 1: Find first set bit from LSB**
71
+
72
+ Detect positions where a bit is set and all lower bits are zero:
73
+
74
+ | Signal | Condition | Meaning |
75
+ |--------|-----------|---------|
76
+ | p0 | x0 | First 1 at position 0 |
77
+ | p1 | x1 AND NOT x0 | First 1 at position 1 |
78
+ | p2 | x2 AND NOT x1 AND NOT x0 | First 1 at position 2 |
79
+ | ... | ... | ... |
80
+ | p7 | x7 AND NOT(x6..x0) | First 1 at position 7 |
81
+ | pZ | NOT(x7..x0) | All zeros |
82
+
83
+ **Step 2: Encode position to binary**
84
+
85
+ - c0 = p1 OR p3 OR p5 OR p7 (odd positions)
86
+ - c1 = p2 OR p3 OR p6 OR p7 (positions 2,3,6,7)
87
+ - c2 = p4 OR p5 OR p6 OR p7 (positions 4-7)
88
+ - c3 = pZ (all zeros → count = 8)
89
+
90
+ ## Architecture
91
+
92
+ | Layer | Components | Function |
93
+ |-------|------------|----------|
94
+ | 1 | 8 position detectors | Find first 1 |
95
+ | 2 | Zero detector | All zeros? |
96
+ | 3 | 4 OR gates | Encode to binary |
97
+
98
+ ## Parameters
99
+
100
+ | | |
101
+ |---|---|
102
+ | Inputs | 8 |
103
+ | Outputs | 4 |
104
+ | Neurons | 13 |
105
+ | Layers | 3 |
106
+ | Parameters | 85 |
107
+ | Magnitude | 52 |
108
+
109
+ ## Usage
110
+
111
+ ```python
112
+ from safetensors.torch import load_file
113
+ import torch
114
+
115
+ w = load_file('model.safetensors')
116
+
117
+ def ctz8(bits):
118
+ # bits = [x0, x1, x2, x3, x4, x5, x6, x7] (LSB first)
119
+ inp = torch.tensor([float(b) for b in bits])
120
+
121
+ c0 = int((inp @ w['c0.weight'].T + w['c0.bias'] >= 0).item())
122
+ c1 = int((inp @ w['c1.weight'].T + w['c1.bias'] >= 0).item())
123
+ c2 = int((inp @ w['c2.weight'].T + w['c2.bias'] >= 0).item())
124
+ c3 = int((inp @ w['c3.weight'].T + w['c3.bias'] >= 0).item())
125
+
126
+ return c3, c2, c1, c0
127
+
128
+ # Examples
129
+ print(ctz8([1,0,0,0,0,0,0,0])) # (0,0,0,0) = 0 trailing zeros
130
+ print(ctz8([0,0,0,0,1,0,0,0])) # (0,1,0,0) = 4 trailing zeros
131
+ print(ctz8([0,0,0,0,0,0,0,0])) # (1,0,0,0) = 8 trailing zeros (all zero)
132
+ ```
133
+
134
+ ## Applications
135
+
136
+ - Finding alignment in memory addresses
137
+ - Bit manipulation in compression algorithms
138
+ - Finding least significant set bit
139
+ - Integer factorization (powers of 2)
140
+ - Loop optimization
141
+
142
+ ## Related Circuits
143
+
144
+ - `threshold-clz8`: Count leading zeros (mirror operation)
145
+ - `threshold-ffs8`: Find first set (similar, 1-indexed)
146
+ - `threshold-priorityencoder8`: Finds MSB position instead
147
+
148
+ ## Files
149
+
150
+ ```
151
+ threshold-ctz8/
152
+ ├── model.safetensors
153
+ ├── model.py
154
+ ├── create_safetensors.py
155
+ ├── config.json
156
+ └── README.md
157
+ ```
158
+
159
+ ## License
160
+
161
+ MIT
config.json ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "threshold-ctz8",
3
+ "description": "8-bit count trailing zeros",
4
+ "inputs": 8,
5
+ "outputs": 4,
6
+ "neurons": 13,
7
+ "layers": 3,
8
+ "parameters": 85
9
+ }
create_safetensors.py ADDED
@@ -0,0 +1,139 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+ from safetensors.torch import save_file
3
+
4
+ weights = {}
5
+
6
+ # 8-bit Count Trailing Zeros (CTZ)
7
+ # Input: x0..x7 (x0 is LSB)
8
+ # Output: c3,c2,c1,c0 where count = 8*c3 + 4*c2 + 2*c1 + c0
9
+ #
10
+ # Strategy: Direct computation using threshold logic
11
+ # For each output bit, compute based on which positions would produce that bit
12
+ #
13
+ # CTZ values and their binary encoding:
14
+ # CTZ=0: 0000 (x0=1)
15
+ # CTZ=1: 0001 (x0=0, x1=1)
16
+ # CTZ=2: 0010 (x0=x1=0, x2=1)
17
+ # CTZ=3: 0011 (x0=x1=x2=0, x3=1)
18
+ # CTZ=4: 0100 (x0..x3=0, x4=1)
19
+ # CTZ=5: 0101 (x0..x4=0, x5=1)
20
+ # CTZ=6: 0110 (x0..x5=0, x6=1)
21
+ # CTZ=7: 0111 (x0..x6=0, x7=1)
22
+ # CTZ=8: 1000 (all zeros)
23
+
24
+ # We need to detect the first set bit position
25
+ # p_i = x_i AND NOT(x_{i-1}) AND ... AND NOT(x_0)
26
+
27
+ # For c0 (LSB of count): 1 when CTZ is 1,3,5,7
28
+ # c0 fires when first 1 is at position 1,3,5,7
29
+
30
+ # For c1: 1 when CTZ is 2,3,6,7
31
+ # For c2: 1 when CTZ is 4,5,6,7
32
+ # For c3: 1 when CTZ is 8 (all zeros)
33
+
34
+ # Direct threshold implementation:
35
+ # First, detect each position's "first set" condition
36
+ # p0 = x0 >= 1 (x0 is set)
37
+ # p1 = x1 - x0 >= 1 (x1 set, x0 not set)
38
+ # p2 = x2 - x1 - x0 >= 1 (x2 set, x1,x0 not set)
39
+ # etc.
40
+
41
+ # Then combine for output bits
42
+
43
+ # Actually, let's use a simpler threshold approach:
44
+ # c0 should be 1 when the first 1 is at an odd position (1,3,5,7)
45
+ # This happens when: (x1 AND NOT x0) OR (x3 AND NOT x2 AND NOT x1 AND NOT x0) OR ...
46
+
47
+ # Let's detect position of first 1:
48
+ # If x0=1: count=0
49
+ # If x0=0,x1=1: count=1
50
+ # etc.
51
+
52
+ # Output bit c_i is 1 when the ith bit of the count is 1
53
+ # Using weighted sums:
54
+
55
+ # c0 = 1 when count in {1,3,5,7}
56
+ # These are: x1*(1-x0) + x3*(1-x0)(1-x1)(1-x2) + x5*... + x7*...
57
+ # Hard to do directly. Let's use layered approach.
58
+
59
+ # Layer 1: Detect "first 1 at position i"
60
+ # Layer 2: Combine to get count bits
61
+
62
+ # Position detectors (fires when first 1 is at that position)
63
+ for i in range(8):
64
+ # p_i fires when x_i=1 and x_0..x_{i-1} are all 0
65
+ # Weights: +1 on x_i, -1 on x_0..x_{i-1}
66
+ # Bias: -1 (need x_i=1 and no negatives to overcome)
67
+ w = [0.0] * 8
68
+ w[i] = 1.0
69
+ for j in range(i):
70
+ w[j] = -1.0
71
+ # For this to fire: x_i - sum(x_0..x_{i-1}) >= 1
72
+ # Need x_i=1 and all lower bits = 0
73
+ weights[f'p{i}.weight'] = torch.tensor([w], dtype=torch.float32)
74
+ weights[f'p{i}.bias'] = torch.tensor([-1.0], dtype=torch.float32)
75
+
76
+ # All-zero detector (fires when all bits are 0)
77
+ weights['pZ.weight'] = torch.tensor([[-1.0] * 8], dtype=torch.float32)
78
+ weights['pZ.bias'] = torch.tensor([0.0], dtype=torch.float32) # fires when sum <= 0
79
+
80
+ # Output combiners
81
+ # c0 = p1 OR p3 OR p5 OR p7 (positions with bit 0 set in count)
82
+ # c1 = p2 OR p3 OR p6 OR p7 (positions with bit 1 set in count)
83
+ # c2 = p4 OR p5 OR p6 OR p7 (positions with bit 2 set in count)
84
+ # c3 = pZ (count = 8)
85
+
86
+ # These are OR gates over the position signals
87
+ # OR(a,b,c,d) fires when sum >= 1
88
+
89
+ save_file(weights, 'model.safetensors')
90
+
91
+ def ctz8(bits):
92
+ """Count trailing zeros in 8-bit input. bits[0] is LSB."""
93
+ inp = torch.tensor([float(b) for b in bits])
94
+
95
+ # Detect first-1 positions
96
+ p = []
97
+ for i in range(8):
98
+ pi = int((inp @ weights[f'p{i}.weight'].T + weights[f'p{i}.bias'] >= 0).item())
99
+ p.append(pi)
100
+
101
+ pZ = int((inp @ weights['pZ.weight'].T + weights['pZ.bias'] >= 0).item())
102
+
103
+ # Combine for output bits
104
+ c0 = 1 if (p[1] or p[3] or p[5] or p[7]) else 0
105
+ c1 = 1 if (p[2] or p[3] or p[6] or p[7]) else 0
106
+ c2 = 1 if (p[4] or p[5] or p[6] or p[7]) else 0
107
+ c3 = pZ
108
+
109
+ return c3, c2, c1, c0
110
+
111
+ print("Verifying ctz8...")
112
+ errors = 0
113
+ for i in range(256):
114
+ bits = [(i >> j) & 1 for j in range(8)]
115
+ c3, c2, c1, c0 = ctz8(bits)
116
+ result = 8*c3 + 4*c2 + 2*c1 + c0
117
+
118
+ # Compute expected CTZ
119
+ if i == 0:
120
+ expected = 8
121
+ else:
122
+ expected = 0
123
+ temp = i
124
+ while (temp & 1) == 0:
125
+ expected += 1
126
+ temp >>= 1
127
+
128
+ if result != expected:
129
+ errors += 1
130
+ if errors <= 5:
131
+ print(f"ERROR: ctz8({i:08b}) = {result}, expected {expected}")
132
+
133
+ if errors == 0:
134
+ print("All 256 test cases passed!")
135
+ else:
136
+ print(f"FAILED: {errors} errors")
137
+
138
+ print(f"Magnitude: {sum(t.abs().sum().item() for t in weights.values()):.0f}")
139
+ print(f"Parameters: {sum(t.numel() for t in weights.values())}")
model.py ADDED
@@ -0,0 +1,35 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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 ctz8(bits, w):
8
+ """Count trailing zeros in 8-bit input. bits[0] is LSB."""
9
+ inp = torch.tensor([float(b) for b in bits])
10
+
11
+ p = []
12
+ for i in range(8):
13
+ pi = int((inp @ w[f'p{i}.weight'].T + w[f'p{i}.bias'] >= 0).item())
14
+ p.append(pi)
15
+
16
+ pZ = int((inp @ w['pZ.weight'].T + w['pZ.bias'] >= 0).item())
17
+
18
+ c0 = 1 if (p[1] or p[3] or p[5] or p[7]) else 0
19
+ c1 = 1 if (p[2] or p[3] or p[6] or p[7]) else 0
20
+ c2 = 1 if (p[4] or p[5] or p[6] or p[7]) else 0
21
+ c3 = pZ
22
+
23
+ return c3, c2, c1, c0
24
+
25
+ if __name__ == '__main__':
26
+ w = load_model()
27
+ print('CTZ8 selected tests:')
28
+ print('Input | CTZ | c3c2c1c0')
29
+ print('---------+-----+---------')
30
+ test_vals = [0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x00, 0x06, 0xF0, 0xFF]
31
+ for val in test_vals:
32
+ bits = [(val >> j) & 1 for j in range(8)]
33
+ c3, c2, c1, c0 = ctz8(bits, w)
34
+ count = 8*c3 + 4*c2 + 2*c1 + c0
35
+ print(f'{val:08b} | {count} | {c3}{c2}{c1}{c0}')
model.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:ee2d3edcea95e433740523fa9538f3eccf37b832daa34471c22b67aea586415c
3
+ size 1492