Paper 2 of 3, DMCI

Compile Once, Differentiate Everywhere

A Differentiable Meta-Circular Interpreter

because the interpreter, not the program, is the differentiable object.

Lucas Sheneman Institute for Interdisciplinary Data Sciences, University of Idaho

Abstract

Most differentiable-programming systems differentiate a program. DMCI differentiates an interpreter. We compile a substantial, self-hosting subset of Scheme into differentiable computation graphs; because the subset is expressive enough to compile its own evaluator, this yields Differentiable Meta-Circular Interpretation: a compiled Scheme interpreter executes programs supplied as data while reverse-mode autodiff propagates gradients through the interpreter to the continuous constants embedded in those programs. The interpreter is compiled once, so new programs inherit differentiability with no recompilation, no custom gradient machinery, and no per-program reimplementation, while retaining closures, recursion, and data structures. Gradients through the compiled interpreter are correct almost everywhere and match direct compilation to numerical precision across 171 recursive and higher-order program–seed pairs. We then use DMCI for program-and-parameter co-search: a language model proposes Scheme programs and exact gradients calibrate their continuous parameters through a single frozen interpreter, recovering a knee-like degradation law on battery capacity-fade data and optimizing an interpreted Kalman-filter likelihood for an El Niño inverse model where gradient-free search fails.

This page is an interactive companion to the paper. The figures below run a real implementation of the compiler pipeline and a reverse-mode autodiff engine in your browser; every chart in the Results section is drawn from the paper's committed data.

program as data P(θ) compiled interpreter differentiable graph · compiled once loss  L ∇θ, gradients reach the constants inside the program
Figure 1. The interpreter, not the program, is the differentiable object. Compile the interpreter once; every program it runs is supplied as input data, yet gradients still reach the learnable constants θ inside that program, no recompilation, no custom gradients, no per-program reimplementation.
What is actually new?
Most differentiable-programming systems differentiate a program
vs
DMCI differentiates an interpreter

The compiled object is a reusable Scheme evaluator. New programs are supplied as data and automatically inherit differentiability, without recompilation, custom gradients, or reimplementation.

1 · Motivation

Known structure, unknown constants

