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×16 descending
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.

// minimise · one source, several possible substrates
problem max_cut(G) {
  state s: spin[G.vertices];
  minimise H(s) = sum((i,j) in G.edges, G.w[i,j] * s[i] * s[j]);
  return best(s), samples(s);
}

execution {
  stochastic     = allowed;
  objective_gap <= 0.02;
  confidence    >= 0.99;
  samples        = 2000;
}

thermodynamics {
  input_distribution = workload.max_cut;
  process_boundary   = encode_to_readout;
  coarse_graining    = logical_spin;
  lower_bound        = mismatch_cost;
}
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=200 if k == 0 else 0, 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 > 0 else "-" 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})")
output2026-10-27 · bench
---- measured 0.485  exact 0.450
++++ measured 0.455  exact 0.450
---+ measured 0.005  exact 0.008
--+- measured 0.000  exact 0.008
total-variation distance: 0.046  (sampling noise alone ~ 0.141)

4 · Memristor current–voltage characteristics evolve

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.

Device 23 · pins 8–16 · 2 V, 1 Hz · +290 / −530 µA · measured 2026-10-12
Device 22 · pins 6–14 · 1 V, 1 Hz · peak ≈4 µA · measured 2026-10-15

Driving a memristor through a multiplexer

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.

W20 … 5 V MUX2channel n memristor n collector 10 kΩ sense end B · V+bias 2.47 V scope 2: current
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 bench
emulated 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().

pip install git+https://github.com/vonneuro/messenger-api.git

github.com/vonneuro/messenger-api · method reference, HTTP endpoints, 16 example studies

editor · vs code 0.1.3

The VS Code extension

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.

code --install-extension vonneuro-vscode-0.1.3.vsix

Download the extension (.vsix, 37 kB) · source at github.com/vonneuro/vn-vscode · installs the Python client on first run if it is missing

browser · no install

Messenger and the Python lab

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.

Create an account · Python lab · guide

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.

Reservoirs · memristive networks · neural cultures

Mismatch cost

MCG(p,q) = D(p‖q) − D(Gp‖Gq) ≥ 0

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.

Static check · runtime assertion · stall escalation

Evidence

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.

SubstrateNative regimeEnergy evidenceExample
Memristive devices behind an analog multiplexerevolveTerminal energy per run, ∫V·I dt2, 4
Series-RC circuits behind an analog multiplexerevolveSource energy per run, ∫V·I dt1, 5
Hardware p-bit sampler, 16 p-bitssample minimiseNot instrumented3
CPU models — analog, memristor, p-bit, spikingsample evolveReference only5
GPU — CUDA, ROCm, Metal, Vulkanminimise sample evolveBoard telemetry
p-bit fabric on FPGAsample minimiseRail measurement
Quantum annealer / Ising machineminimise sampleService-reported
Event-driven neuromorphic ASICevolve sampleOn-chip counters
1T1R memristive crossbarminimise evolveRail measurement
Self-organising nanowire networkevolveProbe-limited
Photonic / analog linear operatorevolveModelled
Neuronal culture on a multi-electrode arrayevolve sampleMetabolic, modelled
Molecular / DNA reaction networksample evolveModelled
Engineered gene circuitsampleModelled

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.

$pip install git+https://github.com/vonneuro/messenger-api.git