quchip.chip¶
Chip topology — the composite quantum system and its two-body couplings.
A Chip bundles devices, couplings, and (optionally) control
equipment into a single composite system that the engine can assemble
into a solver-ready problem. Dressed-state analysis lives in
quchip.chip.analysis and is attached to every chip as
chip._analysis (exposed via the chip’s public methods).
- class quchip.chip.Chip(devices, couplings=None, control_equipment=None, label=None, frame='lab', rwa=True, backend=None, baths=None)[source]¶
Bases:
objectComposite quantum system: devices + couplings + (optional) control.
The chip is the primary user-facing object. It exposes:
Hamiltonian construction (
hamiltonian()),dressed-state analysis (
dress(),energy(),freq(), …) delegated toChipAnalysis,state factories (
state(),bare_state()),observable construction (
observable(),e_ops()),solver surface (
solve(),solve_many()),serialization / cloning (
to_dict(),from_dict(),clone(),updated()).
Methods that accept a device reference take either a device object or its label string; object references are preferred in examples.
- Parameters:
devices (list[BaseDevice]) – Ordered device list. Tensor-product position equals list index; labels must be unique.
couplings (list[BaseCoupling], optional) – Two-body couplings. Each coupling’s referenced devices must be in
devices.control_equipment (ControlEquipment, optional) – Aggregates drive lines and signal-chain transforms (crosstalk, delays, gains). Can also be attached later via
wire()orconnect().label (str, optional) – Human-readable chip label.
frame (FrameSpec) –
Initial frame specification:
"lab"— all reference frequencies 0 GHz (default)."rotating"— per-device rotating frame at dressed drive frequencies.scalar-like — one shared reference frequency for all devices.
dict— per-device references keyed by label or device.
rwa (bool) – Default RWA policy for couplings and drives. Per-component
rwaoverrides inherit this (Nonemeans inherit).backend (str or Backend, optional) – Chip-specific backend.
Noneuses the process default.
Examples
>>> from quchip import DuffingTransmon, Resonator, Capacitive, Chip >>> q = DuffingTransmon(freq=5.0, anharmonicity=-0.25, levels=3, label="q") >>> r = Resonator(freq=7.0, levels=5, label="r") >>> chip = Chip([q, r], couplings=[Capacitive(q, r, g=0.05)]) >>> chip.energy({q: 1}) # dressed |1,0⟩ eigenvalue, GHz
- hamiltonian()[source]¶
Lab-frame static Hamiltonian.
Embeds every device Hamiltonian into the full tensor space and adds each coupling’s embedded interaction. Does not apply the rotating-frame transform or any drive envelopes.
Under resolved RWA (
resolve_rwa()) each coupling contributes only the bands itsrwa_keeps_band()retains — theapply_rwa_mask()of its full interaction. The same bands drop from stage 2’s decomposition, so the two views agree.This is the lab-frame Hamiltonian. At solve time, the engine applies a rotating-frame transformation and subtracts
2π Σ_i ω_ref,i n̂_ifor each device, whereω_ref,iis the frame reference resolved byquchip.engine.stage1_frames.resolve_frame(). The 2π boundary crossing and the subtraction both live inquchip.engine.stage2_assembly._build_static_h0(). Useframe_info()to inspect which reference frequency each device will use.- Returns:
Backend-native operator on
⨂_d H_d.- Return type:
- dynamic_contributions()[source]¶
Chip-owned time-dependent Hamiltonian contributions with their support.
Returns
(local_op, time_dependence, support, origin, tag)tuples: one support index for a device-local operator, two for a coupling’s two-body operator. Operators are lab-frame, ordinary GHz — the engine applies the 2π boundary. Drive terms are not included; they are schedule-owned and enter through stage 2’s drive compilation.
- collapse_contributions()[source]¶
Every Lindblad collapse operator on the chip, with its support.
Returns
(operator, support)pairs: one device index for a device- or drive-line-local operator, two for a coupling’s two-body operator, and an empty tuple for an operator already embedded in the full space (baths). Rates are in 1/ns, Lindblad-ready — each component owns its rate physics, including any intrinsic 2π (e.g. a resonator’s κ = 2π·f/Q).Every returned operator is a component’s LOCAL (bare-basis) operator; the engine combines them with the dressed interacting Hamiltonian at solve time. This is the standard local-Lindblad approximation (Breuer & Petruccione, The Theory of Open Quantum Systems, Oxford, 2002, Ch. 3) rather than a dressed-basis (polaron-frame) master equation, and applies chip-wide regardless of which components carry noise — see the
"chip"entry ofphysics_notes().
- property device_map: dict[str, BaseDevice]¶
Label → device mapping (the chip’s own dict; do not mutate).
- property coupling_map: dict[str, BaseCoupling]¶
Couplings by label, insertion-ordered (do not mutate).
- coupling(coupling)[source]¶
Return a coupling by label or object.
- Parameters:
coupling (str | BaseCoupling)
- Return type:
- property backend: Backend¶
per-call override > chip-specific > process default.
The per-call override is the
backend=argument ofsimulate()/simulate_batch(scoped throughquchip.backend._backend_context()); it outranks even a chip-constructed backend so one chip can serve, e.g., QuTiP sweeps and dynamiqs gradient solves without global state flips.- Type:
Active backend
- property frame: FrameSpec¶
Current frame specification (see
set_frame()).
- property control_equipment: ControlEquipment | None¶
Attached control equipment, if any.
- property devices: tuple[BaseDevice, ...]¶
Ordered tuple of devices; index matches tensor-product position.
- property couplings: tuple[BaseCoupling, ...]¶
Tuple of couplings in insertion order.
- property baths: tuple[Bath, ...]¶
Chip-level baths (shared/collective dissipation), insertion order.
- add_bath(bath)[source]¶
Attach a bath to this chip and return it (for fluent use).
Baths may be added at any time after construction — the next simulate/solve collects the bath’s collapse operators automatically, no rebuild needed. The bath is validated immediately: a non-
Bathargument, a target label not on this chip, or a label colliding with an already-attached bath fails here rather than cryptically at solve time (or, for a label collision, silently overwriting an entry on thephysics_notes()audit surface).
- set_noise(config=None, *, baths=None)[source]¶
Replace this chip’s entire noise description in one call.
The call is the chip’s complete noise state: for every device, each noise parameter (see
noise_parameter_names()) is set from config when given and reset toNoneotherwise, and the chip’s baths become exactly baths (omitted → no baths).chip.set_noise()therefore clears all noise. Re-running the same call is a silent no-op, so notebook cells converge instead of stacking duplicate baths.Only dissipation knobs are reachable — a key that is not a noise parameter of that device (e.g.
freq) raises, so an optimized Hamiltonian cannot be perturbed by this call. The full target state is validated before anything is written (the same checks the constructors run); on error the chip is left exactly as it was. Applied changes are printed one per line; a no-op prints nothing.Custom dissipation is supported by declaring it on the device class beforehand — a
parameter(...)rate field plus aNoiseChannelentry (see the extension guide). The declared rate is then an ordinary noise parameter here: sweepable, differentiable, serializable. Runtime closure-style channels are deliberately not supported — their rates could be neither swept, nor differentiated, nor serialized.- Parameters:
config (mapping, optional) –
{device_or_label: {noise_param: value}}. Devices may appear as objects or labels, once each.baths (list[Bath], optional) – The chip’s complete new bath list (validated like
add_bath()).
- Return type:
None
- device_index(label)[source]¶
Tensor-product index of a device. Accepts a label string or object.
- Parameters:
label (str | BaseDevice)
- Return type:
- plot_graph(path='chip_topology.html', *, full=True, exclude=None, **kwargs)[source]¶
Render chip topology — delegates to
quchip.viz.chip.
- plot_energy_levels(*, ax=None, **kwargs)[source]¶
Render dressed spectrum — delegates to
quchip.viz.chip.
- set_frame(frame)[source]¶
Set the frame used when assembling simulation inputs.
Supported values:
"lab"— all reference frequencies are 0.0 GHz."rotating"— per-device references use dressed drive frequencies.scalar-like — shared reference frequency for all devices.
dict— per-device references keyed by label or device.
Frame changes never alter dressed-state data — dressing is always computed from the lab-frame static Hamiltonian.
- Parameters:
frame (FrameSpec)
- Return type:
None
- property analysis: ChipAnalysis¶
Dressed-state analysis namespace — the chip’s
ChipAnalysis.Canonical entry point for the full dressed-analysis surface (power users, less-common methods). The common quantities are also exposed as flat
chip.*forwarders —energy(),freq(),dress(),dispersive_shift(), … — which delegate here; reach forchip.analysisfor everything else.
- dress(*, overlap_threshold=0.5, force=False, labeling='DE')[source]¶
Compute (or retrieve) the dressed-state decomposition. See
ChipAnalysis.dress().- Parameters:
- Return type:
- property is_dressed: bool¶
Whether a valid dressed-state result is cached. See
ChipAnalysis.is_dressed.
- energy(device_states=None, /, **device_state_kwargs)[source]¶
Dressed eigenenergy (GHz) for a bare-state label. See
ChipAnalysis.energy().
- dressed_spectrum()[source]¶
Raw dressed eigenvalue array without Python scalar coercion. See
ChipAnalysis.dressed_spectrum().- Return type:
- dressed_index(device_states=None, /, **device_state_kwargs)[source]¶
Dressed-state index matching a bare-state label, or
None. SeeChipAnalysis.dressed_index().
- bare_label(dressed_index)[source]¶
Bare-state label assigned to a dressed-state index. See
ChipAnalysis.bare_label().
- operator_in_dressed_basis(device, op, *, truncate=None)[source]¶
Embedded device operator transformed to the dressed eigenbasis.
See
ChipAnalysis.operator_in_dressed_basis().
- drive_matrix_elements(transition, *, drives=None)[source]¶
Return
<final~|D_j|initial~>for wired drive lines.The final dressed state is the row index and the initial dressed state is the column index. In the weak-drive projection these matrix elements set the effective driven-Hamiltonian coefficients. See E. Magesan and J. M. Gambetta, Phys. Rev. A 101, 052308 (2020), DOI 10.1103/PhysRevA.101.052308, and
ChipAnalysis.drive_matrix_elements()for the transition shorthand, parameters, return type, and error conditions.- Parameters:
transition (str | BaseDevice | tuple[Mapping[str | BaseDevice, int], Mapping[str | BaseDevice, int]])
- Return type:
- state_components(state=None, /, *, n_components=5, **device_state_kwargs)[source]¶
Leading bare-basis probabilities for a dressed eigenstate. See
ChipAnalysis.state_components().
- dispersive_shift(device_a, device_b)[source]¶
Dressed cross-Kerr shift (GHz):
E(1,1) − E(1,0) − E(0,1) + E(0,0).See
ChipAnalysis.dispersive_shift().- Parameters:
device_a (str | BaseDevice)
device_b (str | BaseDevice)
- Return type:
- static_zz(device_a, device_b)¶
Dressed cross-Kerr shift (GHz):
E(1,1) − E(1,0) − E(0,1) + E(0,0).See
ChipAnalysis.dispersive_shift().- Parameters:
device_a (str | BaseDevice)
device_b (str | BaseDevice)
- Return type:
- dressed_anharmonicity(device)[source]¶
Dressed anharmonicity of one device with others grounded (GHz).
See
ChipAnalysis.dressed_anharmonicity().- Parameters:
device (str | BaseDevice)
- Return type:
- effective_subspace_hamiltonian(states)[source]¶
Dressed effective Hamiltonian in a labeled bare subspace.
See
ChipAnalysis.effective_subspace_hamiltonian().
- freq(target: None = None, when: dict[str | BaseDevice, int] | None = None) dict[str, float][source]¶
- freq(target: str | BaseDevice, when: dict[str | BaseDevice, int] | None = None) float
All dressed 0→1 frequencies (GHz), or one conditional transition. See
ChipAnalysis.freq().Overloaded: no
targetreturns the full{label: freq}dict; a singletarget(label or device) returns one scalar 0→1 frequency. The runtime body is unchanged — underjax.jitthe scalar is a traced 0-d array, so the overload is type-only and does not alter traceability.- Parameters:
target (str | BaseDevice | None)
when (dict[str | BaseDevice, int] | None)
- Return type:
- frame_info()[source]¶
Per-device frame reference frequency
ω_ref,i(GHz). SeeChipAnalysis.frame_info().
- physics_notes()[source]¶
Aggregate
physics_notes()across every component.Returns a dict keyed
"chip"for the chip-level entry, and"<kind>:<label>"—kindone of"device","coupling","drive","bath"— for every component, mapping to that component’s declared approximations: Hilbert truncation, model regime, RWA status, noise-channel selection, and any other non-obvious assumption the component explicitly declares. Keys are kind-qualified rather than bare labels because the label namespaces are not globally disjoint — a device, coupling, drive, and bath may share a label — and this is an audit surface, so one component’s entry silently overwriting another’s is unacceptable. Drives are enumerated fromcontrol_equipment’s wiring rather than per-deviceconnected_drives, so an edge-targetParametricDrive(pumping a coupling, not a device) is included too. The chip-level entry keyed"chip"states the local-Lindblad approximation every collapse operator this chip assembles is built under (seecollapse_contributions()) — present even with no baths, since it applies regardless. Intended for inspection/audit rather than for runtime dispatch.
- partition()[source]¶
Split into independent sub-chips along the independence graph.
Couplings, non-separable bath target sets, and drive-crosstalk pairs all count as connections. Exact — the joint solve factorizes as the tensor product of the component solves.
simulate/seq.simulateconsult this automatically; call it directly to orchestrate solves yourself.- Return type:
- updated(update_fn)[source]¶
Return a cloned chip after applying one structural update callback.
Convenience for sweeps:
modified = chip.updated(lambda c: setattr(c["q"], "freq", 5.1))
- from_array(data, device=None)[source]¶
Build a backend operator from a raw NumPy array.
With device, the array is interpreted as a local operator on that device’s subspace and embedded into the full tensor-product space. With
device=Nonethe array must already span the full chip Hilbert space.- Parameters:
data (Any)
device (str | BaseDevice | None)
- Return type:
- observable(device, op)[source]¶
Embed a device operator onto the full chip Hilbert space.
Accepts either an operator name (
"X","Y","Z","n","a","a_dag","I") or an already-built local-space operator, and returns it embedded on the chip’s tensor-product space.This is for manual full-space operator construction and analysis; it is not a solver
e_op. For solver expectation values usee_ops(), which keeps operators local so the demodulation pipeline can band-decompose and embed them correctly.- Parameters:
device (str | BaseDevice)
- Return type:
- e_ops(*, correlators=None, **specs)[source]¶
Build a dict-form
e_opsmapping for the solver pipeline.Each keyword maps a device label to an operator specification: a name string, a list of names, a raw local-space operator, or a mixed list of strings and operators. Two-device correlators (e.g.
⟨Z₁⊗Z₂⟩) are specified via correlators as device-label pairs → operator pairs. Returns local-space operators (not embedded) — the demodulation pipeline embeds as needed.Examples
>>> from quchip import DuffingTransmon, Capacitive, Chip >>> q1 = DuffingTransmon(freq=5.0, anharmonicity=-0.25, levels=3, label="q1") >>> q2 = DuffingTransmon(freq=5.2, anharmonicity=-0.22, levels=3, label="q2") >>> chip = Chip([q1, q2], couplings=[Capacitive(q1, q2, g=0.02)]) >>> e = chip.e_ops( ... q1=["X", "Y", "Z"], q2=["X", "Y", "Z"], ... correlators={("q1", "q2"): ("Z", "Z")}, ... )
- set_state_order(*devices, levels=None)[source]¶
Declare the device order used to parse string-state shorthands.
After this is called,
bare_state(),state(), andsuperposition()accept single-string specifications where each character is one level per device in devices order. Level symbols default tog=0, e=1, f=2, h=3; digits0..9are always accepted as raw Fock indices.Every chip device must be named exactly once.
Examples
>>> from quchip import DuffingTransmon, Resonator, Chip >>> qb = DuffingTransmon(freq=5.0, anharmonicity=-0.25, levels=3, label="qb") >>> tc = DuffingTransmon(freq=5.5, anharmonicity=-0.20, levels=3, label="tc") >>> cr = Resonator(freq=7.0, levels=4, label="cr") >>> chip = Chip([qb, tc, cr]) >>> chip.set_state_order(qb, tc, cr) >>> _ = chip.bare_state("eg1") # {qb: 1, tc: 0, cr: 1}
- Parameters:
devices (str | BaseDevice)
- Return type:
None
- superposition(*components)[source]¶
Normalized bare-basis superposition of tensor-product states.
Each component is either a bare-state spec (dict keyed by device or label, or a string when
set_state_order()has been called) or an(amplitude, spec)tuple for weighted mixing. Uniform weights by default; results are normalized to unit norm.Unlike
state(), this stays in the bare product basis — no dressed diagonalization — so the probe basis is explicit.Examples
>>> import numpy as np >>> from quchip import DuffingTransmon, Resonator, Chip >>> qb = DuffingTransmon(freq=5.0, anharmonicity=-0.25, levels=3, label="qb") >>> cr = Resonator(freq=7.0, levels=4, label="cr") >>> chip = Chip([qb, cr]) >>> _ = chip.superposition({qb: 0}, {qb: 1}) # equal |00> + |10> >>> _ = chip.superposition( # weighted mix ... (np.sqrt(0.3), {qb: 1, cr: 0}), ... (np.sqrt(0.7), {qb: 1, cr: 1}), ... )
- state(device_states=None, /, **device_state_kwargs)[source]¶
Dressed eigenstate for the given Fock-indexed bare-state labels.
Accepts a string shorthand (e.g.
"eg1") whenset_state_order()has been called.Safe inside
jax.jit/grad/vmap: under tracing the assigned eigenvector column is selected through thelabel_eigensystem()array kernel, so dressed initial states are differentiable end-to-end. The global phase is gauge-dependent (eighcolumn convention) — populations and|overlap|figures of merit are unaffected.
- bare_state(device_states=None, /, **device_state_kwargs)[source]¶
Bare tensor-product state from per-device Fock indices or kets.
Each device may be specified as either a Fock index (
int) or a ket vector in that device’s local space. Devices not mentioned default to the ground state (Fock index 0). Unlikestate()this does not diagonalize the coupled system.Accepts a string shorthand (e.g.
"eg1") whenset_state_order()has been called.
- wire(*lines, signal_chain=None)[source]¶
Attach or replace classical control wiring.
Preferred user-facing API for attaching control to a chip. If lines is omitted, the existing connected lines are reused and only the signal chain is replaced.
Examples
>>> # chip.wire(drive_q, drive_r, signal_chain=[Crosstalk(drive_q, drive_r, c=0.05)])
- Parameters:
lines (BaseDrive)
signal_chain (Sequence[SignalTransform] | None)
- Return type:
- unwire(line)[source]¶
Remove one control line and every signal-chain transform referencing it.
The inverse of
wire()for a single line. Accepts the drive object or its label. Returns the removed drive so it can be rewired later. Removing the last line detaches the equipment entirely (control_equipmentbecomesNone).
- connect(control_equipment)[source]¶
Attach control equipment to this chip (low-level API).
Validates every drive target and rejects duplicate drive labels, then reconnects each drive to this chip’s canonical device instances. User-facing code should prefer
wire().- Parameters:
control_equipment (ControlEquipment)
- Return type:
None
- disconnect()[source]¶
Detach control equipment entirely (low-level API).
The inverse of
connect()/wire(): removes all lines and the signal chain at once (control_equipmentbecomesNone). Returns the detached equipment so it can be reconnected later.- Returns:
The equipment that was attached before detachment.
- Return type:
- solve(problem, *, check_truncation=True, truncation_threshold=0.001)[source]¶
Solve a typed
SolveProblemthrough this chip’s backend.Routes through the common
solve_problem()chokepoint, so the Hilbert-truncation safety net applies by default; passcheck_truncation=Falseto opt out or retunetruncation_threshold.- Parameters:
problem (SolveProblem)
check_truncation (bool)
truncation_threshold (float)
- Return type:
- solve_many(batch_or_problems, *, progress=True)[source]¶
Solve a
SolveBatch,ProblemBatch, or list of problems.Chip-level validation only enforces what needs
self(every input was built for this chip); the input-shape dispatch and batching are delegated toquchip.engine.solve_many(), which owns the single ProblemBatch / SolveBatch / list ladder.- Parameters:
batch_or_problems (Any)
progress (bool)
- Return type:
- describe()[source]¶
Sectioned plain-text report of everything on the chip.
Devices with their declared parameters (units included), noise settings, couplings, control wiring, and baths — the “what did I just build?” view. Returns a string;
print(chip.describe()). Traced parameters render as<traced>and are never concretized.- Return type:
- class quchip.chip.DressedResult(eigenvalues, state_map, dressed_eigenvalues, assignment_overlaps, hybridized_labels, bare_labels, bare_labels_by_dressed_index, eigenvector_matrix=None, overlap_threshold=0.5, labeling='DE', _eigensystem=None)[source]¶
Bases:
objectStore a frozen snapshot of a dressed-state diagonalization.
- Parameters:
eigenvalues (Any)
eigenvector_matrix (Any)
overlap_threshold (float)
labeling (str)
_eigensystem (EigensystemData | None)
- eigenvalues¶
Sorted dressed eigenvalues in GHz.
- Type:
array-like
- eigenstates¶
Backend eigenstate objects in the same order as
eigenvalues. Materialized lazily on first access from the underlyingEigensystemData— the dressing / sweep hot path never touches them, so the per-column backend kets are only built when a caller actually asks for states.- Type:
array-like
- state_map¶
Assignment from bare product-basis label (one int per device) to the dressed eigenstate index it overlaps most with.
- dressed_eigenvalues¶
Dressed eigenvalue for each assigned bare label — direct lookup path for
Chip.energy().
- assignment_overlaps¶
|⟨bare|dressed⟩|²of each assignment; values belowoverlap_thresholdflag hybridization.
- hybridized_labels¶
Bare labels whose assignment quality is below
overlap_threshold. A non-empty tuple triggers a user warning at dress time.
- bare_labels¶
Full canonical product-basis label set (all combinations of
range(device.levels)for every device, in chip order).
- eigenvector_matrix¶
Columns = dressed eigenvectors in the bare product basis. Used for
ChipAnalysis.operator_in_dressed_basis()andChipAnalysis.state_components().- Type:
array-like or None
- labeling¶
Algorithm id (currently only
"DE"), resolved to the confidence-ordered row-greedy overlap-matching policy actually run byChipAnalysis.dress()(assign_rowwise_greedy()).- Type:
- class quchip.chip.Bath(recipe, targets=None, *, temperature=None, rate=None, correlated=False, label=None)[source]¶
Bases:
objectA shared environment coupling a set of devices to a common bath.
Attach at construction (
Chip(..., baths=[...])) or at any time after viaadd_bath()— the next simulate/solve collects the bath’s collapse operators automatically.- Parameters:
recipe (str) – One of
"thermal","collective_decay","correlated_dephasing".targets (list[BaseDevice | str] | None) – Devices the bath couples to (objects or labels).
None(default) means every device in the chip — natural for a global thermal bath.temperature (float | None) – Bath temperature in mK (required for
"thermal"). May be a JAX tracer for sweeps / gradients.rate (float | None) – Bath–device coupling rate γ in 1/ns. For
"thermal"it is the environmental coupling rate (explicit — never silently borrowed from a deviceT1, so it cannot double-count device-level noise). For the collective recipes it is the overall jump rate.Nonedefaults to1.0(user controls the absolute scale elsewhere).correlated (bool) –
"thermal"only:False(default) emits independent per-device channels sharing one temperature.Trueis reserved for a genuinely collective thermal jump operator and currently raisesNotImplementedError— it is a documented future refinement, not a silent no-op. The collective recipes always emit a single correlated operator regardless of this flag.label (str | None) – Auto-generated
"bath_{n}"when omitted.
Examples
>>> from quchip import DuffingTransmon, Chip, Bath >>> q = DuffingTransmon(freq=5.0, anharmonicity=-0.25, levels=3, label="q") >>> chip = Chip([q]) >>> _ = chip.add_bath(Bath("thermal", temperature=20.0)) # global 20 mK bath >>> _ = chip.add_bath(Bath("collective_decay", targets=[q], rate=0.01))
- property separable: bool¶
Whether this bath factorizes into independent per-target channels.
Truefor recipes that emit one collapse operator per target ("thermal"with independent channels);Falsefor recipes that emit a single jump operator summed over targets ("collective_decay","correlated_dephasing"). Partitioning treats a non-separable bath’s target set as one inseparable block.
- classmethod from_dict(d)[source]¶
Reconstruct a bath from serialized state (targets as label strings).
- physics_notes()[source]¶
Return human-readable declarations of this bath’s recipe and scope.
Mirrors
physics_notes(): one entry naming the recipe and its targets, plus a recipe-specific assumption a user of this bath should be aware of.
- copy()[source]¶
Independent copy of this bath (targets normalize to label strings).
Used by
Chip.cloneandeliminateso a transformed chip never shares liveBathobjects with its source — mutating one chip’s bath must not silently change another chip’s physics. Parameter values (temperature, rate) are carried by reference, so traced values stay traced.- Return type:
- collapse_operators(chip)[source]¶
Fully-embedded collapse operators for this bath.
"thermal"emits independent per-target relaxation/absorption pairs sharing one bath temperature (_bose()). The two collective recipes instead each emit a single jump operator summed over the resolved targets:"collective_decay":L = sqrt(gamma) * sum_i a_i— an equal-phase, equal-weight rank-one collective channel, not general collective (super/subradiant) decay, which requires per-pair phase and weight factors set by the target geometry (Lehmberg, Phys. Rev. A 2, 883 (1970), for the general collective-radiative-decay construction)."correlated_dephasing":L = sqrt(gamma) * sum_i n_i— maximally correlated common-mode dephasing (every target shares the identical dephasing fluctuation), not general correlated dephasing with a target-dependent correlation structure (Breuer & Petruccione, The Theory of Open Quantum Systems, Oxford, 2002, Ch. 3, for the general Lindblad construction).
Always called from inside
with _backend_context(chip.backend):(seequchip.engine.stage4_problem._collect_c_ops()), so this method must not open its own backend context.
- class quchip.chip.Capacitive(device_a, device_b, *, g, rwa=None, label=None)[source]¶
Bases:
CouplingModelCapacitive (charge-charge) coupling between two devices.
The capacitive interaction between two electromagnetic modes takes the canonical dipole-dipole form in the raising/lowering basis:
Full form:
H_int = g · (a + a†)(b + b†)RWA form:
H_int = g · (a†b + a b†)— derived, not authored
The coupling authors only the full form; the RWA form is the engine retaining the
Δa + Δb == 0bands of it, which is exactlyg · (a†b + a b†). The RWA drops the counter-rotating termsa banda† b†, valid whenω_a + ω_b ≫ g— the sum-frequency condition that makes those terms fast-rotating and hence negligible. This is distinct from the dispersive condition|ω_a − ω_b| ≫ g, which instead governs whether the retained exchange termg · (a†b + a b†)can be treated perturbatively (seeTunableCapacitive/eliminate()for the dispersive reduction). Whether to take the RWA is a per-coupling policy; resolution against the chip default happens inChip.resolve_rwa().- Parameters:
device_a (BaseDevice or str) – The two coupled devices, given as objects or label strings. Label-string references are late-bound via
Chip.device_b (BaseDevice or str) – The two coupled devices, given as objects or label strings. Label-string references are late-bound via
Chip.g (float) – Coupling strength in GHz. May be a traced JAX scalar for sweeps / autodiff.
rwa (bool or None) – Per-coupling RWA override.
Noneinherits the chip default.label (str, optional) – Human-readable label; defaults to
"cap_{n}".
References
Blais et al., PRA 69, 062320 (2004), Eq. 11. Krantz et al., Appl. Phys. Rev. 6, 021318 (2019), §V.B. Blais et al., Rev. Mod. Phys. 93, 025005 (2021), §II.B.
Examples
>>> from quchip import DuffingTransmon, Resonator, Capacitive >>> q = DuffingTransmon(freq=5.0, anharmonicity=-0.25, levels=3, label="q") >>> r = Resonator(freq=7.0, levels=5, label="r") >>> coupling = Capacitive(q, r, g=0.05) # RWA inherited from chip >>> _strong = Capacitive(q, r, g=0.05, rwa=False) # force full quantum-Rabi form
- g: Any = Parameter(default=<object object>, positive=False, nonnegative=False, serialize=True, unit='GHz')¶
- interaction(a, b)[source]¶
Return the full capacitive interaction
g * (a + a†)(b + b†).- Parameters:
- Return type:
- classmethod from_dict(d, device_a, device_b)[source]¶
Reconstruct a capacitive coupling from serialized state.
- Parameters:
device_a (BaseDevice)
device_b (BaseDevice)
- Return type:
- class quchip.chip.Coupling(device_a, device_b, g, *, op_a=None, op_b=None, interaction=None, rwa=None, label=None)[source]¶
Bases:
BaseCouplingGeneric two-body coupling with a user-supplied interaction.
Use this escape hatch when no concrete coupling class models the desired physics (inductive, longitudinal, cross-Kerr test forms, synthetic spin-spin couplings, photonics-style beam-splitters, …). The user supplies the operator structure; this class only provides the
gscaling, RWA pass-through, and bookkeeping.Two mutually exclusive modes:
Product form —
H_int = g · op_a(device_a) ⊗ op_b(device_b):Coupling(q, r, g=0.02, op_a=lambda d: d.number_operator(), op_b=lambda d: d.number_operator())
Callable form —
H_int = g · interaction(device_a, device_b, backend):Coupling(q, r, g=0.02, interaction=lambda a, b, bk: ( bk.tensor(bk.dag(a.lowering_operator()), b.lowering_operator()) + bk.tensor(a.lowering_operator(), bk.dag(b.lowering_operator())) ))
The resolved RWA applies the structural band filter to the user-supplied operator too, keeping only the bands
rwa_keeps_band()accepts. Supplyrwa=False(or overriderwa_keeps_band) to keep a hand-built form untouched.- Parameters:
device_a (BaseDevice | str)
device_b (BaseDevice | str)
g (float)
op_a (Callable[[BaseDevice], Operator] | None)
op_b (Callable[[BaseDevice], Operator] | None)
interaction (Callable[[BaseDevice, BaseDevice, 'Backend'], Operator] | None)
rwa (bool | None)
label (str | None)
- classmethod from_dict(d, device_a, device_b)[source]¶
Reject deserialization because callables cannot be reconstructed.
- Parameters:
device_a (BaseDevice)
device_b (BaseDevice)
- Return type:
- class quchip.chip.CrossKerr(device_a, device_b, *, chi, rwa=None, label=None)[source]¶
Bases:
CouplingModelCross-Kerr (dispersive) coupling
H_int = χ · n̂_a n̂_b.The effective diagonal interaction left when an exchange coupling is reduced in the dispersive regime — the natural coupling for effective readout chips (qubit + resonator +
CrossKerrprobed by an ordinary charge line) and static-ZZ modeling. Diagonal in both endpoints, so the RWA and full forms coincide and the term is frame-trivial.Declared approximation: this is a uniform-pull model — one χ per edge, the same shift per endpoint excitation; per-level χ differences, dispersive breakdown at the critical photon number, and Purcell decay are not represented (fold Purcell into endpoint
T1viaeliminate()when it matters).- Parameters:
device_a (BaseDevice or str) – The two coupled devices, as objects or label strings.
device_b (BaseDevice or str) – The two coupled devices, as objects or label strings.
chi (float) – Cross-Kerr shift in GHz per excitation pair, sign included (convention: full pull
E₁₁ − E₁₀ − E₀₁ + E₀₀). May be a JAX tracer.rwa (bool or None) – Per-coupling RWA override; irrelevant to the produced operator (both forms coincide) but stored for policy uniformity.
label (str, optional) – Defaults to
"crosskerr_{n}".
- chi: Any = Parameter(default=<object object>, positive=False, nonnegative=False, serialize=True, unit='GHz')¶
- interaction(a, b)[source]¶
Full form
χ · n̂_a n̂_b(diagonal; identical under RWA).- Parameters:
- Return type:
- class quchip.chip.TunableCapacitive(device_a, device_b, *, g_0, rwa=None, label=None)[source]¶
Bases:
CouplingModelCapacitive coupling with a scheduled parametric pump.
Effective two-body interaction with a static mean coupling strength:
\[\begin{split}H_{\text{int}} \;=\; g_0 \cdot \left\{\begin{array}{ll} (a + a^\dagger)(b + b^\dagger) & \text{if } \texttt{rwa} = \texttt{False} \\ (a^\dagger b + a b^\dagger) & \text{if } \texttt{rwa} = \texttt{True} \end{array}\right.\end{split}\]where \(g_0\) is the static coupling strength in GHz; it may be a JAX tracer and flows through
jax.grad()without concretization.Time-dependence is not a construction-time parameter: a
ParametricDrivewired onto this coupling schedules a pump δ(t) viapump(), multiplying the same operator structure the static term uses (parametric_interaction()/rwa_parametric_interaction()).- Parameters:
device_a (BaseDevice) – The two coupled devices.
device_b (BaseDevice) – The two coupled devices.
g_0 (float) – Static (mean) coupling strength in GHz. May be a JAX tracer.
rwa (bool or None) – Per-coupling RWA override;
Noneinherits the chip default.label (str, optional) – Human-readable label; defaults to
"tunable_cap_{n}".
Notes
The pump multiplies
parametric_interaction()/rwa_parametric_interaction()in the chip’s frame natively — no separate frame or carrier logic lives on the coupling; a pump tone at the qubits’ difference frequency|ω_a − ω_b|activates the parametric beam-splitter / iSWAP exchange, while a tone at the sum frequencyω_a + ω_binstead activates two-mode-squeezing (a†b†) terms. Either is expressed via the drive’sfreqargument, not a coupling-side carrier.References
McKay, Filipp, Mezzacapo, Magesan, Chow & Gambetta, Universal Gate for Fixed-Frequency Qubits via a Tunable Bus, Phys. Rev. Applied 6, 064007 (2016) — parametric two-qubit gates via coupler flux modulation.
Krantz et al., A quantum engineer’s guide to superconducting qubits, Appl. Phys. Rev. 6, 021318 (2019), §V.D — tunable couplers.
Examples
>>> from quchip import Chip, ControlEquipment, DuffingTransmon, ParametricDrive, TunableCapacitive >>> q0 = DuffingTransmon(freq=5.0, anharmonicity=-0.25, levels=3, label="q0") >>> q1 = DuffingTransmon(freq=5.2, anharmonicity=-0.22, levels=3, label="q1") >>> tc = TunableCapacitive(q0, q1, g_0=0.0, label="tc") >>> pump = ParametricDrive(tc, label="pump") >>> chip = Chip([q0, q1], couplings=[tc], control_equipment=ControlEquipment([pump])) >>> # seq.pump(tc, envelope=..., freq=...) schedules δg(t); see QuantumSequence.pump.
- g_0: Any = Parameter(default=<object object>, positive=False, nonnegative=False, serialize=True, unit='GHz')¶
- parametric_interaction(a, b)[source]¶
Modulable structure
(a + a†)(b + b†)a scheduled pump multiplies.- Parameters:
- Return type:
- class quchip.chip.ChipTransform(*args, **kwargs)[source]¶
Bases:
ProtocolStructural protocol for any transformation result that yields a chip.
- class quchip.chip.EliminationResult(chip, effective_params, validity, notes=<factory>)[source]¶
Bases:
objectStore the result of
eliminate().- Parameters:
- chip¶
The reduced chip (eliminated mode removed; effect folded into ordinary surviving devices). The input chip is never mutated.
- Type:
- effective_params¶
Derived quantities per survivor:
{label: {"lamb_shift", "purcell_rate", "freq_after", "chi", "kappa"}}(GHz for frequencies, 1/ns for rates).chiis the dispersive pullχ_pull ≡ f_mode(survivor in |1⟩) − f_mode(survivor in |0⟩)— the full resonator pull per survivor excitation, i.e. 2× the σ_z-convention χ ofH_disp = (ω_r + χσ_z)a†a. For a mode touching more than one survivor, the mode is a bus / coupler, not a readout mode, sochiis reported as0.0for every survivor. Otherwisechiis a deferred entry: computed from the input chip’s dressed spectrum (one full-chip diagonalization) on the first["chi"]access and cached, so reductions that never read it stay pure algebra (LazyEffectiveParams); gradients through it followChip.dispersive_shift’s backend rule, while every other entry remains backend-independent algebra.kappais the eliminated mode’s own decay rate, from itsintrinsic_decay_rate()(e.g. a resonator’s combined2π·f_mode/Qphoton loss plus1/T1), or0.0when the mode declares no decay channel.A mode touching two or more survivors additionally emits an
"exchange"entry describing the mediated survivor-survivor coupling(s) derived from the Sylvester/exact pair extraction (quchip.chip.sw). For exactly two survivors (the bridge case) this is a single dict:{"j_eff", "dJ_domega_c", "between", "folded_into", "zz", "pathways"}. For three or more survivors, every survivor pair induces its own mediated exchange, so"exchange"is instead adictkeyed by the pair(label_a, label_b), each value the same per-pair schema. In both cases:j_effis the mediated exchangeJ = g_a g_b / 2 · (1/Δ_a + 1/Δ_b)(F. Yan et al., Phys. Rev. Applied 10, 054062 (2018)), read off themethod="sw"/"exact"pair extraction;dJ_domega_cis its analytic derivative w.r.t. the eliminated mode’s frequency (used by the flux-drive retarget rule);folded_intois the label of the effective edge the exchange landed on (an existing direct coupling between the pair, or a freshly emittedCapacitiveorTunableCapacitive);zzis the exact residual ZZ between the pair (method="exact"only —Noneunder"sw", where it is a higher-order correction not represented);pathwaysis the top virtual-state attribution of the exchange (method="sw"only, fromquchip.chip.sw.pathway_attribution()—Noneunder"exact", which has no perturbative generator to attribute).
- validity¶
Per eliminated coupling:
{coupling_label: {"g_over_delta", "is_valid", "min_block_gap"}}.min_block_gapis the smallest bare-energy gap the Sylvester generator crossed for this mode (quchip.chip.sw.sylvester_generator()), shared across every coupling touching the mode. Wheneliminateruns underjax.jit/grad,is_valid(g_over_delta < 0.1) is a traced boolean, not a Pythonbool— read it outside the traced region, or branch on it withjnp.whererather thanif.
- Every mapping above is a :class:`~quchip.utils.labeling.LabelKeyedDict`
- the device or coupling *object* is as good a key as its label, and pair
- keys match in either order —
- ``res.effective_params[q1]``, ``res.validity[leg]``, and
- ``exchange[(q1, q0)]`` all resolve.
- describe()[source]¶
Plain-text fold report: every fold stated explicitly, before -> after.
Per survivor: freq (and T1 when either side carries one), each Lamb-shifted/Purcell-folded value read back from
effective_paramsrather than a stored “before” chip —freq_before = freq_after - lamb_shiftandT1_beforefrompurcell_rateare exact identities of howeliminate()derivesfreq_after/T1, not an approximation. Multi-survivor targets add the emitted exchange edge (Yan-formula tag) and a ZZ line (placeholder undermethod="sw", the exact residual undermethod="exact"); any control-line retarget and the per-coupling validity verdict follow. Traced parameters render as<traced>and are never concretized; for use outsidejit/gradregions, like every otherdescribe()in the package.- Return type:
- quchip.chip.eliminate(chip, target, *, method='sw')[source]¶
Reduce a far-detuned device or an edge coupling, returning a reduced chip.
targetis resolved against the chip’s device and coupling namespaces (disjoint by construction —Chiprejects a coupling label that collides with a device label) and dispatches to one of two model reductions:Device target — adiabatic elimination of a far-detuned mode, via
quchip.chip.sw. A mode touching one survivor folds into a Lamb shift (and a Purcell channel when the mode dissipates). A mode touching two or more survivors — bus / tunable-coupler (bridge) or several at once — additionally induces a mediated exchangeJ = g_a g_b / 2 · (1/Δ_a + 1/Δ_b)between every survivor pair (with∂J/∂ω_crecorded alongside it), folded into the direct coupling between that pair when one exists or added as a new edge otherwise. A fixed eliminated mode emits aCapacitive; a mode that declares frequency control, has a retargeted flux line, or folds into an already-modulable edge emits aTunableCapacitive. At a tunable coupler’s idle point, itsg_0is the net coupling the pair feels, including any direct-edge cancellation. Eliminating several couplers is sequential composition:eliminate(eliminate(chip, "TC1").chip, "TC2").Coupling target — dispersive reduction of an exchange edge to its dressed cross-Kerr shift: both endpoint devices survive (Lamb-shifted), and the coupling itself is replaced by a
CrossKerrcarrying the dressed pull (seereduce_coupling()). This is the effective-readout-chip flow — reduce a qubit-resonator exchange edge to the diagonal interaction an ordinary charge line probes.methodhas no effect here: no mode is removed, so the reduction always reads the chip’s exact dressed spectrum.
- Parameters:
chip (Chip) – Source chip (never mutated).
target (Any) – The device or coupling to eliminate — label string or object.
method (str) – Device targets only.
"sw"(default) is the 2nd-order Schrieffer-Wolff reduction (Bravyi, DiVincenzo & Loss, Ann. Phys. 326, 2793 (2011)) — cheap, differentiable, and what every effective parameter above is derived from perturbatively."exact"instead reads the reduced parameters off the chip’s exact dressed spectrum (exact-from-dressing,quchip.chip.sw.exact_reduction()) — exact kept-block energies (what residual ZZ needs) at the cost of a full diagonalization, and it raises when near-degenerate dressed states make the bare labeling ambiguous. Any other value raisesValueError.
- Return type:
Examples
>>> from quchip import DuffingTransmon, Resonator, Capacitive, Chip >>> from quchip.chip.transformations import eliminate >>> q = DuffingTransmon(freq=5.0, anharmonicity=-0.25, levels=3, label="q") >>> r = Resonator(freq=7.0, levels=5, label="r") >>> chip = Chip([q, r], couplings=[Capacitive(q, r, g=0.05)]) >>> result = eliminate(chip, r) # r removed; Lamb shift folded into q.freq >>> reduced = result.chip >>> [d.label for d in reduced.devices] ['q']
- class quchip.chip.ActivePatchResult(chip, sequence, active_labels, eliminated_labels, steps, notes)[source]¶
Bases:
objectA reduced patch chip, the re-bound sequence, and the reduction’s honesty record.
- Parameters:
- chip¶
The patch chip: schedule-active devices plus whatever spectators
active_patch()could not eliminate (unreachable, or where elimination itself declined — seenotes).- Type:
Any
- sequence¶
A
QuantumSequencebound tochip, replaying the source sequence’s entries verbatim.- Type:
Any
- eliminated_labels¶
Device labels actually folded away, in elimination order (farthest-from-active first).
- steps¶
The
EliminationResultfrom each successfuleliminate()call, verbatim and ineliminated_labelsorder — reshape nothing here; read.validity/.effective_paramsoff the step objects through the convenience properties below.- Type:
- notes¶
Every explicitly dropped or deferred piece of physics: unreachable spectators left in place, control lines stripped as unused, and (if elimination itself couldn’t proceed past some spectator) why the reduction stopped early — plus every step’s own notes.
- property validity: dict[str, Any]¶
{eliminated label: that step's .validity}— verbatim, per-coupling shape untouched.
- quchip.chip.active_patch(sequence, *, hops=1, method='sw')[source]¶
Reduce a chip to its schedule-active patch by eliminating spectators.
Spectators are eliminated one at a time via
eliminate(), farthest-from-active first; every step’s validity metrics ride on the result untouched. Explicit opt-in — this approximates (Schrieffer-Wolff, or the exact dressed-spectrum route undermethod="exact"), unlike the exact automatic partitioningsimulate()performs internally at solve time.The elimination order and reachability are recomputed from the current reduced chip before every step, not just once up front: a fold can only ever add a bridging edge between the eliminated mode’s neighbors, never remove one, so a label’s distance from the active set can shorten but never make a previously reachable label unreachable — still, reading the graph fresh each time means a label that has already been folded away is never re-considered, and the order always reflects what
eliminatewill actually see. Bridging edges created by earlier folds may beCapacitiveedges carryinggorTunableCapacitiveedges carryingg_0. Both fold onward, so cycles among spectators reduce all the way down. Ifeliminatestill declines a step (its typedNotImplementedErrorsignal for an unsupported elimination — e.g. a Purcell decay fold onto a survivor that carries thermal occupation, which no collapse-channel API can represent without inventing physics that was never present), the reduction stops there: everything eliminated so far stays folded, and the remaining spectators — including the one that failed — are left on the patch chip untouched, with the reason recorded innotes.- Parameters:
sequence (QuantumSequence) – The schedule to reduce around.
hops (int) – Coupling-graph hops the active set expands beyond the scheduled targets (see
active_labels()).method (str) – Forwarded to
eliminate()for every device elimination.
- Return type:
- quchip.chip.register_retarget_rule(drive_type, target_type, result_kind, rule)[source]¶
Register a converter for
(drive type, eliminated-target type, result kind).Lookup (
lookup_retarget_rule()) walks both types’ MROs, so a rule registered for a base type also covers its subclasses;result_kindmatches exactly. This is the extension point for teachingeliminate()to carry a new kind of stranded control line without modifying it.- Parameters:
drive_type (type) – Control-line class the rule handles.
target_type (type) – Eliminated-target class the rule handles: a device type (the eliminated mode itself) for
"edge"/"leaf-fold".result_kind (str) –
"edge","leaf-fold", or"crosskerr".rule (callable) –
rule(line, ctx: RetargetContext) -> RetargetResult.
- Return type:
None
- quchip.chip.register_elimination_target(target)[source]¶
Register an
EliminationTargetkind foreliminate()to dispatch on.- Parameters:
target (EliminationTarget)
- Return type:
None
- quchip.chip.register_reduction_method(method)[source]¶
Register a reduction strategy under its
ReductionMethod.name.- Parameters:
method (ReductionMethod)
- Return type:
None
Modules
Dressed-state analysis owned by |
|
Chip-level baths — shared / collective Lindblad dissipation. |
|
Composite quantum system — |
|
Coupling base class and registry. |
|
Coupling models for two-body interactions between devices. |
|
Human-readable text summaries — the |
|
JAX-traceable dressed-state labeling primitives. |
|
Observable construction for |
|
Exact subsystem partitioning — connected components of a chip's independence graph. |
|
Retarget registry: convert control lines stranded by |
|
Structural rotating-wave kernels for coupling RWA policy. |
|
Chip serialization, deserialization, and structural cloning. |
|
State factories for |
|
Schrieffer-Wolff reduction kernels (2nd order) on bare chip blocks. |
|
Model reduction: |