quchip.engine.observables

Band-decompose dict-form e_ops and demodulate expectations after the solve.

The simulation runs in the resolved rotating frame, but user-facing observables are in the control frame, where detunings Δ = ω_drive − ω_frame label the matrix elements. This module performs the two operations that connect those frames:

  • Pre-solve (:func:`decompose_eops`): quchip.engine.bands splits each user operator into excitation-change bands w = col − row (single-mode) or (Δa, Δb) (two-mode). Each band is embedded into the full product Hilbert space and carries BandMeta, so the post-solve step knows which phase to apply.

  • Post-solve (:func:`recombine_expect`): each band expectation ⟨O_w⟩(t) is multiplied by exp(±i · 2π · ω_demod · w · t) and summed over bands. The sign selects demodulation or remodulation. Physically, this inverse rotating-frame transformation moves each band from the simulation frame to the control frame.

An external-plane output request uses a parallel path. One request lowers both the resolved L and L dagger L moments before the solve. After the solve, quchip reconstructs S beta + L from the solve-bound input and propagates the complete field trace to the requested reference plane.

Observable reconstruction reads per-device demodulation frequencies from ResolvedFrame and forms the demodulation phase exp(i · 2π · ω · w · t), the inverse of the rotating-frame shift applied to the Hamiltonian during assembly.

Functions

build_observable_traces(solver_result, ...)

Wrap solver expectations into phase-corrected ObservableTrace objects.

collapse_number_operator(coupling, backend, ...)

Build the backend-native L†L operator used for collapse and output fluxes.

decompose_eops(e_ops_dict, chip, backend[, ...])

Flatten dict-form e_ops into (ops, meta) ready for the solver.

prepare_local_op(dev, spec, basis, backend)

Return one observable in the resolved local solver basis.

recombine_expect(flat_expect, meta_list, ...)

Recombine flattened expectations into (band_sum, phase_corrected) dicts.

Classes

BandMeta(key, weight, device_labels[, sub_index])

Post-solve metadata for one flattened e_ops band.

OutputMeta(key, observable, role)

Reconstruction recipe for one solver-facing output operator.

quchip.engine.observables.prepare_local_op(dev, spec, basis, backend)[source]

Return one observable in the resolved local solver basis.

class quchip.engine.observables.BandMeta(key, weight, device_labels, sub_index=None)[source]

Bases: object

Post-solve metadata for one flattened e_ops band.

key is the original dict key. weight is the excitation-change weight (int single-device, tuple[int, int] two-device). device_labels controls the phase lookup. sub_index identifies each entry when the user passed a list of operators for the same key.

key: str | tuple[str, str]
weight: int | tuple[int, int]
device_labels: str | tuple[str, str]
sub_index: int | None = None
class quchip.engine.observables.OutputMeta(key, observable, role)[source]

Bases: object

Reconstruction recipe for one solver-facing output operator.

key: str | tuple[str, str]
observable: OutputField
role: Literal['amplitude', 'flux']
quchip.engine.observables.decompose_eops(e_ops_dict, chip, backend, bases=None, engine_result=None, *, space=None)[source]

Flatten dict-form e_ops into (ops, meta) ready for the solver.

Supported key shapes:

  • "device_label" (or a device object resolved to its label) with a single local operator. The operator is split into single-mode bands on that device.

  • ("label_a", "label_b") with a tuple (op_a, op_b). Each operator is split into single-mode bands on its device. All band pairs are tensored to give two-body (w_a, w_b) bands.

  • A string trace name with an OutputField value. The resolved first moments and the normally ordered second moments are reconstructed after the solve.

For each band, the returned BandMeta records the original key, the weight, and the sub-index, which identifies each entry when the user passed a list of operators for one key. The backend solver can use the matching ops list directly. Device operators act on the retained labels of space, or else of the engine result’s excitation space when it has one, and on the product space otherwise.

quchip.engine.observables.collapse_number_operator(coupling, backend, *, tag)[source]

Build the backend-native L†L operator used for collapse and output fluxes.

quchip.engine.observables.recombine_expect(flat_expect, meta_list, tlist, frame_freqs, *, direction='demodulate')[source]

Recombine flattened expectations into (band_sum, phase_corrected) dicts.

For each band with weight w on device(s) with demodulation frequency ω_demod, the correction factor is

\[\exp\!\big(\pm i \cdot 2\pi \cdot \omega_{\text{demod}} \cdot w \cdot t \big),\]

with the sign selected by direction. "demodulate" (+1) moves the expectation from the simulation’s rotating frame back to the control frame. "remodulate" (−1) does the reverse.

Returns two dicts. band_sum is the raw sum over bands for each key (no phase correction). phase_corrected is the physically meaningful expectation in the control frame.

quchip.engine.observables.build_observable_traces(solver_result, tlist, *, dict_meta, resolved_frame, engine_result)[source]

Wrap solver expectations into phase-corrected ObservableTrace objects.

Gets the flat per-band expectations from solver_result and calls recombine_expect() with the demodulation frequencies from resolved_frame. Returns a dict keyed by the user’s original e_ops keys. Each value is an ObservableTrace, or a list when the user supplied multiple operators for the same key.