Threshold Calculus

Digital circuits encoded as neural network weights.

Each gate is a threshold logic unit: output = step(weights * inputs + bias). The step function fires when the weighted sum >= 0. This maps digital logic to tensor operations.

What's Here

File Description
arithmetic.safetensors 626,374 tensors encoding 208,788 gates
eval.py Test harness (211,581 tests)
build.py Builds tensors and infers gate connectivity

Circuits

Float16 (IEEE 754)

  • float16.add, float16.sub, float16.mul, float16.div
  • float16.sqrt, float16.rsqrt, float16.pow
  • float16.exp, float16.ln, float16.log2
  • float16.sin, float16.cos, float16.tan, float16.tanh
  • float16.neg, float16.abs, float16.cmp
  • float16.toint, float16.fromint
  • float16.pack, float16.unpack, float16.normalize

Handles NaN, Inf, zero, subnormals. Mantissa alignment via barrel shifter. Normalization via CLZ.

Accuracy/rounding:

  • Unary transcendental ops are LUT-backed over all 65,536 float16 inputs.
  • Outputs match torch.float16 results (round-to-nearest-even); NaNs are canonicalized to 0x7E00.
  • float16.pow is defined as exp(b * ln(a)) with float16 rounding at each stage.

16-bit Integer

  • Adders: half, full, ripple carry (2/4/16 bit), add-with-carry (adc16bit)
  • Subtraction: sub16bit, sbc16bit, neg16bit
  • Comparison: cmp16bit, equality16bit
  • Shifts: asr16bit, rol16bit, ror16bit
  • CLZ: 16-bit

Modular Arithmetic

  • mod2 through mod12 (divisibility testing)

Boolean

  • AND, OR, NOT, NAND, NOR, XOR, XNOR, IMPLIES, BIIMPLIES

Threshold

  • k-of-n gates (1-of-8 through 8-of-8)
  • majority, minority, atleastk, atmostk, exactlyk

Pattern Recognition

  • popcount, allzeros, allones, onehotdetector
  • symmetry8bit, alternating8bit, hammingdistance8bit
  • leadingones, trailingones, runlength

Combinational

  • decoder3to8, encoder
  • multiplexer (2/4/8 to 1), demultiplexer (1 to 2/4/8)
  • barrelshifter8bit, priorityencoder8bit

How It Works

A threshold gate computes:

output = 1 if (w1*x1 + w2*x2 + ... + wn*xn + bias) >= 0 else 0

This is a perceptron with Heaviside step activation.

AND gate: weights = [1, 1], bias = -1.5

  • (0,0): 0 + 0 - 1.5 = -1.5 < 0 -> 0
  • (0,1): 0 + 1 - 1.5 = -0.5 < 0 -> 0
  • (1,0): 1 + 0 - 1.5 = -0.5 < 0 -> 0
  • (1,1): 1 + 1 - 1.5 = 0.5 >= 0 -> 1

XOR requires two layers (not linearly separable):

  • Layer 1: OR and NAND in parallel
  • Layer 2: AND of both outputs

Float16 Architecture (Short)

High-level dataflow:

float16.<op>
  a,b -> unpack -> classify -> core op -> normalize/round -> pack -> out

Step-by-step (condensed):

  1. Unpack sign/exponent/mantissa. Subnormals use implicit 0, normals use implicit 1.
  2. Classify inputs: zero, subnormal, normal, inf, NaN.
  3. Core op:
    • add/sub: align exponents, add/sub mantissas, compute sign.
    • mul/div: add/sub exponents (minus bias), multiply/divide mantissas.
    • unary LUT: lookup output for each 16-bit input (torch.float16), with NaN canonicalization.
    • pow: ln(a) -> mul(b, ln(a)) -> exp, rounded at each stage.
  4. Normalize and round-to-nearest-even (CLZ + shifts).
  5. Pack sign/exponent/mantissa and mux special cases (NaN/Inf/zero).

Self-Documenting Format

Each gate has three tensors in arithmetic.safetensors:

  • .weight -- input weights
  • .bias -- threshold
  • .inputs -- int64 tensor of signal IDs (ordered to match .weight)

Signal registry in metadata maps IDs to names:

from safetensors import safe_open
import json

with safe_open('arithmetic.safetensors', framework='pt') as f:
    registry = json.loads(f.metadata()['signal_registry'])
    inputs = f.get_tensor('boolean.and.inputs')
    names = [registry[str(i.item())] for i in inputs]
    # ['$a', '$b']

Signal naming:

  • $name -- circuit input (e.g., $a, $dividend[0])
  • #0, #1 -- constants
  • gate.path -- output of another gate

