---
name: k-dense-ai/qutip
source: https://app.decimal.ai/s/k-dense-ai-qutip@1/SKILL.md
source_sha256: b37df5b223d8
---

# QuTiP 5

## Scope

Use QuTiP for finite-dimensional quantum mechanics, quantum optics, Lindblad
dynamics, trajectories, weak-coupling Bloch-Redfield models, and specialized
Floquet, HEOM, and permutational-invariance methods. It is not a hardware
execution SDK. Circuit and control functionality moved to separate QuTiP family
packages.

This skill targets **QuTiP 5.3.0**, released 2026-05-22. QuTiP 5.3 requires
Python 3.11 or newer. Its required distributions are NumPy (`>=1.23.2`), SciPy
(`>=1.9.2`, excluding `1.16.0` and `1.17.0`), and `packaging`.

## Reproducible uv snapshot

Create a dedicated environment and pin every direct distribution:

```bash
uv venv --python 3.11
uv pip install "qutip==5.3.0"
```

For plots:

```bash
uv pip install "qutip[graphics]==5.3.0"
```

Optional QuTiP family packages are independently versioned:

```bash
uv pip install "qutip-qip==0.4.2"
uv pip install "qutip-qtrl==0.2.0"
uv pip install "qutip-jax==0.1.1"
```

- `qutip-qip` 0.4.2 (2026-06-23) is the production/stable circuit, gate, and
  noisy-device simulation package. Import from `qutip_qip`, not `qutip.qip`.
- `qutip-qtrl` 0.2.0 (2026-06-23) provides GRAPE and CRAB **quantum optimal
  control**. It is not a trajectory viewer. Import from `qutip_qtrl`, not
  `qutip.control`; PyPI still classifies it pre-alpha.
- `qutip-jax` 0.1.1 (2025-05-29) is the official JAX data backend for GPU and
  automatic-differentiation experiments. It is explicitly pre-alpha.
- `qutip-cupy` is an official QuTiP-organization repository, but it has no PyPI
  release and its own README says it is not officially released. Do not put an
  unreleased Git install into a reproducible workflow.

Use a project lockfile or a hash-generating `uv pip compile` workflow when
transitive dependency identity must also be frozen.

## Non-negotiable model contract

Before solving, record:

1. **Units and convention.** QuTiP equations normally set \(\hbar=1\).
   Hamiltonian entries are angular frequencies and rates have reciprocal-time
   units. Convert cyclic frequency with \(2\pi f\); never mix Hz and rad/s.
2. **Subsystem order.** `tensor(A, B, C)` fixes subsystem indices `0, 1, 2`.
   Preserve that order in every state, operator, collapse channel, and partial
   trace. `obj.ptrace([0, 2])` keeps those subsystems; it does not trace them.
3. **State validity.** Check ket norm or density-matrix Hermiticity, unit trace,
   and eigenvalues above a stated negative tolerance. Tiny negative values may
   be numerical; material negativity invalidates a claimed state.
4. **Generator meaning.** A Lindblad channel with rate `gamma` is represented
   by `sqrt(gamma) * A`, not `gamma * A`. Define what each rate measures. For
   example, `sqrt(gamma_phi / 2) * sigmaz()` gives coherence decay
   `exp(-gamma_phi * t)`.
5. **Approximations.** State rotating-wave, Born-Markov, secular, weak-coupling,
   bath-equilibrium, truncation, symmetry, and initial-factorization assumptions
   wherever used.
6. **Numerics.** Justify Hilbert truncation, output grid, integration method,
   tolerances, trajectory count, and random seeds. Report `result.stats`.
7. **Convergence.** Sweep every artificial cutoff: Fock dimension, time/frequency
   window and spacing, ODE tolerances, trajectories, Floquet harmonics, HEOM
   depth and bath exponents, or PIQS representation as applicable.

## Qobj, dimensions, and tensor order

Prefer explicit imports and inspect both shape and structured dimensions:

```python
from qutip import basis, qeye, sigmaz, tensor

psi = tensor(basis(2, 0), basis(3, 1))
z_on_first = tensor(sigmaz(), qeye(3))

assert psi.shape == (6, 1)
assert psi.dims == [[2, 3], [1]]
assert z_on_first.dims == [[2, 3], [2, 3]]
rho_first = psi.proj().ptrace(0)  # keep subsystem 0
```

Matrix shape alone is insufficient: two objects can both be 6-by-6 but encode
different tensor factorizations. Read `references/core_concepts.md` before
building composite, superoperator, or channel models.

