Skill Tracer
Skills › Scientific Agent Skills › qutip › advanced.md

advanced.md

452 lines · skills/qutip/references/advanced.md at 5f2bc03

Open on GitHub
1# QuTiP 5.3 Advanced Methods and Package Boundaries
2
3Research and API verification date: **2026-10-01**. Examples target
4`qutip==5.3.1`.
5
6Specialized methods add assumptions and convergence parameters. Use them only
7when the physical model requires them.
8

QuTiP 5.3 Advanced Methods and Package Boundaries

Research and API verification date: 2026-10-01. Examples target qutip==5.3.1.

Specialized methods add assumptions and convergence parameters. Use them only when the physical model requires them.

Raw lines 1 to 8
9## Bloch-Redfield
11`brmesolve` derives dissipative dynamics from system coupling operators and bath
12noise-power spectra:
14```python
15import numpy as np
16from qutip import basis, brmesolve, sigmax, sigmaz
18def bath_spectrum(w):
19 return 0.02 * w if w > 0.0 else 0.0
21result = brmesolve(
22 0.5 * sigmaz(),
23 basis(2, 0),
24 np.linspace(0.0, 30.0, 601),
25 a_ops=[(sigmax(), bath_spectrum)],
26 e_ops={"z": sigmaz()},
27 sec_cutoff=0.1,
28 options={"atol": 1e-10, "rtol": 1e-8, "progress_bar": ""},
30```
32Required assumptions:
34- weak system-environment coupling (Born approximation);
35- initially factorized system/bath state where the derivation requires it;
36- bath correlations decay faster than system evolution (Markov approximation);
37- stationary bath spectra with the correct angular-frequency and
38 positive/negative-frequency convention;
39- a justified secular or partial-secular cutoff.
41`sec_cutoff=-1` disables secularization. QuTiP's documentation warns that the
42non-secular equation may produce negativity. Inspect trace, Hermiticity, and
43minimum density-matrix eigenvalue through the entire run.
45Environment objects can express thermal and fitted spectra more clearly than
46callbacks. When using a callback, test it over every transition frequency and
47near zero.

Bloch-Redfield

brmesolve derives dissipative dynamics from system coupling operators and bath noise-power spectra:

import numpy as np
from qutip import basis, brmesolve, sigmax, sigmaz

def bath_spectrum(w):
    return 0.02 * w if w > 0.0 else 0.0

result = brmesolve(
    0.5 * sigmaz(),
    basis(2, 0),
    np.linspace(0.0, 30.0, 601),
    a_ops=[(sigmax(), bath_spectrum)],
    e_ops={"z": sigmaz()},
    sec_cutoff=0.1,
    options={"atol": 1e-10, "rtol": 1e-8, "progress_bar": ""},
)

Required assumptions:

  • weak system-environment coupling (Born approximation);
  • initially factorized system/bath state where the derivation requires it;
  • bath correlations decay faster than system evolution (Markov approximation);
  • stationary bath spectra with the correct angular-frequency and positive/negative-frequency convention;
  • a justified secular or partial-secular cutoff.

sec_cutoff=-1 disables secularization. QuTiP's documentation warns that the non-secular equation may produce negativity. Inspect trace, Hermiticity, and minimum density-matrix eigenvalue through the entire run.

Environment objects can express thermal and fitted spectra more clearly than callbacks. When using a callback, test it over every transition frequency and near zero.

Raw lines 9 to 48
49## Diffusive stochastic evolution
51Use `ssesolve` for conditioned pure states and `smesolve` for density matrices:
53```python
54result = smesolve(
55 H,
56 rho0,
57 tlist,
58 c_ops=unmonitored_channels,
59 sc_ops=monitored_channels,
60 heterodyne=False,
61 e_ops={"signal": measured_quadrature},
62 ntraj=300,
63 seeds=20260723,
64 options={
65 "dt": 0.001,
66 "store_measurement": "end",
67 "progress_bar": "",
68 },
70```
72`heterodyne=False` selects homodyne and `True` selects heterodyne. Legacy
73integer noise selectors are stale.
75Converge:
77- stochastic integration `dt`;
78- output grid;
79- trajectory count;
80- seed sensitivity;
81- monitored efficiency/model choices;
82- measurement timing (`"start"` versus default end-of-step semantics when
83 relevant).
85`SMESolver.run_from_experiment` can replay known numeric noise or measurement
86records. Treat records as bounded numeric data; do not accept executable
87callbacks from untrusted configuration.

