quchip.results.results

Backend-agnostic wrapper around solver output.

This module is the contract between backends and users. The engine emits backend-agnostic physics descriptions, and each backend converts them into its own optimal solver-ready form; the result of solving is then re-wrapped here so that user code never has to know whether QuTiP or dynamiqs/JAX produced the numbers.

Key classes:

  • SimulationResult — one solve’s states, times, expectations, and partial-trace / population accessors.

  • SimulationBatchResult — ordered, immutable batch of results with helpers that stack across the batch for e.g. sweeps.

  • ObservableTrace — one named expectation-value trace, keeping the pre-processing and post-processing values side by side (band reconstruction, demodulation, etc.).

All helpers that return arrays stay inside the backend’s array module (JAX / NumPy) to preserve differentiability. The convenience wrappers SimulationResult.overlap() and SimulationResult.population() additionally materialize to a concrete numpy.ndarray when the underlying values are concrete (e.g. the QuTiP backend, or an eager dynamiqs call) — but return the backend-native array unchanged when concretization would break differentiability (a traced value under jax.jit/grad on the dynamiqs backend).

Functions

wrap_solver_result(solver_result, problem, ...)

Wrap a raw backend SolverResult into a user-facing SimulationResult.

wrap_solver_results_from_batch(...)

Wrap backend results from a batched solve that shares one SolveBatch context.

Classes

ObservableTrace(values, raw)

One named expectation-value trace, with pre- and post-processing values.

SimulationBatchResult(results, *[, shape, axes])

Ordered, immutable batch of SimulationResult with stacked helpers.

SimulationResult(solver_result, backend, dims, *)

Backend-agnostic container for the output of one solve.

class quchip.results.results.ObservableTrace(values, raw)[source]

Bases: object

One named expectation-value trace, with pre- and post-processing values.

Parameters:
values

Post-processed expectation values over time (e.g. demodulated, phase-corrected, band-summed). This is what user code normally wants.

Type:

Any

raw

The same quantity before post-processing — useful for debugging frame conventions, band decomposition, and demodulation.

Type:

Any

values: Any
raw: Any
class quchip.results.results.SimulationResult(solver_result, backend, dims, *, device_info=None, observable_traces=None)[source]

Bases: object

Backend-agnostic container for the output of one solve.

A SimulationResult bundles everything a user needs after a solve finishes:

  • times — the time grid the solver stored (ns).

  • states — the list of stored states (kets or density matrices), or None if store_states was off.

  • solver — the name reported by the backend.

  • stats — a plain dict of solver statistics.

  • dims — the per-device Hilbert-space dimensions, in chip order.

  • device_info[(label, computational), …] for partial-trace helpers to resolve device indices by label or by object.

Expectation values live in observable_traces when e_ops was passed as a dict. Each entry is an ObservableTrace or a list of them (list-valued observables, one per band, etc.).

Parameters:
property observable_traces: dict[Any, ObservableTrace | list[ObservableTrace]] | None

Return the dict of named ObservableTrace entries, or None.

None when e_ops was not passed as a dict. Visualization and analysis code that wants the full dict should read this property rather than the private _expect_data attribute.

expect(key, index=None)[source]

Return the full expectation-value array for observable key over self.times.

Parameters:
  • key (Any)

  • index (int | None)

Return type:

Any

expect_final(key, index=None)[source]

Return the final expectation value for observable key (self.expect(key)[-1]).

Parameters:
  • key (Any)

  • index (int | None)

Return type:

Any

expect_values(key, index=None)

Return the full expectation-value array for observable key over self.times.

Parameters:
  • key (Any)

  • index (int | None)

Return type:

Any

overlap_array(target)[source]

Return the overlap with target at every stored time.

For ket trajectories returns |<target|psi(t)>|**2; for density matrices returns <target|rho(t)|target>. Stays in the backend’s array module so the result is differentiable. One batched op over the leading time axis — no per-point loop.

Parameters:

target (Any)

Return type:

Any

amplitude_array(target)[source]

Return the phase-sensitive complex projection <target|psi(t)> for kets.

Density-matrix trajectories raise TypeError — there is no single phase-sensitive amplitude for a mixed state; use overlap_array() instead. One batched op, no per-point loop.

Parameters:

target (Any)

Return type:

Any

overlap(target)[source]

Wrap overlap_array() for convenience.

Returns a NumPy array under the QuTiP backend; under JAX (dynamiqs) returns the backend-native array so the call stays JIT/grad-friendly.

Parameters:

target (Any)

Return type:

Any

state(t=None, *, dm=False)[source]

Return the state at time t (or final state if t is None); optionally coerced to a DM.

Parameters:
Return type:

Any

state_at(t)[source]

Return the state at the stored time nearest to t (ns).

Parameters:

t (float)

Return type:

Any

dm_at(t)[source]

Return the density matrix at the stored time nearest to t (ns) — promotes kets on demand.

Parameters:

t (float)

Return type:

Any

property final_state: Any

Return the final state — explicit final_state if stored, else the last stored trajectory entry.

reduced_state(t, device)[source]

Partial-trace the state at time t down to device’s subspace.

Parameters:
Return type:

Any

property populations: dict[tuple[int, ...], ndarray]

Return per-basis-state populations |<n1, n2, ...|psi(t)>|**2 over time.

Returns a dict keyed by Fock tuple (n1, n2, ..., nK) — one per computational basis vector of the full chip — mapping to a real numpy.ndarray of length len(self.times).

Requires store_states; density-matrix trajectories are handled transparently by reading the diagonal of each timestep’s DM.

population_array(device, level=0)[source]

Return the population of Fock level on device over time, in the backend’s array module.

Parameters:
Return type:

Any

population(device, level=0)[source]

Wrap population_array() for convenience.

Returns a NumPy array under the QuTiP backend; under JAX (dynamiqs) returns the backend-native array so the call stays JIT/grad-friendly. Use population_array() directly when you want to keep gradient flow regardless of context.

Parameters:
Return type:

Any

plot_populations(*, trace_out=None, computational=False, ax=None, **kwargs)[source]

Plot per-basis-state populations over time (delegates to quchip.viz.plot_populations()).

Parameters:
Return type:

Any

plot_state(index, *, trace_out=None, computational=False, mode='population', ax=None, **kwargs)[source]

Plot one stored state as populations or a density matrix (delegates to quchip.viz.plot_state()).

Parameters:
Return type:

Any

plot_expectation(*, keys=None, ax=None, **kwargs)[source]

Plot expectation-value traces over time (delegates to quchip.viz.plot_expectation()).

Parameters:
Return type:

Any

plot_wigner(index=-1, *, trace_out=None, ax=None, **kwargs)[source]

Plot the Wigner function of one stored state (delegates to quchip.viz.plot_wigner()).

Parameters:
Return type:

Any

check_truncation(*, threshold=0.001, top_levels=1)[source]

Emit a UserWarning per device whose top-top_levels population exceeds threshold.

Uses the final stored state only — cheap, one full-chip diagonal read, no per-timestep loop, no partial traces. Silently no-ops when no final state is available (e.g. store_states and store_final_state both disabled).

Returns the per-device top-level population actually observed, keyed by device label, so callers can surface the numbers without re-parsing the warning text.

Parameters:
Return type:

dict[str, float]

class quchip.results.results.SimulationBatchResult(results, *, shape=None, axes=None)[source]

Bases: object

Ordered, immutable batch of SimulationResult with stacked helpers.

Returned by solve_many() and by any sweep that solves many problems in one call. The batch preserves iteration order so that per-element results map one-to-one onto the inputs that produced them.

The final_* helpers stack along a new leading batch axis in the backend’s array module, and the grid-aware expect() / population() accept reduce='last' for a final-value slice, so a loss function that sums over the batch stays JAX-traceable end-to-end.

Parameters:
property results: tuple[SimulationResult, ...]

Return the per-element SimulationResult objects in input order.

property shape: tuple[int, ...]

Return the natural sweep-grid shape for this batch.

property axes: tuple[tuple[str, Any], ...]

Return sweep-axis metadata as (name, values) pairs.

property backend: Backend

Return the backend shared by every element (raises on an empty batch).

with_sweep_metadata(*, shape, axes)[source]

Return an equivalent batch annotated with sweep-axis metadata.

Parameters:
Return type:

SimulationBatchResult

expect(key, index=None, *, reduce=None)[source]

Return expectation traces reshaped to the natural sweep grid.

Parameters:
  • key (Any)

  • index (int | None)

  • reduce (str | None)

Return type:

Any

population(device, level=0, *, reduce=None)[source]

Return population traces reshaped to the natural sweep grid.

Parameters:
Return type:

Any

final_overlap_magnitudes(targets)[source]

Return stacked final overlap magnitudes, one per (result, target) pair.

Parameters:

targets (list[Any] | tuple[Any, ...])

Return type:

Any

final_amplitudes(targets)[source]

Return stacked final complex amplitudes (phase-sensitive), one per (result, target) pair.

Parameters:

targets (list[Any] | tuple[Any, ...])

Return type:

Any

quchip.results.results.wrap_solver_result(solver_result, problem, backend)[source]

Wrap a raw backend SolverResult into a user-facing SimulationResult.

The engine-side SolveProblem carries the metadata needed to rebuild dict-form observables (build_observable_traces()) and to label devices for partial-trace helpers.

Parameters:
Return type:

SimulationResult

quchip.results.results.wrap_solver_results_from_batch(solver_results, batch, backend)[source]

Wrap backend results from a batched solve that shares one SolveBatch context.

Avoids rematerializing a per-element SolveProblem (and its per-element HamiltonianDescription) just to read the shared fields.

Parameters:
Return type:

list[SimulationResult]