Doing a neural network's math with resistors — the physics checks out to 0.0011%
PASS · max error 0.0011%
We took a tiny neural network, rebuilt its math as a grid of resistors, and simulated the real circuit. The currents it produces match the math it's meant to do to within 0.0011% — so the idea is physically sound, before spending a cent on a chip.
A neural network's core move is boring: multiply two numbers, then add up the results. Do that
billions of times and you get things like ChatGPT.
A resistor multiplies for free. Push a voltage through it and Ohm's law hands you a current — voltage
divided by resistance. Wire up a grid of resistors and those currents add themselves up. So a grid
of resistors can do a neural network's math directly, as physics — no processor crunching numbers.
This page checks one thing: in a full circuit simulation, do the resistor currents actually match the
math the network intended? They do — to within 0.0011%.
The catch: it's still a simulation of a tiny model, not a real chip. But the physics holds, and
that's the part worth proving first.
01
Train the toy model
In plain words: first, teach a tiny neural network to add single digits — so we have some real trained numbers to turn into a circuit.
A single self-attention block (16-dim, one head, residual around attention) is trained on all 100 ordered digit pairs (0–9 + 0–9), predicting the sum as one of 19 classes. This is a memorization task by design — the physical circuit will only ever be asked about digits 0–9, so there is no "unseen input" to generalize to.
100%final accuracy
100epoch to 100%
0.00152final loss
100 / 100pairs correct
Training loss
Cross-entropy, log scale, 1500 epochs
Accuracy
Full 100-pair set, reaches 100% by epoch 100
02
Quantize to resistance
In plain words: turn each of those trained numbers into a resistor value. A resistor can't be negative, so every number is stored as a pair — one for the positive part, one for the negative — and the readout subtracts them.
Each trained weight is clipped to [−1, 1] and rounded to one of 15 signed levels (−7…+7, a 4-bit range). A resistor can only pass more or less current — never subtract — so every weight becomes a differential pair: a strong positive weight is a low-resistance path on the "positive" row and a near-open path on the "negative" row; a strong negative weight is the reverse. Resistance runs 10k–100kΩ, with levels mapped linearly onto conductance (1/R) — the domain the crossbar actually computes in. (The obvious linear-in-ohms mapping distorts every mid-range weight; see the Lab write-up.)
W_V — trained (float)
W_V — quantized (4-bit)
−max+max— hover any cell for its exact value
03
Wire the crossbar
In plain words: lay the resistors out in a grid. Voltages go in along the rows; the currents add themselves up down the columns — and that sum is the answer.
Simplified to 4 inputs × 1 differential output pair below (the real circuit is 16×16×2 = 512 resistors per matrix). Row voltages are the token's embedding; column buses are held at a virtual ground by an ideal 0V "ammeter" source — exactly what a real transimpedance readout amplifier does. That virtual ground is what makes each resistor see a clean Vrow − 0, so Ohm's law applies with no cross-talk between rows.
04
Simulate & compare
In plain words: run the actual circuit in a physics simulator and check its currents against the intended math. If they match, the design is wired right — this is the whole point of the report.
Three independent test cases — different weight matrix, different digit, different voltage scale — run through ngspice. Each column's current is compared against the same computation done in pure Python (Ohm's law over the identical quantized resistor values). Perfect agreement here means the wiring and sign convention are correct; any real gap would show up as points off the diagonal.
The whole pipeline is a handful of scripts — train, quantize to resistor values, emit a raw SPICE netlist, simulate, and diff the currents against a pure-software Ohm's-law model. The core of the recipe:
# emit a SPICE netlist for a weight matrix + input digit, then simulate
python3 gen_netlist.py W_V 7 1.0 # → W_V_digit7.cir (512 resistors)
ngspice -b W_V_digit7.cir # solve; print each column's current# or open the same netlist in xschem to inspect/redraw the schematic
xschem W_V_digit7.cir
05
Tiny Tapeout
Not started · parked
Real silicon, real money
Today's resistors are ideal SPICE elements — proving the math, not yet fabrication-ready. A real shuttle submission needs Sky130 physical resistor primitives (their achievable range, sizing, and matching tolerance all differ from an ideal 10k–100kΩ behavioral model), a GDSII export, and a paid submission (~$150–300) on a months-long shuttle schedule. That leg stays parked until the design clearly earns a fabrication slot.
See it working
This report is the physics check behind a live demo: run the calculator at the
Lab — the same architecture and resistor physics, retrained for all four operations.
Re-running this report's ngspice check on that calculator's own WV crossbar gives
0.0004% worst-case error across seven token inputs.