quchip.chip.chip¶
Composite quantum system — Chip bundles devices, couplings, and control.
A chip owns the devices (which own their local Hamiltonians), the
couplings between them (which own their two-body interaction
Hamiltonians), and, optionally, the ControlEquipment wiring
classical control lines. The engine consumes a chip to produce a
solver-ready problem.
All public parameters — device frequencies, coupling strengths, drive amplitudes, crosstalk coefficients — remain JAX-traceable and sweepable so a single loss function can span any of them.
Classes
|
Composite quantum system: devices + couplings + (optional) control. |
- class quchip.chip.chip.Chip(devices, couplings=None, control_equipment=None, label=None, frame='lab', approximation=RWA(keep_bands=None), basis='native', backend=None, baths=None, ports=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(),steadystate()),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.
approximation (Approximation) – Engine strategy applied after the complete authored Hamiltonian is assembled. Defaults to
RWA.basis ({"native", "eigen"}) – Chip-wide local solver-basis policy.
"native"preserves each device’s authored coordinate basis;"eigen"transforms into its retained local energy subspace. A device-levelbasisoverrides this policy.backend (str or Backend, optional) – Chip-specific backend.
Noneuses the process default.baths (list[Bath], optional) – Shared or collective unobserved environments.
ports (list[Port], optional) – Accessible Markovian input-output channels. Ports add collapse channels without adding Hilbert-space factors.
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
- unresolved_hamiltonian()[source]¶
Return the authored 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.
This is the exact authored lab-frame Hamiltonian. RWA and frame transformations are applied only inside the engine. At solve time, the engine subtracts
2π Σ_i ω_ref,i n̂_ifor each device, whereω_ref,iis the frame reference resolved byquchip.engine.frames.resolve_frame(). The 2π boundary crossing and the subtraction both live inquchip.engine.assembly._build_static_h0(). Useframe_info()to inspect which reference frequency each device will use.- Returns:
Symbolic Hamiltonian on
⨂_d H_d. Call.matrix()for a dense numerical view using current bindings.- Return type:
- resolve(*, frame=None, approximation=None)[source]¶
Return a frozen engine contract without mutating chip intent.
frame=Noneusesframe; an explicit frame resolves this snapshot only.approximation=Nonelikewise uses the chip default. Neither override mutates chip intent.- Parameters:
frame (FrameSpec | None)
approximation (Approximation | None)
- Return type:
- dynamic_contributions()[source]¶
Chip-owned time-dependent Hamiltonian contributions with their support.
Returns
(local_op, time_dependence, support, owner, 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 engine assembly’s drive compilation.
- collapse_contributions(bases=None)[source]¶
Every Lindblad collapse operator on the chip, with its support.
Returns
(operator, rate, support, source, channel, parameter_paths)tuples: 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 authored locally and transformed by the engine into the same fixed local solver bases as the Hamiltonian. 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 approximation: Approximation¶
Default engine approximation strategy.
- 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 basis: Literal['native', 'eigen']¶
Chip-wide local solver-basis policy inherited by devices.
- resolve_basis(device)[source]¶
Resolve a device override against the chip basis policy.
- Parameters:
device (str | BaseDevice)
- Return type:
Literal[‘native’, ‘eigen’]
- 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 with a
parameter(noise=True)rate field and adissipation()override. 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, values='bare', **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:
- 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:
- kerr_matrix()[source]¶
Return the labeled dressed self-Kerr and cross-Kerr matrix in GHz.
See
ChipAnalysis.kerr_matrix().- 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:
- transition_frequency(target, lower, upper, when=None)[source]¶
Return a dressed transition in GHz with optional spectator levels.
Unspecified spectators are in their ground state.
targetand entries inwhenaccept either device objects or labels.- Parameters:
target (str | BaseDevice)
lower (int)
upper (int)
when (dict[str | BaseDevice, int] | None)
- 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. Underjax.jitthe scalar is a traced 0-d array; the overload is type-only and preserves 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","port"— 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.
- property settings: Mapping[str, Any]¶
Read-only structural choices that are not numerical fit parameters.
- with_params(bindings)[source]¶
Return an isolated structural copy with component-owned values rebound.
- 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:
- 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 energy-level 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 assigned from the given product-state level 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]¶
Product state from per-device energy levels or authored local kets.
Each device may be specified as either an energy-level index (
int) or a ket vector in that device’s authored local space. Devices not mentioned default to the ground state (level 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 or coupling instance. 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
SolveBatchor 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 SolveBatch / list dispatch.- Parameters:
batch_or_problems (Any)
progress (bool)
- Return type:
- steadystate(*, e_ops=None, options=None, frame=None, approximation=None)[source]¶
Solve this chip’s unique static Lindblad steady state.
- Parameters:
e_ops (dict | None)
options (dict | None)
frame (FrameSpec | None)
approximation (Approximation | None)
- Return type:
- steadystate_batch(*axes, e_ops=None, options=None, frame=None, approximation=None, progress=True)[source]¶
Solve static Lindblad steady states over parameter sweep axes.
- Parameters:
axes (Any)
e_ops (dict | None)
options (dict | None)
frame (FrameSpec | None)
approximation (Approximation | None)
progress (bool)
- Return type:
Any
- 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: