Differentiable circuit simulator based on JAX
Project description
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-diciplined 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 diffrax, jax, jax.numpy as jnp
from circulax import compile_circuit
from circulax.components.electronic import Capacitor, Inductor, Resistor, VoltageSource
from circulax.solvers import setup_transient
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()
sim = setup_transient(groups=circuit.groups, linear_strategy=circuit.solver)
sol = sim(
t0=0.0, t1=3e-9, dt0=3e-12, y0=y_op,
saveat=diffrax.SaveAt(ts=jnp.linspace(0, 3e-9, 500)),
max_steps=100_000,
)
v_cap = circuit.get_port_field(sol.ys, "C1,p1") # capacitor voltage over time
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.
- 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
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file circulax-0.1.6.tar.gz.
File metadata
- Download URL: circulax-0.1.6.tar.gz
- Upload date:
- Size: 454.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b9a0b43482520a3991190fa519f8facc0403a781f09a483a295e3972ca03fa4f
|
|
| MD5 |
5089c98cbfb9641d6aa78178231ec1c1
|
|
| BLAKE2b-256 |
b10a8767ea5bdc2531b942b662acfec34a0f8e6d568fcb7220814d00c998b253
|
Provenance
The following attestation bundles were made for circulax-0.1.6.tar.gz:
Publisher:
publish.yaml on gdsfactory/circulax
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
circulax-0.1.6.tar.gz -
Subject digest:
b9a0b43482520a3991190fa519f8facc0403a781f09a483a295e3972ca03fa4f - Sigstore transparency entry: 1393351775
- Sigstore integration time:
-
Permalink:
gdsfactory/circulax@7e727c1039c04bd0285845ea494d98a821ae8db8 -
Branch / Tag:
refs/tags/v0.1.6 - Owner: https://github.com/gdsfactory
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yaml@7e727c1039c04bd0285845ea494d98a821ae8db8 -
Trigger Event:
push
-
Statement type:
File details
Details for the file circulax-0.1.6-py3-none-any.whl.
File metadata
- Download URL: circulax-0.1.6-py3-none-any.whl
- Upload date:
- Size: 73.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c0a7ad3da8b257f5aecaac01539315da0c5de50fe15c44a5daa1445d4e5b7e78
|
|
| MD5 |
bf9ff6f79441759b80510044bba63ae2
|
|
| BLAKE2b-256 |
31b18c07688a844ba11ae7412424bac8f89946d08d7dcabe7e2c82236814ab5f
|
Provenance
The following attestation bundles were made for circulax-0.1.6-py3-none-any.whl:
Publisher:
publish.yaml on gdsfactory/circulax
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
circulax-0.1.6-py3-none-any.whl -
Subject digest:
c0a7ad3da8b257f5aecaac01539315da0c5de50fe15c44a5daa1445d4e5b7e78 - Sigstore transparency entry: 1393351779
- Sigstore integration time:
-
Permalink:
gdsfactory/circulax@7e727c1039c04bd0285845ea494d98a821ae8db8 -
Branch / Tag:
refs/tags/v0.1.6 - Owner: https://github.com/gdsfactory
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yaml@7e727c1039c04bd0285845ea494d98a821ae8db8 -
Trigger Event:
push
-
Statement type: