CharlesCNorton commited on
Commit
3487f81
·
0 Parent(s):

Add T flip-flop threshold circuit

Browse files

6 neurons, 2 layers, 18 parameters, magnitude 19.

Files changed (6) hide show
  1. .gitattributes +1 -0
  2. README.md +112 -0
  3. config.json +9 -0
  4. create_safetensors.py +104 -0
  5. model.py +30 -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,112 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ license: mit
3
+ tags:
4
+ - pytorch
5
+ - safetensors
6
+ - threshold-logic
7
+ - neuromorphic
8
+ - sequential
9
+ - flipflop
10
+ ---
11
+
12
+ # threshold-t-flipflop
13
+
14
+ T flip-flop (toggle) next-state logic as threshold circuit.
15
+
16
+ ## Circuit
17
+
18
+ ```
19
+ T ───────┐
20
+ Q_prev ──┴──► T-FF ──┬──► Q
21
+ └──► Qn
22
+ ```
23
+
24
+ ## Modes
25
+
26
+ - **T=0 (Hold):** Q stays same
27
+ - **T=1 (Toggle):** Q flips
28
+
29
+ ## Truth Table
30
+
31
+ | T | Q_prev | Q | Qn | Mode |
32
+ |---|--------|---|----|----|
33
+ | 0 | 0 | 0 | 1 | Hold |
34
+ | 0 | 1 | 1 | 0 | Hold |
35
+ | 1 | 0 | 1 | 0 | Toggle |
36
+ | 1 | 1 | 0 | 1 | Toggle |
37
+
38
+ ## Logic
39
+
40
+ ```
41
+ Q = T XOR Q_prev
42
+ Qn = T XNOR Q_prev
43
+ ```
44
+
45
+ XOR is not linearly separable (the classic Minsky/Papert result), requiring 2 layers.
46
+
47
+ ## Architecture
48
+
49
+ ```
50
+ T Q_prev
51
+ │ │
52
+ ├────┬───┤
53
+ │ │ │
54
+ ▼ ▼ ▼
55
+ ┌────┬────┬────┬────┐
56
+ │ OR │NAND│NOR │AND │ Layer 1
57
+ └────┴────┴────┴────┘
58
+ │ │ │ │
59
+ └─┬──┘ └─┬──┘
60
+ │ │
61
+ ▼ ▼
62
+ ┌─────┐ ┌─────┐
63
+ │ AND │ │ OR │ Layer 2
64
+ └─────┘ └─────┘
65
+ │ │
66
+ ▼ ▼
67
+ Q Qn
68
+ ```
69
+
70
+ - Q = AND(OR, NAND) = XOR
71
+ - Qn = OR(NOR, AND) = XNOR
72
+
73
+ ## Parameters
74
+
75
+ | | |
76
+ |---|---|
77
+ | Inputs | 2 |
78
+ | Outputs | 2 |
79
+ | Neurons | 6 |
80
+ | Layers | 2 |
81
+ | Parameters | 18 |
82
+ | Magnitude | 19 |
83
+
84
+ ## Application: Binary Counter
85
+
86
+ T flip-flops with T=1 create divide-by-2 counters:
87
+
88
+ ```python
89
+ q = 0
90
+ for _ in range(8):
91
+ q = t_flipflop(1, q) # toggles: 0,1,0,1,0,1,0,1
92
+ ```
93
+
94
+ Chain multiple T flip-flops for multi-bit counters.
95
+
96
+ ## Usage
97
+
98
+ ```python
99
+ from safetensors.torch import load_file
100
+
101
+ w = load_file('model.safetensors')
102
+
103
+ # Toggle mode creates oscillation
104
+ q = 0
105
+ for _ in range(4):
106
+ q_next = compute(t=1, q_prev=q, w)
107
+ q = q_next # 0 -> 1 -> 0 -> 1
108
+ ```
109
+
110
+ ## License
111
+
112
+ MIT
config.json ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "threshold-t-flipflop",
3
+ "description": "T flip-flop (toggle)",
4
+ "inputs": 2,
5
+ "outputs": 2,
6
+ "neurons": 6,
7
+ "layers": 2,
8
+ "parameters": 18
9
+ }
create_safetensors.py ADDED
@@ -0,0 +1,104 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+ from safetensors.torch import save_file
3
+
4
+ weights = {}
5
+
6
+ # T Flip-Flop (Toggle)
7
+ # Inputs: T (toggle), Q_prev (previous state)
8
+ # Outputs: Q, Qn
9
+ #
10
+ # When T=1: Q toggles (Q = NOT Q_prev)
11
+ # When T=0: Q holds (Q = Q_prev)
12
+ #
13
+ # Q = T XOR Q_prev = (T AND NOT_Q_prev) OR (NOT_T AND Q_prev)
14
+ # Qn = NOT Q
15
+ #
16
+ # XOR is not linearly separable, requires 2 layers:
17
+ # XOR(a,b) = AND(OR(a,b), NAND(a,b))
18
+
19
+ # Layer 1: OR and NAND for Q, and for Qn
20
+ # OR(T, Q_prev)
21
+ weights['or.weight'] = torch.tensor([[1.0, 1.0]], dtype=torch.float32)
22
+ weights['or.bias'] = torch.tensor([-1.0], dtype=torch.float32)
23
+
24
+ # NAND(T, Q_prev)
25
+ weights['nand.weight'] = torch.tensor([[-1.0, -1.0]], dtype=torch.float32)
26
+ weights['nand.bias'] = torch.tensor([1.0], dtype=torch.float32)
27
+
28
+ # For Qn = NOT(T XOR Q_prev) = XNOR(T, Q_prev) = (NOT_T AND NOT_Q_prev) OR (T AND Q_prev)
29
+ # XNOR = AND(OR(NOT_T, NOT_Q_prev), NAND(NOT_T, NOT_Q_prev))
30
+ # = AND(NAND(T, Q_prev), OR(T, Q_prev))... wait, that's not right
31
+ # Let me think: XNOR(a,b) = NOT XOR(a,b) = NOR(a,b) OR AND(a,b)
32
+ # Actually: XNOR = (a AND b) OR (NOT_a AND NOT_b)
33
+ # Which in threshold: we need NOR and AND, then OR them
34
+ # NOR(T, Q_prev): fires when both are 0
35
+ weights['nor.weight'] = torch.tensor([[-1.0, -1.0]], dtype=torch.float32)
36
+ weights['nor.bias'] = torch.tensor([0.0], dtype=torch.float32)
37
+
38
+ # AND(T, Q_prev): fires when both are 1
39
+ weights['and.weight'] = torch.tensor([[1.0, 1.0]], dtype=torch.float32)
40
+ weights['and.bias'] = torch.tensor([-2.0], dtype=torch.float32)
41
+
42
+ # Layer 2: Combine
43
+ # Q = AND(OR, NAND) = XOR
44
+ weights['q.weight'] = torch.tensor([[1.0, 1.0]], dtype=torch.float32)
45
+ weights['q.bias'] = torch.tensor([-2.0], dtype=torch.float32)
46
+
47
+ # Qn = OR(NOR, AND) = XNOR
48
+ weights['qn.weight'] = torch.tensor([[1.0, 1.0]], dtype=torch.float32)
49
+ weights['qn.bias'] = torch.tensor([-1.0], dtype=torch.float32)
50
+
51
+ save_file(weights, 'model.safetensors')
52
+
53
+ def t_flipflop(t, q_prev):
54
+ inp = torch.tensor([float(t), float(q_prev)])
55
+
56
+ # Layer 1
57
+ or_out = int((inp @ weights['or.weight'].T + weights['or.bias'] >= 0).item())
58
+ nand_out = int((inp @ weights['nand.weight'].T + weights['nand.bias'] >= 0).item())
59
+ nor_out = int((inp @ weights['nor.weight'].T + weights['nor.bias'] >= 0).item())
60
+ and_out = int((inp @ weights['and.weight'].T + weights['and.bias'] >= 0).item())
61
+
62
+ # Layer 2
63
+ l1_q = torch.tensor([float(or_out), float(nand_out)])
64
+ q = int((l1_q @ weights['q.weight'].T + weights['q.bias'] >= 0).item())
65
+
66
+ l1_qn = torch.tensor([float(nor_out), float(and_out)])
67
+ qn = int((l1_qn @ weights['qn.weight'].T + weights['qn.bias'] >= 0).item())
68
+
69
+ return q, qn
70
+
71
+ def reference_t_ff(t, q_prev):
72
+ if t == 1:
73
+ return 1 - q_prev, q_prev
74
+ else:
75
+ return q_prev, 1 - q_prev
76
+
77
+ print("Verifying T Flip-Flop...")
78
+ errors = 0
79
+ for t in range(2):
80
+ for q_prev in range(2):
81
+ result = t_flipflop(t, q_prev)
82
+ expected = reference_t_ff(t, q_prev)
83
+ if result != expected:
84
+ errors += 1
85
+ print(f"ERROR: T={t}, Q_prev={q_prev} -> {result}, expected {expected}")
86
+
87
+ if errors == 0:
88
+ print("All 4 test cases passed!")
89
+ else:
90
+ print(f"FAILED: {errors} errors")
91
+
92
+ print("\nTruth Table:")
93
+ print("T Q_prev | Q Qn | Mode")
94
+ print("-" * 25)
95
+ for t in range(2):
96
+ for q_prev in range(2):
97
+ q, qn = t_flipflop(t, q_prev)
98
+ mode = "Toggle" if t == 1 else "Hold"
99
+ print(f"{t} {q_prev} | {q} {qn} | {mode}")
100
+
101
+ mag = sum(t.abs().sum().item() for t in weights.values())
102
+ print(f"\nMagnitude: {mag:.0f}")
103
+ print(f"Parameters: {sum(t.numel() for t in weights.values())}")
104
+ print(f"Neurons: {len([k for k in weights.keys() if 'weight' in k])}")
model.py ADDED
@@ -0,0 +1,30 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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 t_flipflop(t, q_prev, weights):
8
+ """T Flip-Flop: T=1 toggles, T=0 holds."""
9
+ inp = torch.tensor([float(t), float(q_prev)])
10
+
11
+ or_out = int((inp @ weights['or.weight'].T + weights['or.bias'] >= 0).item())
12
+ nand_out = int((inp @ weights['nand.weight'].T + weights['nand.bias'] >= 0).item())
13
+ nor_out = int((inp @ weights['nor.weight'].T + weights['nor.bias'] >= 0).item())
14
+ and_out = int((inp @ weights['and.weight'].T + weights['and.bias'] >= 0).item())
15
+
16
+ l1_q = torch.tensor([float(or_out), float(nand_out)])
17
+ q = int((l1_q @ weights['q.weight'].T + weights['q.bias'] >= 0).item())
18
+
19
+ l1_qn = torch.tensor([float(nor_out), float(and_out)])
20
+ qn = int((l1_qn @ weights['qn.weight'].T + weights['qn.bias'] >= 0).item())
21
+
22
+ return q, qn
23
+
24
+ if __name__ == '__main__':
25
+ w = load_model()
26
+ print('T Flip-Flop (counter demo):')
27
+ q = 0
28
+ for i in range(8):
29
+ print(f' Step {i}: Q={q}')
30
+ q, _ = t_flipflop(1, q, w) # Always toggle
model.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:caab286e66aa5a932677ce989f270f518d970ad8a5376ebadc9e0b7283a08f6d
3
+ size 840