quchip.viz.results

Simulation-result plots: populations, snapshots, expectations, Wigner.

These helpers consume a SimulationResult (which is backend-agnostic) and emit Matplotlib figures. None of them depend on a specific solver backend: partial-trace, expect, and ket construction all go through Backend.

References

  • Nielsen & Chuang, Quantum Computation and Quantum Information, Cambridge University Press (2010) — density matrices, populations.

  • Wigner, “On the Quantum Correction for Thermodynamic Equilibrium”, Phys. Rev. 40, 749 (1932) — original Wigner-function definition.

  • Cahill & Glauber, “Density Operators and Quasiprobability Distributions”, Phys. Rev. 177, 1882 (1969) — Laguerre-polynomial expansion used by _wigner_from_density_matrix().

  • Leonhardt, Essential Quantum Optics, Cambridge University Press (2010) — modern continuous-variable treatment of the Wigner function.

Functions

plot_expectation(result, *[, keys, ax, ...])

Plot dict-form expectation values over time.

plot_populations(result, *[, trace_out, ...])

Plot basis-state populations over time.

plot_state(result, index, *[, trace_out, ...])

Plot a single stored state at time-index index.

plot_wigner(result[, index, trace_out, ...])

Plot the Wigner quasi-probability distribution of a stored state.

quchip.viz.results.plot_populations(result, *, trace_out=None, computational=False, ax=None, linewidth=2.5, legend=True, colors=None, threshold=0.01)[source]

Plot basis-state populations over time.

The figure has time (ns) on the x-axis and population p_n(t) = Tr(|n><n| rho(t)) on the y-axis, one line per represented-basis ket |n> = |n_1 n_2 ...> of the retained subsystems. States whose peak population stays below threshold are hidden; set threshold to 0 to show every state.

Parameters:
  • result (SimulationResult) – Output of quchip.engine.simulate().

  • trace_out (device, label, or list thereof, optional) – Subsystems to partial-trace over before computing populations. Accepts either device objects or their string labels (UX favourability). Requires options={"store_states": True} on the solver call.

  • computational (bool) – When True, restricts computational subsystems to their {|0>, |1>} subspace.

  • ax (matplotlib.axes.Axes, optional) – Existing axes to draw onto. When None a new figure is created.

  • linewidth (float) – Line width for every population trace.

  • legend (bool) – Whether to draw a legend of the visible states.

  • colors (dict, optional) – Per-state colour overrides, keyed by the same basis-state tuples used internally; unlisted states fall back to a tab20 cycle.

  • threshold (float) – Populations whose time-max falls below this value are omitted.

Returns:

The figure holding the population-trace axes (ax.figure when ax was given).

Return type:

Figure

Raises:
  • RuntimeErrortrace_out is given but no states were stored (pass options={"store_states": True} to the solver).

  • ValueErrortrace_out would remove every subsystem.

quchip.viz.results.plot_state(result, index, *, trace_out=None, computational=False, mode='population', ax=None, cmap='RdBu_r', color=None)[source]

Plot a single stored state at time-index index.

Two modes are supported:

  • "population" — a bar chart of diagonal elements p_n.

  • "dm" — side-by-side heatmaps of Re(rho) and Im(rho); the colormap cmap is divergent, and both heatmaps share one symmetric normalization (vmin=-m, vmax=+m for m = max(|Re(rho)|, |Im(rho)|)) so their colours are directly comparable — an entry that looks equally saturated in both panels really is equal in magnitude.

When both computational and non-computational subsystems are present, pass trace_out to focus on a target register and/or computational = True to restrict to the {|0>, |1>} subspace on computational devices (see Nielsen & Chuang, Ch. 2).

Parameters:
  • result (SimulationResult) – Output of quchip.engine.simulate(), with options={"store_states": True}.

  • index (int) – Stored-time index to plot. Supports Python-style negative indexing (-1 is the last stored time); must satisfy -N <= index < N for N = len(result.times).

  • trace_out (device, label, or list thereof, optional) – Subsystems to partial-trace over before plotting.

  • computational (bool) – When True, restricts computational subsystems to their {|0>, |1>} subspace.

  • mode ({"population", "dm"}) – Which representation to draw.

  • ax (matplotlib.axes.Axes, optional) – For mode="population": a single axes (or None for a new figure). For mode="dm": an iterable of exactly two axes (real_ax, imag_ax) (or None for a new 1x2 figure).

  • cmap (str) – Divergent colormap for mode="dm" heatmaps.

  • color (str, optional) – Bar colour override for mode="population". Defaults to a per-state tab10 cycle.

Returns:

The figure holding the plotted axes (ax.figure when ax was given).

Return type:

Figure

