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

analysis.md

325 lines · skills/qutip/references/analysis.md at 5f2bc03

Open on GitHub

Most marks come from matching words in the file, so treat them as pointers.

1# QuTiP 5.3 Analysis, Steady States, and Spectra
2
3Research and API verification date: **2026-10-01**. Examples target
4`qutip==5.3.1`.
5

QuTiP 5.3 Analysis, Steady States, and Spectra

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

Raw lines 1 to 5
6## Analysis starts with invariants
7
8For every reported state, record quantitative checks before interpreting an
9observable:
11```python
12import numpy as np
14def density_audit(rho, tolerance=1e-9):
15 if not rho.isoper:
16 raise ValueError("density_audit requires an operator")
17 hermitian = bool(rho.isherm)
18 eigenvalues = np.linalg.eigvalsh(rho.full()) if hermitian else None
19 trace = complex(rho.tr())
20 return {
21 "is_hermitian": hermitian,
22 "trace_error": float(abs(trace - 1.0)),
23 "minimum_eigenvalue": float(eigenvalues.min()) if hermitian else None,
24 "positive_within_tolerance": hermitian and bool(eigenvalues.min() >= -tolerance),
25 }
26```
28Also check:
30- `state.dims` matches every observable and the declared subsystem order;
31- ket norm or density-matrix trace stays stable over time;
32- Hermitian observables have negligible imaginary expectation;
33- populations remain within tolerance of `[0, 1]`;
34- symmetry, conserved quantity, or analytic-limit checks hold where applicable;
35- numerical tolerance is smaller than the effect being claimed.
37Do not repair a state by clipping eigenvalues or renormalizing unless that
38post-processing is part of a documented method and its impact is reported.

Analysis starts with invariants

For every reported state, record quantitative checks before interpreting an observable:

import numpy as np

def density_audit(rho, tolerance=1e-9):
    if not rho.isoper:
        raise ValueError("density_audit requires an operator")
    hermitian = bool(rho.isherm)
    eigenvalues = np.linalg.eigvalsh(rho.full()) if hermitian else None
    trace = complex(rho.tr())
    return {
        "is_hermitian": hermitian,
        "trace_error": float(abs(trace - 1.0)),
        "minimum_eigenvalue": float(eigenvalues.min()) if hermitian else None,
        "positive_within_tolerance": hermitian and bool(eigenvalues.min() >= -tolerance),
    }

Also check:

  • state.dims matches every observable and the declared subsystem order;
  • ket norm or density-matrix trace stays stable over time;
  • Hermitian observables have negligible imaginary expectation;
  • populations remain within tolerance of [0, 1];
  • symmetry, conserved quantity, or analytic-limit checks hold where applicable;
  • numerical tolerance is smaller than the effect being claimed.

Do not repair a state by clipping eigenvalues or renormalizing unless that post-processing is part of a documented method and its impact is reported.

Raw lines 6 to 39
40## Expectations and uncertainty
42```python
43from qutip import expect, num, variance
45n_op = num(N)
46mean_n = expect(n_op, rho)
47variance_n = variance(n_op, rho)
48```
50For solver output, dict-form `e_ops` gives named `result.e_data`:
52```python
53result = mesolve(
54 H,
55 rho0,
56 tlist,
57 c_ops=c_ops,
58 e_ops={"number": n_op, "energy": H},
60number_vs_time = result.e_data["number"]
61```
63For Monte Carlo/stochastic results, report both ensemble means and sampling
64uncertainty. `std_expect` is trajectory spread, not automatically the standard
65error; a simple independent-trajectory standard error scales as
66`std / sqrt(ntraj)`, subject to the solver's sampling design.

Expectations and uncertainty

from qutip import expect, num, variance

n_op = num(N)
mean_n = expect(n_op, rho)
variance_n = variance(n_op, rho)

For solver output, dict-form e_ops gives named result.e_data:

result = mesolve(
    H,
    rho0,
    tlist,
    c_ops=c_ops,
    e_ops={"number": n_op, "energy": H},
)
number_vs_time = result.e_data["number"]

For Monte Carlo/stochastic results, report both ensemble means and sampling uncertainty. std_expect is trajectory spread, not automatically the standard error; a simple independent-trajectory standard error scales as std / sqrt(ntraj), subject to the solver's sampling design.