Diffusive stochastic evolution

Use ssesolve for conditioned pure states and smesolve for density matrices:

result = smesolve(
    H,
    rho0,
    tlist,
    c_ops=unmonitored_channels,
    sc_ops=monitored_channels,
    heterodyne=False,
    e_ops={"signal": measured_quadrature},
    ntraj=300,
    seeds=20260723,
    options={
        "dt": 0.001,
        "store_measurement": "end",
        "progress_bar": "",
    },
)

heterodyne=False selects homodyne and True selects heterodyne. Legacy integer noise selectors are stale.

Converge:

  • stochastic integration dt;
  • output grid;
  • trajectory count;
  • seed sensitivity;
  • monitored efficiency/model choices;
  • measurement timing ("start" versus default end-of-step semantics when relevant).

SMESolver.run_from_experiment can replay known numeric noise or measurement records. Treat records as bounded numeric data; do not accept executable callbacks from untrusted configuration.

Raw lines 49 to 88
89## Non-Markovian Monte Carlo with time-local rates
91`nm_mcsolve` is for time-local master equations whose decay rates can become
92negative. Its current input is a collection of operator/rate pairs, not a
93generic two-time bath-correlation callback:
95```python
96import numpy as np
97from qutip import basis, nm_mcsolve, sigmam, sigmaz
99def rate(t):
100 return 0.1 * np.sin(t)
102result = nm_mcsolve(
103 0.5 * sigmaz(),
104 basis(2, 0),
105 np.linspace(0.0, 5.0, 101),
106 [(sigmam(), rate)],
107 e_ops=[basis(2, 0).proj()],
108 ntraj=400,
109 seeds=20260723,
110 options={"progress_bar": ""},
112```
114Here the integrated rate is `0.1 * (1 - cos(t)) >= 0`, so the exact
115excited population `exp(-0.1 * (1 - cos(t)))` stays in `[0, 1]` even
116when the instantaneous rate becomes negative. A cosine rate instead would
117produce population above one after pi for this initially excited state.
119This method does not make an arbitrary non-Markovian model valid. Verify that
120the time-local generator and influence-martingale construction apply, report
121sampling uncertainty, and audit completeness/positivity behavior.

Non-Markovian Monte Carlo with time-local rates

nm_mcsolve is for time-local master equations whose decay rates can become negative. Its current input is a collection of operator/rate pairs, not a generic two-time bath-correlation callback:

import numpy as np
from qutip import basis, nm_mcsolve, sigmam, sigmaz

def rate(t):
    return 0.1 * np.sin(t)

result = nm_mcsolve(
    0.5 * sigmaz(),
    basis(2, 0),
    np.linspace(0.0, 5.0, 101),
    [(sigmam(), rate)],
    e_ops=[basis(2, 0).proj()],
    ntraj=400,
    seeds=20260723,
    options={"progress_bar": ""},
)

Here the integrated rate is 0.1 * (1 - cos(t)) >= 0, so the exact excited population exp(-0.1 * (1 - cos(t))) stays in [0, 1] even when the instantaneous rate becomes negative. A cosine rate instead would produce population above one after pi for this initially excited state.

This method does not make an arbitrary non-Markovian model valid. Verify that the time-local generator and influence-martingale construction apply, report sampling uncertainty, and audit completeness/positivity behavior.

Raw lines 89 to 122
123## Floquet theory
125For \(H(t+T)=H(t)\), the current QuTiP 5 abstraction is `FloquetBasis`:
127```python
128import numpy as np
129from qutip import FloquetBasis, QobjEvo, sigmax, sigmaz
131drive_frequency = 2.0
132period = 2.0 * np.pi / drive_frequency
134def drive(t, amplitude, omega):
135 return amplitude * np.cos(omega * t)
137H = QobjEvo(
138 [0.5 * sigmaz(), [sigmax(), drive]],
139 args={"amplitude": 0.2, "omega": drive_frequency},
141floquet = FloquetBasis(H, period)
142quasienergies = floquet.e_quasi
143modes_at_zero = floquet.mode(0.0)
144```
146Before using Floquet dynamics:
1481. numerically check `H(t + period) - H(t)` over representative times;
1492. state the quasi-energy branch convention;
1503. sweep Hilbert truncation and any precomputation grid;
1514. inspect near-degenerate quasi-energies;
1525. compare one-period propagation with direct evolution.
154`fsesolve` handles closed periodic dynamics. `fmmesolve` handles a
155Floquet-Markov construction:
157```python
158result = fmmesolve(
159 floquet,
160 rho0,
161 tlist,
162 c_ops=coupling_operators,
163 spectra_cb=spectrum_callbacks,
164 e_ops={"z": sigmaz()},
165 w_th=temperature,
167```
169The coupling operators and spectrum callbacks are paired by position. They are
170not ordinary Lindblad channels. Spectrum callbacks must accept and return
171NumPy arrays (use `np.where`, not scalar `if w > 0`). Use `FMESolver`
172directly to sweep its `kmax` sideband cutoff and `nT` quadrature count.
173Verify weak-coupling, bath, and thermal
174assumptions. QuTiP 5 result states are in the lab basis by default; the
175`store_floquet_states` option controls additional Floquet-basis storage.
177Old free-function mode workflows may remain for compatibility, but new work
178should use `FloquetBasis`.

Floquet theory

For \(H(t+T)=H(t)\), the current QuTiP 5 abstraction is FloquetBasis:

import numpy as np
from qutip import FloquetBasis, QobjEvo, sigmax, sigmaz

drive_frequency = 2.0
period = 2.0 * np.pi / drive_frequency

def drive(t, amplitude, omega):
    return amplitude * np.cos(omega * t)

H = QobjEvo(
    [0.5 * sigmaz(), [sigmax(), drive]],
    args={"amplitude": 0.2, "omega": drive_frequency},
)
floquet = FloquetBasis(H, period)
quasienergies = floquet.e_quasi
modes_at_zero = floquet.mode(0.0)

Before using Floquet dynamics:

  1. numerically check H(t + period) - H(t) over representative times;
  2. state the quasi-energy branch convention;
  3. sweep Hilbert truncation and any precomputation grid;
  4. inspect near-degenerate quasi-energies;
  5. compare one-period propagation with direct evolution.

fsesolve handles closed periodic dynamics. fmmesolve handles a Floquet-Markov construction:

result = fmmesolve(
    floquet,
    rho0,
    tlist,
    c_ops=coupling_operators,
    spectra_cb=spectrum_callbacks,
    e_ops={"z": sigmaz()},
    w_th=temperature,
)

The coupling operators and spectrum callbacks are paired by position. They are not ordinary Lindblad channels. Spectrum callbacks must accept and return NumPy arrays (use np.where, not scalar if w > 0). Use FMESolver directly to sweep its kmax sideband cutoff and nT quadrature count. Verify weak-coupling, bath, and thermal assumptions. QuTiP 5 result states are in the lab basis by default; the store_floquet_states option controls additional Floquet-basis storage.

Old free-function mode workflows may remain for compatibility, but new work should use FloquetBasis.

