threshold-crc4 / create_safetensors.py
phanerozoic's picture
Upload folder using huggingface_hub
81725db verified
import torch
from safetensors.torch import save_file
weights = {}
# CRC-4 Generator
# Polynomial: x^4 + x + 1 (CRC-4-ITU, binary 10011)
# Inputs: d3, d2, d1, d0 (4 data bits)
# Outputs: crc3, crc2, crc1, crc0 (4 CRC bits)
#
# Derived parallel CRC equations (starting from CRC=0):
# crc0 = d3 XOR d0
# crc1 = d2 XOR d1 XOR d0
# crc2 = d3 XOR d1
# crc3 = d3 XOR d2
#
# Each XOR is implemented as AND(OR, NAND)
def add_xor2_weights(prefix, idx_a, idx_b, total_inputs):
"""Add weights for XOR(input[a], input[b])."""
w_or = [0.0] * total_inputs
w_or[idx_a] = 1.0
w_or[idx_b] = 1.0
weights[f'{prefix}.or.weight'] = torch.tensor([w_or], dtype=torch.float32)
weights[f'{prefix}.or.bias'] = torch.tensor([-1.0], dtype=torch.float32)
w_nand = [0.0] * total_inputs
w_nand[idx_a] = -1.0
w_nand[idx_b] = -1.0
weights[f'{prefix}.nand.weight'] = torch.tensor([w_nand], dtype=torch.float32)
weights[f'{prefix}.nand.bias'] = torch.tensor([1.0], dtype=torch.float32)
weights[f'{prefix}.and.weight'] = torch.tensor([[1.0, 1.0]], dtype=torch.float32)
weights[f'{prefix}.and.bias'] = torch.tensor([-2.0], dtype=torch.float32)
def add_xor2_stage_weights(prefix):
"""Add weights for XOR of two intermediate signals."""
weights[f'{prefix}.or.weight'] = torch.tensor([[1.0, 1.0]], dtype=torch.float32)
weights[f'{prefix}.or.bias'] = torch.tensor([-1.0], dtype=torch.float32)
weights[f'{prefix}.nand.weight'] = torch.tensor([[-1.0, -1.0]], dtype=torch.float32)
weights[f'{prefix}.nand.bias'] = torch.tensor([1.0], dtype=torch.float32)
weights[f'{prefix}.and.weight'] = torch.tensor([[1.0, 1.0]], dtype=torch.float32)
weights[f'{prefix}.and.bias'] = torch.tensor([-2.0], dtype=torch.float32)
# Input indices: d3=0, d2=1, d1=2, d0=3
# crc0 = d3 XOR d0 (indices 0, 3)
add_xor2_weights('crc0', 0, 3, 4)
# crc1 = d2 XOR d1 XOR d0 = XOR(XOR(d2, d1), d0) (indices 1, 2, 3)
add_xor2_weights('crc1.x12', 1, 2, 4) # d2 XOR d1
add_xor2_stage_weights('crc1.final') # (d2^d1) XOR d0
# crc2 = d3 XOR d1 (indices 0, 2)
add_xor2_weights('crc2', 0, 2, 4)
# crc3 = d3 XOR d2 (indices 0, 1)
add_xor2_weights('crc3', 0, 1, 4)
save_file(weights, 'model.safetensors')
def xor2_eval(a, b, w, or_w, or_b, nand_w, nand_b, and_w, and_b):
"""Evaluate XOR using threshold gates."""
inp = torch.tensor([float(a), float(b)])
or_out = int((inp @ or_w.T + or_b >= 0).item())
nand_out = int((inp @ nand_w.T + nand_b >= 0).item())
l1 = torch.tensor([float(or_out), float(nand_out)])
return int((l1 @ and_w.T + and_b >= 0).item())
def crc4(d3, d2, d1, d0):
inp = torch.tensor([float(d3), float(d2), float(d1), float(d0)])
# crc0 = d3 XOR d0
or_out = int((inp @ weights['crc0.or.weight'].T + weights['crc0.or.bias'] >= 0).item())
nand_out = int((inp @ weights['crc0.nand.weight'].T + weights['crc0.nand.bias'] >= 0).item())
l1 = torch.tensor([float(or_out), float(nand_out)])
crc0 = int((l1 @ weights['crc0.and.weight'].T + weights['crc0.and.bias'] >= 0).item())
# crc1 = XOR(XOR(d2, d1), d0)
or_out = int((inp @ weights['crc1.x12.or.weight'].T + weights['crc1.x12.or.bias'] >= 0).item())
nand_out = int((inp @ weights['crc1.x12.nand.weight'].T + weights['crc1.x12.nand.bias'] >= 0).item())
l1 = torch.tensor([float(or_out), float(nand_out)])
x12 = int((l1 @ weights['crc1.x12.and.weight'].T + weights['crc1.x12.and.bias'] >= 0).item())
inp2 = torch.tensor([float(x12), float(d0)])
or_out = int((inp2 @ weights['crc1.final.or.weight'].T + weights['crc1.final.or.bias'] >= 0).item())
nand_out = int((inp2 @ weights['crc1.final.nand.weight'].T + weights['crc1.final.nand.bias'] >= 0).item())
l1 = torch.tensor([float(or_out), float(nand_out)])
crc1 = int((l1 @ weights['crc1.final.and.weight'].T + weights['crc1.final.and.bias'] >= 0).item())
# crc2 = d3 XOR d1
or_out = int((inp @ weights['crc2.or.weight'].T + weights['crc2.or.bias'] >= 0).item())
nand_out = int((inp @ weights['crc2.nand.weight'].T + weights['crc2.nand.bias'] >= 0).item())
l1 = torch.tensor([float(or_out), float(nand_out)])
crc2 = int((l1 @ weights['crc2.and.weight'].T + weights['crc2.and.bias'] >= 0).item())
# crc3 = d3 XOR d2
or_out = int((inp @ weights['crc3.or.weight'].T + weights['crc3.or.bias'] >= 0).item())
nand_out = int((inp @ weights['crc3.nand.weight'].T + weights['crc3.nand.bias'] >= 0).item())
l1 = torch.tensor([float(or_out), float(nand_out)])
crc3 = int((l1 @ weights['crc3.and.weight'].T + weights['crc3.and.bias'] >= 0).item())
return crc3, crc2, crc1, crc0
def crc4_ref(d3, d2, d1, d0):
"""Reference CRC-4 computation."""
crc0 = d3 ^ d0
crc1 = d2 ^ d1 ^ d0
crc2 = d3 ^ d1
crc3 = d3 ^ d2
return crc3, crc2, crc1, crc0
print("Verifying CRC-4 Generator (polynomial x^4+x+1)...")
errors = 0
for i in range(16):
d3, d2, d1, d0 = (i >> 3) & 1, (i >> 2) & 1, (i >> 1) & 1, i & 1
result = crc4(d3, d2, d1, d0)
expected = crc4_ref(d3, d2, d1, d0)
if result != expected:
errors += 1
print(f"ERROR: {d3}{d2}{d1}{d0} -> {result}, expected {expected}")
if errors == 0:
print("All 16 test cases passed!")
else:
print(f"FAILED: {errors} errors")
print("\nTruth Table:")
print("Data | CRC")
print("d3d2d1d0 | c3c2c1c0")
print("-" * 22)
for i in range(16):
d3, d2, d1, d0 = (i >> 3) & 1, (i >> 2) & 1, (i >> 1) & 1, i & 1
c3, c2, c1, c0 = crc4(d3, d2, d1, d0)
print(f" {d3} {d2} {d1} {d0} | {c3} {c2} {c1} {c0}")
mag = sum(t.abs().sum().item() for t in weights.values())
print(f"\nMagnitude: {mag:.0f}")
print(f"Parameters: {sum(t.numel() for t in weights.values())}")
print(f"Neurons: {len([k for k in weights.keys() if 'weight' in k])}")