A programming system for physical computing · in development
Minimise, sample, or evolve.
VonNeuro is a language, compiler and runtime for computation carried out by physical
dynamics: stochastic bits, annealers, memristive and analog circuits, neuromorphic chips
and, in principle, living tissue. A program states which of three tasks it poses —
minimising an objective, sampling a distribution, or evolving a driven system — together
with the conditions a result must satisfy and the thermodynamic boundary of the process.
The compiler maps that statement onto a device, or rejects it and names the condition the
device cannot meet.
Programs reach hardware through vonNeuro Messenger, a job server in front of connected
devices, from Python or from VS Code. The examples on this page were run on a laboratory bench
of memristors, RC circuits and a p-bit sampler, and their output is reproduced as printed.
Python API · VS Code extension · Messenger job server · VOLT language
relax · xbar0 · 16×16descending
Energy E—
Inv. temp β—
Settled in—
Substrate classes
p-bit fabric
quantum annealer
1T1R crossbar
nanowire mesh
event ASIC
photonic operator
neuronal culture
reaction network
FPGA
GPU · CUDA / ROCm / Metal / Vulkan
x86-64 · arm64 · riscv64
Approach
Three design choices the rest of the system follows from.
portability
Programs describe mathematics, not hardware
A program specifies a map, a distribution or a dynamical system, with tolerances. Each
backend lowers that specification to a native physical process — a coupling matrix, an
anneal schedule, a drive waveform — rather than emulating a processor on the device.
regime
Equilibrium is one case of three
Many physical systems compute without descending an energy function: they sample, or
they respond to a drive while dissipating. Sampling and driven dynamics are treated as
primary regimes with their own correctness tests, not as approximations to
optimisation.
accounting
Energy reported with its reference
An energy per task means little on its own. A run reports the dissipation measured at a
stated boundary together with a thermodynamic lower bound for the same process, and
declines the comparison when the two are not defined on the same boundary.
Regimes
The semantic core is kept small: every program reduces to one of these three.
Three computational regimes
Sampling and driven computation are often described as imperfect optimisation.
Here they are distinct problems with distinct correctness criteria: an objective gap for
minimisation, a distributional distance for sampling, and tolerances on declared observables
for driven dynamics.
minimise / relax
Descend a landscape
Objectives, ground states, fixed points. The program declares the energy and the
tolerated gap; the backend chooses the schedule and reports where it landed.
p-bits at low temperature · annealers · Ising machines · crossbars · GPU
sample
Draw from a distribution
Inference, marginals, low-energy ensembles. The contract is distributional — total
variation, effective sample size — so devices that generate randomness by different
mechanisms can be compared on the same test.
p-bits · annealers as samplers · stochastic devices · GPU chains · example 3
evolve / drive
Run out of equilibrium
Continuous-time dynamics under an external drive: reservoirs, memristive networks,
analog circuits, limit cycles and hysteresis. No energy function is required, and none
is assumed.
memristive networks · neuromorphic · analog · photonic · living tissue · examples 1–2
Language
Source files end in .volt and compile to a semantic graph (.vng).
VOLT 0.1 implements system programs, as in example 2; the other forms are specified.
VOLT: intent and contracts
A VOLT program has four parts: the mathematical intent, a computational contract
(what counts as a correct result), a thermodynamic contract (process boundary, input
distribution, coarse-graining) and the device requirements. The last two make it possible to
compare runs on different substrates — or to establish that they cannot be compared.
// sample · the target is a distribution, not a single answermodel infer(y, W, b) {
latent z: binary[W.rows];
energy E(z; y) = -0.5 * z' W z - b(y)' z;
sample z ~ boltzmann(E, beta);
return marginal(z), samples(z);
}
execution {
total_variation <= 0.05;
effective_samples >= 1000;
stochastic = required;
}
// the compiler does not assume annealer output is Boltzmann-distributed;// that claim has to come from the backend, with calibration evidence.thermodynamics {
input_distribution = data.y_distribution;
process_boundary = load_to_sample;
lower_bound = mismatch_cost;
}
// evolve · driven dynamics, never required to settlesystem classify_stream(u, target) {
state x: real[n] @node;
parameters theta: device_controls;
evolve dx/dt = F(x, u, theta) for T @drive(periodic, f=2.4kHz);
observe y(t) = C x(t);
train readout C tominimise loss(y, target);
}
execution {
latency <= 5ms;
accuracy >= 0.95;
device_noise = allowed; // hysteresis and memory stay in the model
}
thermodynamics {
process_boundary = initialise_to_readout;
time_partition = measurement_grid; // finer grid, tighter bound
coarse_graining = declared_observables;
steady_state = nonequilibrium; // no detailed balance assumed
lower_bound = mismatch_cost;
}
# illustrative ledger output · the figures are examples, not measurements
$ vnstat --run 4417
program max_cut substrate pbit0 (FPGA, 4096 p-bits)
regime minimise + sample boundary encode→readout
──────────────────────────────────────────────────────────────────────
samples 2,000 objective gap 0.014 contract met
wall 184 ms latency ok
──────────────────────────────────────────────────────────────────────
measured dissipation 41.2 mJ (board rail, ±3%)
mismatch-cost bound 9.7 mJ (T = 300 K, prior = anneal_family)
ratio to bound 4.2×
entropy production 0.043 k_B / update
bound status preserved through embedding
──────────────────────────────────────────────────────────────────────
vs run 118 (xbar0) not comparable — different process boundary
(readout coarse-graining differs; narrow or re-declare)
// VOLT 0.1 · compiled to .vng and run on a bench memristor in example 2system memristor_evolve(u: real) {
state material: adaptive;
evolve material for10 ms using drive.interval(u);
observe response: realfrom material.terminal_current;
return response;
execution {
intent = evolve;
output = response;
tolerance = 0.05;
}
device {
dialects = [dynamical, adaptive];
required_capabilities = [drive.interval, observe.terminals];
calibration = current_epoch;
}
thermodynamics {
process_boundary = "drive_to_observation";
energy_class = uncharacterized; // no bound claimed for this device yet
}
}
minimise · sample · evolve
The three intents of the semantic core. A program states which one it means, and the
conformance test follows from it: an objective gap, a distributional distance, or a
tolerance on declared observables.
execution { … }
What counts as a correct result: stochasticity, admissible approximation, precision,
confidence, latency. A backend that cannot meet it is rejected at compile time, with
the failing clause named.
thermodynamics { … }
The process model, input distribution, prior, boundary, time decomposition and
coarse-graining. Without these, an energy figure does not refer to a defined
process.
steady_state = nonequilibrium
The run is held away from equilibrium by an external drive. Detailed balance is not
assumed, so the analysis uses the driven forms and tracks entropy production rather
than energy descent.
Examples
Run on 27 October 2026 on the laboratory bench. Output reproduced as printed.
Worked examples on a laboratory bench
The bench used here is small and deliberately heterogeneous: sixteen memristive
devices and sixteen series-RC circuits behind two 16-channel analog multiplexers, measured
with an Analog Discovery Studio, and a hardware sampler of sixteen probabilistic bits (p-bits)
performing Gibbs updates. It is reached only through Messenger, as any connected device is.
Each script starts with a # vonNeuro line and runs
unchanged from VS Code, from the browser's Python lab, or from a terminal with
python -m vonneuro_messenger.lab script.py. The names
client (signed in), np and
plt are predefined.
1 · Time constant of an RC circuit evolve
A 1 V step is applied to RC circuit 2 for 400 ms, and the current is recorded as 24
window means. For a series RC circuit I(t) = (V/R)·e−t/RC, so a linear fit of
ln I against t gives τ = RC. The fit returns 143 ms. Over the 383 ms between the first and
last window centres, that τ predicts a decay from 759 µA to 52 µA; 50 µA was measured.
rc_tau.py
# vonNeuro# Charge RC coprocessor 2 with a 1 V step and estimate its time constant.for device in client.devices():
print(f"{device.ref:24s} {device.kind:10s} {device.status}")
reply = client.pulse("rc:2", 1.0, 400) # 1 V for 400 ms
i = np.array(reply["result"]["value"]) # 24 window means, A
t = (np.arange(len(i)) + 0.5) * 400 / len(i) # window centres, ms
ok = i > 0.05 * i[0]
slope, _ = np.polyfit(t[ok], np.log(i[ok]), 1)
print(f"\napplied {reply['pulse']['applied_v']:+.3f} V, "f"I(0) = {i[0]*1e6:.1f} uA, I(end) = {i[-1]*1e6:.2f} uA")
print(f"tau from log-linear fit: {-1/slope:.0f} ms")
print(f"energy at the terminals: {reply['measured_energy_j']*1e6:.2f} uJ")
output2026-10-27 · bench
admin/memristor-array memristor online
admin/memristor-d22 memristor disabled
admin/mux16 rc online
admin/pbit pbit online
admin/sim-rc rc offline
applied +1.000 V, I(0) = 759.2 uA, I(end) = 49.97 uA
tau from log-linear fit: 143 ms
energy at the terminals: 104.37 uJ
2 · A VOLT program and a device contract evolve
The program declares that it needs an adaptive state — the internal variable of a
memristor — driven over an interval and observed at the terminals. On memristor 15 it
compiles and runs: u = 0.5, which is 0.15 V across the device, held for 10 ms gives a
terminal current of 28.6 nA (about 5 MΩ) and 43 pJ delivered at the terminals, equal to
V·I·t. Submitted to an RC circuit, the same program is rejected at compile time, before any
voltage is applied, because the RC backend's contract does not include the adaptive
dialect.
volt_memristor.py
# vonNeuro# A VOLT 0.1 program: drive a memristor for 10 ms and observe its terminal current.
program = """
system memristor_evolve(u: real) {
state material: adaptive;
evolve material for 10 ms using drive.interval(u);
observe response: real from material.terminal_current;
return response;
execution { intent = evolve; output = response; }
device { dialects = [dynamical, adaptive];
required_capabilities = [drive.interval, observe.terminals]; }
thermodynamics { process_boundary = "drive_to_observation"; }
}"""for target in ("memristor:15", "rc:2"):
reply = client.volt_run(program, {"u": 0.5}, target)
if reply.get("ok"):
print(f"{target:13s} response {reply['result']['value']:+.3e} A, "f"energy {reply['measured_energy_j']:.2e} J")
else:
print(f"{target:13s} refused: {reply['error']}")
output2026-10-27 · bench
memristor:15 response +2.856e-08 A, energy 4.27e-11 J
rc:2 refused: device.dialects: adaptive not a dialect of rc (dynamical)
3 · Sampling a ferromagnetic ring sample
Four p-bits on a ring with unit ferromagnetic couplings, at T = 1. The exact Boltzmann
distribution follows from enumerating the 16 configurations: the two ground states,
all up and all down, each carry probability 0.450. From 200 hardware samples (200 burn-in
sweeps, then 10 sweeps between samples) the total-variation distance to the exact
distribution is 0.046, below ½√(16/n) ≈ 0.14, an upper estimate of the distance expected
from finite sampling alone.
pbit_tv.py
# vonNeuro# Gibbs-sample a 4-spin ferromagnetic ring on the hardware p-bit sampler# and compare with the exact Boltzmann distribution.import itertools
from collections import Counter
J = [[0, 1, 0, 1], [1, 0, 1, 0], [0, 1, 0, 1], [1, 0, 1, 0]]
h, T, n = [0, 0, 0, 0], 1.0, 200
client.nano("connect", mode="serial", port="COM3")
try:
client.nano("load", J=J, h=h, temperature=T, seed=11)
samples = [tuple(client.nano_sample(burn_in=200if k == 0else0, thin=10)["spins"])
for k in range(n)]
finally:
client.nano("disconnect")
E = lambda s: -0.5 * sum(J[a][b] * s[a] * s[b] for a in range(4) for b in range(4))
w = {s: np.exp(-E(s) / T) for s in itertools.product((-1, 1), repeat=4)}
Z = sum(w.values())
counts = Counter(samples)
tv = 0.5 * sum(abs(counts[s] / n - w[s] / Z) for s in w)
for s in sorted(w, key=w.get, reverse=True)[:4]:
print("".join("+"if x > 0else"-"for x in s), f"measured {counts[s]/n:.3f} exact {w[s]/Z:.3f}")
print(f"total-variation distance: {tv:.3f} (sampling noise alone ~ {0.5*np.sqrt(16/n):.3f})")
Sine sweeps from 0.25 to 2 V and from 1 Hz to 1 kHz, one terminal pair at a time, with
the device wired directly to the instrument. The curves are phase-averaged measurements:
current against device voltage, pinched at the origin, with loop area decreasing as the
drive frequency increases — the signature of a state-dependent resistance. The same
sweep is available through the API as client.memristor_array(channel,
amplitude, frequency), limited to ±1 V on the multiplexed array.
An analog multiplexer passes only voltages between 0 V and its 5 V supply, while a
memristor has to be driven in both polarities to show hysteresis. The far end of every
memristor is therefore held at 2.47 V, and the generator swings around that level instead
of around zero.
W2 above the bias
Positive voltage across the device; current flows towards end B.
W2 at the bias
0 V across the device. The generator rests here whenever a channel is selected, so a memristor never sees the −2.47 V a 0 V generator would apply.
W2 below the bias
Negative voltage; current flows back towards the multiplexer.
sweep: W2 = 2.47 V + A·sin(2πft), A ≤ 1 V · device voltage = W2 − 2.47 V − I·10 kΩ ·
current = scope 2 / 10 kΩ, relative to its open level · any W2 output outside 0 … 5 V is refused
5 · Allocation between a device and its model
A small game serves as a test of scheduling across measured and modelled devices. Each
enemy's motion is computed by one of the bench's RC circuits, measured live from the
player's position (label LIVE). When the circuit is busy, or in the
fully emulated run, a model of the same circuit calibrated on earlier measurements is used
instead (EMU). Same level, same route, 30 s each, recorded in real time.
live RC circuits measured on the benchemulated calibrated models, no hardware
The browser game is available on
request from the Play page, with a Messenger account.
Access
Hardware stays in the laboratory that owns it; programs reach it as jobs.
Messenger, the Python API and VS Code
Messenger is the job server between users and hardware. It authenticates each
request, checks it against the limits declared for the device, queues it on the shared
instrument, meters the time used, and returns the result with its raw data, settings and
device identity. A small agent on the computer next to the hardware connects it to the
server, so a device does not have to be redesigned to be reachable.
python · open source, MIT
The Python API
vonneuro_messenger is a thin client over Messenger's HTTP API,
with httpx as its only dependency. The examples above use five
of its calls: devices(), pulse(),
volt_run(), nano_sample() and
memristor_array().
Sign in once; the session is kept in VS Code's secret storage. A Python file that begins
with # vonNeuro gets a Run on vonNeuro action: the script
runs with your own Python, figures open beside the code, and the status bar shows each
job, its queue time and its time on the hardware. The sidebar lists devices, recent jobs
and the nodes you administer.
The Messenger dashboard submits jobs and returns their files. Its Python lab runs the same
scripts in the browser (Pyodide), with plots and downloads; run results there are deleted
after 24 hours, while saved scripts are kept. Every account starts with one hour of bench
time.
volt_runA VOLT 0.1 program on a device that supports VOLT, such as memristor:N or rc:N on the bench. pulse() is a one-step program of this kind.
messenger_instrumentA capture from the instrument, including guarded I–V sweeps of any memristor in the array.
volt_processAn installed VOLT processing program applied to your inputs; returns result.json.
device runAny operation a connected device declares, with typed, bounded parameters: client.device("owner/slug").run(operation, ...).
Connecting other hardware
Messenger is a network: any laboratory can connect its own devices. An agent on the computer
next to the device dials out to Messenger, publishes the operations the device offers with
their parameter ranges, and executes incoming jobs through the owner's driver, which decides
what is safe. The bench above is one node on this network; every device has a page with its
status and usage.
connect
The existing control stack is kept: a short driver exposes only the operations and measurements the device already supports.
describe
Each operation declares typed, bounded parameters; Messenger checks them, and the driver checks every job again.
share
New devices start private. Owners open them to every account or grant time to selected users.
Messenger provides the endpoint, registry and job path. Scientific claims about a
device rest on its declaration and on the evidence its measurements provide.
From a new device to a backend
Many candidate processors have no settled programming model. A prototype can start with a
partial device contract; characterisation runs then show which controls, observables and
dynamical regimes are reproducible enough to support computation, and a digital reference
shows what the device gains and where it departs from the intended model.
discover
Explore relaxation, sampling and driven regimes to find a usable computation in a new physical device.
characterise
Map response, noise, drift, stability, latency and energy across the operating range.
develop
Refine controls, calibration and compilation together, then fix the result as a versioned backend.
The same path applies to a one-off research chip, to successive fabrication
revisions and to a production device: connect it, measure it, formalise its contract, and
test the hardware against executable computational criteria.
Architecture
L5 → L0: program meaning at the top, physical process at the bottom.
The stack, from meaning to matter
Compilation here is not translation into machine instructions. It is a
model-conditioned transformation: program meaning, contracts and the current device model
produce an executable physical process, together with a record of the assumptions it
required.
L5
VOLT
volt · voltc
State spaces, intent, and three contracts: what counts as correct, which thermodynamic
description holds, and what the device must support.
L4
Semantic graph
.vng
A serialisable representation of maps, dynamics, distributions, objectives,
measurements and their composition: the portable object, independent of any one front
end.
L3
Passes
check · analyse · lower · partition
Contract checking (the refusal in example 2), the thermodynamic analysis pass, algorithm
selection, and partitioning across modalities with conversion and communication costs
made explicit.
L2
Kernel
vnk
Event-driven scheduling, capability-based device isolation, the device registry, remote
endpoints, and the ledger that meters each run. Messenger implements the remote job path,
registry and metering of this layer.
L1
Device contract
vn-hal/*
A versioned, machine-readable statement of native operations, connectivity, parameter
ranges, noise, timing, calibration state and available energy measurements, graded by
maturity A0–A5. Compilation targets a device instance; when its calibration
drifts, the program is recompiled and its source is unchanged.
L0
Matter
fluctuations, conductances, light, cells
Stochastic bits, annealing hardware, adaptive networks, spiking silicon, analog and
photonic operators, cultured neurons and reaction networks — or a digital reference that
simulates any of them as a baseline.
Physics
Each result is stated with the conditions under which it holds.
Learning rules and thermodynamic accounting
Backpropagation requires an exact reverse pass over stored activations, which
physical systems do not provide. The rules below use only what such systems do natively:
relax, fluctuate, respond to a drive, and dissipate.
Contrastive equilibrium learning
ΔW ∝ (1/β) [ ⟨s sᵀ⟩clamped − ⟨s sᵀ⟩free ]
Settle twice and subtract the correlations. Every term is local to the coupling, so the
update can be computed in the array without storing activations.
Applies to relaxation programs only
Driven, dissipative learning
σ = Σt J(t)·F(t) / T > 0
A system held out of equilibrium has no free phase to contrast against. Updates come from
correlations along the driven trajectory, and the cost is a steady entropy production
rate rather than a descent that terminates.
For a fixed stochastic map G, the actual input distribution p and a process-dependent
prior q, the contraction of the KL divergence lower-bounds entropy production. It is the
first analysis the compiler implements, and the reason the prior must be declared.
Reported in kB, and in joules at a stated T
Hysteresis as model state
Wdiss = ∮ h dm · ẋ = αx − βV(t) + ξ(t)
Memristive elements have internal state, drift and loop area (example 4). They are
modelled explicitly, and pulse trains are planned against the device's own dynamics
rather than assuming a write lands where it was aimed.
Calibrated at start-up · re-fitted on drift
Noise as a resource
⟨e−βW⟩ = e−βΔF
Fluctuation theorems of Jarzynski–Crooks type turn device noise into free-energy
estimates, so the runtime can sample with the hardware's randomness instead of spending
energy to suppress it.
Anneal schedules · free-energy readout
Verified descent, where it applies
dE/dt ≤ 0 · ‖st+1 − st‖ → 0
For programs that declare an energy, the compiler checks the update against it and emits
runtime assertions where it cannot decide statically. Programs that declare no energy get
tolerances on observables instead of an assumed Lyapunov function.
At each step of compilation the declared bound is kept, recomputed, strengthened or weakened.
Thermodynamic provenance through compilation
A provenance graph accompanies the program through compilation. At each
transformation the compiler records whether the declared bound still holds. A comparison that
turns out to be ill-defined is reported as a result in its own right.
Source
Declared boundary
Input distribution, prior family, process start and end, coarse-graining.
bound stated
Lowering
Minor embedding
Logical spins become chains of physical qubits; the state space changes shape.
recomputed
Schedule
Finer time grid
Evaluating the process at higher temporal resolution tightens the bound.
strengthened
Readout
Spatial coarse-graining
Variables removed at readout need justification from the backend before bounds can be compared.
weakened
Compare
Across substrates
Different boundaries or priors mean the two energy figures do not describe the same process.
not comparable
Wetware
The device contract asks for state, drive and noise characterisation; biological systems can provide all three.
Biological substrates
Nothing in the semantic core assumes silicon. A backend qualifies if it can
report its state, accept a drive and characterise its own noise. Neuronal cultures, reaction
networks and gene circuits can meet these requirements; they differ from electronic devices
mainly in timescale, in drift, and in how well their dissipation can be measured.
neuronal culture
Neurons on an electrode array
State
Population activity across the electrodes
Drive
Closed-loop stimulation, patterned in time
Readout
Declared observables over a response window
Non-stationary over hours: the device contract is re-fitted continuously.
reaction network
Molecules in solution
State
Species concentrations, strand occupancy
Drive
Fuel species, temperature, flow
Readout
Fluorescence or sequencing at declared times
Seconds to hours per run: latency, not accuracy, is the binding constraint.
gene circuit
Regulation inside cells
State
Expression levels across a population
Drive
Inducers, light, growth conditions
Readout
Distributional — the population is the sample
Intrinsically stochastic, so it maps naturally onto a sampling contract.
Biological backends are held to the same contracts. Their dissipation is
metabolic and can only be estimated, so the ledger marks it modelled
rather than measured and declines comparisons across boundaries it cannot justify. Protocol,
provenance and approval constraints are part of the device contract.
Substrates
Native regime and the kind of energy evidence each class can provide.
Substrate classes
How each class maps onto the three regimes, and whether its dissipation can be
measured at a rail or terminal, is reported by a service, or can only be modelled. The first
rows are the bench devices used in the examples.
Substrate
Native regime
Energy evidence
Example
Memristive devices behind an analog multiplexer
evolve
Terminal energy per run, ∫V·I dt
2, 4
Series-RC circuits behind an analog multiplexer
evolve
Source energy per run, ∫V·I dt
1, 5
Hardware p-bit sampler, 16 p-bits
sampleminimise
Not instrumented
3
CPU models — analog, memristor, p-bit, spiking
sampleevolve
Reference only
5
GPU — CUDA, ROCm, Metal, Vulkan
minimisesampleevolve
Board telemetry
p-bit fabric on FPGA
sampleminimise
Rail measurement
Quantum annealer / Ising machine
minimisesample
Service-reported
Event-driven neuromorphic ASIC
evolvesample
On-chip counters
1T1R memristive crossbar
minimiseevolve
Rail measurement
Self-organising nanowire network
evolve
Probe-limited
Photonic / analog linear operator
evolve
Modelled
Neuronal culture on a multi-electrode array
evolvesample
Metabolic, modelled
Molecular / DNA reaction network
sampleevolve
Modelled
Engineered gene circuit
sample
Modelled
The GPU as reference backend
Large instances fit on GPUs, and every physical run needs a digital baseline to be compared
against. All three regimes are native there.
minimise
Many instances relaxed at once; anneal schedules run as batched tensor
operations, with the objective gap checked per instance.
sample
Independent chains in parallel, held to the same distributional contract
a p-bit fabric has to meet.
evolve
Stiff and non-stiff integrators for driven dynamics, on the declared
measurement grid so that the time decomposition stays valid.
Board telemetry feeds the ledger, so GPU runs carry measured dissipation rather
than a model, which is what makes them usable as a baseline.
The client, the server and the bench tools are written in Python; the server runs
wherever Python and FastAPI do. Neuromorphic backends are intended to lower through existing
intermediate representations rather than around them.
Run the examples.
Create an account, install the Python client or the VS Code extension, and run the scripts
above on the bench. New accounts include one hour of bench time.