Raw lines 40 to 67
68## Entropy, purity, and distances
70```python
71from qutip import entropy_linear, entropy_vn, fidelity, tracedist
73von_neumann_nats = entropy_vn(rho) # default natural-log base
74von_neumann_bits = entropy_vn(rho, base=2)
75linear_entropy = entropy_linear(rho)
76purity = float((rho * rho).tr().real)
77state_fidelity = fidelity(rho, sigma)
78trace_distance = tracedist(rho, sigma)
79```
81Always state the logarithm base. QuTiP `fidelity` returns root fidelity
82`Tr(sqrt(sqrt(rho) * sigma * sqrt(rho)))`; for two kets this is absolute
83overlap. Square it only when the comparison uses the squared convention.
85For bipartite entropy:
87```python
88rho_A = rho_AB.ptrace(0) # keep subsystem 0
89entanglement_entropy = entropy_vn(rho_A, base=2)
90```
92This is an entanglement entropy only when the global bipartite state and the
93chosen measure meet the necessary assumptions. For mixed states, reduced-state
94entropy also contains classical mixture.
96Common specialized functions include `concurrence`, `negativity`,
97`entropy_mutual`, and `partial_transpose`. Verify their supported dimensions and
98argument definitions in the current API before applying them.

Entropy, purity, and distances

from qutip import entropy_linear, entropy_vn, fidelity, tracedist

von_neumann_nats = entropy_vn(rho)          # default natural-log base
von_neumann_bits = entropy_vn(rho, base=2)
linear_entropy = entropy_linear(rho)
purity = float((rho * rho).tr().real)
state_fidelity = fidelity(rho, sigma)
trace_distance = tracedist(rho, sigma)

Always state the logarithm base. QuTiP fidelity returns root fidelity Tr(sqrt(sqrt(rho) * sigma * sqrt(rho))); for two kets this is absolute overlap. Square it only when the comparison uses the squared convention.

For bipartite entropy:

rho_A = rho_AB.ptrace(0)  # keep subsystem 0
entanglement_entropy = entropy_vn(rho_A, base=2)

This is an entanglement entropy only when the global bipartite state and the chosen measure meet the necessary assumptions. For mixed states, reduced-state entropy also contains classical mixture.

Common specialized functions include concurrence, negativity, entropy_mutual, and partial_transpose. Verify their supported dimensions and argument definitions in the current API before applying them.

Raw lines 68 to 99
100## Steady-state calculation
102Current signature:
104```text
105steadystate(A, c_ops=[], *, method="direct", solver=None, **kwargs)
106```
108`A` may be a Hamiltonian or a Liouvillian. Available high-level methods include
109`direct`, `eigen`, `svd`, `power`, and `propagator`; linear-system solver choices
110are separate.
112```python
113from qutip import liouvillian, operator_to_vector, steadystate
115rho_ss = steadystate(H, c_ops, method="direct")
116L = liouvillian(H, c_ops)
117residual = (L * operator_to_vector(rho_ss)).norm()
118```
120Report:
122- residual norm and normalization error;
123- Hermiticity and minimum eigenvalue;
124- method and linear solver;
125- matrix/data representation and relevant tolerances;
126- whether the zero eigenvalue is unique;
127- comparison with long-time evolution from more than one initial state when
128 uniqueness matters.
130A small residual does not prove uniqueness or physicality. Degenerate steady
131spaces require analysis of the Liouvillian nullspace and initial-state
132dependence.
134The `svd` method is dense and intended for small systems. Sparse/direct methods
135can still be memory intensive; monitor fill-in and compare methods on a reduced
136model.
138For periodically driven systems, a static `steadystate` call is generally not
139the desired asymptotic object. Use an appropriate periodic/Floquet approach.
140In QuTiP 5.3, `steadystate_fourier` is the current name for the specialized
141cosine-driven Fourier solver; `steadystate_floquet` is deprecated.

Steady-state calculation

Current signature:

steadystate(A, c_ops=[], *, method="direct", solver=None, **kwargs)

A may be a Hamiltonian or a Liouvillian. Available high-level methods include direct, eigen, svd, power, and propagator; linear-system solver choices are separate.