Raw lines 123 to 179
180## HEOM
182Import from the current namespace:
184```python
185from qutip.solver.heom import DrudeLorentzBath, HEOMSolver
186```
188The legacy QuTiP 4 nonmarkov HEOM namespace is stale.
190Example:
192```python
193import numpy as np
194from qutip import basis, sigmax, sigmaz
195from qutip.solver.heom import DrudeLorentzBath, HEOMSolver
197H_system = 0.5 * sigmaz()
198rho0 = basis(2, 0).proj()
200bath = DrudeLorentzBath(
201 sigmax(),
202 lam=0.05,
203 gamma=1.0,
204 T=0.5,
205 Nk=3,
207solver = HEOMSolver(
208 H_system,
209 bath,
210 max_depth=4,
211 options={
212 "atol": 1e-10,
213 "rtol": 1e-8,
214 "store_states": True,
215 "store_ados": False,
216 "progress_bar": "",
217 },
219result = solver.run(rho0, np.linspace(0.0, 10.0, 201))
220reduced_states = result.states
221```
223For arbitrary exponential expansions, the full current constructor is:
225```text
226BosonicBath(Q, ck_real, vk_real, ck_imag, vk_imag,
227 combine=True, tag=None)
228```
230Do not omit the imaginary coefficient/frequency lists; use empty lists only
231when the modeled correlation genuinely has no imaginary expansion.
233HEOM convergence requires independent sweeps of:
235- hierarchy `max_depth`;
236- bath expansion count (`Nk` or fitted exponent count);
237- Matsubara versus Padé/environment approximation;
238- ODE tolerances/integrator;
239- system Hilbert truncation;
240- time grid and duration.
242Record \(\lambda\), cutoff, temperature, and all energies in one consistent
243\(\hbar=k_B=1\) unit convention if that convention is used.
245`result.states` are reduced system states. Set `store_ados=True` only when the
246full auxiliary-density hierarchy is needed; then `result.ado_states` can be
247large. A previous final ADO state may initialize a continuation only when its
248hierarchy is compatible. QuTiP 5.3.1 fixes `final_ado_state` to return
249the hierarchy object; it returns `None` when ADOs were not stored. Do not
250replace the full hierarchy with `final_state` when continuing correlated dynamics.
252HEOM can mix supported bosonic and fermionic baths. Fermionic odd parity is a
253special solver construction and must match the initial operator parity.

HEOM

Import from the current namespace:

from qutip.solver.heom import DrudeLorentzBath, HEOMSolver

The legacy QuTiP 4 nonmarkov HEOM namespace is stale.

Example:

import numpy as np
from qutip import basis, sigmax, sigmaz
from qutip.solver.heom import DrudeLorentzBath, HEOMSolver

H_system = 0.5 * sigmaz()
rho0 = basis(2, 0).proj()

bath = DrudeLorentzBath(
    sigmax(),
    lam=0.05,
    gamma=1.0,
    T=0.5,
    Nk=3,
)
solver = HEOMSolver(
    H_system,
    bath,
    max_depth=4,
    options={
        "atol": 1e-10,
        "rtol": 1e-8,
        "store_states": True,
        "store_ados": False,
        "progress_bar": "",
    },
)
result = solver.run(rho0, np.linspace(0.0, 10.0, 201))
reduced_states = result.states

For arbitrary exponential expansions, the full current constructor is:

BosonicBath(Q, ck_real, vk_real, ck_imag, vk_imag,
             combine=True, tag=None)

Do not omit the imaginary coefficient/frequency lists; use empty lists only when the modeled correlation genuinely has no imaginary expansion.

HEOM convergence requires independent sweeps of:

  • hierarchy max_depth;
  • bath expansion count (Nk or fitted exponent count);
  • Matsubara versus Padé/environment approximation;
  • ODE tolerances/integrator;
  • system Hilbert truncation;
  • time grid and duration.

Record \(\lambda\), cutoff, temperature, and all energies in one consistent \(\hbar=k_B=1\) unit convention if that convention is used.

result.states are reduced system states. Set store_ados=True only when the full auxiliary-density hierarchy is needed; then result.ado_states can be large. A previous final ADO state may initialize a continuation only when its hierarchy is compatible. QuTiP 5.3.1 fixes final_ado_state to return the hierarchy object; it returns None when ADOs were not stored. Do not replace the full hierarchy with final_state when continuing correlated dynamics.

HEOM can mix supported bosonic and fermionic baths. Fermionic odd parity is a special solver construction and must match the initial operator parity.

Raw lines 180 to 254
255## Permutational invariance (PIQS)
257In QuTiP 5.3, use the `piqs` module exported by `qutip`:
259```python
260import numpy as np
261from qutip import mesolve, piqs
263N = 10
264Jz = piqs.jspin(N, "z", basis="dicke")
265rho0 = piqs.dicke(N, N / 2, N / 2)
267ensemble = piqs.Dicke(
268 N,
269 emission=0.05,
270 dephasing=0.01,
271 collective_emission=0.02,
273L = ensemble.liouvillian()
274result = mesolve(
275 L,
276 rho0,
277 np.linspace(0.0, 20.0, 201),
278 e_ops={"Jz": Jz},
280```
282`piqs.Dicke.pisolve(initial_state, tlist)` is an optimized method only for
283diagonal Hamiltonians and diagonal initial density matrices. It takes no
284`e_ops`; use the general Liouvillian path for arbitrary observables and
285non-diagonal cases.
287PIQS exploits permutation symmetry in a Dicke basis. Before using it:
289- verify identical two-level constituents and permutation-symmetric dynamics;
290- distinguish local and collective rates;
291- keep operators and states in the same `dicke` or `uncoupled` basis;
292- do not interpret Dicke-basis matrix dimension as \(2^N\);
293- compare with a small full-Hilbert-space model where feasible.
295`piqs.collapse_uncoupled` returns ordinary collapse operators in a \(2^N\)
296space and is only practical for modest `N`.

Permutational invariance (PIQS)

In QuTiP 5.3, use the piqs module exported by qutip:

import numpy as np
from qutip import mesolve, piqs

N = 10
Jz = piqs.jspin(N, "z", basis="dicke")
rho0 = piqs.dicke(N, N / 2, N / 2)

ensemble = piqs.Dicke(
    N,
    emission=0.05,
    dephasing=0.01,
    collective_emission=0.02,
)
L = ensemble.liouvillian()
result = mesolve(
    L,
    rho0,
    np.linspace(0.0, 20.0, 201),
    e_ops={"Jz": Jz},
)

piqs.Dicke.pisolve(initial_state, tlist) is an optimized method only for diagonal Hamiltonians and diagonal initial density matrices. It takes no e_ops; use the general Liouvillian path for arbitrary observables and non-diagonal cases.

PIQS exploits permutation symmetry in a Dicke basis. Before using it:

  • verify identical two-level constituents and permutation-symmetric dynamics;
  • distinguish local and collective rates;
  • keep operators and states in the same dicke or uncoupled basis;
  • do not interpret Dicke-basis matrix dimension as \(2^N\);
  • compare with a small full-Hilbert-space model where feasible.

piqs.collapse_uncoupled returns ordinary collapse operators in a \(2^N\) space and is only practical for modest N.

Raw lines 255 to 297
298## Superoperators and channels
300Current conversions:
302```python
303from qutip import (
304 kraus_to_super,
305 operator_to_vector,
306 spre,
307 spost,
308 to_choi,
309 to_kraus,
310 to_super,
311 vector_to_operator,
313```
315QuTiP column-stacks vectorized operators. Use the conversion functions rather
316than manual reshape logic. Check complete positivity and trace preservation in
317the intended representation, and preserve structured dimensions.

Superoperators and channels

Current conversions:

from qutip import (
    kraus_to_super,
    operator_to_vector,
    spre,
    spost,
    to_choi,
    to_kraus,
    to_super,
    vector_to_operator,
)

QuTiP column-stacks vectorized operators. Use the conversion functions rather than manual reshape logic. Check complete positivity and trace preservation in the intended representation, and preserve structured dimensions.

Raw lines 298 to 318
319## QuTiP family packages
321Official PyPI metadata snapshot:
323| Distribution | Latest published | Release date | Maturity | `Requires-Python` | Required distributions |
324|---|---:|---:|---|---|---|
325| `qutip` | 5.3.1 | 2026-08-04 | production/stable | `>=3.11` | NumPy `>=1.23.2`; SciPy `>=1.9.2` except `1.16.0`/`1.17.0`; `packaging` |
326| `qutip-qip` | 0.4.2 | 2026-06-23 | production/stable | not declared | NumPy `>=1.16.6`; SciPy `>=1.0`; QuTiP `>=4.6`; `packaging` |
327| `qutip-qtrl` | 0.2.0 | 2026-06-23 | pre-alpha classifier | not declared | NumPy `>=1.19`; SciPy `>=1.0`; QuTiP `>=5.0.1`; `packaging` |
328| `qutip-jax` | 0.1.1 | 2025-05-29 | pre-alpha classifier | not declared | QuTiP `>=5.1.0`; JAX; Diffrax; Equinox |
329| `qutip-cupy` | no PyPI project | — | unreleased repository | — | no released metadata |
331“Not declared” means the current PyPI `Requires-Python` field is empty, not
332that every Python release is supported. Resolve and test each extension in the
333same Python 3.11+ environment as QuTiP 5.3. Direct pins do not freeze transitive
334JAX/CuPy stacks; use a lockfile for a deployable environment.

QuTiP family packages

Official PyPI metadata snapshot:

DistributionLatest publishedRelease dateMaturityRequires-PythonRequired distributions
qutip5.3.12026-08-04production/stable>=3.11NumPy >=1.23.2; SciPy >=1.9.2 except 1.16.0/1.17.0; packaging
qutip-qip0.4.22026-06-23production/stablenot declaredNumPy >=1.16.6; SciPy >=1.0; QuTiP >=4.6; packaging
qutip-qtrl0.2.02026-06-23pre-alpha classifiernot declaredNumPy >=1.19; SciPy >=1.0; QuTiP >=5.0.1; packaging
qutip-jax0.1.12025-05-29pre-alpha classifiernot declaredQuTiP >=5.1.0; JAX; Diffrax; Equinox
qutip-cupyno PyPI project—unreleased repository—no released metadata

“Not declared” means the current PyPI Requires-Python field is empty, not that every Python release is supported. Resolve and test each extension in the same Python 3.11+ environment as QuTiP 5.3. Direct pins do not freeze transitive JAX/CuPy stacks; use a lockfile for a deployable environment.

Raw lines 319 to 335
336### qutip-qip 0.4.2
338Status: production/stable on PyPI, released 2026-06-23.
340Purpose:
342- circuit and gate models;
343- `QubitCircuit` unitary circuit simulation;
344- `Processor` pulse/noise/open-system device simulation.
346Migration boundary:
348```python
349from qutip_qip.circuit import QubitCircuit
350```
352Do not import `qutip.qip` in QuTiP 5 code. This package is a local simulator,
353not a hardware provider or execution service.

qutip-qip 0.4.2

Status: production/stable on PyPI, released 2026-06-23.

Purpose:

  • circuit and gate models;
  • QubitCircuit unitary circuit simulation;
  • Processor pulse/noise/open-system device simulation.

Migration boundary:

from qutip_qip.circuit import QubitCircuit

Do not import qutip.qip in QuTiP 5 code. This package is a local simulator, not a hardware provider or execution service.

Raw lines 336 to 354
355### qutip-qtrl 0.2.0
357Status: latest published release 2026-06-23; PyPI classifier is pre-alpha.
359Purpose: quantum optimal control with GRAPE and CRAB, emphasizing integration
360with QuTiP physics models.
362Migration boundary:
364```python
365from qutip_qtrl import pulseoptim
366```
368It replaces the old `qutip.control` import. It is **not** a trajectory viewer.
369For unitary control use `pulseoptim.optimize_pulse_unitary(H_d, H_c, U_0,
370U_targ, num_tslots=..., evo_time=...)`. The Hamiltonian per slot is
371`H_d + sum(amplitude[j] * H_c[j])`; no implicit `2*pi` conversion is added.
372With `H_c=[sigmax()/2]`, a constant angular amplitude `omega` rotates by
373`omega * evo_time`. Recompute the ordered product of slot propagators from
374`result.final_amps` and `np.diff(result.time)`, and compare it with
375`result.evo_full_final`. The equal-slot shortcut constructs float32 durations
376in 0.2.0, so replaying a fresh `evo_time / num_tslots` can differ. Supply an
377explicit float64 `tau` array when that precision matters. A PSU
378objective ignores global phase. Specify `max_iter`, `max_wall_time`, amplitude
379bounds and an intentional initial pulse; `seeds=` is not an optimizer argument.
380Random pulse initialization uses NumPy RNG state in this release.
382Optimization success does not establish robustness: report bounds, objective,
383gradient/termination status, seeds, discretization, and validation under model
384uncertainty.

qutip-qtrl 0.2.0

Status: latest published release 2026-06-23; PyPI classifier is pre-alpha.

Purpose: quantum optimal control with GRAPE and CRAB, emphasizing integration with QuTiP physics models.

Migration boundary:

from qutip_qtrl import pulseoptim

It replaces the old qutip.control import. It is not a trajectory viewer. For unitary control use pulseoptim.optimize_pulse_unitary(H_d, H_c, U_0, U_targ, num_tslots=..., evo_time=...). The Hamiltonian per slot is H_d + sum(amplitude[j] * H_c[j]); no implicit 2*pi conversion is added. With H_c=[sigmax()/2], a constant angular amplitude omega rotates by omega * evo_time. Recompute the ordered product of slot propagators from result.final_amps and np.diff(result.time), and compare it with result.evo_full_final. The equal-slot shortcut constructs float32 durations in 0.2.0, so replaying a fresh evo_time / num_tslots can differ. Supply an explicit float64 tau array when that precision matters. A PSU objective ignores global phase. Specify max_iter, max_wall_time, amplitude bounds and an intentional initial pulse; seeds= is not an optimizer argument. Random pulse initialization uses NumPy RNG state in this release.

Optimization success does not establish robustness: report bounds, objective, gradient/termination status, seeds, discretization, and validation under model uncertainty.

Raw lines 355 to 385
386### qutip-jax 0.1.1
388Status: latest published release 2025-05-29; explicitly pre-alpha and described
389as not ready for production use.
391Purpose: a JAX linear-algebra data backend for GPU execution and automatic
392differentiation. It depends on QuTiP 5.1 or newer plus JAX, Diffrax, and Equinox.
394Importing `qutip_jax` registers `Qobj.to("jax")`, `"jaxdia"`, and the
395`"diffrax"` integrator. Select those explicitly; installing it alone does not
396move an ordinary solver to GPU. Avoid process-wide `set_as_default()` in
397shared applications. Diffrax controls tolerances through a
398`diffrax.PIDController(atol=..., rtol=...)` passed as
399`options["stepsize_controller"]`, rather than ordinary solver `atol`/`rtol`.
401CPU smoke tests used JAX/JAXlib 0.11.2, Diffrax 0.7.2 and Equinox 0.13.8.
402That is a tested snapshot, not compatibility with every future release or
403with another package's JAX pin. Check `jax_enable_x64`, actual device, JIT and
404gradients for the required operations against the native backend. GPU,
405MPI, and large-system scaling were not tested. Diffrax can emit its upstream
406complex-dtype warning; a small CPU agreement test does not establish every
407complex solve or derivative as correct.

qutip-jax 0.1.1

Status: latest published release 2025-05-29; explicitly pre-alpha and described as not ready for production use.

Purpose: a JAX linear-algebra data backend for GPU execution and automatic differentiation. It depends on QuTiP 5.1 or newer plus JAX, Diffrax, and Equinox.

Importing qutip_jax registers Qobj.to("jax"), "jaxdia", and the "diffrax" integrator. Select those explicitly; installing it alone does not move an ordinary solver to GPU. Avoid process-wide set_as_default() in shared applications. Diffrax controls tolerances through a diffrax.PIDController(atol=..., rtol=...) passed as options["stepsize_controller"], rather than ordinary solver atol/rtol.

CPU smoke tests used JAX/JAXlib 0.11.2, Diffrax 0.7.2 and Equinox 0.13.8. That is a tested snapshot, not compatibility with every future release or with another package's JAX pin. Check jax_enable_x64, actual device, JIT and gradients for the required operations against the native backend. GPU, MPI, and large-system scaling were not tested. Diffrax can emit its upstream complex-dtype warning; a small CPU agreement test does not establish every complex solve or derivative as correct.