## Choose the solver by physics

| Model | Current API | Required justification |
|---|---|---|
| Closed, pure, unitary | `sesolve` | Hermitian Hamiltonian; no dissipation |
| Lindblad/open or mixed | `mesolve` | Markovian completely positive model and channel rates |
| Quantum jumps | `mcsolve` | Unravelling, trajectory convergence, seeds |
| Microscopic weak bath | `brmesolve` | Born-Markov/weak coupling, spectra, secular choice |
| Diffusive measurement | `ssesolve`, `smesolve` | monitored versus unmonitored channels |
| Periodic drive | `FloquetBasis`, `fsesolve`, `fmmesolve` | verified period and Floquet convergence |
| Structured non-Markovian bath | `qutip.solver.heom` | bath expansion and hierarchy convergence |
| Symmetric spin ensemble | `qutip.piqs` | permutation symmetry and basis choice |

Do not select a more specialized solver merely because it exists.

## Deterministic open-system example

QuTiP 5.3 uses ordinary option dictionaries. Solver controls, `e_ops`, and
`args` are keyword-only; the old mutable options object is gone.

```python
import numpy as np
from qutip import basis, mesolve, sigmam, sigmaz

omega = 2.0
gamma = 0.15
tlist = np.linspace(0.0, 20.0, 401)
excited = basis(2, 0)

result = mesolve(
    0.5 * omega * sigmaz(),
    excited,
    tlist,
    c_ops=[np.sqrt(gamma) * sigmam()],
    e_ops={"sigma_z": sigmaz(), "excited": excited.proj()},
    options={
        "method": "adams",
        "atol": 1e-10,
        "rtol": 1e-8,
        "store_final_state": True,
        "progress_bar": "",
    },
)

population = np.asarray(result.e_data["excited"])
assert np.max(np.abs(population - np.exp(-gamma * tlist))) < 2e-6
assert isinstance(result.stats, dict)
```

If the problem is stiff, compare `bdf` or `lsoda`; do not change an integrator
without rerunning tolerance and invariant checks. QuTiP 5.3 also supports
`options={"matrix_form": True}` in `mesolve`; benchmark and validate it before
using it as a default.

## Time-dependent systems

Prefer trusted Pythonic callables or numeric coefficient arrays. Do not create
coefficient source strings from user input.

```python
import numpy as np
from qutip import QobjEvo, sigmax, sigmaz

def envelope(t, amplitude, center, width):
    return amplitude * np.exp(-0.5 * ((t - center) / width) ** 2)

H = QobjEvo(
    [0.5 * sigmaz(), [sigmax(), envelope]],
    args={"amplitude": 0.2, "center": 5.0, "width": 1.0},
)
instantaneous_H = H(5.0)
H.arguments(amplitude=0.1)
```

The older `f(t, args)` coefficient signature is deprecated in 5.3 and is
scheduled for removal in 5.5. See `references/time_evolution.md`.

## Trajectories and stochastic solvers

```python
import numpy as np
from qutip import basis, mcsolve, sigmam, sigmaz

tlist = np.linspace(0.0, 10.0, 201)
result = mcsolve(
    0.5 * sigmaz(),
    basis(2, 0),
    tlist,
    [np.sqrt(0.2) * sigmam()],
    e_ops=[basis(2, 0).proj()],
    ntraj=400,
    seeds=20260723,
    options={"keep_runs_results": False, "progress_bar": ""},
)
```

Report `ntraj`, `result.seeds`, uncertainty or repeated-seed sensitivity, and
whether individual runs were retained. Reuse `seeds=previous_result.seeds` only
when paired trajectories are intentional. `ssesolve` and `smesolve` use the
boolean `heterodyne` argument, not legacy integer noise codes.

## Steady states, spectra, and phase space

```python
import numpy as np
from qutip import QFunc, liouvillian, operator_to_vector, qfunc, steadystate

rho_ss = steadystate(H, c_ops, method="direct")
residual = (liouvillian(H, c_ops) * operator_to_vector(rho_ss)).norm()
assert residual < 1e-9

xvec = np.linspace(-5.0, 5.0, 151)
Q_once = qfunc(rho_ss, xvec, xvec)
q_many = QFunc(xvec, xvec)
Q_again = q_many(rho_ss)
assert Q_once.shape == (len(xvec), len(xvec))
```