from qutip import liouvillian, operator_to_vector, steadystate

rho_ss = steadystate(H, c_ops, method="direct")
L = liouvillian(H, c_ops)
residual = (L * operator_to_vector(rho_ss)).norm()

Report:

  • residual norm and normalization error;
  • Hermiticity and minimum eigenvalue;
  • method and linear solver;
  • matrix/data representation and relevant tolerances;
  • whether the zero eigenvalue is unique;
  • comparison with long-time evolution from more than one initial state when uniqueness matters.

A small residual does not prove uniqueness or physicality. Degenerate steady spaces require analysis of the Liouvillian nullspace and initial-state dependence.

The svd method is dense and intended for small systems. Sparse/direct methods can still be memory intensive; monitor fill-in and compare methods on a reduced model.

For periodically driven systems, a static steadystate call is generally not the desired asymptotic object. Use an appropriate periodic/Floquet approach. In QuTiP 5.3, steadystate_fourier is the current name for the specialized cosine-driven Fourier solver; steadystate_floquet is deprecated.

Raw lines 100 to 142
143## Two-time correlations
145Current stationary/transient two-operator API:
147```python
148from qutip import correlation_2op_1t, correlation_2op_2t
150corr_1t = correlation_2op_1t(
151 H,
152 rho0,
153 taulist,
154 c_ops,
155 a_op,
156 b_op,
157 solver="me",
158 options={"atol": 1e-10, "rtol": 1e-8},
161corr_2t = correlation_2op_2t(
162 H,
163 rho0,
164 tlist,
165 taulist,
166 c_ops,
167 a_op,
168 b_op,
170```
172For `correlation_2op_1t`, the quantity is ordered according to the function's
173documented \(A(\tau)B(0)\)-style convention. Do not infer operator order from a
174variable name.
176Passing `state0=None` requests a steady-state initial condition only for
177supported constant systems with collapse operators. Compute and audit the
178steady state explicitly when provenance matters.
180Current three-operator entry points include:
182```python
183from qutip import correlation_3op, correlation_3op_1t, correlation_3op_2t
184```
186QuTiP 5.3 added `max_t_plus_tau` and mapping controls to selected two-time and
187three-operator routines. The old `correlation_4op_1t` recipe is not a current
188public API; express a four-operator quantity through the documented
189three-operator interfaces when mathematically appropriate, or derive a tested
190regression workflow.
192Correlation checks:
194- operator ordering and adjoints;
195- transient versus stationary definition;
196- normalized versus unnormalized coherence;
197- regression-theorem assumptions;
198- convergence of both `tlist` and `taulist`;
199- tail decay before finite-window transforms.

Two-time correlations

Current stationary/transient two-operator API:

from qutip import correlation_2op_1t, correlation_2op_2t

corr_1t = correlation_2op_1t(
    H,
    rho0,
    taulist,
    c_ops,
    a_op,
    b_op,
    solver="me",
    options={"atol": 1e-10, "rtol": 1e-8},
)

corr_2t = correlation_2op_2t(
    H,
    rho0,
    tlist,
    taulist,
    c_ops,
    a_op,
    b_op,
)

For correlation_2op_1t, the quantity is ordered according to the function's documented \(A(\tau)B(0)\)-style convention. Do not infer operator order from a variable name.

Passing state0=None requests a steady-state initial condition only for supported constant systems with collapse operators. Compute and audit the steady state explicitly when provenance matters.

Current three-operator entry points include:

from qutip import correlation_3op, correlation_3op_1t, correlation_3op_2t

QuTiP 5.3 added max_t_plus_tau and mapping controls to selected two-time and three-operator routines. The old correlation_4op_1t recipe is not a current public API; express a four-operator quantity through the documented three-operator interfaces when mathematically appropriate, or derive a tested regression workflow.

Correlation checks:

  • operator ordering and adjoints;
  • transient versus stationary definition;
  • normalized versus unnormalized coherence;
  • regression-theorem assumptions;
  • convergence of both tlist and taulist;
  • tail decay before finite-window transforms.
Raw lines 143 to 200
201## Direct stationary spectrum
203Current signature:
205```text
206spectrum(H, wlist, c_ops, a_op, b_op, solver="es")
207```
209```python
210import numpy as np
211from qutip import spectrum
213wlist = np.linspace(-5.0, 5.0, 1001)
214S = spectrum(H, wlist, c_ops, a_op, b_op, solver="es")
215```
217The function computes the Fourier transform of a **steady-state** correlation.
218Supported solver strategies include exponential-series (`"es"`),
219pseudo-inverse (`"pi"`), and generic linear solve (`"solve"`).
221QuTiP 5 removed public `spectrum_ss` and `spectrum_pi`. Select the strategy with
222the `solver` argument to `spectrum`; do not call the removed functions.
224Audit:
226- stationarity and steady-state uniqueness;
227- angular-frequency units;
228- operator order;
229- whether the spectrum is symmetrized, one-sided, or normally ordered;
230- negative-frequency interpretation and thermal detailed balance;
231- frequency window/resolution;
232- convergence across solver strategies near singular points.

Direct stationary spectrum

Current signature:

spectrum(H, wlist, c_ops, a_op, b_op, solver="es")
import numpy as np
from qutip import spectrum

wlist = np.linspace(-5.0, 5.0, 1001)
S = spectrum(H, wlist, c_ops, a_op, b_op, solver="es")

The function computes the Fourier transform of a steady-state correlation. Supported solver strategies include exponential-series ("es"), pseudo-inverse ("pi"), and generic linear solve ("solve").

QuTiP 5 removed public spectrum_ss and spectrum_pi. Select the strategy with the solver argument to spectrum; do not call the removed functions.

Audit:

  • stationarity and steady-state uniqueness;
  • angular-frequency units;
  • operator order;
  • whether the spectrum is symmetrized, one-sided, or normally ordered;
  • negative-frequency interpretation and thermal detailed balance;
  • frequency window/resolution;
  • convergence across solver strategies near singular points.
Raw lines 201 to 233
234## FFT of a sampled correlation
236Current signature:
238```text
239spectrum_correlation_fft(tlist, y, inverse=False)
240```
242```python
243from qutip import spectrum_correlation_fft
245frequencies, spectrum_values = spectrum_correlation_fft(taulist, corr)
246```
248Before trusting peaks:
2501. require a uniform, strictly increasing `taulist`;
2512. verify the correlation has decayed at the end of the window;
2523. double the time window to test frequency resolution;
2534. halve the timestep to test aliasing and high-frequency content;
2545. compare window functions and disclose any window applied outside QuTiP;
2556. check forward/inverse sign and normalization conventions against an analytic
256 signal;
2577. avoid interpreting zero-padding as additional physical resolution.
259Use a direct `spectrum` calculation as a cross-check when its steady-state
260assumptions apply.

FFT of a sampled correlation

Current signature:

spectrum_correlation_fft(tlist, y, inverse=False)
from qutip import spectrum_correlation_fft

frequencies, spectrum_values = spectrum_correlation_fft(taulist, corr)

Before trusting peaks:

  1. require a uniform, strictly increasing taulist;
  2. verify the correlation has decayed at the end of the window;
  3. double the time window to test frequency resolution;
  4. halve the timestep to test aliasing and high-frequency content;
  5. compare window functions and disclose any window applied outside QuTiP;
  6. check forward/inverse sign and normalization conventions against an analytic signal;
  7. avoid interpreting zero-padding as additional physical resolution.

Use a direct spectrum calculation as a cross-check when its steady-state assumptions apply.

Raw lines 234 to 261
262## Liouvillian and eigenvalue diagnostics
264```python
265eigenvalues = L.eigenenergies()
266gap_candidates = sorted(
267 (-value.real for value in eigenvalues if value.real < -1e-12)
269```
271Liouvillian spectra are non-Hermitian in general. Eigenvalue conditioning,
272degeneracy, and sparse solver targeting can make naive sorting misleading.
273Verify left/right eigenvector conventions and residuals before interpreting a
274spectral gap.
276For Hamiltonians:
278```python
279energies, states = H.eigenstates()
280ground_energy, ground_state = H.groundstate()
281```
283Track basis and units, handle degeneracy explicitly, and sweep truncation before
284claiming spectral convergence.

