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 and heterogeneous problem lists 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

is_native_state(state)[source]

Return whether state is a QuTiP quantum object.

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

steadystate(problem)[source]

Solve a static Lindblad generator with qutip.steadystate().

Parameters:

problem (Any)

Return type:

SteadyStateSolverResult

parallel_solve_problems(problems, *, progress=True)[source]

Solve large structurally heterogeneous problem lists through loky workers.

Parameters:
Return type:

list[SolverResult] | None

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 EngineResult 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 avoids cubic-spline error from pre-sampling the full envelope·carrier product, including for resonant carriers in the lab frame.

Parameters:
  • description (Any)

  • tlist (Any | None)

Return type:

PreparedHamiltonian

prepare_batch(batch)[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 larger concrete sweeps can build and solve each point inside loky workers.

Parameters:

batch (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