Raises:
  • IndexErrorindex is outside [-N, N) for N = len(result.times).

  • ValueErrormode is not "population" or "dm", or trace_out would remove every subsystem.

  • RuntimeError – No states were stored (pass options={"store_states": True} to the solver).

quchip.viz.results.plot_expectation(result, *, keys=None, ax=None, linewidth=2.5, legend=True, real=True)[source]

Plot dict-form expectation values over time.

The x-axis is time (ns); the y-axis is observable_traces [key].values — the post-processed recorded trace for each observable O registered in the solver’s e_ops dict. This is not unconditionally Tr(O rho(t)): depending on how the observable was requested, .values may already include demodulation, phase correction, or band summation (see ObservableTrace; its .raw field holds the pre-processing quantity instead). When real is True (the default) only Re of the trace is drawn; when False both the real part (solid) and imaginary part (dashed, lower alpha) are drawn in the same colour per key.

Parameters:
  • result (SimulationResult) – Output of quchip.engine.simulate(), with e_ops passed as a dict.

  • keys (list, optional) – Each entry is either a bare key ("cav") or a (key, index) tuple selecting one element of a list-valued observable, matched against the registered observable_traces keys first — see _collect_expectation_traces(). String and device/drive keys are resolved with resolve_label so both are accepted interchangeably. Defaults to every registered trace.

  • ax (matplotlib.axes.Axes, optional) – Existing axes to draw onto. When None a new figure is created.

  • linewidth (float) – Line width for every trace.

  • legend (bool) – Whether to draw a legend of the plotted keys.

  • real (bool) – When True, draw only the real part of each trace; when False, draw both the real (solid) and imaginary (dashed) parts.

Returns:

The figure holding the expectation-trace axes (ax.figure when ax was given).

Return type:

Figure

Raises:
  • TypeErrore_ops was not passed as a dict (observable_traces is None).

  • KeyError – An entry in keys does not resolve to a registered trace or a valid (key, index) selector.

quchip.viz.results.plot_wigner(result, index=-1, *, trace_out=None, xvec=None, yvec=None, ax=None, cmap='RdBu_r', colorbar=True)[source]

Plot the Wigner quasi-probability distribution of a stored state.

Axes are the phase-space quadratures x (position-like) and p (momentum-like); the colourmap is divergent and symmetric about zero so negative regions — the hallmark of non-classical states — stand out directly.

When xvec is not supplied the plot window is auto-sized from the mean photon number <n> = Tr(rho n_hat) of the reduced state (computed directly from diag(rho) to avoid an O(d^2) matmul for a diagonal-only observable), extending to at least +/-3.

Exactly one subsystem must remain after trace_out — a Wigner function is a single-mode phase-space picture, and its basis indices are interpreted directly as photon numbers n = 0, 1, 2, .... This is checked; what is not, and cannot be, checked from result metadata alone is the remaining precondition: the retained subsystem’s represented basis must actually be a photon-number ladder (true for a bosonic mode such as Resonator, false for a device whose represented basis is not Fock, e.g. a charge- or flux-basis qubit) — passing such a device silently produces a Wigner-shaped plot with no such physical meaning.

Parameters:
  • result (SimulationResult) – Output of quchip.engine.simulate(), with options={"store_states": True}.

  • index (int) – Stored-time index to plot. Supports Python-style negative indexing (-1, the default, is the last stored time); must satisfy -N <= index < N for N = len(result.times).

  • trace_out (device, label, or list thereof, optional) – Subsystems to partial-trace over before plotting. Required whenever more than one subsystem is stored — see Raises.

  • xvec (ndarray, optional) – Phase-space grids for the x/p quadratures. Defaults to an auto-sized, evenly spaced grid (see above); yvec defaults to xvec when only xvec is given.

  • yvec (ndarray, optional) – Phase-space grids for the x/p quadratures. Defaults to an auto-sized, evenly spaced grid (see above); yvec defaults to xvec when only xvec is given.

  • ax (matplotlib.axes.Axes, optional) – Existing axes to draw onto. When None a new figure is created.

  • cmap (str) – Divergent colormap, symmetric about zero.

  • colorbar (bool) – Whether to attach a colorbar.

Returns:

The figure holding the Wigner-function axes (ax.figure when ax was given).

Return type:

Figure

Raises:
  • IndexErrorindex is outside [-N, N) for N = len(result.times).

  • ValueError – More or fewer than one subsystem remains after trace_out; the message lists the retained device labels and a trace_out value that isolates a single one of them.

  • RuntimeError – No states were stored (pass options={"store_states": True} to the solver).

References

  • Wigner, Phys. Rev. 40, 749 (1932).

  • Cahill & Glauber, Phys. Rev. 177, 1882 (1969).

  • Leonhardt, Essential Quantum Optics (2010), Ch. 3.