Skip to content

[fix] Repair Dynamiqs backend resolve and add QuTiP parity tests#52

Open
Ronit-Raj9 wants to merge 1 commit into
OpenQuantumDesign:masterfrom
Ronit-Raj9:fix/dynamiqs-backend-43
Open

[fix] Repair Dynamiqs backend resolve and add QuTiP parity tests#52
Ronit-Raj9 wants to merge 1 commit into
OpenQuantumDesign:masterfrom
Ronit-Raj9:fix/dynamiqs-backend-43

Conversation

@Ronit-Raj9

Copy link
Copy Markdown

● Fixes the Dynamiqs backend so it actually runs and matches QuTiP. The old code called dq.sesolve with solver=dq.solver.Tsit5(), but that kwarg and the dq.solver namespace don't exist in dynamiqs 0.3.2, so it just crashed. Switched to method=dq.method.Tsit5 with explicit dq.Options.

Changes:

  • New DynamiqsSolverOptions (Tsit5, rtol=atol=1e-8), progress meter off by default which also fixes the Jupyter ZMQError in DynamiqsBackend failing with ZMQError #26.
  • Save times are built host-side from duration/timestep, so nothing traced gets branched on and the solve is jit-able.
  • Added TaskArgsAtomicEmulator + run_task so solver options can go through a Task, like the analog emulator's QutipBackend.
  • Dropped a duplicate Hilbert-space pass, raise a real ValueError instead of a bare string, and fixed the empty-gate timeline + initial_state check in both the Dynamiqs and QuTiP VMs.
  • Documented the rad/s + seconds units in code and docs.

Fixes #43

  • Please check if the PR fulfills these requirements
  • The commit message follows our guidelines
  • Tests for the changes have been added (for bug fixes / features)
  • Docs have been added / updated (for bug fixes / features)
  • What kind of change does this PR introduce? (Bug fix, feature, docs update, ...)

Mostly a bug fix, plus a small Task-based entry point and a docs update.

  • What is the current behavior? (You can also link to an open issue here)

The backend crashes on any real run (#43) because of the bad sesolve call, and throws a ZMQError in Jupyter from the progress meter (#26). The existing tests only build the VMs, they never run a solve, so nothing was actually verified.

  • What is the new behavior (if this is a feature change)?

It runs end to end and matches QuTiP to ~1e-8 on the Rabi carrier and red-sideband cases. Solver and tolerances are explicit and overridable (on the backend or via a Task), the progress meter is off by default, and the units are documented. Added parity, Hamiltonian-at-gate-time, jit and gradient tests.

  • Does this PR introduce a breaking change? (What changes might users need to make in their application due to this PR?)

No. The only change is the solver_options default going from {} to None (None just falls back to the defaults). The old {} couldn't run anyway, and passing a dict still works.

  • Other information:

run_task and TaskArgsAtomicEmulator are additive, so compile/run are untouched and still mirror QutipBackend. TaskArgsAtomicEmulator subclasses oqd-core's TaskArgsAtomic so a normal Task validates it (no model_construct). map_OperatorMul is left as-is, the 1e-8 parity confirms it's already correct. ruff, copyright, pytest (13 passed, 2 xfailed) and mkdocs all pass locally.

The Dynamiqs backend crashed inside dq.sesolve: it passed a solver= kwarg
and dq.solver.Tsit5(), neither of which exists in dynamiqs 0.3.2. It now
calls sesolve with method=dq.method.Tsit5 and an explicit dq.Options.

Changes:
- Add DynamiqsSolverOptions with explicit Tsit5 defaults (rtol=atol=1e-8)
  and the progress meter off by default, which also avoids the Jupyter
  ZMQError from issue OpenQuantumDesign#26.
- Build the sesolve save times host-side from the float duration and
  timestep, so no traced value is branched on and the solve stays jit-able.
- Add TaskArgsAtomicEmulator (a TaskArgsAtomic subclass) and run_task so
  solver options can flow through a Task, mirroring the analog emulator's
  QutipBackend.
- Drop the duplicate Hilbert-space analysis pass and raise a real
  ValueError instead of a bare string.
- Fix the empty-gate timeline double-offset and the initial_state check in
  both the Dynamiqs and QuTiP VMs.
- Document the rad/s and seconds unit convention in code and in the docs.

Tests cover QuTiP vs Dynamiqs parity on the microwave Rabi and red-sideband
circuits (they agree to ~1e-8), the lowered Hamiltonian at gate times, jit
and gradient compatibility, the solver options, and run_task.

Fixes OpenQuantumDesign#43
@Ronit-Raj9 Ronit-Raj9 changed the title [fix] Repair Dynamiqs backend sesolve and add QuTiP parity tests [fix] Repair Dynamiqs backend resolve and add QuTiP parity tests Jun 8, 2026
@benjimaclellan

Copy link
Copy Markdown
Member

Going to accept this for the unitaryHACK bounty. The QuTiP parity at ~1e-8 is good, and I appreciate the explicit tests for end-to-end jax.jit and jax.grad compatibility, and the cleanups you made on the QuTiP side while you were in there. Thanks for the work!

@Ronit-Raj9

Copy link
Copy Markdown
Author

Thanks for accepting this @benjimaclellan.. Do let me know if i could do any changes or something... I liked working with this... Probably want to contribute after unitaryHack too..

@benjimaclellan

Copy link
Copy Markdown
Member

Thanks @Ronit-Raj9, that sounds great. We have a few changes/PRs to merge from the unitaryHACK and other work, so I'm going to leave this PR open as-is, and we'll keep in touch if there's some tweaks that would be useful here or other contributions to the repo.

@Ronit-Raj9

Copy link
Copy Markdown
Author

Sure @benjimaclellan

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

unitaryHACK 2026: Dynamiqs backend for TrICal

2 participants