quchip.backend.qutip

QuTiP backend implementation of the quchip.backend.protocol contract.

QuTiP stores operators as qutip.Qobj (sparse CSR by default, dense on request). This backend lowers engine IR into Qobj / QobjEvo form and dispatches the sesolve/mesolve drivers directly.

Batched sweeps are parallelized via the loky reusable process pool — QuTiP solvers release the GIL only partially, so processes beat threads. Final QobjEvo assembly happens inside the workers so the main process never pays for it.

References

  • Johansson, Nation, Nori — QuTiP 2, Comput. Phys. Commun. 183, 1760 (2012)

  • Breuer & Petruccione — The Theory of Open Quantum Systems (OUP, 2002)

Classes

QuTiPBackend()

Concrete backend backed by QuTiP.

class quchip.backend.qutip.QuTiPBackend[source]

Bases: Backend

Concrete backend backed by QuTiP. Operator = State = qutip.Qobj.

Example

>>> from quchip.backend.qutip import QuTiPBackend
>>> backend = QuTiPBackend()
>>> a = backend.destroy(3)
>>> float((a.dag() * a).diag()[2].real)
2.0
property array_module: Any

Return the array module for backend-aware numeric code (numpy).

to_array(op)[source]

Return a dense numpy array for op.

Parameters:

op (Any)

Return type:

Any

overlap(a, b)[source]

Return the scalar inner product ⟨a|b⟩ for two kets.

Parameters:
Return type:

complex

norm(state_or_op)[source]

Return the norm of a state or operator via Qobj.norm.

Parameters:

state_or_op (Any)

Return type:

float

trace(op)[source]

Return the scalar trace Tr(op) via Qobj.tr.

Parameters:

op (Any)

Return type:

complex

destroy(n)[source]

Return the annihilation operator for an n-level Fock space (qutip.destroy, memoized).

Parameters:

n (int)

Return type:

Any

create(n)[source]

Return the creation operator for an n-level Fock space (qutip.create, memoized).

Parameters:

n (int)

Return type:

Any

number(n)[source]

Return the number operator for an n-level Fock space (qutip.num, memoized).

Parameters:

n (int)

Return type:

Any

identity(n)[source]

Return the identity operator for an n-level space (qutip.qeye, memoized).

Parameters:

n (int)

Return type:

Any

from_array(data, dims=None)[source]

Construct a Qobj from a dense matrix with optional row/col dims.

Parameters:
Return type:

Any

to_canonical_operator(op)[source]

Serialize a Qobj into the backend-agnostic canonical IR (CSR or dense).

Parameters:

op (Any)

Return type:

Any

from_canonical_operator(canonical)[source]

Reconstruct a Qobj from the canonical IR payload.

Parameters:

canonical (Any)

Return type:

Any

coerce_operator(op)[source]

Wrap an array-like operator into a Qobj (native Qobj passthrough).

Parameters:

op (Any)

Return type:

Any

dag(op)[source]

Return the Hermitian conjugate op† via Qobj.dag (array-likes coerced first).

Parameters:

op (Any)

Return type:

Any

eigenenergies(op)[source]

Return the ascending eigenvalues of a Hermitian operator (Qobj.eigenenergies).

Parameters:

op (Any)

Return type:

Any

eigensystem_data(op)[source]

Return ascending eigenvalues, eigenvector matrix, and primed eigenstate kets.

A Qobj uses Qobj.eigenstates verbatim to preserve the exact degenerate-subspace basis; dense inputs fall through to the protocol default.

Parameters:

op (Any)

Return type:

EigensystemData

expect(op, state)[source]

Return the expectation value ⟨op⟩ for a ket or density matrix via qutip.expect.

Parameters:
Return type:

complex

ptrace(state, keep, dims)[source]

Reduce onto subsystem(s) keep via Qobj.ptrace (partial trace).

Rebuilds the composite dims when the incoming state carries flat dims.

Parameters:
Return type:

Any

permute_state(state, dims, order)[source]

Reorder a Qobj’s subsystems via Qobj.permute (sparse-friendly, keeps dims).

Rebuilds composite dims first when the incoming state carries flat dims, mirroring ptrace(). Non-Qobj inputs fall through to the protocol default.

Parameters:
Return type:

Any

tensor(*operators)[source]

Return the tensor product of operators via qutip.tensor (array-likes coerced first).

Parameters:

operators (Any)

Return type:

Any

embed_two_body(op_ab, index_a, index_b, dims)[source]

Embed a two-body operator on devices index_aindex_b into the full space.

Reorders subsystems (via sparse SWAP when index_a > index_b) and identity-pads spectators without densifying the full matrix.

Parameters:
Return type:

Any

basis(n, k)[source]

Return the Fock basis ket |k⟩ in an n-level space (qutip.basis).

