Skip to content

unitaryHACK 2026: Dynamiqs backend for TrICal #43

@benjimaclellan

Description

@benjimaclellan

What's the goal:

Fix and polish the Dynamiqs backend in TrICal so that it reliably runs AtomicCircuit programs end-to-end and is usable as a JAX-native, GPU-capable, jit-compilable alternative to the QuTiP backend.

TrICal exposes two light-matter interaction backends for simulating an AtomicCircuit: a QuTiP-based backend (the reliable default) and a Dynamiqs-based backend (JAX + Diffrax under the hood). The Dynamiqs path is what unlocks GPU acceleration and jit-compiled simulation — things the QuTiP backend can't do. Today, that path doesn't reliably reproduce the QuTiP results, and the suspicion is around how timescales are being handed to Dynamiqs' sesolve.

Some details:

The Dynamiqs backend lives under src/oqd_trical/backend/dynamiqs and lowers an AtomicCircuit (defined in oqd-core) into a Dynamiqs simulation by:

  1. Building the time-dependent Hamiltonian from the system's ions, phonon modes, and beams via the compiler-infrastructure passes (oqd-compiler-infrastructure).
  2. Constructing Dynamiqs operators and integrating with dq.sesolve.
  3. Returning final states and metric expectation values back to the user through the Task API.

Suspected timescale handling in sesolve

The current backend produces dynamics that don't cleanly match the QuTiP reference on simple test cases (e.g., a single-ion Rabi flop), and the suspicion is in how time/frequency units flow into dq.sesolve. Things to look at:

  • The atomic-interface inputs (level energies, beam Rabi frequencies, detunings, pulse durations) carry SI units and factors of $2\pi$. The lowering needs to consistently produce a Hamiltonian and a tsave array in matching units (typically Hamiltonian in angular frequency, time in seconds, or both rescaled to a dimensionless reference).
  • Diffrax's adaptive step-size controller is sensitive to the absolute scale of t0/t1 and dt0. With $H \sim 2\pi \cdot 10^{12}$ rad/s and $t \sim 10^{-6}$ s, mismatched rtol/atol defaults can silently under-resolve fast oscillations. Step-size controller and solver defaults should be set explicitly and documented.
  • Time-dependent coefficients (pulse envelopes, modulation) should be expressed using Dynamiqs' time-array / modulated formats rather than reconstructed via Python closures, so the simulation stays jit-able.

A useful test: pick one or two minimal cases (single-ion Rabi flop on the carrier, single-ion + COM phonon sideband transition), run both backends, and confirm the populations agree to a documented tolerance.

Requirements

  • Dynamiqs backend reproduces the QuTiP backend on a single-ion Rabi flop and a single-ion + phonon sideband case, to within a small tolerance.
  • Timescale / unit handling between AtomicCircuit inputs, the lowered Hamiltonian, and Dynamiqs' sesolve is consistent and documented in code.
  • Diffrax solver and tolerances are set explicitly and exposed via TaskArgs.
  • Public backend API mirrors QutipBackend from oqd-analog-emulator; any breaking changes are documented.

Other rough edges

  • Make sure the backend is jax.jit-compatible, with no Python-side branching on traced values.
  • Sensible, explicit defaults for the Diffrax solver and step-size controller (e.g., Tsit5 + PIDController with documented rtol / atol), overridable via TaskArgs.
  • Resolving #26 (DynamiqsBackend failing with ZMQError) would be a nice bonus along the way, if possible

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions