Skip to main content

Stochastic bundling of Lindblad operators for QuTiP

Project description

qutip-bundling

Stochastic bundling of Lindblad operators for QuTiP.

Implements the stochastically bundled dissipator of

S. Adhikari and R. Baer, Stochastically Bundled Dissipators for the Quantum Master Equation, J. Chem. Theory Comput. 2025, 21, 4142–4150. https://doi.org/10.1021/acs.jctc.5c00145

It is an independent package that builds on QuTiP — install alongside QuTiP and use with the solvers you already know.

What it does

A Lindblad master equation with a large number $N_L$ of collapse operators $c_\alpha = \sqrt{\gamma_\alpha}\ L_\alpha$ is expensive: the dissipator costs one matrix product per operator, and $N_L$ typically grows as the square of the Hilbert-space dimension.

The dissipator is quadratic in the operators, so it can be reproduced in expectation by a much smaller set of $$M$$ randomly bundled operators:

$$ R_m = \frac{1}{\sqrt{M}} \sum_\alpha r_{m,\alpha}, c_\alpha $$

where the $r_{m,\alpha}$ are independent random numbers with zero mean and unit second moment. The bundled dissipator is an unbiased estimator of the full one for any $$M$$, and the bundled operators still have Lindblad form, so the dynamics stay completely positive and trace preserving.

Pair that with the deterministic Davies Lamb-shift Hamiltonian (built from the bare $L_\alpha$, never bundled), and a single bundled mesolve call reproduces full master-equation dynamics with $$M$$ independent of the Hilbert-space dimension — per-step cost drops from $$O(N^{5})$$ to $$O(N^{3})$$.

Benchmarks

On a dissipative spin chain, full mesolve (paying for all $$N_{L}$$ Lindblad operators) becomes infeasible as the system grows, while bundling — using only $$M$$ of them — keeps going at a fraction of the cost:

cost versus system size

Below the crossover the full solve is the better choice; above it, bundling is what lets the calculation finish. Against QuTiP's trajectory solver mcsolve, bundling reaches the same accuracy for less compute on both test systems. See benchmarks/BENCHMARKS.md for the full study — both systems defined, accuracy-versus-$$M$$ convergence, and the accuracy-versus-cost frontier — all reproducible with the scripts in that folder. The benchmark scripts also accept optional Davies-construction thresholds, e.g. COUPLING_THRESHOLD=1e-6, so you can separate build-time operator pruning from the bundling parameter $$M$$.

Install

pip install qutip-bundling           
# or, from a checkout:
pip install -e ".[examples,test]"

API: three operator transforms plus optional solver helpers

The easiest entry point is davies_operators, which builds the collapse operators directly from a Hamiltonian H and a system-bath coupling operator X, with the correct sign convention baked in (see CONVENTIONS.md -- getting the sign wrong silently runs the dynamics backwards):

from qutip_bundling import davies_operators, bundle, rk4_mesolve

c_ops = davies_operators(H, X, gamma)          # gamma(omega) callable
R = bundle(c_ops, M=8, rng=0)                  # M bundled operators
res = rk4_mesolve(H, rho0, tlist, c_ops=R, e_ops=e_ops)

Under the hood the method is three pure operator transforms that return operators, not solver results:

Function Purpose
davies_operators(H, X, gamma) recommended for Davies/Bohr setups -- build collapse operators from H and coupling op X, correct sign baked in
build_collapse_ops(L_ops, omegas, gamma) bare L_alpha and the spectral function -> standard collapse operators sqrt(gamma) * L_alpha
bundle(c_ops, M, ...) the method -- any collapse-operator list -> M randomly bundled operators
lamb_shift_hamiltonian(L_ops, omegas, imag_gamma) bare L_alpha and Im gamma -> Lamb-shift Hamiltonian H_LS = sum_alpha Im(gamma(omega_alpha)) * L_alpha^dag L_alpha (built once)

Spectral inputs (gamma, imag_gamma) may be either a callable f(omega) -> float or an array aligned with the Bohr frequencies.

Bringing your own operators (bundling is not tied to Davies)

bundle is the actual method, and it takes any list of collapse operators — it does not care how they were built. The bundling is a purely algebraic operation: it replaces $$N_{L}$$ operators $$c_{alpha}$$ with $$M$$ random linear combinations

$$ R_m = \frac{1}{\sqrt{M}} \sum_\alpha r_{m,\alpha}\ c_\alpha , $$

so that the bundled dissipator $$\sum_{m} D[R_{m}]$$ equals the full dissipator $$\sum_{\alpha} D[c_{\alpha}]$$ in expectation. Nothing in that statement assumes the Davies construction, an ohmic bath, or even a master equation derived from a microscopic model.

So if your collapse operators come from somewhere else — a Redfield equation mapped to Lindblad form, a phenomenological model, quantum-optics jump operators, local Lindbladians on a lattice, or operators that already include their rates — you skip davies_operators and build_collapse_ops entirely and bundle them directly:

from qutip_bundling import bundle, mesolve_ensemble

my_c_ops = [...]                       # any collapse operators you like
R = bundle(my_c_ops, M=8, rng=0)
res = qutip.mesolve(H, rho0, tlist, c_ops=R, e_ops=e_ops)

# or with averaging + error bars:
ens = mesolve_ensemble(H, rho0, tlist, my_c_ops, M=8,
                       e_ops=e_ops, n_realizations=32)

davies_operators is provided because the Davies/Bohr construction is the common case and the sign convention is easy to get wrong — but it is one optional way to produce an operator list, not a requirement of the method.

Any bath spectral function

When you do use davies_operators or build_collapse_ops, the spectral function $$\gamma(\omega)$$ is arbitrary — the library never assumes a particular form (the ohmic example in the demos and tests is just an example). Pass any callable or array. For physically sensible relaxation to the Gibbs state it should be non-negative and satisfy detailed balance $$\gamma(-\omega)/\gamma(\omega) = exp(-\omega/kT)$$ (see CONVENTIONS.md). For instance a Drude–Lorentz bath:

def gamma_drude(omega, lam=0.5, gamma_c=2.0, kT=1.0):
    # spectral density J(omega) = 2*lam*gamma_c*omega / (omega**2 + gamma_c**2)
    if abs(omega) < 1e-12:
        return 2.0 * lam * kT / gamma_c          # classical/zero-frequency limit
    J = 2.0 * lam * gamma_c * omega / (omega**2 + gamma_c**2)
    return J / (1.0 - math.exp(-omega / kT))     # detailed-balance weighting

c_ops = davies_operators(H, X, gamma_drude)

A microscopically-derived construction may not even factor into operators x $$\gamma(\omega)$$ — some methods hand you collapse operators with the rates already included and no separate spectral function. Those just go straight into bundle.

A convenience composes the three for the common case:

from qutip_bundling import prepare_bundled_dynamics
import qutip

bundled = prepare_bundled_dynamics(
    L_ops, omegas, gamma=gamma, imag_gamma=imag_gamma, M=8, rng=0,
)
result = qutip.mesolve(
    H_sys + bundled.H_lamb_shift, rho0, tlist,
    c_ops=bundled.c_ops, e_ops=e_ops,
)

For ensemble averaging with error bars:

from qutip_bundling import mesolve_ensemble

ens = mesolve_ensemble(H_total, rho0, tlist, c_ops_full,
                       M=8, e_ops=e_ops, n_realizations=32)

mean      = ens.expect[0]    # mean of observable 0 over realizations
error_bar = ens.sem[0]       # standard error of the mean (shrinks as 1/sqrt(n))
spread    = ens.std[0]       # standard deviation across trajectories (single-run noise)
raw       = ens.samples      # shape (n_realizations, n_e_ops, n_times)

