Circulax
A differentiable circuit simulator built on JAX. Define netlists, run transient / DC / AC / harmonic-balance analysis, and differentiate through the solver for gradient-based optimization and inverse design. Circulax aims to be flexible multi-disciplined circuit simulator offering a similar interface to the linear s-parameter solver SAX.
Installation
pip install circulax
Quickstart
Simulate an underdamped LCR circuit in the time domain:
import jax, jax.numpy as jnp
from circulax import compile_circuit
from circulax.components.electronic import Capacitor, Inductor, Resistor, VoltageSource
jax.config.update("jax_enable_x64", True)
net_dict = {
"instances": {
"GND": {"component": "ground"},
"V1": {"component": "source_voltage", "settings": {"V": 1.0, "delay": 0.25e-9}},
"R1": {"component": "resistor", "settings": {"R": 10.0}},
"C1": {"component": "capacitor", "settings": {"C": 1e-11}},
"L1": {"component": "inductor", "settings": {"L": 5e-9}},
},
"connections": {
"GND,p1": ("V1,p2", "C1,p2"),
"V1,p1": "R1,p1", "R1,p2": "L1,p1", "L1,p2": "C1,p1",
},
}
models = {
"resistor": Resistor, "capacitor": Capacitor,
"inductor": Inductor, "source_voltage": VoltageSource, "ground": lambda: 0,
}
circuit = compile_circuit(net_dict, models)
y_op = circuit.dc()
sol = circuit.transient(
t0=0.0, t1=3e-9, dt0=3e-12, y0=y_op,
saveat=jnp.linspace(0, 3e-9, 500),
max_steps=100_000,
)
v_cap = circuit.port(sol.ys, "C1,p1") # capacitor voltage over time
Common analyses use the same compiled object:
op = circuit.dc()
op_sweep = circuit.dc(params={"R1.R": 20.0, "wavelength_nm": 1310.0})
S = circuit.ac(ports=["C1,p1"], freqs=jnp.logspace(6, 10, 101), y_dc=op)
y_time, y_freq = circuit.hb(freq=1e6, harmonics=5, y0=op)
v_out = circuit.port(op, "C1,p1")
Parameter keys like "R1.R" update one instance. Keys without a dot, such as
"wavelength_nm", are broadcast to every component that declares the parameter.
Defining Components
Components are plain Python functions — no boilerplate, no subclassing:
from circulax.components.base_component import component, Signals, States
@component(ports=("p1", "p2"))
def Resistor(signals: Signals, s: States, R: float = 1e3):
i = (signals.p1 - signals.p2) / R
return {"p1": i, "p2": -i}, {} # (currents, charges)
@component(ports=("p1", "p2"))
def Capacitor(signals: Signals, s: States, C: float = 1e-12):
q = C * (signals.p1 - signals.p2)
return {}, {"p1": q, "p2": -q} # dq/dt becomes current automatically
Non-linear opto-electronic components are just as simple — the Jacobian is computed automatically via Automatic Differentiation:
@component(ports=("optical_in", "anode", "cathode"))
def Photodetector(signals: Signals, s: States,
responsivity: float = 0.8, dark_current: float = 1e-9):
optical_power = jnp.abs(signals.optical_in) ** 2 # non-linear
i_photo = responsivity * optical_power + dark_current
i_reflect = -0.01 * signals.optical_in # small back-reflection
return {"optical_in": i_reflect, "anode": i_photo, "cathode": -i_photo}, {}
Existing SAX models plug in directly — reuse your photonic PDK as-is:
import sax
from circulax.s_transforms import sax_component
Straight = sax_component(sax.models.straight) # that's it — ready to simulate
Features
- Transient — implicit ODE stepping via Diffrax; handles stiff circuits.
- DC operating point — Newton-Raphson root-finding via Optimistix.
- Harmonic Balance — periodic steady state directly in the frequency domain.
- AC sweep — linearise at DC op-point, sweep frequency, return S-parameters.
- OSDI compact models — load OpenVAF-compiled Verilog-A models through bosdi.
- Automatic differentiation — differentiate through the solver for gradient-based inverse design.
- Hardware-agnostic — CPU, GPU, or TPU with no code changes.
- Mixed-domain — electronic and photonic circuits in a single netlist.
Comparison to SPICE
Circulax is a SPICE-like simulator but built with modern tooling so users can easily create their own models in a language they know.
| SPICE | circulax | |
|---|---|---|
| Model definition | Verilog-A / hardcoded C++ | Python functions |
| Derivatives | Hardcoded or compiler-generated | Automatic differentiation |
| Solver | Fixed/heuristic stepping | Adaptive ODE (Diffrax) |
| Hardware | CPU-only | CPU / GPU / TPU |
Inverse Design via Back-propagation
Because the entire solver is written in JAX, gradients flow end-to-end from a loss function back through the simulation and into component parameters. Use jax.grad and standard optimizers to automatically tune circuit designs — the cost is one forward + one backward pass regardless of parameter count.
See the Inverse Design guide for a comparison with finite differences and worked examples.
Copyright © 2026 Chris Daunt — Apache-2.0
Metadata
Release files for circulax 0.2.3
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| circulax-0.2.3.tar.gz | 3.5 MB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| circulax-0.2.3-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 3.7 MB
Release files / circulax-0.2.3.tar.gz
| Download URL | circulax-0.2.3.tar.gz |
|---|---|
| Size | 3.5 MB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
3ea8f6f5a0b175a034de3bdae64dc77ea121b9a758524d5c50ccfaaed22aecaa
|
|
BLAKE2b-256 checksum How to use checksums |
aed7d4838228e30a54ab4dbc78682364a7fbeb8e9031db3180ef7cc10e269943
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jul 31, 2026.
Transparency logRelease files / circulax-0.2.3-py3-none-any.whl
| Download URL | circulax-0.2.3-py3-none-any.whl |
|---|---|
| Size | 191.0 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
d5a99d207ce6053acb5adfc17fd35ed057ff13d6a1264de0523d9c995c4c9c15
|
|
BLAKE2b-256 checksum How to use checksums |
1dbf0ad8b46152e90699af38df5c063fc999269c56c4290d626579f91a41a563
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jul 31, 2026.
Transparency log