quchip.engine.stage3_observables¶
Stage 3: band-decompose dict-form e_ops and demodulate expectations post-solve.
The simulation is performed in the rotating frame chosen by stage 1;
user-facing observables, however, live in the control frame (where
matrix elements are labeled by detunings Δ = ω_drive − ω_frame).
This stage performs the two operations that tie those frames together:
Pre-solve (:func:`decompose_eops`): each user operator is split into excitation-change bands
w = col − row(single-mode) or(Δa, Δb)(two-mode) viaquchip.engine.bands. Each band is embedded into the full product Hilbert space and carriesBandMetaso the post-solve step knows which phase to apply.Post-solve (:func:`recombine_expect`): each band expectation
⟨O_w⟩(t)is multiplied byexp(±i · 2π · ω_demod · w · t)(the sign selects demodulation vs. remodulation) and summed over bands. Physically this is the inverse rotating-frame transformation applied to each band — equivalent to moving the observable from the simulation frame to the control frame.
Stage 3 reads per-device demodulation frequencies from
ResolvedFrame and forms the demodulation
phase exp(i · 2π · ω · w · t). This is the inverse of the
rotating-frame shift that stage 2 applied to the Hamiltonian.
Functions
|
Wrap solver expectations into phase-corrected |
|
Flatten dict-form |
|
Recombine flattened expectations into |
Classes
|
Post-solve metadata for one flattened e_ops band. |
- class quchip.engine.stage3_observables.BandMeta(key, weight, device_labels, sub_index=None)[source]¶
Bases:
objectPost-solve metadata for one flattened e_ops band.
keyis the original dict key,weightis the excitation-change weight (intsingle-device,tuple[int, int]two-device),device_labelsdrives phase lookup, andsub_indexdistinguishes entries when the user passed a list of operators for the same key.- Parameters:
- quchip.engine.stage3_observables.decompose_eops(e_ops_dict, chip, backend)[source]¶
Flatten dict-form
e_opsinto(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 and all band pairs are tensored, producing two-body(w_a, w_b)bands.
For each band, the returned
BandMetarecords the original key, the weight, and the sub-index (distinguishing entries when the user passed a list of operators for the same key). The matchingopslist is ready for direct consumption by the backend solver.
- quchip.engine.stage3_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
won 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_sumis the raw per-key sum over bands (no phase correction) andphase_correctedis the physically meaningful expectation in the control frame.
- quchip.engine.stage3_observables.build_observable_traces(solver_result, tlist, chip, *, dict_meta, resolved_frame)[source]¶
Wrap solver expectations into phase-corrected
ObservableTraceobjects.Pulls the flat per-band expectations out of solver_result, calls
recombine_expect()with the demodulation frequencies from resolved_frame, and returns a dict keyed by the user’s original e_ops keys. Each value is anObservableTrace(or a list when the user supplied multiple operators for the same key).- Parameters:
solver_result (SolverResult)
tlist (ndarray)
chip (Chip)
resolved_frame (ResolvedFrame)
- Return type:
dict[str | tuple[str, str], ObservableTrace | list[ObservableTrace]]