Release notes¶
Unreleased¶
Changes since 0.4.0¶
Full comparison: v0.4.0 → main.
Changes and migration¶
quchip 0.5 removes the names that 0.4 deprecated. Use
thermal_occupationforthermal_population(in constructors,with_params(),set_noise(), declared parameters, symbolic noise and saved device dictionaries),jump_rate()forcollapse_flux(),port()forside(),external_port()andexternal_portsforexposure()andexposures, andNetworkPortandComponentPortforFieldExposureandFieldSide. Thecheck_truncation()result methods stay until 0.6.Warnings are shorter, name the one action that sets or silences them, and point at the caller’s line. The default-frame and automatic-carrier warnings have fixed text, so Python shows each once per line of code instead of once per chip in a loop. A dressed label that keeps less than 90% of its bare product state warns once per chip, not at every query, and
chip.freq()and frame references still raise for an ambiguous label (overlap below one half or a vanishing margin). Partitioned results no longer warn when they build the joint state; the memory guard stays.The dynamiqs
steadystate()counts the Liouvillian nullity only up todiagnostic_max_dimension(default 16), as the QuTiP backend does. Above it, the singular value decomposition, which cost about ten times the solve, does not run:nullityisNone, and the solve gives no uniqueness guarantee: a singular system can return NaN or Inf, and a nearly singular one an unreliable state. Pass a largerdiagnostic_max_dimensionto keep the check. AD = 24steady state takes about 0.05–0.1 s instead of 0.5–0.8 s.A QuTiP batch falls back to one process only when a worker process stops. Other pool errors, such as a pickling failure, now raise instead of rerunning the batch.
A pulse scheduled without
freqon a charge, phase, or two-photon line takes a carrier at the line’s device and warns. Charge and phase lines usechip.freq(device), the dressed0 -> 1frequency, and a two-photon line uses twice it. Previously, such a pulse had no carrier, so a charge drive stayed about 5 GHz below a transmon and left it in|0>: a 20 ns Gaussian π pulse fromchip.rotation_amplitude()gaveP1 = 5e-11without a warning, and now gives0.99. The new default isfreq="auto", and its warning comes once per line in each sequence. Flux and coupling drives, custom drives, and coherent inputs keep a baseband pulse. A custom drive can set the class attributecarrier_harmonicto take an automatic carrier. To keep a previous pulse without a carrier, passfreq=None.freq=0.0stays a carrier at zero frequency.QuantumSequence.charge()takes the same default and warning, andcharge(freq=None)now gives a pulse without a carrier, asschedule(freq=None)does. Previously,charge(freq=None)usedchip.freq(target). If you passedfreq=Nonetocharge()for that carrier, omitfreq, or passfreq=chip.freq(target)to keep it without the warning. (#213)A time-domain solve on a chip without a frame uses the rotating frame and warns with that choice.
Chip(frame=None)is the new default, and static calculations keep the lab frame:chip.resolve(),chip.hamiltonian(), dressed analysis and steady states. Previously,Chip()setframe="lab", so a solve resolved each device’s phase at its transition frequency. A transmon withT1and a resonator, 15 states, driven for 20 ns and solved for 100 ns with automatic sampling, took 2.8 s and 1.76 GB. It now takes 0.43 s and 0.33 GB, and itsP1error at QuTiP’s default tolerances falls from 1.0e-4 to 3.5e-6. Expectation values do not depend on the frame. Stored states and trajectory measurement records are now in the rotating frame, andresult.state(..., frame="lab")converts a state. A ketinitial_stateis an integration-frame vector attlist[0], so whentlist[0]is not zero it is now a vector of the frame that the warning names. When a collapse or port operator does not fit the rotating frame, the solve uses the frame thatframe="auto"plans and warns. Examples are a reduced chip whose eliminated bus carries each transmon’s jump onto the other, and two readouts on one feedline. When the planned frame does not fit either, the solve uses the lab frame. A batch checks every point and solves all of them in the first of these frames that fits each point. In the planned frame, each point gets its own plan, as withframe="auto", and each point starts in the same physical state as its own solve. When a device has no reliably assigned dressed0 -> 1frequency, as in a resonant pair of identical devices, the solve raises, names the devices, and asks for a frame such asChip(frame="lab").chip.frameisNoneon a chip without a frame.chip.frame_info()returns the frequencies of the frame that a solve without pulses would use, without a warning, or raises as the solve does. To keep the previous behavior, passChip(frame="lab"). A saved chip keeps its saved frame. (#214)eliminate(..., method="sw")keys the exchangepathwaysby occupation tuples in the source chip’s device order, asanalyze_static_zz()does. Previously, the keys were flat product indices, such as1and17for(0, 0, 1)and(1, 1, 1)on a chip with dims(3, 3, 4). The pathways of both calls now list every nonzero contribution, so they sum to the second-order part of their element. Previously, contributions with|V_ik V_kj| <= 1e-12GHz² were left out, so a weak coupling across a small gap could list no pathway for a shift of-1e-9GHz. (#199, #208)A level label such as
{"q0": 1}or"eg"names its dressed state everywhere. Aninitial_statelabel starts a solve in the dressed state of the label, under the solve’s approximation and excitation cutoff, placed in the solve frame attlist[0]as an omittedinitial_stateis. Soinitial_state={}equals an omitted state. Previously, a labelinitial_statewas the bare product, so a coupled chip exchanged excitation from the start. Two transmons 100 MHz apart withg = 30 MHzexchanged 26% of the population.chip.superposition()andchip.compact_state()also give dressed states, andcompact_state(..., dressed=False)keeps the bare form. Uncoupled chips do not change. To keep a bare start, passinitial_state=chip.bare_state(...), which also takes(amplitude, label)pairs for bare superpositions. Insidejax.jit,chip.bare_state()now stays a constant when it depends on no traced value, so this start keeps the exact excitation space of its levels. Previously, the traced ket made the solve use the full product space. A mapping with a local ket raises as aninitial_state. Passchip.bare_state(...)instead, orchip.compact_state(..., dressed=False), which now also takes local kets, on a chip too large for its product space.population()stays the local projector. (#206, #205)active_patch()eliminates spectators in the default order ofeliminate_spectators(). Previously, it took the spectator farthest from the active devices first, then label order. On rings of transmons and coupling resonators, that order spread a drive over 768 states and dropped the near-resonant exchange shifts of kept transmons. The reduced chip, its notes andeliminated_labelscan change. To keep a previous order, pass itseliminated_labelsasorder=. (#151, #183)chip.state(),DressedResult.eigenstatesandDressedResult.eigenvector_matrixgive each dressed vector a real, nonnegative amplitude on its assigned bare state. Previously, their signs came from the eigensolver, so a state could differ in sign between QuTiP and dynamiqs. Dressed-basis operators and drive matrix elements already used this phase. When the assigned amplitude is zero, the vector’s largest amplitude now sets the phase of all these outputs. Energies, populations and overlap magnitudes do not change. Code that transforms with the eigenvector matrix directly can see off-diagonal entries change phase. (#163, #170)frame="auto"ranks coupling bands whose integer weights sum to a nonzero value after tones, exchange bands and reference pins. Previously, underExact(), a counter-rotating band could set a negative-frequency frame, or the lab frame as the solve window grew. Without tones,"auto"now follows exchange constraints and device references. Passframe="lab"to keep the previous no-tone frame. The chip default stays"lab", and frame planning removes no Hamiltonian terms. Stored integration-frame phases and numerical integration errors can change. (#135, #141)Devices whose Hamiltonian is diagonal by construction, such as
Resonator,DuffingTransmonorKerrCavity, keep their levels in authored order whenjax.jitorjax.gradtraces their parameters. Previously, a traced eigensolve sorted the levels by energy, as plain evaluation does. A call now raises an error when traced values put the levels out of energy order, or put a dropped level below a kept one. For example, aDuffingTransmonwhose spectrum turns over within its levels raises. Use fewer levels or an eigenbasis truncation below the turnover, or evaluate without JAX transformations. (#138)Static-coupling
DroppedTermrecords describe the entry with the largest|V|²/ΔE, whereΔE = |E_col - E_row|uses the isolated energies.amplitudeis that entry’s|V|, andfrequencyis itsΔEin any frame. Previously,amplitudewas the band’s largest entry andfrequencywas the frame carrier, which is 0 GHz in the lab frame. The carrier moves to the newframe_frequencyfield. Time-dependent coupling and drive records leaveamplitudeandfrequencyasNone. For records from a coupling’sdropped_terms(), assembly fills a missingframe_frequencyinstead offrequency. Labels print the unchangedcolumn - rowweights withoutΔ, and the reason reads “discarded by RWA band selection”. (#132, #134, #137)RWA()warns when a discarded static band has a nonzero resonant entry or a larger|V|²/ΔEthan every retained off-diagonal band. The band mask is unchanged. For uneven spectra such asFluxonium, compare withExact()or list every band to keep inRWA(keep_bands=...), which replaces the default selection. Each resolved snapshot reports these warnings and the “vanishes entirely” warning for its own approximation. Chip analysis reports them once, at the first query that uses its cached model. Implicit reference-frequency lookups do not report them. (#133, #137)An explicit
thermal_occupation=0keeps a zero-rate absorption channel. Previously, a concrete zero dropped it. Dynamics and gradients do not change, but each emission channel of that device gains a zero-rate partner, and dissipative pulse solves of the chip use the full product space. Omitthermal_occupationfor a vacuum-only model. (#125)An eligible
SolveProblemstores its initial state, operators andengine_result.dimsin compact excitation coordinates, andEngineResult.dress()returns compact eigenvectors. Results keep physicaldims. Rebuild a problem withbuild_problem(..., initial_state=...)instead of replacinginitial_statewith a product-space state. (#125)Custom
RWA(keep_bands=...)selections must contain every adjoint band. Add each tuple’s negation to preserve Hermiticity. Asymmetric selections now raise instead of producing non-Hermitian Hamiltonians. (#122)CR susceptibility now includes real-carrier halving.
IX_per_amplitudeandZX_per_amplitudeare half their previous values, while raw matrix elements stay unchanged. Recalculate gate-time estimates from the corrected rates. (#122)Under
RWA(), carrier drives keep their weight-zero (longitudinal) bands with the complete waveform, including at an explicitfreq=0.0. Previously, quchip dropped these bands and listed them as dropped terms, so a transmonFluxDrivepulse scheduled withfreq=had no effect. Pulse results change for drive operators with diagonal energy-basis elements. (#122)eliminate()of a port-coupled mode keeps the mode’s scattering as a reference section at its dressed transition. A port alone on its plane getsPortNetwork.mode_reflection(...). Previously, the reduced chip kept only the transformed port, so its VNA response missed the mode’s reflection by about κ/Δ. The residual is of order (g/Δ)²κ/(2π|Δ|) from Purcell dispersion, plus omitted survivor damping and, for SW, higher-order energy shifts. A port that shares its plane with fields outside its series path now raisesNotImplementedError. Keep such a mode in the model. (#76, #85, #115)Capacitive couplings use each device’s declared charge operator, as charge drives do. For ordinary Fock devices, this changes
a + a†toi(a − a†). Exchange terms keep their sign, and counter-rotating terms change sign. DrivenExact()results and mixed-basis exchange phases can change.EigenbasisDevicenow requirescharge_operatorfor capacitive coupling. (#108)FluxTunableTransmonscales charge bys = (E_J(Φ)/E_J,max)^(1/4)and phase by1/s. Coupling strengths and charge-drive amplitudes now refer to the sweet spot. To retain a calibration at another static bias, dividegby each flux-tunable endpoint’ss, and charge-drive amplitude by its target’ss. Phase-drive amplitudes require multiplication bys. Named charge and phase observables and port operators use these scales. AFluxDrivepulse still changes only the frequency and does not vary the charge scale in time. (#108)pulse.<i>.detuningnow names the carrier detuning. A custom envelope parameter nameddetuningmoves topulse.<i>.envelope.detuning, like one namedphase. Code that rebinds or sweepspulse.<i>.detuningfor such an envelope now adds a carrier offset and keeps the envelope’s value. Use the new path withwith_params()andQuantumSequence.vary(). (#106)Without an excitation cutoff,
VNA.finite_power()andVNA.measure()withoptions=Nonesolve a chip that conserves the total excitation and has more than 64 product states on the labels withN <= K, and they warn whichKthey kept. WhenK + 1fits in memory, they compare the results with it at a tolerance of1e-6, keepK + 1, and the warning says whether this check ran. The check does not bound the error against the full product space. Passoptions={}to keep the full product-space solve. (#156, #185)The truncation check is now the function
quchip.check_truncation(result), next towith_truncation. It accepts the same results and returns the same reports as thecheck_truncation()methods ofSimulationResult,TrajectoryResultandPartitionedSimulationResult. These methods still work but warn withDeprecationWarning, and quchip 0.6 removes them. Under anExcitationCutoff, a request now records only the cutoff and its boundary totals for the check, and the check finds the compact rows when it runs. (#188)On a chip that conserves the total excitation
Nand has more than 64 product states or anExcitationCutoff,steadystate()returns the vacuum without a Liouvillian solve when the lowering channels prove it unique. The result holds the vacuum on the labels withN <= 1, whichresult.excitation_spacenames, soresult.stateis no longer a product-space matrix.reduced_state()ande_opsvalues do not change. The call no longer raises in a frame where the resolved Hamiltonian is not static, because the vacuum is the same in every frame. Passoptions={}to keep the product-space solve. (#191, #203, #205)Without an excitation cutoff,
VNA.output_spectrum(),VNA.g1()andVNA.g2()withoptions=Nonesolve a chip that conservesNand has more than 64 product states on the labels withN <= K, asfinite_power()does, and warn whichKthey kept. They compare each offset or delay withK + 1asfinite_power()does. Passoptions={}to keep the full product-space solve.finite_power()andmeasure()also take this route when the VNA has pumps at the probe frequency. (#191, #205)QuTiP batches take their default worker count from loky’s
cpu_count(), which followsLOKY_MAX_CPU_COUNT, CPU affinity and container limits, instead ofos.cpu_count(). The default pool shrinks to the number of workers that the available memory holds, and each result records its count instats["workers"]. (#196, #205)pulse.<n>parameter paths name the n-th scheduled pulse. Virtual-Z shifts, delays and barriers no longer count, sovz()ordelay()does not move the paths of later pulses inparameters,with_params()andvary(). Sequences without such entries keep their paths. In a sequence with avz(),delay()orbarrier()before a pulse, renumber the paths: aftervz(),delay()and one pulse,pulse.2.amplitudeis nowpulse.0.amplitude. (#217, #221)Every built-in device now uses the textbook charge sign
[φ, n] = +i, as Krantz et al. (2019), Koch et al. (2007), Blais et al. (2021) and scqubits do. The Fock devices (DuffingTransmon,Resonator,KerrCavity,Qubit) andFluxTunableTransmonuseQ = i(a† − a)instead ofi(a − a†), and the charge basis ofChargeBasisTransmonhase^{iφ}raisen, which flips itssin φphase operator.Fluxoniumand the scqubits imports already used this sign. From energy eigenstates, populations under a charge drive alone or charge couplings alone do not change. Drive matrix elements on the Fock devices change sign, for examplem = iinstead of−ion the0 -> 1transition of a Duffing transmon, so a real π pulse gives−iYinstead ofiY. The cross-resonance coefficientsIX_per_amplitudeandZX_per_amplitudebetween these devices change sign together. To keep a calibrated gate, add π to the phase of itsChargeDrivepulses on these devices. APhaseDriveon aChargeBasisTransmonchanges sign in the same way, and atn_g ≠ 0its populations can also change, because the sign flip is then not a symmetry of the device. The relative sign between aCoherentInputport drive and aChargeDriveflips.PHYSICS.mdgained a cross-walk from quchip’s conventions to these references. (#222)ChargePhaseDrivenow drives a transition at a positive carrier, asChargeDrivedoes, and takes the automatic carrierchip.freq(target). Its Hamiltonian staysI·Q_c − Q·P, but with the textbook charge sign above it isi(s a† − s* a)on a Fock device: the resonant sideband at twice the rate ofChargeDrivewith no counter-rotating term, so its π amplitude is half that ofChargeDrive. It now also accepts devices whose operators are matrices, such asChargeBasisTransmonandFluxonium, where the resonant sideband is the stronger of two. Previously, a 20 ns Gaussian at ChargeDrive’s π amplitude and +5 GHz left a 5 GHz transmon in|0>, and only a negative carrier drove it. To keep the Hamiltonian of a 0.4 schedule on a Fock device, negatefreqand replace the pulse phase φ by π − φ. (#221, #222)
New features¶
VNA(chip).resonances()returns the poles of the weak-probe response on the routes ofVNA.sweep(): for each pole, its lab-frame frequency, its response linewidthκ = −2 Re λ, its composition over the modes, the dominant label and the residue of each S entry.scattering(frequencies)rebuilds the swept response from the poles, anddarkmarks poles whose residues are negligible at the selected ports. Ill-conditioned poles that are too close to resolve, as near an exceptional point, raise. Frequencies and linewidths have JAX derivatives. (#192, #208)The exact route of
eliminate()reports the margins of its labeling checks undervalidity["<mode>_labeling"]: the largest dressed weight of each diagnostic label, the runner-up and the margin to0.5 + 1e-6, and the smallest eigenvalue and orthonormalization residual of the kept overlap matrix. A failed check of either route raisesEliminationError, aValueErrorthat carries the route, the check, and the label with its weight, the residual, or the two degenerate levels with their coupling. Its message is the previous message followed by these values, also insidejax.jit. (#198, #208)EliminationResult.pathways(final, initial)attributes any element of an SW-retained Hamiltonian to virtual states, including dispersive and Lamb shifts. It takes level mappings such as{"qa": 1}. (#199, #208)sequence.subspace_propagator(devices, levels=(0, 1))returns the propagator of a sequence on the dressed states of the selected levels, as aPropagatorResult. One batch starts each label in its dressed state, and the record converts the label amplitudes to the dressed frame by default, or to the lab or integration frame. The dressed frame turns each device at its dressed frequency under the solve’s approximation and cutoff, so an idle pair of qubits givesdiag(1, 1, 1, exp(-2πiζT))with the static ZZζ. The record gives the leakage of each input, andfidelity(target)returns the average gate fidelity, which counts that leakage. The solves leave out collapse channels, whichomitted_channelsnames, anddissipation=Trueraises and points tosubspace_channel(). (#193, #207, #209, #210)sequence.subspace_channel(devices, levels=(0, 1))returns the gate with the chip’s collapse channels as aChannelResult: a superoperator on the same dressed labels and frames, fromd²pure-state master-equation solves. It gives the infidelity in quchip’s frame, with ideal preparation and measurement, and the eigenvalue infidelity, a spectral score that a frame tilt does not change, with the average leakage beside it. Labels may include leakage levels, andseepage()reads them.error(target)returns the error map after the ideal gate, with its Pauli transfer matrix, Pauli fidelities, Pauli rates and twirl. PHYSICS.md §9.5 gives the definitions. (#223)Envelope.area()returns the complex area of an envelope: closed forms for the built-in shapes, and Gauss-Legendre quadrature between the sampling times for other shapes, also under JAX tracing.chip.rotation_amplitude(transition, line, envelope, angle=π)returns a first-guess amplitude fromθ = 2π |m| |A a|, with the dressed matrix elementmof the line. PHYSICS.md §6.2.1 states the relation, its rotation axis and its limits. (#197, #207)TrajectoryResult.records(),clicks()andrun_expect(key)read the measurement currents, click events and per-run expectations of QuTiP and Dynamiqs in one layout. Records have shape(runs, channels, intervals)and name their native sampling. Click records keep the sampling weight of each run, the no-click weight and a full-buffer flag.result.nativedoes not change. (#194, #207)eliminate_spectators(chip, keep)reduces a chip to the kept devices, oneeliminate()step at a time, and returns every step. Its default order keeps the operators of kept devices small underlocal=True. A spectator waits while a spectator neighbour links it to a kept device near it in frequency, so the chain keeps their fourth-order exchange shift. It also waits while its step would couple two near-degenerate spectators beyond the fold limitg/Δ = 0.1. On a ring of eight transmons with four kept, the drives stay within 256 states. The largest f01 error is 22.8 kHz, against 98.1 kHz when each arc goes from one end.order=sets the steps, and the default order raises under JAX tracing of device parameters. Eacheliminate(..., local=True)step reports its widest operator support innotesand warns when one dense copy needs 256 MiB or more.active_patch()acceptsorder=. (#151, #183)eliminate_spectators()andactive_patch()estimate the shift that each Schrieffer-Wolff step omits from a kept device when a device that stays on the chip links a removed spectator to it. An example is the coupler thatactive_patch(hops=1)keeps next to a driven transmon.omitted_shiftsholds the estimate in GHz, and a note names each term. For a transmon with a kept coupler and a spectator 40 MHz away, the estimate is -39.9 kHz and the f01 error is +39.7 kHz. Outside the dispersive regime, and next to a direct exchange, the estimate isNone.method="exact"withlocal=Falsekeeps the shift. (#184, #183)ExcitationCutoff(max_total=K)keeps the product energy labels whose levels sum to at mostK. Set it withChip(..., space=...)orchip.with_space(...). It is separate fromRWA()andExact(), and quchip never sets it. The truncated model keepsP H(t) P, with every element between retained sectors, and the projected jumpsP L P. Labeled dressed queries,Chip.state(),Chip.compact_state(),fit_a_dress(evaluator="full")and deterministic pulse solves on QuTiP and dynamiqs use it, also under JAX tracing. UnderExact(), the frequencies of a ring of 24 three-level devices then need blocks of 301 and 2600 states atK = 3. Labels and supplied states above the cutoff raise.quchip.check_truncation(result)reports the sampled population in the sectors that connect above the cutoff, as a heuristic.Chip.dress(), VNA sweeps, output spectra and correlations, steady-state, stochastic, elimination, local-fit and partition workflows raise for an active cutoff. Compare results at increasingK, because convergence need not be monotone. (#180)VNA.finite_power()andVNA.measure()solve a chip that conserves the total excitation on the product labels withN <= K, without the product space. This route applies when every port lowersN, other channels lower or conserve it, the inputs are vacuum, and the chip has no pump or thermal occupation. A finite probe couples everyNtoN ± 1, so the route truncates. Withchip.with_space(ExcitationCutoff(max_total=K)),Kis the chip’s. Without a cutoff, a chip with more than 64 product states starts atK = 2, compares every grid point withK + 1, raisesKuntil the change is at most1e-6of each value, keepsK + 1, and warns which cutoff it kept. A truncation that leaves more than one stationary state raises. Two transmon-readout pairs take 0.06 s instead of 18 s for two grid points, and 12 pairs keep 325 of 3²⁴ product states atK = 2. Under a cutoff, a chip outside this route and a JAX-traced call solve the compact model directly. (#156, #185)eliminate()removes a readout with one port in each direction of a two-way hanger feedline. The reduced network keeps the readout’s dressed pole at the original ports, so vacuumVNA.sweep()keeps the dip and the reflection into the other direction. Both transformed ports keep each qubit’s directional Purcell decay. In conserving models, a one-port readout that shares a line with such a mode also keeps a pole instead of amode_transmission(...)section. Only vacuum sweeps use the pole. Pumped, finite-power, noisy and excited-reference field calculations raise, and so do time-domain coherent inputs at the pole’s ports. Elimination raisesNotImplementedErrorfor two ports of one mode on one directional line. It also raises for a multiport mode in a model that does not conserve excitation number. The result’svalidityreportsself_energy_over_widthfor each kept pole. It compares the return path through the omitted pole with the damping of each kept mode on the other ports, anddescribe()shows it. (#131, #140, #174, #177)Deterministic pulse simulations on QuTiP and dynamiqs evolve in an invariant excitation space when quchip can prove one. The proof covers the initial state and every retained Hamiltonian, control, collapse operator and no-jump product. A batch proves one space for the initial states of all its points. A closed one-excitation evolution of a ring of 24 three-level devices uses 24 coordinates, or 25 when decay adds the vacuum, instead of 3²⁴. Transverse drives and thermal raising can require the full space, and stochastic solvers keep it. When the dense operators of a proven space do not fit in memory, the solve uses sparse operators in the full space. Local observables, measurements and saved-state replay stay compact.
Chip.compact_state()prepares bare or conserving dressed states without allocating the product space. Full-space operator assembly and state exports check available memory first. Under JAX tracing, the proof holds for traced coupling strengths and for traced parameters of diagonal device Hamiltonians, such as aDuffingTransmonfrequency. quchip applies no excitation cutoff. (#125, #138)VNA.sweep(..., when={q: 1})computes weak-probe scattering from a frozen dressed reference with zero or one excitation. The response includes all declared loss channels and coherence recycling. Diagnostics report reference escape and stationarity rates. This is an approximate CW response when the reference decays, and does not simulate a finite acquisition. QuTiP and dynamiqs both compute it, with JAX derivatives on fixed structure. On dynamiqs, eager sweeps of every length reuse one compiled solve for one frequency, and a traced sweep compiles one loop over its frequencies. Iterative blocks stop at convergence and check the residual of every response and derivative solve. (#124, #154, #176)Labeled dressed queries,
drive_matrix_elements(), weak-probe VNA andfit_a_dress()diagonalize only the excitation sectors they need when the chip conserves total excitation number. The engine proves conservation from the retained operators, so conservingExact()models qualify as well asRWA()models. Other chips keep the full-space route.fit_a_dress()appliesmax_hilbert_dimto the largest matrix it diagonalizes and checks it again after rebinding or tracing. A 24-transmonExact()exchange chain now fits withmax_hilbert_dim=24. Full spectra, dressed states anddress()still diagonalize the complete chip. (#98, #114, #123)Dispersive readout accepts
kappa_detectedandefficiency. Total linewidth controls pointer states and dephasing. Observed loss controls detected SNR. Defaults preserve ideal collection. Invalid concrete input domains raise. (#122)eliminate()removes a readout whose port shares a feedline with other ports.PortNetwork.mode_transmission(...)keeps its dressed transmission as a one-pass reference section. Reference sections include internal loss that earlier eliminations carried onto several devices. The result reports a dispersive field-error scale for kept ports beyond the section askappa_over_delta, not a strict bound. (#116)eliminate()removes port-coupled modes on chips with more than two devices. The transformed port acts jointly on every survivor, and VNA accepts it. A later elimination transforms that port again, so you can eliminate both readout modes of a chip in either order. (#77, #85)PortNetwork.mode_reflection(...)adds a serializable two-sided reference section that reflects like a damped linear mode. (#76, #85)eliminate(chip, device, local=True)computes the SW reduction from a patch around the device. The retained Hamiltonian, channels and map match the full reduction up to round-off omissions, which the result’s notes report in GHz. Each correction part and channel keeps its own support, so repeated local eliminations around a ring stay local. Noisy channels can still spread along the captured maps. Ports on a shared feedline enter each patch as independent copies, and the reduced chip keeps the complete feedline. On one machine, a lossy 24-mode ring with every readout on one feedline reduces to its eight readouts in 8 s with a 1.3 GB peak, as with independent ports. The removed mode’s reference section includes spectator dressing from interactions outside the patch, as withlocal=False.chidescribes only the patch.Chip.partition()keeps the devices of each captured map in one part. Bothactive_patch()entry points forwardlocal.method="exact"and chips with baths raiseNotImplementedError. (#112, #113, #130, #139)fit_a_dress(evaluator="local")accepts chips with aPortNetwork. Each neighborhood keeps the ports whose targets it holds, with their field subgraphs. A cut through a shared feedline, a joint port or unsupported boundary scattering raisesNotImplementedError. Previously, any port network raised. (#100, #109)QuantumSequence.schedule(),charge()andphase()acceptdetuning=, a carrier offset in GHz referenced to the pulse start. The pulse is then the same in its frame at every start time, so a detuned calibrated gate keeps its rotation axis when it moves.pulse.<i>.detuningrebinds, sweeps and differentiates likefreq. A carrier atfreq = f + δstays a fixed oscillator. (#106)QuantumSequence.schedule(),charge()andphase()acceptframe=, the device whose virtual-Z phase a carrier pulse follows on any line. For cross-resonance control, set the target qubit as the frame of both the cross-resonance and cancellation tones. The default remains the driven device. (#92, #105)FitADressResult.history_paramsandFitADressResult.history_residualsgive the parameter vector and the target residuals in GHz at eachhistoryentry. Previously, the path of a fit needed one rerun withmax_nfevper point. With a numerical Jacobian, they include the finite-difference probes, ashistorydoes. (#152, #168)Every stationary result records its numerical route under
diagnostics["route"]. TheSolveRoutegives the route, the reason for it, the dimension that it solves on, its array library and an estimate of its peak memory.SteadyStateResult,OutputSpectrumResultandOutputCorrelationResultgaindiagnostics. One planner chooses the route of each stationary workflow, andPHYSICS.md§6.6 lists the routes. On dynamiqs, the sector route solves with SciPy on the host, and its reason says so. (#203, #205)steadystate(),VNA.output_spectrum(),VNA.g1()andVNA.g2()accept a chip with anExcitationCutoff. A chip that conservesNsolves on the excitation sectors, and other chips solve the projected model with the backend’s stationary Liouvillian. Three transmon-readout pairs have 729 product states, and atN <= 2each of these calls takes less than 0.2 s. (#191, #205)Every result records its integration frame as
result.frame.state(),state_at(),dm_at()andreduced_state()takeframe="lab".overlap(),amplitude_array(),final_overlap_magnitudes()andfinal_amplitudes()takeframe="lab", which reads a ket target as a lab-frame vector. Their label targets, such as{"q0": 1}, are dressed eigenstates that turn with the label. An idle dressed state then reads 1 in every frame. A ket target keeps its meaning, a vector of the integration frame. (#190, #202, #205)chip.excitation_structure()reports whether the static model conserves the total excitationN, the change ofNof each coupling, retained term, port, drive and collapse channel, the largest entry of each term that breaks conservation, and the largest block that dressed queries diagonalize.describe()shows the verdict and the block size. When only entries below1e-12of the largest local level span break conservation, dressed queries warn and name the terms. (#195, #201, #205)simulate_batch(),Chip.solve_many(),solve_many()andsolve_batch()takemax_workers.max_workers=1solves in the calling process, and a count that does not fit in memory raisesMemoryError. (#196, #205)quchip warnings have categories.
QuchipWarning, aUserWarning, holdsAutoChoiceWarningfor notices about automatic choices (frame, carrier, cutoff, solver fallbacks) andAccuracyWarningfor advice that a result can be inaccurate (truncation, approximation, dressed labels, fits). One filter such aswarnings.filterwarnings("ignore", category=AutoChoiceWarning)silences a group. Renamed APIs keepDeprecationWarning. (#221)chip.rotation_amplitude()works on lines with signal-chain transforms and crosstalk. It passes a tone at the dressed transition frequency through the chain and sums each reached line’s matrix element times its transfer, so a gaingdivides the seed by|g|and delayed or crosstalk paths to one device add as phasors. Previously, any transform on the line raised. A transform must set the new class attributeSignalTransform.linear_time_invariantto take part;Gain,Delay,Crosstalk,CrosstalkMatrixandCableLossset it, and a transform without it still raises.ControlEquipment.tone_transfer(line, frequency)returns the transfer to each line. (#218, #221)
Fixes¶
Under
RWA(), a pulse with a zero carrier (freq=0.0, orfreq + detuning = 0) keeps its complete real drive, as underExact(). Both partners of a zero-frequency band are resonant, butRWA()kept one, so the drive had half its amplitude at phase 0 and did not go to zero at phaseπ/2. On a 50 MHz mode, a 20 ns Gaussian pulse of amplitude 0.02 gaveP1 = 0.085withfreq=0.0and0.276withfreq=None; both now give0.276. A zero carrier still followsphase=andvz(): on a charge line its carrier phasepscales a real envelope bycos p. A carrier near zero but not zero keeps one partner underRWA(); useExact()there. (#224)Every calculation evaluates a generic
Couplingwith the chip’s backend. Previously,resolve(),eliminate(),chip.state()andchip.freq()used the process default backend, so on a dynamiqs chip while QuTiP was the default, the first three raisedTypeError. The callable form now receives the chip’s backend. (#158, #166)The sequence of
active_patch()keeps the timing of the source schedule. Previously, a delay on the line of a removed device no longer reached a later barrier, so later pulses of the patch sequence started earlier, and avz()on a removed device madesimulate()raiseKeyError. (#162, #167)The conservation check of the total energy-level index reads each cascaded port in the chip’s retained coordinates, as assembly does. Previously, it read the authored port. A retained map that mixes the port’s levels could then put the chip on the excitation-sector route, which drops the parts of the generated Hamiltonian that change the total level. (#171)
After
eliminate()keeps a removed readout’s response on its line,simulate()raisesNotImplementedErrorfor anOutputFieldwhose channel carries emission from the response’s ports, and for diffusive monitoring of such a channel with a nonzero efficiency, including pure-state SSE. Monitoring a hidden channel that acted on the removed readout, such as its internal loss or a bath on it, also raises.EffectiveTerms.omitted_responsesand the baths record these channels. The record survives serialization and later reductions, and stays when the port network drops the response. Previously, these outputs omitted the response with no error, e.g. a steady output amplitude 160 times too large at the response frequency. Keep the boundary mode for these outputs. Other outputs, SME records of other channels, and population observables still run. (#159, #172)After
eliminate()removes a readout with a one-port reflection or transmission section, diffusive monitoring with a nonzero efficiency raisesNotImplementedErrorfor the line output through the section and for a hidden channel that acted on the readout, such as its internal loss or a bath on it. Native monitoring applies no reference section, so these records missed the readout’s response, e.g. a mean of the internal-loss field almost 800 times too small under a weak resonant input. The transformed port, the carried channels and the baths record the readout.OutputFieldtraces, other records and unconditional solves still run. (#175, #178)Cascaded ports keep the Hamiltonian that they generate in two more cases. Previously, a compact pulse solve with
dissipation=Falsedropped it, so a photon stayed in the first of two cascaded resonators instead of moving to the second. A small port rate, e.g. 1e-9/ns, also dropped it, because an absolute tolerance treated the term as zero. Now an element counts as zero only below a round-off threshold set by the port products that form it. (#179)Pump-free
VNA.sweep()computes models that are static in the lab frame but dynamic in the probe frame, e.g. capacitive couplings underExact(). Previously, it raised. It solves one lab-frame stationary state per chip point and a shifted Liouvillian at each probe frequency, andTis zero away from DC. Other pump-free stationary sweeps also use this route, so their residual and condition-number diagnostics describe the lab-frame generator. (#148, #155)On a chip reduced by
eliminate()oractive_patch(),drive_matrix_elements()passes each drive operator through the retained coordinate maps, as pulses do. Previously, it used the authored operator and missed the dressing by removed devices, e.g. 0.125% at g/Δ = 0.05. (#146, #150)QuTiP calculations that receive traced JAX values, e.g. under
jax.grad, raise aValueErrorthat namesbackend='dynamiqs'. Previously, they failed inside operator arithmetic withTypeErrororConcretizationTypeError. The compact weak-probe VNA route applies the same check to port rates and phases. (#128, #142)jax.gradandjax.jitwith a traced device frequency pass the collapse-channel frame check aftereliminate()and for a collectiveBathin a common frame, also on a reduced chip. Previously, they raised aValueError.jax.jitof the rate of a collectiveBathonEigenbasisDevicetargets also passes the check. Aftereliminate(), a traced coupling also passes the check on the port of a surviving mode in per-device frames. TracedVNA.sweep()calls keep the mode-space route of plain evaluation, also on reduced chips, andwhen=no longer raises. (#126, #127, #144, #138)Local eigenvector derivatives retain resolved small-gap responses. Their cutoff now uses matrix dimension and floating-point precision instead of
1e-9of the spectral span. Degenerate eigenvector derivatives remain undefined. (#122)eliminate(..., method="exact")diagonalizes each total-excitation sector separately when the static model conserves excitation number, as underRWA(). Retained terms no longer carry ~1e-12 entries between sectors. These entries made the rotating-frame collapse-operator check fail at some parameter values. (#78, #82)resolve()of a reduced chip, including kept and transformed ports, gives the same result underjax.jitandjax.gradas eagerly. Previously, an exactly reduced chip could fail the rotating-frame check underjax.jit, even when no traced value entered the reduction.EffectiveTerms(..., excitation_changes=...)andPhysicsExpr.from_matrix(..., excitation_changes=...)declare the structure that keeps this check independent of traced values. (#78, #79, #82)A band whose level changes cancel among devices that share a frame frequency is now exactly static. Its frame frequency was summed one device at a time, so level changes such as (3, −1, −2) left carriers of approximately 1e-14 GHz. An exactly reduced two-qubit readout chip in a 5.2 GHz frame resolved 44 static couplings as time-dependent terms. It also split drive bands with equal carriers into separate terms. (#78, #82)
QuTiP
mesolve,smesolveand stationary solves raiseMemoryErrorbefore they assemble a superoperator whose estimated peak exceeds the available memory. Previously, the operating system killed them. The dynamiqs stationary Liouvillian uses the same check. (#80, #82)eliminate()reportsj_effanddJ_domega_cin the emitted edge’s units, accounting for complex or non-unit charge matrix elements. Previously, taking the real exchange element could produce a zero or mis-scaled edge. Retained terms still carry the full reduced Hamiltonian.g_over_deltanow uses resolved exchange rather than authored strength. Parallel device couplings report their combined exchange. (#111)QuTiP preserves envelope steps listed in
sampling_times()after pulse starts and line delays, within floating-point precision. Previously, these steps could become interpolation ramps. Unlisted steps still require explicit feature times. Pulse edges at zero no longer cause interpolation overflow warnings when the solve starts before zero. (#104)QuTiP stationary solves estimate the memory of their LU factors before they factorize, and raise
MemoryErrorwhen it does not fit. Previously, they checked only the generator. On three transmon-readout pairs, whose factors need about 1.6 TB, SuperLU ran for more than 15 minutes, and the call now raises after 2.3 s. The dynamiqs steady state counts the sixD² × D²arrays of its solve instead of the generator alone. An 81-state chip peaked at 4.65 GB against a check of 0.69 GB. (#189, #205)Memory checks run on macOS and Windows. Previously, without
/proc/meminfoor an installed psutil, every check accepted every request. (#200, #205)A solve with
dissipation=Falseno longer checks the collapse channels that it leaves out against its frame. Previously, such a channel raised when it did not fit the frame: a collective decay of two detuned devices refusedframe="rotating"even without dissipation. On a chip without a frame, these solves andsubspace_propagator()now use the rotating frame instead of a fallback. Ports still need to fit the frame, because drives through a port and the Hamiltonian of a cascade stay in the solve. (#214)Dressed label queries, such as
dispersive_shift(),freq(..., when=...),static_zz()andkerr_matrix(), warn when the couplings mix a label with states beyond the levels of a truncated device. The check counts coupling steps for dispersive, nondegenerate couplings. It warns at second and fourth order, and up to the order at which the excitations of the label first interact, such as sixth order for ZZ through two couplers in series. UnderExact(), steps that create excitations also count. Diagonal couplings, such asCrossKerr, and couplings that the approximation removes do not. Silence is not a convergence check. The warning names the device and thelevelsthat remove it. Values do not change. Previously, a two-levelResonatorgave a dispersive shift of +9.98 MHz with no warning, against -0.99 MHz with three levels. AQubitis not a truncation and never warns. (#211)After an earlier
eliminate(..., method="exact")step,effective_params[...]["purcell_rate"]and["kappa"]count the removed mode’s own loss channels. Previously, both read 0.0, because the channel acted on several devices in the retained coordinates. On every route, they now count only the channels that the mode declares, asPHYSICS.mdstates. Previously,local=Falsealso counted a retained channel that acted on the mode alone. The retained channels did not change. (#211)Chips with a port network, sequences that hold them,
SimulationResult,SParameterResultandResonanceResultpickle and load. Previously,pickle.dumpsraised on each of them. A loaded chip rebuilds its caches on demand. (#211)Error messages name the cause and the fix in five cases.
fit_a_dress()says when it read a coupling’s declared strength as a dressedcross_kerrtarget. A solve that meets an unwired line nameschip.wire(...).matrix()withouttnames the time-dependent terms and their rotation frequencies. A carried collapse channel or a feedline exposure that a frame cannot compose names its devices and the frames that work, such asframe="auto". (#211)Chip.describe()printsT1,T2andthermal_occupationonce for each device. Previously, a device that declares them printed them twice. (#211)vz(device, θ)turns a laterTwoPhotonDrivepump ondeviceby 2θ, and in general a pulse on a line whose drive setscarrier_harmonic = kby kθ when its frame is the line’s target. Previously, every carrier pulse took θ. A virtual Z of π/2 between two squeezing pumps then acted like a second pump with phase π/2, not π, and leftP(vacuum)at 0.50 instead of 1.00. One-photon drives, coherent inputs, and pulses in another device’s frame keep θ. (#216)A dynamiqs batch over a pulse carrier that includes
freq=0.0runs. In the lab frame underRWA(), a zero carrier and the frame cancel, so that point had another signal structure, andsimulate_batch(pulse.vary("freq", [0.0, 0.05]))raised “Heterogeneous signal pytree”. QuTiP already ran this batch.freq=0.0stays a carrier at zero frequency. (#216)An explicit
frame="rotating"warns when a device’s dressed0 -> 1reference has no reliable label assignment, and namesframe="auto". The frame does not change. Two identical transmons withg = 10 MHztook references of 5.01 and 4.99 GHz without a warning, and their idle exchange then had an error of 9e-7 against 2e-13 in the lab frame. A chip without a frame already raises on the same check. (#216)Steady-state results and
VNA.finite_power()results pickle and load. Previously,pickle.dumpsraised on their read-only mappings and on the deferred memory estimate of the stationary route. Every read-only mapping in quchip records now pickles as a copy, and a deferred value pickles its calculated value. (#216)SimulationResult.population()works on a result saved withstates="final"and holds only the final value, sopopulation(q, 1)[-1]reads it with either storage choice. Previously, it raised “Full state history is unavailable” although the final state was saved. The newfinal_population()onSimulationResultandSimulationBatchResultreturns the final-time population and needs only the final state. (#216)On the QuTiP backend, a pulse envelope reaches the accuracy of the solver’s
rtol. QuTiP sampled each windowed envelope at 40 points/ns whatever the tolerance, so a Gaussian π pulse kept an error inP1of 4.1e-6 at 12 ns and 8.6e-5 at 4 ns, also atrtol = 1e-10. The sample step now followsh = sqrt(8 rtol max|f| / max|f''|), never below 40 points/ns. At QuTiP’s defaultrtol = 1e-6, the errors fall to 1.0e-6 and 3.1e-7 at the same cost, and atrtol = 1e-8to 9.4e-9 and 1.6e-8. A window that would need more than 2,000,000 samples uses the exact envelope as a callable and warns. QuTiP outputs can move at the 1e-6 level. (#216)IQReadout.from_wiring()no longer warns about the chip’s frame. It reads only the output wiring, which does not depend on the frame, so it resolves the chip in the lab frame. (#221)Under
RWA(), a pulse with a negative carrier keeps the signal band that co-rotates with its transition. Previously,RWA()picked the band by position and kept the counter-rotating one, so aChargeDriveπ pulse atfreq=-5.0left a 5 GHz transmon in|0>(P1 = 0.99 underExact()). A carrier at-fnow drives as at+fwith the conjugate phase, also when the carrier is traced, and the dropped-term record gives the partner at|f| + f_transition. A zero carrier keeps its conventional half. (#221)
Performance¶
VNA.measure()with noise and an automatic cutoff settles the means first and starts the noise comparison at the first agreeing pair, which keeps the same cutoff and skips the noise solves below it: 49 s instead of 55 s for a four-level transmon and a 20-level resonator at five frequencies.On QuTiP, exact excitation spaces and
ExcitationCutoffsolves store each compact operator as CSR instead of a dense matrix of the compact dimension. A dense backend, such as dynamiqs, checks each dense compact operator against the available memory before it allocates it, instead of budgeting eight such matrices for any number of terms. A driven ring of 24 three-level transmons atExcitationCutoff(max_total=3)has 2,901 states. Itsbuild_problem()andsimulate()now peak at 0.25 GB instead of 7.35 GB, andcheck_truncation()takes 0.6 s instead of 82 s. (#186, #187)On a chip whose static model conserves the total energy-level index,
chip.state()diagonalizes only the sector of its label, and the default initial state of a solve is the bare product with no static model. The full eigensystem’s memory check now runs before its decomposition, not before the static model build. For example, on a reduced 15,552-state chain,chip.state({})and the default initial state no longer stop withMemoryErroron an 8 GB machine. (#153, #171)Multi-device operators project factor by factor into the retained local eigenbases instead of through the dense native product space. Two coupled default
Fluxoniumdevices previously needed an estimated 820 GB. Theirchip.freq()now peaks below 256 MB. Opaque operators keep the dense route. The engine estimates both routes, takes the smaller, and raisesMemoryErrorbefore an allocation that exceeds available memory. (#96, #107)VNA.sweep()solves weak-probe scattering in the one-excitation block when a pump-free chip conserves total excitation number and every input is vacuum. This covers Duffing transmons, pure dephasing, reduced chips with retained terms, and shared feedlines whose ports each lower excitation by one. Previously, these chips fell back to a dense D²×D² stationary solve. On one machine, the reported transmon-resonator sweep at Hilbert dimension 64 took 540 s. Now it takes 10 s, mostly for the frame’s reference-frequency analysis. Dimension 400 runs in 10 s instead of needing about 410 GB. Diagnostics name the route"vacuum_response". (#81, #85, #110)The QuTiP backend stores dense operators that are at most one quarter nonzero as CSR. Superoperator terms built from reduced-model bands and jumps then stay sparse. A lossy
mesolveof a reduced model at Hilbert dimension 144 now peaks near 1.3 GB instead of an estimated 1.2 TB. (#80, #82)dynamiqs solves sum the time-dependent Hamiltonian into one array per step from operators assembled on the host. A traced chip reuses its resolution and dressed analysis within one trace. On one machine, a 576-state chip’s jitted reverse-mode gradient through an exact elimination and a dynamiqs pulse took 830 s per call. It now takes 3.7 s. A batch of eight pulse phases takes 4.1 s instead of 20 s. (#85)
QuTiP
mesolveof a Hermitian density matrix integrates its packed upper triangle with one sparse product per step. Drive carriers that act through the same operator share one coefficient. A lossy pulse on the same chip takes 200 s instead of 400 s. A coherent 16-pulse train takes 7.3 s instead of 49 s. (#85)Large packed QuTiP master-equation products split their rows over up to four threads, which brings that lossy pulse to 145 s.
QUCHIP_NUM_THREADS, or elseOMP_NUM_THREADS, sets the count. Worker processes use one thread unlessQUCHIP_NUM_THREADSis set. Each row sums in the same order, so results do not depend on the thread count. (#85)Static analysis of concrete chips sums, diagonalizes, reduces and labels operators with NumPy instead of compiling a JAX program for each array shape.
fit_a_dress()on the 576-state chip takes 3.1 s per fit instead of 15 s. (#85)On the general stationary route,
VNA.sweep()solves an operating point that stays stationary across probe frequencies once, not at every frequency. The QuTiP backend runs its default direct steady-state solve without QuTiP’s per-call option scope, which rebuilt every data-layer dispatcher.steadystate_batch()over 40 points runs 5.4 times faster, andVNA.finite_power()over 44 points runs 3.2 times faster. (#85)On two transmon-readout pairs with 81 product states,
VNA.output_spectrum()takes 0.07 s instead of 86.7 s on QuTiP, and 0.11 s instead of 144.7 s on dynamiqs. On dynamiqs,chip.steadystate()takes 0.6 s instead of 124 s. (#191, #203, #205)VNA.measure()with an automatic cutoff starts each cutoff’s noise solves from the solutions at the previous cutoff, as its stationary solves already did. The solves keep their equations and residual limit, so only the solver path changes. On a 4-level transmon with a 20-level resonator, 5 probe frequencies and 5 noise offsets, the call takes 7.4 s instead of 11.4 s, with the same kept cutoff and noise spectra within 2e-13. The solutions are kept only while they fit in memory. (#221)
Compatibility¶
The dynamiqs backend supports dynamiqs 0.3.7, where
QArrayis abstract, and keeps support for 0.3.6. Sparse operators use the public constructor and keep JAX gradients. Batched integration uses the public time-array conversion and native save callbacks, and still reports numerical failures. (#136, #149)Backend.steadystate()takes an optionalguess: a native stationary state of a related problem, which a backend can return if it is also stationary here. A backend that overridessteadystate()must accept the keyword but can ignore it. (#85)Backend.coherence_response()solves conditional VNA responses. Its default raisesNotImplementedError, so a custom backend must implement it to supportVNA.sweep(..., when=...). The QuTiP and dynamiqs backends implement it. (#124)The backend protocol gains
sector_generator(),sector_solve_bytes(),sector_unique(),stationary_bytes(),compact_operator(),compact_operators_fit()andtrajectory_view(). Their defaults run the host SciPy sector solver and dense storage, so a custom backend needs no change.Backend.solve_batch()takesmax_workers, and a backend that overrides it must accept the keyword. (#200, #203, #204, #205)
Documentation and examples¶
PHYSICS.md§10.4 identifies the Löwdin map ofeliminate(..., method="exact")with the canonical Schrieffer–Wolff rotation to all orders, provided the kept/dressed overlap block is nonsingular. Off-diagonal reads differ frommethod="sw"because SW truncates at second order. (#129, #143)Correct dispersive signs, readout rate definitions, VNA phase notation, and reduction limits. Document device and bath models, convergence requirements, and parametric control. Add primary references for the physical conventions. Align guide calculations, fitting targets, and plots with ground-positive Z. (#122)
Rewrite docstrings, guides, examples, the README and
PHYSICS.mdin shorter, plain sentences. Code, error messages and notebook code cells are unchanged. (#87)Remove the NV-centre guide, its figures and its links. (#85)
Re-execute the example notebooks and guides against the current code. The transmon avoided crossing in example 01 now reports the RWA splitting of 3.99 MHz, which agrees with second-order theory to 0.3%. The fluxonium readout comparison uses
Exact(), because counter-rotating terms contribute strongly to its dispersive shift. (#88)PHYSICS.md§4.2 describes result frames and dressed label targets, §6.5 the sector routes ofsteadystate()and the output statistics, §6.6 the route record, and §9 the dressed meaning of level labels. (#205)PHYSICS.mdopens with a short orientation and a table of its main conventions, and uses one symbol for each quantity. It corrects statements in §1, §6.4, §8, §11 and §13 that no longer matched the code, among them the default frame. On docs.quchip.org, its source links now point to GitHub, and table rows render whole. (#212)The
effective_paramsdocstring ofEliminationResultstates whatlamb_shiftandfreq_aftermean under each method.method="sw"reports the shift through the removed mode.method="exact"reports the labeled dressed transition, which includes couplings that stay on the chip. (#211)PHYSICS.mdlistsVNA.resonances()among the workflows that raise under a cutoff and corrects two stale source references. The README no longer describes the removed quchip.org walkthrough. (#211)
Development¶
CONTRIBUTING.mddocuments the release-note format, and the pull request template uses checkboxes for AI assistance. (#75, #89)tools/theme_figures.pyrenders a PDF or dark PNG only when its light SVG changed.--forcerenders every figure. (#88)The engine owns the resolution kernels, state preparation, excitation structure, frame map and route planner. Engine kernels move out of
chip/, state preparation moves out ofresults/, andresults/no longer importsanalysis/or dynamiqs.quchip/utils/memory.pyholds one memory cost model. (#200, #201, #204, #205)
[0.4.0] - 2026-09-29 ¶
0.4 series highlights¶
The 0.4 series adds continuous-measurement trajectories and extends custom-device and effective-Hamiltonian workflows:
Quantum-jump and diffusive trajectories on QuTiP and Dynamiqs, with native results, parameter batches, and explicit monitored channels.
Custom operator names across noise, ports, observables, and elimination, plus an ideal two-level
Qubitmodel.Elimination of retained
EffectiveTerms, with dressed parameter reports and inspectable approximation notes. Dressed analysis follows the chip’s selected approximation.
Changes since 0.3.2¶
Full comparison: v0.3.2 → v0.4.0.
Changes and migration¶
Dressed analysis now follows
chip.approximation, including frequencies, states, Kerr shifts and drive matrix elements. Previously these queries silently retained all coupling bands even on anRWA()chip. Construct the chip withapproximation=Exact()to recover the previous full-Hamiltonian analysis. Dressed queries remain independent of the integration frame. Elimination and its χ report follow the same choice;method="exact"diagonalizes the selected Hamiltonian without restoring discarded bands. (#71)Solves with
initial_state=Nonenow use the all-ground-labeled eigenstate of the undriven static lab-frame Hamiltonian retained by the solve’s approximation. The default RWA with ordinary couplings remains a bare product. Before the frame transform, a solve using the chip’s approximation selects the same physical state aschip.state()for the all-ground label. Retained bands, effective terms, or network Hamiltonian terms that couple the vacuum can dress an RWA start. Rotating-frame solves applyU(tlist[0])†. Passchip.bare_state()explicitly to keep the previous bare start. (#64)Deterministic Dynamiqs solves default to
Dopri8(rtol=1e-9, atol=1e-11)instead of the nativeTsit5()(rtol = atol = 1e-6), at which pulse-parameter gradients could be off by a factor of two. Passmethodto restore the old integrator. (#57)VNA S-parameters, finite-power fields, field correlations, and IQ statistics now follow the engineering
e^{+jωt}convention, wherej = −i, including network phases and delays. Remove manual conjugation of VNA outputs; conjugate old complex probe and pump amplitudes to reproduce the same physical drive. Internal mode observables and authored network parameters keep their physics convention. (#46)Wiring-derived IQ readouts share the VNA convention for supplied boundary means, detector fields, and receiver filtering. Conjugate raw simulation fields before supplying them as readout templates. Receiver-filter validation now also accepts JAX arrays. (#46)
Truncation diagnostics are now explicit. Remove
check_truncationandtruncation_thresholdfrom solve calls; useresult.check_truncation()or preparewith_truncation(problem)when saving only diagnostics. (#45)Omitted stochastic storage uses native defaults. Put native call keywords in
run_argsand integrator options inoptions. For stochastic Dynamiqs calls,optionsholds native solver keywords;methodandgradientgo inrun_args. See the backend options. (#45)
New features¶
Added native quantum-jump trajectories (
mcsolveon QuTiP,jssesolveon Dynamiqs), diffusive stochastic Schrödinger equations (ssesolve/dssesolve), and diffusive stochastic master equations (smesolve/dsmesolve). Supports parameter batches, native results, and explicit monitored-channel selection. See the solver and storage guide. (#45)Added the ideal two-level
Qubitmodel. (#45)Custom devices can use declared operator names for ports and observables, and existing lowering, raising and number hooks for T1/T2 channels. Elimination supports non-capacitive mediated exchange, derives decay summaries from declared channels, and accepts custom harmonic Fock boundaries. (#58)
eliminate()accepts the label of anEffectiveTermscontribution. Withmethod="exact"it diagonalizes those terms together with the local Hamiltonians of their devices, keeps every device and edge authored, and retains the level shifts as a correction, so dressed queries on the reduced chip return the source spectrum.effective_paramsreports each device’s dressed transition, Lamb shift, anharmonicity and cross-Kerr shifts.Chiprejects effective-term labels that collide with device or coupling labels. (#69)EffectiveTermscarry producernotes.Chip.physics_notes()lists each contribution undereffective:<label>with its support, assembly rule, channels and notes, anddescribe()lists them. Device and coupling eliminations record their approximations on the retained terms, including the notes of earlier contributions they absorb. Serialized effective terms includenoteswhen present; payloads without them still load.Resonator.physics_notes()states its harmonic approximation once. (#69)fit_a_dresssummaries mark targets whose error exceeds 1% of the target. (#57)
Fixes¶
from_scqubitsandto_scqubitsnow convert energies between scqubits’ global unit,scqubits.get_units(), and GHz. Previously they assumed GHz, so objects built afterscqubits.set_units("MHz"), such as Quantum Metal’s LOM composites, imported 1000 times too large. (#72)Per-call backend overrides no longer re-project QuTiP
Qobjor dynamiqsQArrayinitial states returned bychip.state(),chip.bare_state(), orchip.superposition()insimulate()orsimulate_batch(). Hand-built foreign-backend states now also use resolved solver coordinates. NumPy/JAX arrays and symbolic or callable states remain authored-space kets. (#63)effective_hamiltonianandeffective_hamiltonian_between_statesindex the resolved product basis, so eigen-projected charge-basis devices no longer raise a singular-Gram-matrix error. Traced dressed states anddescribe()report the same retained dimensions. (#50)Collective and other multi-device collapse channels are now checked for a single removable frame phase. Unequal device frames raise before solving instead of silently retaining a static jump operator. (#50)
Retained effective terms enter the dressed-analysis cache key; a chip resolved before a coupling elimination no longer reports bare energies. (#57)
Preserve shifted square-pulse endpoints when floating-point subtraction rounds the local time past the pulse duration. (#46)
The hybridization warning has fixed text and is shown once per call site. (#57)
Compatibility¶
The dynamiqs backend targets dynamiqs 0.3.6, whose qarray rewrite broke the previous import. The
dynamiqsextra requires dynamiqs 0.3.6 or newer and caps jax below 0.11.1 until a dynamiqs release includes dynamiqs/dynamiqs#1145. Stochastic Dynamiqsoptionsare passed as native solver keywords:save_states,cartesian_batchingandsave_extra, plust0andnmaxclickforjssesolve. (#59)
Documentation and examples¶
Added an NV-centre guide modelling a spin-1 defect and its ¹⁴N nucleus through field-dependent resonances, pulsed ODMR, and Ramsey fringes. (#66)
Added a continuous-measurement guide for stochastic trajectories. (#45)
Clarified that
Capacitiveauthorsg n_a n_bon charge and phase-grid endpoints;PortNetwork.filterleaves Purcell decay unchanged. (#57)
Development¶
The contributor workflow starts PRs as drafts. Marking a PR ready for review runs validation; later pushes rerun it. Documentation, workflow changes and version-only release metadata use lightweight checks. (#73)
Historical release notes are consolidated in this changelog. Tagged releases extract their notes from the matching dated section. (#68)
0.3.2 - 2026-09-10 ¶
Changes since v0.3.1.
Network noise API¶
Passive network components now use
thermal_occupation; replace previousoccupationandloss_occupationarguments and saved parameter keys.Amplifiers require explicit
added_noise. Temperature, noise-figure, andnoise_frequencyconstructor arguments have been removed; convert these inputs to noise quanta before declaring components. See the network-noise migration guidance.
API documentation¶
Added parameter options, defaults, units,
Nonebehavior, result shapes, and physics references across public constructors and methods.Reuse inherited backend contracts and suppress empty type-only parameter tables. A coverage check detects missing descriptions and stale names.
Serve README images from the documentation site for consistent rendering.
Development and releases¶
PR fast and pre-merge test selections cover the suite without repeating the fast tests on Python 3.11. Merges require checks against current
main.Remove post-merge test reruns, make benchmarks manual, and fail change classification when Git cannot read the compared revisions.
Tagged releases verify package metadata and release notes, publish to PyPI, and create the corresponding GitHub Release after publication.
0.3.1 - 2026-09-08 ¶
Changes since v0.3.0.
Measurements and fridge noise¶
VNA.measure()captures the driven steady-state response, internal mode amplitudes and photon numbers, and output noise spectra. Receiver bandwidth, integration time, calibration and repeated IQ sampling reuse those results without another physical solve.Fridge noise propagates through declared attenuators, isolators, circulators, filters and amplifiers. Measurements report output noise density and contributions by source, including cross-output IQ covariance.
result.measure()samples saved quantum states after either Schrödinger or master-equation evolution. Joint measurements preserve correlations and support assignment errors and calibrated conditional IQ distributions.result.iq_readout()uses captured downstream wiring;IQReadout.from_wiring()uses a supplied wired model. Both transform supplied conditional coherent fields and accumulate receiver noise without requiring a readout pulse. These detector models do not infer IQ signals from qubit populations or add measurement backaction to the simulated evolution.
Documentation¶
Added section navigation and aligned page titles across headings, sidebars and the README.
Extended the fridge guide with steady-state versus sampled resonator responses and Rabi counts and IQ using the same wiring.
Moved the Purcell calculation into Focused studies, retaining its existing URL.
Shortened the cookbook to practical API choices and common pitfalls; example-authoring guidance now lives under Contribute.
Public names and compatibility¶
Public names now distinguish bath occupation, jump rates and network ports:
Previous name |
Preferred name |
|---|---|
|
|
|
|
|
|
|
|
|
|
The previous names remain compatibility aliases through 0.4 and are scheduled
for removal in 0.5. Old thermal constructor arguments, parameter bindings,
noise configurations and saved device dictionaries are accepted. Parameter
discovery and new serialized dictionaries use thermal_occupation only;
supplying both spellings in one update raises an error. The value is the bath’s
mean occupation, not an initial qubit population. Jump rates include absorption
and dephasing channels and are not generally emitted photon fluxes.
The new measurement results are named StateMeasurement and StateSamples
for saved quantum states, and VNAMeasurement, VNAMeasurementStatistics and
VNAMeasurementSamples for VNA calculations. result.measure(...) and
VNA.measure(...) keep their existing call syntax. fit_a_dress is unchanged.
0.3.0 - 2026-09-06 ¶
Fixes¶
Fixed solver selection so density-matrix initial states use
mesolveeven without collapse terms. QuTiP and dynamiqs now reject a density matrix passed explicitly tosesolve.QuTiP no longer selects
method="diag"automatically when the resolved SLH Hamiltonian contains network-generated static terms, avoiding diagonal-propagator failures for cascaded degenerate modes. Solver failure messages now include the underlying exception details.Replaced the local eigensolver’s custom VJP with a custom JVP.
jax.jacfwdandjax.hessiannow work through traced device parameters, whilejax.gradis unchanged; exact degeneracies mask the eigenvector connection to zero, and second derivatives through a degeneracy remain undefined.Automatic QuTiP
method="diag"now covers open systems up to Hilbert dimension 32 (Liouvillian dimension 1024), up from 12. It made a static 20-dimensional transmon-resonator chip 100× faster than adaptive stepping.
Network and state diagnostics¶
chip.state()now warns when the requested label’s assignment overlap is below0.9and points tochip.bare_state()for the product state. Degenerate cascaded modes can dress into superpositions and weaken the product-state assignment.PHYSICS.mdnow states the multi-port Lamb-shift convention:phase_shift(phase=2π f τ)gives+γ sin φ, matching Kockum et al. It also states that the SLH core is Markovian:delay()shifts reference planes and is not retardation.Portnow documents thatrateandexternal_quality_factorremain constant within each solve. Shaped emission uses an explicit buffer or coupler device with a staticPortand a modulated Hamiltonian coupling.Added
SimulationResult.collapse_channels,collapse_flux(), andcollapse_integral()for resolved per-channel jump rates and cumulative expected jump counts. Batch results provide the same methods withreduce=; an exposed plane’sraw_photon_fluxmatches its collapse flux only for vacuum input.
Frames¶
Added opt-in
frame="auto", which chooses per-device frame frequencies from retained couplings and delivered scheduled signals. Drive tones include control gain, attenuation, delay, and crosstalk; coherent-input tones include network scattering and the2πconversion to ordinary GHz. Frequencies, pins, clusters, and residual oscillations are available throughresolved_frame.plan,chip.describe(), andsequence.describe().QuantumSequence.build_problem(),build_solve_problem(), andprepare_solve_problem_context()now acceptframe=. Entry-axis batches resolve"auto"at each point and use per-point problems when the selected frames differ. Stationary VNA and steady-state analyses retain their existing errors for incompatible tones or dynamic Hamiltonians; chips still default to"lab", and"rotating"is unchanged.
Input-output architecture¶
Added an immutable, input-free scalar-S SLH normal form to every resolved engine snapshot. With no ports, ordinary closed/open-system quchip workflows retain their existing behavior.
Added public
series_product,concatenate, andfeedback_reducehelpers inquchip.enginefor textbook composition of resolved SLH triples.Added
PortNetworkfor symbolic series composition, named exposures, convenient scalar scattering, and unitary vacuum dilation of attenuation. Two-sided reference sections usenetwork.delay(...); their sweepable, differentiable duration lives atnetwork.component.<label>.duration, outside the MarkovianS,L, andH. The engine applies their reference-plane factors, so backends no longer apply reference-plane phases.network.filter(...)adds passive two-sided reference sections with sweepable, differentiable transfer parameters; continuous-wave response usesH(f)exactly, while transients use its narrowband carrier value.network.amplifier(...)adds phase-preserving output-line gain with sweepable, differentiable input-referred added noise.VNA(chip)selects every external plane, whileVNA(chip, ports=...)selects a subset. Each sweep returns the complete small-signal matrix asresult.matrix;result.s(output, input)selects one entry. Backends solve all input columns from one factorization per frequency. Ordinary chip-parameterSweepaxes are accepted beside pump axes and rebind the chip at each point.VNA.sweep()now also reports the phase-conjugating small-signal matrixTasresult.conjugate_matrix, with the same[..., output, input]layout asresult.matrix;result.t(output, input)selects one entry. Around a phase-sensitive operating point,delta <b_out> = S delta beta + T conj(delta beta). The stationary route obtains both matrices from one shifted-Liouvillian factorization, while the passive-linear route returns zero forT.Added
vna.finite_power(...), which solves the stationary Liouvillian for a finite coherent probe and returns aMeanFieldResponseResultcontaining<b_out>,<b_out>/beta, and the broadcast incident field at every selected plane.PortNetwork.cascade(*items)accepts variadic chains of ports, single-channel components, and explicit field terminals;PortNetwork.expose(...)accepts ports or components as shorthand for their sole or signal terminals. VNA pump tones own their frequency and amplitude axes throughpump.vary(...), withname=setting the result axis name;vna.zip(...)pairs axes point by point.Added physical connector sides through
port.sideandcomponent.side(k), bidirectional cabling throughPortNetwork.link(...), side exposures throughPortNetwork.expose(..., at=...), and idealPortNetwork.circulator(...)andPortNetwork.isolator(...)components.PortNetwork.attenuator(...)is two-sided and reciprocal, with two hidden vacuum channels.PortNetworknow compiles instantaneous feedback loops inside the Markov core. It reduces each connection cycle with the scalar Gough–James feedback rule, including loop gain inS,L, structural reachability, and the generated series/feedback Hamiltonian; closing the same connections in turn withfeedback_reducegives the same resolved triple. Reference sections cannot lie inside a loop. Concrete singular loops raise, while traced JAX scattering produces non-finite values at the singular point.Added external-plane input scheduling through
network.expose(...).input; coherent amplitudes are insqrt(photons/ns)and are not stored onResolvedSLH.Added complete transient field traces through
result.output(plane), with complex amplitude, arbitrary post-solve quadratures, normally ordered photon flux, and the Markov-boundary values before outbound reference sections derived from the sameb_out = S b_in + Lmodel.VNA.sweep()remains small signal, whileVNA.finite_power()reports the stationary mean field.Renamed the
OutputSpectrumResultfields to distinguishtotal_fluctuation_spectrumfromsignal_fluctuation_spectrumandadded_noise_spectrum, and to marksignal_photon_flux,signal_coherent_flux, andsignal_incoherent_fluxas signal-only fluxes. Added amplifier noise remains a spectral density because converting it to flux requires a detection bandwidth;total_fluxwas removed.beam_splitternow uses the directional convention[[sqrt(eta), sqrt(1-eta)], [-sqrt(1-eta), sqrt(eta)]]; directional splitters and 90-degree hybrids have no physical sides, socomponent.side(...)points callers to terminals orcascade().PortNetwork.to_dict()now records every built-in component by factorykindandparameters, andPortNetwork.from_dict()rebuilds it through that factory. Genericcomponent(...)entries retain their terminals and scattering matrix; unknown kinds raiseTypeError.Added
PortNetwork.restrict(ports), which copies the independent field-graph components reached from selected ports, including their connections, exposures, tracked parameters, filter callables, and boundary scattering.Chip.partition()now carries separable field lines into their device-group sub-chips; if a field graph spans groups, contains components unreachable from any one group’s ports, or has boundary scattering that mixes groups, it keeps one joint solve and records why inpartition.notes. This includes a passive swap between otherwise independent ports.Added reusable network blocks through
host.include(template, prefix=...). A portless template without boundary scattering can be copied repeatedly under distinct label prefixes; its exposures become interfaces reached throughblock.side(),block.input(), orblock.output(), and copied parameters use paths such asnetwork.component.<prefix>/<label>.<name>. Exposure membership checks are now direction-aware, so one side’s input and output may belong to different asymmetric planes.
Visualization¶
Added
plot_port_network(...)for field-network schematics andplot_sparameters(...)for magnitude, dB-and-phase, and complex-plane views of small-signal scattering results.
Resolved analysis and transformations¶
Added
EngineResult.dress(at_time=...). Static snapshots may omit the time; dynamic snapshots require it and return an instantaneous eigensystem rather than a Floquet spectrum.Chip.dress()keeps its exact intrinsic lab-static meaning.Partition connectivity now records resolved multi-device support, including Hamiltonian terms generated by SLH cascade composition. Passive scattering alone does not connect independent devices, and field-reference-plane requests safely use the joint solve.
eliminate()can transform default ports on a linear resonator into an effective lowering channel on one unprojected Fock-space survivor while preserving the attached network and exposure. Custom or collective ports, active cascades, projected bases, and multi-survivor field reductions raise rather than dropping or double-counting field physics.
Current scope¶
Scattering is scalar and instantaneous in 0.3; operator-valued scattering, time-dependent collapse channels, Floquet dressing, and thermal input fields remain outside this release. Static composition of several quantum-port couplings requires a shared rotating-frame frequency.
Breaking changes and migration¶
Labels are immutable; create a replacement component to rename one. Replace
device.dressed_freqand chip-bounddevice.drive_freqwithchip.freq(device).Local state indices, populations, and Pauli operators use isolated energy levels; excited-state Z is −1.
Replace
population_array()/overlap_array()withpopulation()/overlap()(NumPy on QuTiP, JAX on dynamiqs).Set
states="all","final", or"none"instead of native storage options. For the old nearest-time behavior, passmethod="nearest"tostate_at()/dm_at(); the default is now"exact".chip.parametersincludes unset optional fields asNone. Skip them before numerical conversion; unchanged rebinding still works.Replace deprecated fitting arguments
coupling_targets,observable_targets, andfit_parameterswithconstraints,vary, andstart. Fitting defaults toevaluator="full"; choose"local"explicitly.Replace reduction metadata key
"folded_into"with"coupling". Keep the returned chip’s effective terms; parameter summaries no longer reconstruct the reduction.Custom signal transforms use
parameter()/setting()instead of_parameter_names; custom reductions implementretained_hamiltonian(ctx)andembedding(ctx). See extensions.Saved models require
format_version: 1; recreate older models from Python declarations.
0.2.1 - 2026-08-28¶
Fixed¶
ControlEquipment.set_crosstalk_matrix()now accepts nested Python lists and preserves JAX tracers when stacking their rows.Sequence parameter rebinding preserves traced array leaves when rebuilding engine records, keeping
QuantumSequence.with_params()differentiable through dynamiqs solves.
Documentation¶
Added a post-talk guide that follows the SQA 2026 presentation from dressed statics and pulse-level dynamics through model reduction and differentiation using public APIs.
Added three executed notebooks covering a bus-mediated avoided crossing with an RWA audit, active-patch reduction with a forward comparison, and pulse-gradient checks against central finite differences.
Inverse design¶
fit_a_dress(desired)treats component values as numerical dressed targets and accepts additionalconstraints=, an explicitvary=allowlist, andstart=overrides.Fit results now report target sources, bare-parameter seed and sign choices, bounds, final Jacobian rank, condition number, and weak parameter directions through structured fields and
fit.summary().
Analysis¶
Added
chip.kerr_matrix(), returning a frozen, labeled, differentiable matrix of dressed self-Kerr and full-pull cross-Kerr coefficients in chip device order.Devices with fewer than three resolved levels report
NaNonly on the self-Kerr diagonal; their defined pairwise cross-Kerr entries remain available.FluxTunableTransmon.flux_biasis now a bindable, sweepable chip parameter. A flux-only rebind preserves the SQUID calibration and retunes the local Hamiltonian; supplyingfreqandflux_biastogether defines a new anchor.
Deprecated¶
fit_a_dress()keyword argumentscoupling_targets=,observable_targets=, andfit_parameters=retain their 0.2.x seed-chip behavior but now emitDeprecationWarning. Use the desired-chip API withconstraints=,vary=, andstart=; the compatibility keywords will be removed in 0.3.0.
0.2.0 - 2026-08-14¶
Highlights¶
Inspect authored and solver-resolved physics as backend-neutral symbolic expressions without first evaluating a numerical matrix.
Choose explicit Fock, charge, phase-grid, or custom local spaces, with native or energy-eigenstate solver bases.
Extend every part of the model, from custom devices to classical signal transforms, through tested public contracts.
Physics and modelling¶
Added
PhysicsExprfor symbolic parameters, matrices, time-dependent scalars, labels, and opaque JAX callables. Expressions support semantic display, immutable parameter rebinding, and explicit numerical evaluation through.matrix(). #4Added
LocalSpace,FockSpace,ChargeSpace,PhaseGridSpace, andCustomSpace. A chip or individual device can use its authored native basis or project into a retained local energy basis withprojection_levels. #6Basis resolution now transforms Hamiltonians, couplings, drives, pumps, states, observables, collapse operators, frames, and RWA bands through one engine-owned boundary. Native solving remains the default. #6
Added distinct inspection paths:
unresolved_hamiltonian()preserves authored static physics, whilehamiltonian()reports the canonical result after basis, frame, and RWA resolution. Sequence Hamiltonians also include scheduled drives. #6Added declarative surfaces for custom devices, couplings, component-owned time dependence, drives, envelopes, scalar time coefficients, dissipation, local spaces, classical signal transforms, and scqubits mappings. Installed references exercise each supported path. #10
Drives now map complete delivered analytic signals to quantum Hamiltonians through
hamiltonian(target, signal). Envelopes define local pulse shapes, while scheduling andControlEquipmentown carrier, phase, gain, delay, filtering, and crosstalk. #10Added isolated and dressed transition queries through
device.transition_frequency(...)andchip.transition_frequency(...);chip.freq(target)remains the concise dressed0 -> 1query. #10
Engine and performance¶
Replaced the previous Hamiltonian containers with the frozen
EngineResult,SolveProblem, andSolveBatchcontracts shared by QuTiP and dynamiqs. #4Preserved sparse canonical operators through assembly, compiled native batches once, avoided unnecessary result densification, and selected compact dynamiqs storage. #6
Reused resolved engine assembly and chip snapshots when their physics inputs are unchanged, including the common state-preparation and sequence-build path. #8
Added reproducible QuTiP and dynamiqs benchmark CI with separate cold-build, repeated-build, first-solve, and warm-solve measurements, physics-parity checks, environment receipts, and raw samples. Timing changes remain informational. #7
Developer tooling and CI¶
Added per-Python CI constraint files and a weekly unconstrained dependency canary. Published package metadata remains unpinned. #3
Added a format-aware prose audit for Python docstrings, Markdown, and rendered HTML. It reports recognizable patterns and coverage without guessing authorship. #9
Added a pull-request template covering verification, documentation, paired notebooks,
PHYSICS.md, and AI-assistance disclosure. #9Declarative constructors now expose required fields, defaults, and positional or keyword-only arguments to third-party type checkers through PEP 681 metadata, without generated stubs. #10
Documentation¶
Updated the README, physics reference, cookbook, API docstrings, documentation home, and executed hello-chip example for symbolic inspection, local spaces, basis projection, and authored versus resolved Hamiltonians. #9
Kept the hello-chip plots unchanged after clean execution, receipt checks, strict Jupytext pairing, and image comparison. #9
Breaking changes and migration¶
0.2.0 intentionally breaks compatibility with 0.1.x. The removed APIs below have no compatibility aliases, and the 0.2.0 loader rejects serialized chip payloads produced by 0.1.x. Rebuild those models in code and serialize them again with 0.2.0.
Replace
HamiltonianDescriptionwithEngineResult,build_hamiltonian_description()withbuild_engine_result(), andSolveProblem.hamiltonianwithSolveProblem.engine_result.Replace
ProblemBatchandBatchedHamiltonianDescriptionwithSolveBatchand theQuantumSequencebatch APIs.CircuitDeviceis no longer public. Custom devices should declare an explicitLocalSpacethroughBaseDeviceorFockDevice, as appropriate; built-in charge-basis and phase-grid devices use the same boundary.Declarative methods receive the symbolic parameter namespace
p. Custom models uselocal_hamiltonian(op, p),interaction(a, b, p), andtime_terms(...)returningTimeDependentTermvalues.Replace Boolean and per-component
rwa=arguments with the chip-levelapproximation=RWA()orapproximation=Exact()strategy.RWA(keep_bands=...)supports an explicit structural band selection.Replace
DriveChannel,DriveModulation, andDriveSignalSpecwith drive methods: implementhamiltonian(target, signal)and overridesignal(pulse, target)only when needed. The delivered signal exposes physicalsignal.iandsignal.qquadratures after the classical signal chain.Replace
EnvelopeShapewithEnvelope. Custom envelopes implementvalue(local_time); pulse timing, global phase, and carrier frequency belong to scheduling. In particular, moveSquare.phasetoQuantumSequence.schedule(..., phase=...).Replace
Modulationwith componenttime_terms(...)returningTimeDependentTermvalues and aTimeCoefficient, such asCosineCoefficient.Replace
NoiseChannelwithdissipation(...)returningCollapseChannelvalues containing unscaled operators and rates in inverse nanoseconds.BaseDriveandSignalTransformare no longer top-level exports. Import them fromquchip.control; extension authors should normally start fromDeviceDriveorCouplingDrive.Use
chip.unresolved_hamiltonian()when authored lab-frame physics is required.chip.hamiltonian()now returns the resolved basis/frame/RWA view used by the engine.Required CI currently constrains
qutip<5.3.1because scqubits 4.3.1 and earlier cannot consume the SciPy sparse arrays returned by qutip 5.3.1. This is a CI compatibility constraint, not a package dependency pin. #3
0.1.1 - 2026-07-21¶
Added¶
Added the executed Hello, drive and readout example, its reader-facing walkthrough, and a cookbook for executable quchip studies. #1
Added
CITATION.cffwith the accompanying paper as the preferred citation.Published the API and physics documentation at docs.quchip.org.
Fixed¶
Heterogeneous QuTiP problem lists now use the loky process pool while preserving input order. #2
Aligned the physics reference with the implementation and widened one physics-sentinel symmetry bound to a platform-independent solver-accuracy floor.
Packaging and infrastructure¶
Added PyPI trusted publishing for version tags, project links, Python 3.11/3.12 pull-request checks, and scheduled full-suite CI.
Served README figures from quchip.org so they render consistently on GitHub and PyPI.
0.1.0 - 2026-07-19¶
Initial public release of the open-source Python toolkit for modelling superconducting quantum chips.
Included device, coupling, control, frame, RWA, dissipation, transformation, sweep, visualization, and inverse-design APIs; QuTiP and dynamiqs backends; and JAX-compatible differentiation paths.
Published the README, contribution guide, code of conduct, physics reference, and test suite.