Format details:

  • Metadata includes signal_registry (JSON map from ID to name) and format_version (currently 2.0).
  • .inputs stores global signal IDs; these IDs are resolved through signal_registry.
  • External inputs are names starting with $ or containing .$ (e.g., float16.add.$a[3]).
  • All gates include .inputs; build.py infers them and --inputs-coverage fails if resolution is missing.

How to Reproduce

Rebuild tensors:

python build.py

Run full evaluation (always full + verbose):

python eval.py

Run coverage and input-routing validation:

python eval.py --coverage --inputs-coverage

Expected runtimes (ballpark, CPU dependent):

  • build.py: ~1-2 minutes, produces ~247 MB arithmetic.safetensors
  • eval.py --coverage --inputs-coverage: ~3-4 minutes for 211,581 tests

Running Eval

python eval.py

Tests all circuits. Small circuits are exhaustive; 16-bit arithmetic is sampled on grids (plus edge cases). Float16 tests cover special cases (NaN, Inf, +/-0, subnormals) plus normal arithmetic. Eval runs full + verbose by default; there is no quick/verbose mode. Use --circuit to filter reported circuits.

For coverage and input-routing validation:

python eval.py --coverage --inputs-coverage

--inputs-coverage evaluates gates via their .inputs tensors using seeded external inputs and explicit overrides, and fails if inputs cannot be resolved. This is for coverage and routing sanity, not a correctness proof.

Python Calculator Interface (Gate-Level)

calculator.py provides a pure gate-level evaluator that uses only .inputs + .weight/.bias (no arithmetic shortcuts). This is intended as a rigorous proof-of-concept for using the weights as a calculator. Expression mode routes even constants through a circuit (via float16.add with +0) to ensure gate-level evaluation.

Examples:

python calculator.py float16.add 1.0 2.0
python calculator.py float16.sqrt 2.0
python calculator.py float16.add --inputs a=0x3c00 b=0x4000 --hex
python calculator.py --expr "1 + 1"
python calculator.py "sin(pi / 2)"
python calculator.py --expr "1 + 1" --json
python calculator.py "pi" --strict

Programmatic use:

from calculator import ThresholdCalculator

calc = ThresholdCalculator("arithmetic.safetensors")
out, _ = calc.float16_binop("add", 1.0, 2.0)
print(out)

Development History

Started as an 8-bit CPU project. Built boolean gates, then arithmetic (adders -> multipliers -> dividers), then CPU control logic. The CPU worked but the arithmetic core turned out to be the useful part, so it was extracted.

Float16 was added later. The commit history shows the iterative process--float16.add went through multiple rounds of bug fixes for edge cases (zero handling, sign logic, normalization). Mul and div required multi-bit carry infrastructure.

Project Origin

This began as an attempt to build a complete threshold-logic CPU. The CPU is in a separate repo (phanerozoic/8bit-threshold-computer). This repo focuses on the arithmetic core.

Roadmap

Done:

  • Float16 core (add/sub/mul/div)
  • Float16 utilities (pack/unpack/normalize/conversions)
  • Float16 IEEE-754 half compliance for add/sub/mul/div + toint/fromint (including subnormals)
  • Float16 unary LUTs (sqrt/rsqrt/exp/ln/log2/log10/sin/cos/tan/tanh/asin/acos/atan/sinh/cosh/floor/ceil/round)
  • Float16 pow via exp(b * ln(a))
  • 16-bit integer arithmetic (add/sub/cmp/shifts/CLZ)
  • Boolean, threshold, modular, pattern recognition, combinational

Next:

  • Add 32-bit integer arithmetic circuits (add/sub/shift/compare, then mul/div).
  • Add higher precision modes (float32 or fixed-point) for typical calculator accuracy.

Cleanup:

  • None (8-bit arithmetic scaffolding removed)

TODO (Unified)

  • 32-bit integer circuits and an int32 calculator mode.
  • Degree-mode trig and implicit multiplication parsing.
  • Higher-precision arithmetic (float32 or fixed-point).
  • Complex-number support (or explicit domain errors).

Hugging Face Space (Proof of Concept)

A minimal Space UI is included in app.py. It uses the gate-level evaluator only (no arithmetic shortcuts) and exposes a normal calculator-style expression input.

Example expressions:

  • 1 + 1
  • sin(pi / 2)
  • exp(ln(2))

Notes:

  • All results are float16 (IEEE-754 half) and may be rounded.
  • pow uses the exp(b*ln(a)) definition; negative bases yield NaN.

License

Apache 2.0

Downloads last month

-

Downloads are not tracked for this model. How to track
Inference Providers NEW
This model isn't deployed by any Inference Provider. 🙋 Ask for provider support

Space using phanerozoic/threshold-calculus 1