Liouvillian and eigenvalue diagnostics

eigenvalues = L.eigenenergies()
gap_candidates = sorted(
    (-value.real for value in eigenvalues if value.real < -1e-12)
)

Liouvillian spectra are non-Hermitian in general. Eigenvalue conditioning, degeneracy, and sparse solver targeting can make naive sorting misleading. Verify left/right eigenvector conventions and residuals before interpreting a spectral gap.

For Hamiltonians:

energies, states = H.eigenstates()
ground_energy, ground_state = H.groundstate()

Track basis and units, handle degeneracy explicitly, and sweep truncation before claiming spectral convergence.

Raw lines 262 to 285
286## Convergence matrix
288Vary one numerical control at a time, then perform selected joint checks:
290| Control | Typical comparison |
291|---|---|
292| Hilbert cutoff | observables and boundary occupation |
293| output grid | interpolated trace/peak/FFT quantities |
294| `atol`, `rtol` | endpoint and maximum trajectory differences |
295| integrator | representative observable and invariant differences |
296| simulation duration | steady-state distance and correlation tail |
297| frequency range/spacing | peak location, area, and edge sensitivity |
298| trajectories | mean, uncertainty, and seed sensitivity |
299| `sec_cutoff` | positivity and observable stability |
300| HEOM depth/exponents | reduced state and target observable |
302Define acceptance thresholds before looking at the final comparison. Report
303absolute and relative differences and handle near-zero denominators explicitly.

Convergence matrix

Vary one numerical control at a time, then perform selected joint checks:

ControlTypical comparison
Hilbert cutoffobservables and boundary occupation
output gridinterpolated trace/peak/FFT quantities
atol, rtolendpoint and maximum trajectory differences
integratorrepresentative observable and invariant differences
simulation durationsteady-state distance and correlation tail
frequency range/spacingpeak location, area, and edge sensitivity
trajectoriesmean, uncertainty, and seed sensitivity
sec_cutoffpositivity and observable stability
HEOM depth/exponentsreduced state and target observable

Define acceptance thresholds before looking at the final comparison. Report absolute and relative differences and handle near-zero denominators explicitly.

Raw lines 286 to 304
305## Portable result audit
307`../scripts/result_audit.py` reads only bounded strict JSON. It checks schema,
308version, finite values, monotonic time grids, population bounds, analytic
309reference error when available, convergence deltas, and whether assumptions,
310seeds, and solver stats were recorded. It does not load QuTiP result files or
311other Python-object serialization. Its pass status means report consistency,
312not numerical convergence or scientific validity; separately inspect analytic
313error, invariant checks, and the sweep acceptance status.
315`../scripts/steady_state_spectrum_planner.py` produces a bounded plan for
316steady-state and direct/FFT spectrum checks without running a model.

Portable result audit

../scripts/result_audit.py reads only bounded strict JSON. It checks schema, version, finite values, monotonic time grids, population bounds, analytic reference error when available, convergence deltas, and whether assumptions, seeds, and solver stats were recorded. It does not load QuTiP result files or other Python-object serialization. Its pass status means report consistency, not numerical convergence or scientific validity; separately inspect analytic error, invariant checks, and the sweep acceptance status.

../scripts/steady_state_spectrum_planner.py produces a bounded plan for steady-state and direct/FFT spectrum checks without running a model.

Raw lines 305 to 317
Runs code scripts/result_audit.py :307
Runs code scripts/steady_state_spectrum_planner.py :315
318## Sources (verified 2026-10-01)
320- [Solver, correlation, spectrum, and steady-state API](https://qutip.readthedocs.io/en/stable/apidoc/solver.html)
321- [Steady-state guide](https://qutip.readthedocs.io/en/stable/guide/guide-steady.html)
322- [Correlation guide](https://qutip.readthedocs.io/en/stable/guide/guide-correlation.html)
323- [Quantum-object API](https://qutip.readthedocs.io/en/stable/apidoc/quantumobject.html)
324- [QuTiP 5.3.1 release notes](https://github.com/qutip/qutip/releases/tag/v5.3.1)
325- [QuTiP 5 changelog](https://qutip.readthedocs.io/en/stable/changelog.html)