Parameters:
Return type:

Any

tensor_states(*states)[source]

Return the tensor product of states via qutip.tensor.

Parameters:

states (Any)

Return type:

Any

coherent(n, alpha)[source]

Return the coherent state |α⟩ truncated to n Fock levels (qutip.coherent).

Parameters:
Return type:

Any

state_to_dm(state)[source]

Return a density matrix; pass through if state is already one.

Parameters:

state (Any)

Return type:

Any

is_ket(state)[source]

Return whether state is a ket rather than a density matrix (Qobj.isket).

Parameters:

state (Any)

Return type:

bool

resolve_solver_options(options, *, metadata, tlist)[source]

Fill in nsteps and max_step from Hamiltonian-metadata heuristics when unset.

nsteps is an abort ceiling on the integrator’s total internal step count, not a step-size bound — see default_solver_steps() for the heuristic that derives it from the Hamiltonian’s fastest frequency scale.

max_step bounds the size of an individual adaptive step. Without it, QuTiP’s adaptive integrator can step clean over a finite-support pulse that sits inside a long idle span, sampling it only at points where its envelope happens to be near zero. When the user has not set max_step and metadata["max_step_ns"] carries a concrete, positive, finite value — half the narrowest window across the Hamiltonian’s dynamic terms, computed by _solver_hint_metadata() — it is used as the ceiling. An explicit user max_step is always authoritative, including QuTiP’s own max_step=0 (unbounded): the key’s presence in options decides, not its truthiness.

This is QuTiP’s single option-merge boundary (shared by the single and batched solve paths), so the dynamiqs-only gradient knob — which QuTiP’s SolverOptions rejects — is stripped here exactly once for portability; downstream runners trust the merged dict.

Parameters:
Return type:

dict[str, Any]

coerce_state(state, dims=None)[source]

Wrap a foreign-native array state (ket or density matrix) into a Qobj.

Used when a per-call backend="qutip" override consumes states built under the dynamiqs backend. dims restores the tensor structure QuTiP’s solvers require to match the Hamiltonian’s dims.

Parameters:
Return type:

Any

sesolve(H, psi0, tlist, e_ops=None, options=None)[source]

Solve the Schrödinger equation — wraps qutip.solver.sesolve.SESolver.

Parameters:
Return type:

SolverResult

mesolve(H, rho0, tlist, c_ops=None, e_ops=None, options=None)[source]

Solve the Lindblad master equation — wraps qutip.solver.mesolve.MESolver.

Parameters:
Return type:

SolverResult

batched_sesolve(problems, *, n_jobs=-1, progress=True)[source]

Run sesolve in parallel via loky workers; fall back to sequential on failure.

Parameters:
Return type:

list[SolverResult]

batched_mesolve(problems, *, n_jobs=-1, progress=True)[source]

Run mesolve in parallel via loky workers; fall back to sequential on failure.

Parameters:
Return type:

list[SolverResult]

prepare_hamiltonian(description, tlist=None)[source]

Convert a HamiltonianDescription into a Qobj or QobjEvo.

Each dynamic coefficient is band-normalized: every carrier stays analytic while only its slow, carrier-free envelope is sampled (on tlist, locally densified around any window edge — see _band_coefficient() / _augmented_sample_grid()). This is exact regardless of how resonant a carrier is — the lab frame no longer accumulates the cubic-spline error that pre-sampling the full envelope·carrier product caused.

Parameters:
  • description (Any)

  • tlist (Any | None)

Return type:

PreparedHamiltonian

prepare_batch(description, tlist)[source]

Build a deferred-construction batch; per-element QobjEvo is built in workers.

Each unique CanonicalOperator is converted exactly once (shared across elements) and only the slow, carrier-free envelope is sampled on the user grid, locally densified around any window edge (carriers stay analytic — see _band_coefficient()). Final QobjEvo assembly lives in solve_batch() so it runs inside loky workers, keeping the main process overhead O(1) in batch size.

Parameters:
  • description (Any)

  • tlist (Any)

Return type:

DeferredBatch

solve_batch(batch, *, progress=True)[source]

Solve a SolveBatch with per-element QobjEvo built in loky workers.

Parameters:
Return type:

list[SolverResult]

warmup(n_jobs=-1)[source]

Pre-spawn the reusable loky worker pool out of any timed region.

Entirely optional: solve_batch spins the pool up on demand, so the only reason to call this is to move that one-time spin-up cost out of a region you are timing (benchmarks). Ordinary scripts and notebooks never need it.

Forks the workers and pays the per-worker import cost up front by mapping a no-op over the worker count. A subsequent sweep then reuses the live pool instead of paying cold spin-up inside the timed solve. No-op (with a warning) when loky is unavailable.

Parameters:

n_jobs (int)

Return type:

None