Scientists often know the form of a model (Coulomb's law, a decaying exponential, a coupled recurrence) but not its exact constants, and sometimes not even which structural variant is right. DMCI does not compete head-to-head with neural networks; it makes executable scientific models directly optimizable against data, and searchable.

Existing approaches

Fixed scientific code

Exact structure, but its constants are tuned by hand. You cannot gradient-optimize the unknowns; every parameter becomes a manual sweep.

exact · manual tuning

Neural networks

Learned structure, but an opaque internal representation of physics you may already know. Data-hungry, and weak at extrapolation.

learned · opaque

Differentiable frameworks

Exact gradients (PyTorch, JAX, Zygote, Enzyme), but each model must be re-implemented in the framework before it can be differentiated.

exact · per-model effort
DMCI

Compile a differentiable interpreter once. Express scientific models as executable programs. Optimize their continuous parameters directly through interpreted execution, and let a search propose the structures.

  • One compiled evaluator serves every runtime-supplied program, no per-model reimplementation
  • Exact reverse-mode gradients reach the constants inside any program it runs
  • Programs are data, so a search loop can explore structures while gradients fit parameters

Structure frozen, constants learned

A program's structure (its control flow, recursion, composition) stays fixed while its numeric constants become trainable parameters fit by gradient descent. This erases the boundary between fixed scientific programs and gradient-optimized parameters. Where exact structure is known, that structural inductive bias helps: in one inverse heat-equation benchmark from the Neural Compiler paper, encoding the known equation let gradient descent recover the diffusivity accurately where a physics-informed neural-network (PINN) baseline struggled, an illustration of structural prior, not a claim of universal superiority over PINNs.

2 · Why DMCI is different

Where is differentiation located?

Existing systems differentiate an individual model or program; each new model must be re-implemented in the framework first. DMCI is best understood by where it locates differentiation: the differentiated object is a compiled, self-hosting interpreter.

SystemWhat is differentiated?New model needs reimplementation?
PyTorchIndividual modelYes
JAXIndividual programYes
ZygoteIndividual programYes
EnzymeIndividual programYes
Differentiable interpretersHand-built interpreterOften (hand-engineered)
DMCICompiled meta-circular interpreterNo

DMCI moves differentiation from the model level to the interpreter level, the central conceptual shift of the paper.

3 · Flagship capability

Program-and-Parameter Co-Search

A language model (e.g. via OpenEvolve, an open implementation of AlphaEvolve, Google DeepMind's Gemini-powered coding agent) searches the discrete space of program structures; DMCI's exact gradients search each program's continuous parameters. Because every runtime-supplied program inherits differentiability from one frozen interpreter, the two close into a single joint structure-and-parameter discovery loop, with no per-program reimplementation.

discrete structure search continuous parameter search LLM / OpenEvolve propose program structure DMCI exact gradient calibration evaluate held-out forecast skill program as data fitted constants fitness → select best structure, refine, repeat
Figure 2. The co-search loop. A non-differentiable outer search proposes program structures; an exact-gradient inner loop (DMCI) calibrates each candidate's constants; held-out skill scores it and feeds back. The inner calibration is exact and reimplementation-free for any structure the outer loop proposes.

What gradients optimize

  • Continuous constants
  • Tensor parameters
  • Numerical coefficients

Optimized by DMCI's exact reverse-mode gradients, taken only with respect to the bound parameter tensors.

What gradients do not optimize

  • Program syntax
  • Control-flow structure
  • Interpreter dispatch

Structure is searched externally, by an LLM, OpenEvolve, or evolutionary search. Keeping the two distinct is essential.

Battery capacity fade: co-search recovers a degradation law Exp L

From a smooth square-root seed (which cannot express a "knee"), the co-search rediscovers a knee-capable structure from the generating family on mechanism-labeled synthetic cells, and improves held-out extrapolation on real Severson lithium-ion cells. Held-out RMSE after a rigorous 300-step DMCI calibration of each candidate (lower is better).

Synthetic (knee target)Knee?RMSE
Smooth √t seedno0.0301
Hand two-reservoir (true family)yes0.0143
Hand sigmoidal kneeyes0.0149
Co-search (recovered)yes0.0111
Real Severson cellsLateEarly
Persistence (naive)0.0840.089
Linear extrapolation0.0800.078
Best smooth family (DMCI)0.0830.086
Best hand knee (DMCI)0.0510.057
Co-search (selected, DMCI)0.0510.034

Neither alone suffices. Holding the discovered structure fixed and replacing DMCI's exact gradients with gradient-free search makes held-out skill collapse (9–27× worse): OpenEvolve proposes the structure; DMCI's exact gradients are what make it forecastable.

El Niño (LIM-ENSO): an interpreted Kalman-filter likelihood Exp C

The entire Kalman-filter negative-log-likelihood of a Linear Inverse Model of ENSO is written once as a 17-line Scheme program; DMCI yields exact gradients of the likelihood (58–611 free parameters). Gradient optimizers recover stable operators where gradient-free differential evolution fails. All solvers optimize the same compiled objective.

SolverTrain NLL (D=6)Train NLL (D=20)ACC@3ρ(F)Stable
Exact-grad Adam (DMCI)−2060−44940.900.96Yes
Multi-start L-BFGS (DMCI)−2065−45510.900.96Yes
Differential evolution (gradient-free)+636+113830.871.08No
Green-function (reference)n/an/a0.910.96Yes

The fitted operator carries the canonical ENSO signature and forecasts held-out Niño-3.4 anomaly correlation 0.90 / 0.65 / 0.50 / 0.51 at the 3 / 6 / 9 / 12-month leads, beating persistence at every lead and matching a hand-built Green-function reference, with zero per-structure implementation.

The fitness-fidelity caveat

Co-search can overfit its own proxy: a parallel influenza study (FluZoo) found that an under-converged inner fit flattered a more flexible program, an advantage that vanished under rigorous re-scoring. The honest scope is a pair: co-search recovers correct structure when the data discriminate it, and degrades to the baseline when they do not.

4 · Background

A program is an expression

The system speaks a small subset of Scheme, a minimalist Lisp. A program is an S-expression: a parenthesized tree that evaluates to a number. Edit the program or its inputs and run it; this interpreter runs entirely in your browser.

The pieces you'll need

Everything in this tutorial is built from these forms. Click a chip to load it into the editor.

(+ - * /)arithmetic
(if t a b)conditional
(let ((x 1)) …)local binding
(lambda (x) …)function / closure
(define (f x) …)named definition
sin cos exp log √math primitives
Figure 3. A live Scheme read–eval loop. Coulomb's law with an unknown constant k reads (/ (* k (* q1 q2)) (* r r)). In the next section we will learn k from data by descending through this exact expression.
5 · The compiler

Parse → ANF → Compute Graph

To make a program differentiable, the compiler lowers it through three stages. Type a program below and step through each one; the highlighting links every stage to the next.

Figure 4. The three lowering stages. Source text is parsed to an AST, rewritten to A-Normal Form so each binding maps 1:1 to a node, then realized as a dataflow graph that autograd can traverse.
1

Parse

A hand-written tokenizer + recursive-descent parser turns text into an immutable AST. Surface forms like cond, let* and when desugar to a small core.

2

A-Normal Form

Every compound subexpression is hoisted into a named let-binding, so each binding maps 1:1 to a graph node. Think "SSA for functional languages": showing your work line by line.

3

Compute Graph

Each binding becomes a node in a dataflow DAG. Walk it in PyTorch and, because every primitive is differentiable, autograd flows from a loss back to the constants.

6 · Differentiation

Gradient descent through a program

Here the idea is made concrete: given data and a program with an unknown constant, the interactive figure below runs real reverse-mode autodiff, in your browser, on the graph just built, and the constant converges to its true value. This is the same procedure the paper uses, at smaller scale.

Figure 5. Reverse-mode autodiff fitting a program's constant to data, in the browser. The model contributes zero approximation error on its valid domain; all the error lives in the single constant being learned.

Why this beats a neural network here

The program contributes zero approximation error on its valid domain; all the error lives in the one constant we're learning. That is why a model with 1–4 parameters routinely beats an 8,500-parameter MLP, and why it extrapolates. On the LLM-generated benchmark, the compiled model's loss is up to 4,100× lower than a pure MLP, which fails outright on Coulomb's 1/r² singularity.

7 · The core idea

Don't compile the program. Compile the interpreter.

The original Neural Compiler compiled each program on its own. But the compiler is expressive enough to compile something far more powerful: a Scheme interpreter written in Scheme. Compile that once, and every program becomes data you hand to it.

Figure 6. Direct compilation (left) builds a new graph per program; DMCI (right) compiles the interpreter once and treats each program as data, reusing the same graph. Feed it a new program to compare.

The interpreter is differentiable, not the model

"The model is not differentiable; the interpreter is." A scientific model expressed as a Scheme program is not itself a differentiable computation graph; it is input data to the compiled interpreter, which is a differentiable computation graph. Because the interpreter is compiled once and verified once, any program it evaluates automatically inherits differentiability. Gradients flow through the interpreter's dispatch logic, environment management, and heap operations to reach the learnable parameters θ inside P(θ).

A real, self-hosting interpreter

The evaluator below is the actual core of bootstrap/compiler.scm: 288 lines of Scheme. The compiler turns this into the differentiable graph. Notice it is just a cond over expression shapes: a number evaluates to itself, a symbol looks up the environment, a list dispatches on its head.

Self-hosting is the proof of expressiveness: the compiler can compile its own evaluator, which can then evaluate Scheme, including itself.

288lines of Scheme
4backends (PyTorch diff.)
1,225tests
bootstrap/compiler.scmscheme
(define (scheme-eval expr env)
  (cond
    ((number? expr) expr)                 ; numbers self-evaluate
    ((symbol? expr) (env-lookup expr env)) ; variables → environment
    ((pair? expr)
     (let ((head (car expr)))
      (cond
        ((eq? head 'quote) (car (cdr expr)))
        ((eq? head 'if)
         (let ((t (scheme-eval (car (cdr expr)) env)))
           (if t (scheme-eval (car (cdr (cdr expr))) env)
                 (scheme-eval (car (cdr (cdr (cdr expr)))) env))))
        ((eq? head 'lambda)
         (list 'closure (car (cdr expr))   ; capture params,
               (car (cdr (cdr expr))) env)) ; body, & environment
        (#t (eval-apply head (cdr expr) env)))))
    (#t 0)))

Compile once. Ship it. Run any program.

compile-once.pypython
from neural_compiler import compile_interpreter, evaluate_program, save_compiled, load_compiled

interp = compile_interpreter()             # compile the evaluator ONCE
save_compiled(interp, "interpreter.ncg")   # portable JSON artifact, ship it anywhere

interp = load_compiled("interpreter.ncg")  # consumer: no Scheme toolchain, no recompilation
y = evaluate_program(interp, "(* a (exp (* (- 0 b) x)))", {"x": 1.5, "a": 2.5, "b": 0.8})
z = evaluate_program(interp, "(+ (* a x) (* b (* x x)))", {"x": 2.0, "a": 3.0, "b": 1.5})
# Two different programs, same compiled graph. Gradients flow to a, b in both.
8 · Mechanisms

Three mechanisms keep it differentiable

For autograd to flow through an interpreter, every value, every memory operation, and every branch has to stay on the differentiable path. Three mechanisms make that true.

01

Tagged values

Every runtime value, a number, a boolean, a pair, even a closure, is the same shape: a fixed 14-dimensional tensor. The first 10 dimensions are a one-hot type tag that routes dispatch; the last 4 are a payload. Gradients flow through the payload while the tag merely decides what to do. Click a type to inspect it.

02

A write-once heap

Pairs, lists and closures live on a dictionary-backed heap keyed by integer addresses. Because the language is pure, every slot is written exactly once and never mutated. PyTorch's autograd version-counter is never tripped, so the gradient chain survives arbitrarily many cons / car / cdr operations.

Step through building a list on both a write-once heap and a naive mutating buffer, and watch the autograd chain stay intact on one and shatter on the other.

03

Soft control flow

For non-recursive conditionals whose branches are both total, if is a differentiable multiplexer: sel·then + (1−sel)·else (recursive conditionals use lazy evaluation to bound depth). Branch decisions are made by program structure, not by the constants we're learning, so the shape of the autograd tape stays fixed even as parameter values change. Slide the test value to see the gate blend continuously.

Figures 7–9. The three mechanisms, interactive. Left to right: the 14-dimensional tagged-value layout, a write-once heap versus a mutating buffer, and the soft if as a differentiable multiplexer.

The honest caveat

If a learnable parameter sits inside a branch condition, say (< x α), the comparison is a hard 0/1 step and α receives zero gradient. The compiler doesn't introduce this; it inherits the source program's own non-differentiability. This is a real boundary, not a bug, and the paper demonstrates it directly.

9 · Guarantees

Compile the interpreter once, verify once

Because correctness is established for the interpreter, it holds for every program the interpreter runs. Three theorems, stated over a 13-primitive core language.

Theorem 1

Compilation correctness

Every supported program, run by the compiled graph, produces the same value as the source semantics, to floating-point precision. Proved by structural induction. Empirically, compiled-interpreter and direct runs are bit-for-bit identical.

Theorem 2

Gradient correctness a.e.

For learnable constants θ, the gradient through the compiled graph equals the true gradient for almost every θ, a set of full Lebesgue measure. The exceptions are a measure-zero union of branch boundaries, where the program inherits the source's own non-differentiability.

Theorem 3

Composition preservation

Compose two almost-everywhere-gradient-correct programs and the result is too. The boundary set stays measure zero. This is what makes runtime composition of models trustworthy.

Why "almost everywhere" matters

Gradients are exact on the open trace-constant region Θtc, where the discrete execution trace (which branches are taken, which tags dispatch) doesn't change with θ. Drag the point around the parameter space: inside a region the gradient is well-defined; crossing a boundary, a branch flips and the gradient is undefined exactly there.

The reassuring part: this region has full measure. A random initialization lands inside it with probability 1, and gradient steps keep you there until you deliberately cross a decision boundary.

Correctness ≠ success. Compilation makes the gradients correct; recovering a constant still depends on the loss landscape and the optimizer. Gradient descent is not magic, but now it is available, everywhere the math allows.

Figure 10. The parameter space (θ₁, θ₂). Branch boundaries (dashed) partition it into trace-constant regions of full measure; the gradient is well-defined everywhere except on the boundaries themselves.
10 · Results

Results, organized by claim

Twelve experiments support five claims, from gradient fidelity to real-data discovery. Every chart is drawn from the paper's committed data (hover for exact values); the flagship co-search results appear in §3 above.

1Interpreter execution preserves gradients Exp A & C

Across 171 (program, seed) pairs, DMCI matches direct compilation to zero relative gradient error and within 7×10⁻⁷ final loss: DMCI, direct compilation, and a hand-coded PyTorch interpreter trace the same convergence curve. A pure MLP, given the same task, diverges by orders of magnitude.

Convergence: recursive model C08 (cascaded EMA)
log loss vs epoch · DMCI = direct = hand-coded, MLP ~1,100× worse
Wall-clock by method Exp A
seconds to converge, log scale · autograd methods vs gradient-free

2Generated programs become optimizable Exp B

A constrained, prompt-refined LLM produced compilable Scheme for all 15 model descriptions; with no per-program implementation, DMCI matched direct compilation on every one (75/75 zero loss difference). A pure MLP fails badly where structure matters; note the log scale.

Final loss per model: DMCI vs pure MLP
15 LLM-generated models · lower is better · log scale

3Real scientific programs work Exp C · LIM-ENSO

An interpreted Kalman-filter likelihood for a Linear Inverse Model of ENSO (58–611 parameters) is optimized by exact gradients through the interpreter, recovering stable operators and held-out Niño-3.4 skill where gradient-free search fails. See the LIM-ENSO table in §3 ↑

0.90
held-out ACC@3 (3-month lead), matching a hand-built Green-function reference
611
free parameters fit by exact gradients (at D=20)
0.96
spectral radius ρ(F) < 1 (stable); gradient-free search gives 1.08 (unstable)

4Co-search discovers structure Exp L · Battery

LLM-and-DMCI co-search recovers a knee-like degradation law from a smooth seed on mechanism-labeled synthetic cells, and improves held-out extrapolation on real Severson lithium-ion cells. See the battery tables in §3 ↑

0.0111
synthetic held-out RMSE, below the smooth seed (0.0301) and hand-written knee baselines
1.7×
better early-tail forecast than the best hand-crafted model on real cells
9–27×
worse without DMCI's exact gradients (gradient-free ablation)

5Interpreter overhead can be amortized Exp H

A single DMCI evaluation is ~14× slower than direct compilation. But that is a latency cost, not a throughput cost: over 99.8% of sequential time is Python bookkeeping paid once per batch. Batching amortizes it into an 875× throughput speedup at batch size 1024 (and a 3,848× population-optimization speedup).

Throughput scaling DiffSoc-S
ms per evaluation & speedup vs batch size · log axes
Where the per-eval time goes
overhead decomposition · raw arithmetic is just 0.1%

Further evidence: compile-once scaling, robustness, and discrete-search limits Exp E, I, J

Compiling the interpreter is O(1) in program size, while per-program approaches scale linearly, a flat-versus-linear crossover. DMCI is also the most robust optimizer as models grow, but it is deliberately not a discrete program-synthesis engine.

Compile time vs size Exp J
DMCI O(1) vs baselines O(N) · log scale
Recovery error vs model size Exp I
DMCI stays robust where L-BFGS diverges
Discrete operator recovery Exp E
structure search is NOT DMCI's job
100%
autograd convergence · 171/171 runs (Exp A·B·C)
4,100×
lower loss than MLP (model M14)
3,848×
population-optimization speedup
0
relative gradient error vs direct
11 · Limitations

Current limitations

DMCI is a specific design point with honest boundaries. Gradient correctness is guaranteed; everything below is the scope around it.

scope Continuous parameters only

Gradients optimize numeric constants, tensor parameters, and coefficients, not program syntax. Structure is searched externally.

scope Structure searched externally

Program structure is proposed by an LLM, OpenEvolve, or evolutionary search; a Gumbel-Softmax relaxation of dispatch recovers only 10.8% on a 64-combination space, so discrete search stays exploratory and is not a primary claim.

math Branch boundaries non-differentiable

A parameter appearing only in a branch condition receives zero gradient, the compiled program inherits the source's non-differentiability rather than introducing new discontinuities.

backend PyTorch-only differentiability

The differentiable meta-circular interpreter requires PyTorch's define-by-run autograd; the JAX, NumPy, and CuPy backends run the direct-compile path but not the differentiable interpreter.

cost Interpretation overhead

A single evaluation is ~14× slower than direct compilation (tagged-value wrapping 41%, dispatch 32%). Batching amortizes it, but single-evaluation latency remains and still trails jax.vmap.

caveat Correct ≠ easy to optimize

Compilation gives correct gradients; recovering a constant still depends on the loss landscape and optimizer. Multimodal frequency landscapes and parameter compensation can still defeat a single optimizer.

Why the overhead is worthwhile

One compiled interpreter can optimize arbitrarily many runtime-supplied programs, with gradient correctness following from the interpreter's correctness rather than per-program verification. The conversion happens once, at the interpreter level, that is the central trade-off, and it is what makes program search differentiable at all.

Where the program goes next

The cost limitation above is a representation cost, not an arithmetic one: profiling in the follow-up paper attributes 85–90% of DMCI's forward time to value boxing (49–61%) and evaluator walking (25–40%), with arithmetic near 1%. That is exactly the cost the third paper removes. NDVM, a native CPU runtime, separates discrete structure from differentiable numeric state while keeping the same programs-as-data semantics, making one Kalman-filter calibration from this paper 8,144.1× faster with a bit-identical likelihood.

Paper 3: NDVM, Differentiate the Evaluator, Not the Program →

DMCI shifts differentiability from individual models to a reusable interpreter.

Once the interpreter is compiled, any subsequently supplied program inherits exact gradient-based parameter optimization. Combined with OpenEvolve-style program search, this enables joint exploration of discrete scientific-model structures and continuous model parameters through a single frozen differentiable evaluator, the key contribution the paper establishes.

Resources

Read it, run it, cite it

The paper, its open-source repository, and the neighboring chapters of the research program. Everything you need to reproduce the results or build on the system.

Install & first run

terminalbash
git clone https://github.com/sheneman/dmci.git
cd dmci && pip install -e .

python -c "from neural_compiler.compiler import run_scheme; \
print(run_scheme('(+ (* 3 4) 5)'))"   # 17.0

Fit a program's constants

fit.pypython
graph = compile_program("(* a x)", inputs={"a": None, "x": None})
a = torch.nn.Parameter(torch.tensor(0.0))
opt = torch.optim.Adam([a], lr=0.1)
for _ in range(200):
    pred = unwrap_number(evaluate(graph, {"a": make_float(a), "x": make_float(x)}))
    loss = (pred - y) ** 2
    opt.zero_grad(); loss.backward(); opt.step()
print(a.item())   # ≈ 3.0, recovered by descending through the program

This paper's co-search results also appear, alongside the other two papers, on the program-level Co-Search page.

Cite this paper

@article{sheneman2026dmci,
  title         = {Compile Once, Differentiate Everywhere:
                   A Differentiable Meta-Circular Interpreter},
  author        = {Sheneman, Lucas},
  year          = {2026},
  journal       = {arXiv preprint arXiv:2606.09930},
  eprint        = {2606.09930},
  archivePrefix = {arXiv},
  primaryClass  = {cs.PL},
  doi           = {10.48550/arXiv.2606.09930}
}
Brainstorm