phanerozoic commited on
Commit
0d00548
·
verified ·
1 Parent(s): 9f7feaf

Rename from tiny-XOR-verified

Browse files
Files changed (4) hide show
  1. README.md +115 -0
  2. config.json +24 -0
  3. model.py +80 -0
  4. model.safetensors +3 -0
README.md ADDED
@@ -0,0 +1,115 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ license: mit
3
+ tags:
4
+ - pytorch
5
+ - safetensors
6
+ - threshold-logic
7
+ - neuromorphic
8
+ - multi-layer
9
+ ---
10
+
11
+ # threshold-xor
12
+
13
+ The function that broke single-layer perceptrons. XOR is not linearly separable - no single threshold neuron can compute it. This circuit uses the minimal two-layer solution.
14
+
15
+ ## Circuit
16
+
17
+ ```
18
+ x y
19
+ │ │
20
+ ├───┬───┤
21
+ │ │ │
22
+ ▼ │ ▼
23
+ ┌──────┐│┌──────┐
24
+ │ OR │││ NAND │ Layer 1
25
+ │w:1,1 │││w:-1,-1│
26
+ │b: -1 │││b: +1 │
27
+ └──────┘│└──────┘
28
+ │ │ │
29
+ └───┼───┘
30
+ ▼
31
+ ┌──────┐
32
+ │ AND │ Layer 2
33
+ │w: 1,1│
34
+ │b: -2 │
35
+ └──────┘
36
+ │
37
+ ▼
38
+ XOR(x,y)
39
+ ```
40
+
41
+ ## Why Two Layers?
42
+
43
+ Plot the XOR truth table on a plane:
44
+
45
+ ```
46
+ y
47
+ 1 │ 1 0
48
+ │
49
+ 0 │ 0 1
50
+ └────────
51
+ 0 1 x
52
+ ```
53
+
54
+ No single line can separate the 1s from the 0s. This is linear inseparability. Minsky and Papert's 1969 proof of this contributed to the first AI winter.
55
+
56
+ The solution: compute OR and NAND in parallel, then AND them together.
57
+
58
+ ## Trace
59
+
60
+ | x | y | OR | NAND | AND(OR,NAND) |
61
+ |---|---|-----|------|--------------|
62
+ | 0 | 0 | 0 | 1 | 0 |
63
+ | 0 | 1 | 1 | 1 | 1 |
64
+ | 1 | 0 | 1 | 1 | 1 |
65
+ | 1 | 1 | 1 | 0 | 0 |
66
+
67
+ OR catches "at least one." NAND catches "not both." Their intersection is "exactly one."
68
+
69
+ ## Parameters
70
+
71
+ | Layer | Weights | Bias |
72
+ |-------|---------|------|
73
+ | OR | [1, 1] | -1 |
74
+ | NAND | [-1, -1] | +1 |
75
+ | AND | [1, 1] | -2 |
76
+ | **Total** | | **9** |
77
+
78
+ ## Properties
79
+
80
+ - Commutative: XOR(x,y) = XOR(y,x)
81
+ - Associative: XOR(x,XOR(y,z)) = XOR(XOR(x,y),z)
82
+ - Self-inverse: XOR(x,x) = 0
83
+ - Parity: XOR is addition mod 2
84
+
85
+ ## Usage
86
+
87
+ ```python
88
+ from safetensors.torch import load_file
89
+ import torch
90
+
91
+ w = load_file('model.safetensors')
92
+
93
+ def xor_gate(x, y):
94
+ inp = torch.tensor([float(x), float(y)])
95
+
96
+ or_out = int((inp * w['layer1.neuron1.weight']).sum() + w['layer1.neuron1.bias'] >= 0)
97
+ nand_out = int((inp * w['layer1.neuron2.weight']).sum() + w['layer1.neuron2.bias'] >= 0)
98
+
99
+ l1 = torch.tensor([float(or_out), float(nand_out)])
100
+ return int((l1 * w['layer2.weight']).sum() + w['layer2.bias'] >= 0)
101
+ ```
102
+
103
+ ## Files
104
+
105
+ ```
106
+ threshold-xor/
107
+ ├── model.safetensors
108
+ ├── model.py
109
+ ├── config.json
110
+ └── README.md
111
+ ```
112
+
113
+ ## License
114
+
115
+ MIT
config.json ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "model_type": "threshold_network",
3
+ "task": "xor_gate",
4
+ "architecture": "2 -> 2 -> 1",
5
+ "input_size": 2,
6
+ "hidden_size": 2,
7
+ "output_size": 1,
8
+ "num_neurons": 3,
9
+ "num_layers": 2,
10
+ "num_parameters": 9,
11
+ "activation": "heaviside",
12
+ "weight_constraints": "integer",
13
+ "verification": {
14
+ "method": "coq_proof",
15
+ "exhaustive": true,
16
+ "inputs_tested": 4
17
+ },
18
+ "accuracy": {
19
+ "all_inputs": "4/4",
20
+ "percentage": 100.0
21
+ },
22
+ "properties": ["not_linearly_separable", "minimal_depth"],
23
+ "github": "https://github.com/CharlesCNorton/coq-circuits"
24
+ }
model.py ADDED
@@ -0,0 +1,80 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Threshold Network for XOR Gate
3
+
4
+ A formally verified two-layer threshold network computing exclusive or.
5
+ XOR is not linearly separable, requiring at least 2 layers.
6
+ Architecture: Layer1 computes OR and NAND, Layer2 computes AND of results.
7
+ """
8
+
9
+ import torch
10
+ from safetensors.torch import load_file
11
+
12
+
13
+ class ThresholdXOR:
14
+ """
15
+ XOR gate implemented as a 2-layer threshold network.
16
+
17
+ Layer 1: OR (neuron 1) and NAND (neuron 2) in parallel
18
+ Layer 2: AND of layer 1 outputs
19
+
20
+ XOR(x,y) = AND(OR(x,y), NAND(x,y))
21
+ """
22
+
23
+ def __init__(self, weights_dict):
24
+ self.l1_n1_weight = weights_dict['layer1.neuron1.weight']
25
+ self.l1_n1_bias = weights_dict['layer1.neuron1.bias']
26
+ self.l1_n2_weight = weights_dict['layer1.neuron2.weight']
27
+ self.l1_n2_bias = weights_dict['layer1.neuron2.bias']
28
+ self.l2_weight = weights_dict['layer2.weight']
29
+ self.l2_bias = weights_dict['layer2.bias']
30
+
31
+ def __call__(self, x1, x2):
32
+ inputs = torch.tensor([float(x1), float(x2)])
33
+
34
+ or_out = ((inputs * self.l1_n1_weight).sum() + self.l1_n1_bias >= 0).float()
35
+ nand_out = ((inputs * self.l1_n2_weight).sum() + self.l1_n2_bias >= 0).float()
36
+
37
+ layer1_out = torch.tensor([or_out, nand_out])
38
+ output = ((layer1_out * self.l2_weight).sum() + self.l2_bias >= 0).float()
39
+
40
+ return output
41
+
42
+ @classmethod
43
+ def from_safetensors(cls, path="model.safetensors"):
44
+ return cls(load_file(path))
45
+
46
+
47
+ def forward(x1, x2, weights):
48
+ """
49
+ Forward pass with Heaviside activation.
50
+
51
+ Args:
52
+ x1, x2: Input values (0 or 1)
53
+ weights: Dict with layer weights and biases
54
+
55
+ Returns:
56
+ XOR(x1, x2)
57
+ """
58
+ inputs = torch.tensor([float(x1), float(x2)])
59
+
60
+ or_out = ((inputs * weights['layer1.neuron1.weight']).sum() + weights['layer1.neuron1.bias'] >= 0).float()
61
+ nand_out = ((inputs * weights['layer1.neuron2.weight']).sum() + weights['layer1.neuron2.bias'] >= 0).float()
62
+
63
+ layer1_out = torch.tensor([or_out, nand_out])
64
+ output = ((layer1_out * weights['layer2.weight']).sum() + weights['layer2.bias'] >= 0).float()
65
+
66
+ return output
67
+
68
+
69
+ if __name__ == "__main__":
70
+ weights = load_file("model.safetensors")
71
+ model = ThresholdXOR(weights)
72
+
73
+ print("XOR Gate Truth Table:")
74
+ print("-" * 25)
75
+ for x1 in [0, 1]:
76
+ for x2 in [0, 1]:
77
+ out = int(model(x1, x2).item())
78
+ expected = x1 ^ x2
79
+ status = "OK" if out == expected else "FAIL"
80
+ print(f"XOR({x1}, {x2}) = {out} [{status}]")
model.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:01a1a18f425725466c246b1268548b0d5ce3a0d4d368c4911fa670adfc084f46
3
+ size 476