For `wigner`, `qfunc`, and `QFunc`, array element `[j, k]` corresponds to
`yvec[j]`, `xvec[k]`. In QuTiP 5.3, `QFunc` is initialized with fixed
coordinates and called with a state; it has no `.eval` method. This skill never
uses Python dynamic-code execution. Prefer `plot_wigner`, `Result.plot_expect`,
or explicit Matplotlib axes as documented in `references/visualization.md`.

Direct `spectrum` is a stationary steady-state spectrum. An FFT of a finite
correlation requires explicit checks for tail decay, timestep aliasing,
frequency resolution, window sensitivity, and transform convention. See
`references/analysis.md`.

## Advanced boundaries

- Import HEOM from `qutip.solver.heom`; the legacy QuTiP 4 nonmarkov HEOM
  namespace is stale.
- Use `FloquetBasis` for modes and quasi-energies. Verify
  `H(t + T) == H(t)` numerically and sweep basis/truncation choices.
- Access PIQS with `from qutip import piqs`. `Dicke.pisolve` is only the
  optimized diagonal-state/diagonal-Hamiltonian route; general Dicke-basis
  dynamics use the Liouvillian with `mesolve`.
- `brmesolve` can violate positivity, especially without secularization. Check
  density-matrix eigenvalues over time.
- QIP and optimal control are extension-package concerns. Never present local
  simulation as quantum-hardware execution.

See `references/advanced.md` for HEOM, Floquet, PIQS, stochastic, and extension
boundaries.

## Safe local CLIs

All bundled tools are local-only, emit strict JSON, reject non-finite JSON and
unknown keys, and never load pickle files or executable model code. Simulation
imports are lazy, so every `--help` works without QuTiP installed.

| Script | Purpose |
|---|---|
| `scripts/qobj_model_validator.py` | Validate bounded Qobj model JSON, dimensions, states, rates, and role compatibility |
| `scripts/two_level_simulation.py` | Run a bounded two-level Lindblad or jump simulation |
| `scripts/solver_config_planner.py` | Select a current solver and option/checklist plan |
| `scripts/convergence_sweep.py` | Sweep tolerances/grid size or trajectory count on a synthetic model |
| `scripts/result_audit.py` | Audit JSON output without deserializing Python objects |
| `scripts/steady_state_spectrum_planner.py` | Plan bounded steady-state and direct/FFT spectral checks |

Example:

```bash
python skills/qutip/scripts/two_level_simulation.py --help
python skills/qutip/scripts/two_level_simulation.py \
  --decay-rate 0.2 --t-final 10 --time-points 201 \
  --output two-level.json
python skills/qutip/scripts/result_audit.py two-level.json
```

## Completion checklist

- Record units, \(\hbar\), tensor order, initial state, channels, and model
  assumptions.
- Validate Hermiticity, norm/trace, positivity, dimensions, and generator units.
- Pin QuTiP and direct extensions; record platform, Python, NumPy, and SciPy.
- Inspect result options and stats; do not assume states were stored.
- Perform cutoff, grid, tolerance/integrator, and stochastic convergence sweeps.
- Save portable numeric/configuration summaries as JSON or text. Do not load
  untrusted QuTiP object/result files because object serialization can execute
  code.

## References

- `references/core_concepts.md` — Qobj, dimensions, tensor products, states,
  channels, and unit conventions
- `references/time_evolution.md` — current solver signatures, options, results,
  QobjEvo, trajectories, and numerical controls
- `references/analysis.md` — physical-state audits, steady states,
  correlations, spectra, and convergence
- `references/visualization.md` — Wigner, Q functions, `QFunc`, Bloch, result,
  and matrix plots
- `references/advanced.md` — Bloch-Redfield, stochastic, Floquet, HEOM, PIQS,
  and QuTiP family package boundaries

## Dated official sources

Verified **2026-07-23**:

- [QuTiP 5.3.0 PyPI metadata](https://pypi.org/project/qutip/)
- [QuTiP 5.3.0 release](https://github.com/qutip/qutip/releases/tag/v5.3.0)
- [QuTiP 5.3 changelog](https://qutip.readthedocs.io/en/stable/changelog.html)
- [QuTiP 5.3 API](https://qutip.readthedocs.io/en/stable/apidoc/apidoc.html)
- [QuTiP version-5 tutorials](https://github.com/qutip/qutip-tutorials/tree/main/tutorials-v5)
- [qutip-qip PyPI](https://pypi.org/project/qutip-qip/)
- [qutip-qtrl PyPI](https://pypi.org/project/qutip-qtrl/)
- [qutip-jax PyPI](https://pypi.org/project/qutip-jax/)
- [official unreleased qutip-cupy repository](https://github.com/qutip/qutip-cupy)