# Everything else can be recomputed from raw -- your own fluctuation
# definition, percentiles, histograms -- since no information is discarded.

Advanced: bias reduction with the jackknife

A finite bundle size $$M$$ introduces an O(1/M) bias (eq. 14 of the paper). You can usually control it just by increasing $$M$$ or inspecting ens.samples directly. When you specifically want to cancel the leading bias term at fixed $$M$$, the package provides the jackknife-2 estimator (eqs. 15–16):

from qutip_bundling import mesolve_jackknife

jack = mesolve_jackknife(H_total, rho0, tlist, c_ops_full,
                         M=8, e_ops=e_ops, n_realizations=32)
# jack.expect[0] is bias-corrected; jack.extra["direct"] is the uncorrected mean

This is an optimization, not a required step — the core method is bundle → solve → average.

How the pieces fit together

Bundling is a Monte Carlo trick for the dissipator only. The Davies Lamb-shift Hamiltonian $$H_{LS}$$ is a deterministic part of the renormalized Hamiltonian (eq. 4 of the paper) — it's built once from the bare $L_\alpha$, added to the system Hamiltonian, and never enters the bundling step. If $$\mathrm{Im}{\gamma}$$ is identically zero (the paper's test cases), $$H_{LS}=0$$ and the package reduces to the dissipator-only case.

Because the API returns operators rather than wrapping a solver, the same building blocks work with qutip.mesolve, mcsolve, smesolve, or any custom propagator.

Tutorial

A step-by-step Jupyter notebook is in examples/tutorial.ipynb. It builds a system, bundles its operators, shows convergence to the deterministic answer as $$M$$ grows, shows the standard deviation vs standard error, and uses the native backend. Every cell is executed and its output checked.

Validation

examples/oscillator_demo.py builds an open system with many Davies/Bohr Lindblad operators and a non-zero Lamb shift, and verifies all three guarantees:

  1. The bundled dissipator is an unbiased estimator of the full one (error decays as 1/sqrt(samples)).
  2. The Lamb shift is deterministic, Hermitian, and unchanged by any bundling call.
  3. A bundled mesolve with M = 8 plus $$H_{LS}$$ reproduces the dynamics of the full operator set within the dynamic range of the observables.

Acknowledgments

Developed in the group of Prof. Roi Baer at the Fritz Haber Center for Molecular Dynamics, Institute of Chemistry, The Hebrew University of Jerusalem.

License

BSD-3-Clause.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

qutip_bundling-0.6.2.tar.gz (172.5 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

qutip_bundling-0.6.2-py3-none-any.whl (22.1 kB view details)

Uploaded Python 3

File details

Details for the file qutip_bundling-0.6.2.tar.gz.

File metadata

  • Download URL: qutip_bundling-0.6.2.tar.gz
  • Upload date:
  • Size: 172.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.9

File hashes

Hashes for qutip_bundling-0.6.2.tar.gz
Algorithm Hash digest
SHA256 2864b7b27b4ce19d57bdd73a0016183e200ae82c6bf7a9732b9428987d21c404
MD5 50476a6fe4f32cea348ffb9df66b4a62
BLAKE2b-256 e9712fe4d0bb6daa6dcc35f6fa54d81c0c3927ff39523c2a0cb0304798c847ae

See more details on using hashes here.

File details

Details for the file qutip_bundling-0.6.2-py3-none-any.whl.

File metadata

  • Download URL: qutip_bundling-0.6.2-py3-none-any.whl
  • Upload date:
  • Size: 22.1 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.9

File hashes

Hashes for qutip_bundling-0.6.2-py3-none-any.whl
Algorithm Hash digest
SHA256 f8b32fb0928689356f628d14a64e9ca32548ff2585d3a830e7971822123e5b36
MD5 1d47a0b7a46328cb4219f40561cfa120
BLAKE2b-256 0cff27946a00fc1bf297af79e3b544598fa43ffc6babd8e9334b5f80f8f62d2f

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page