Raw lines 386 to 408
409### qutip-cupy
411The repository belongs to the QuTiP GitHub organization and implements a CuPy
412data backend, but:
414- PyPI returns no `qutip-cupy` project;
415- the repository README says it is not officially released;
416- the repository's installation text targets development-era QuTiP and is not
417 a reproducible 5.3 release recipe.
419Do not recommend it as a stable extension. If a user explicitly accepts an
420experimental source build, isolate and audit that separately rather than adding
421it to this pinned skill snapshot.

qutip-cupy

The repository belongs to the QuTiP GitHub organization and implements a CuPy data backend, but:

  • PyPI returns no qutip-cupy project;
  • the repository README says it is not officially released;
  • the repository's installation text targets development-era QuTiP and is not a reproducible 5.3 release recipe.

Do not recommend it as a stable extension. If a user explicitly accepts an experimental source build, isolate and audit that separately rather than adding it to this pinned skill snapshot.

Raw lines 409 to 422
423## Parallel and performance boundaries
425- `mcsolve`/stochastic solvers expose `map`, `num_cpus`, and related options.
426 Parallelism changes scheduling and cost, not the required trajectory
427 convergence.
428- `parallel_map` executes Python callables. Use only trusted, statically defined
429 local functions and bounded task lists.
430- Sparse matrices help only when operations preserve sparsity.
431- Large HEOM, Liouvillian, dense diagonalization, and PIQS/full-space conversions
432 can grow rapidly. Estimate dimensions and memory before construction.
433- QuTiP 5.3's `matrix_form` option for `mesolve` and new Krylov density-matrix
434 support are performance choices that require output equivalence tests.

Parallel and performance boundaries

  • mcsolve/stochastic solvers expose map, num_cpus, and related options. Parallelism changes scheduling and cost, not the required trajectory convergence.
  • parallel_map executes Python callables. Use only trusted, statically defined local functions and bounded task lists.
  • Sparse matrices help only when operations preserve sparsity.
  • Large HEOM, Liouvillian, dense diagonalization, and PIQS/full-space conversions can grow rapidly. Estimate dimensions and memory before construction.
  • QuTiP 5.3's matrix_form option for mesolve and new Krylov density-matrix support are performance choices that require output equivalence tests.
Raw lines 423 to 435
436## Sources (verified 2026-10-01)
438- [Bloch-Redfield guide](https://qutip.readthedocs.io/en/stable/guide/dynamics/dynamics-bloch-redfield.html)
439- [Stochastic solver guide](https://qutip.readthedocs.io/en/stable/guide/dynamics/dynamics-stochastic.html)
440- [Floquet API](https://qutip.readthedocs.io/en/stable/apidoc/solver.html#floquet-states-and-floquet-markov-master-equation)
441- [HEOM API](https://qutip.readthedocs.io/en/stable/apidoc/heom.html)
442- [PIQS API](https://qutip.readthedocs.io/en/stable/apidoc/piqs.html)
443- [QuTiP 5.3.1 release](https://github.com/qutip/qutip/releases/tag/v5.3.1)
444- [qutip-qip 0.4.2](https://pypi.org/project/qutip-qip/)
445- [QIP circuit and device API](https://qutip-qip.readthedocs.io/en/stable/apidoc/qutip_qip.circuit.html)
446- [QTRL 0.2.0 pulse API source](https://github.com/qutip/qutip-qtrl/blob/v0.2.0/src/qutip_qtrl/pulseoptim.py)
447- [QTRL 0.2.0 slot construction](https://github.com/qutip/qutip-qtrl/blob/v0.2.0/src/qutip_qtrl/dynamics.py)
448- [JAX solver guide](https://qutip-jax.readthedocs.io/en/latest/solver.html)
449- [JAX 0.1.1 integrator source](https://github.com/qutip/qutip-jax/blob/v0.1.1/src/qutip_jax/ode.py)
450- [qutip-qtrl 0.2.0](https://pypi.org/project/qutip-qtrl/)
451- [qutip-jax 0.1.1](https://pypi.org/project/qutip-jax/)
452- [official qutip-cupy repository](https://github.com/qutip/qutip-cupy)