Concepts
QUASAR (QUantum Assembler for Star-topology ARchitectures) is a hardware-aware compilation workspace for star-topology quantum devices. Its main targets are central-spin platforms, such as nitrogen–vacancy (NV) centers in diamond. There, one central electron spin is coupled to several peripheral nuclear spins.
This page explains the concepts behind the package. The tutorials show how to use them, and the API Reference documents every public class and function.
QUASAR is organized as three independent objects, each in its own subpackage:
GateSet (
quasar.gatesets) describes a native gate catalogue. It is independent of the two compilers and of any simulator.GateCompiler (
quasar.gate_compiler) executes the compilation of a logical circuit to native gates, for a bound gate set.PulseCompiler (
quasar.pulse_compiler) executes the lowering of a native circuit to pulses, for a bound gate set, backend and device model.
Conventions
QUASAR follows Qiskit’s qubit order everywhere.
object |
order |
example on three qubits |
|---|---|---|
Pauli string |
the last letter is qubit |
|
basis state |
qubit |
|
matrix of a gate or a circuit |
qubit |
as Qiskit’s |
Many textbooks put the control on the left
The formulas of the two-qubit entanglers on this page follow Qiskit’s order. The control, the gate’s first qubit, is the right tensor factor. Many textbooks write the control as the left factor, for example \(\mathrm{CX} = |0\rangle\langle 0| \otimes I + |1\rangle\langle 1| \otimes X\).
Simphony orders its spins the other way
In a Simphony model, spin 0 is the left tensor factor and the highest bit. A state or an
operator read from a Simphony simulation is in that order. Reverse it before comparing it with
Qiskit, for example with Statevector.reverse_qargs() or QuantumCircuit.reverse_bits().
The benchmarks of PulseCompiler do this themselves.
Gate sets
A GateSet describes the native gate catalogue of a device. It says which single- and two-qubit operations are native, and which qubits they connect. Optionally, it also says how each operation maps to a pulse sequence. Each native operation is a Gate of some GateType.
Native gates
The gate sets build their circuits from these gates.
Single-qubit gates
rz— a rotation about \(Z\): \(R_z(\lambda) = e^{-i\lambda Z/2}\).It is a virtual gate: the device realizes it as a frame change.
A frame change is no pulse. It takes no time and carries no error.
r— a rotation by \(\theta\) about an axis in the \(XY\) plane: \(R(\theta, \phi) = e^{-i\frac{\theta}{2}\left(\cos\phi\,X + \sin\phi\,Y\right)}\).rx— the same rotation about \(X\): \(R_x(\theta) = R(\theta, 0)\).sx— a fixed rotation with no angle: \(SX = \sqrt{X} = e^{i\pi/4} R_x(\pi/2)\).
Two-qubit entanglers
The first qubit is the control, the second the target. The formulas put the control on the right, in Qiskit’s order (Conventions).
crot— a controlled rotation, where the control flips the sign of the target’s rotation: \(\mathrm{CROT}(\theta, \phi) = R(\theta, \phi) \otimes |0\rangle\langle 0| + R(-\theta, \phi) \otimes |1\rangle\langle 1|\).crotxpi2—crotwith both angles fixed: \(\mathrm{CROT}(\pi/2, 0)\).cx— the controlled NOT: \(I \otimes |0\rangle\langle 0| + X \otimes |1\rangle\langle 1|\).crx— the controlled \(X\) rotation: \(I \otimes |0\rangle\langle 0| + R_x(\theta) \otimes |1\rangle\langle 1|\).
Rotations of the synthesis
The synthesis searches with two general rotations. Neither is native. The compiler writes each one in the gate set’s single-qubit gates, as Layers and levels shows.
U3 — any single-qubit rotation, up to a global phase: \(U3(\theta, \phi, \lambda) = R_z(\phi)\, R_y(\theta)\, R_z(\lambda)\).
U2 — a U3 with \(\theta\) fixed at \(\pi/2\): \(U2(\phi, \lambda) = U3(\pi/2, \phi, \lambda)\).
Star topology
QUASAR targets devices where a single central qubit, qubit 0, is directly connected to every
outer qubit. The outer qubits are not connected to each other.
StarTopologyGateSet captures this connectivity.
Its topology holds the directed edges ((0, 1), (0, 2), …, (0, n-1)). Every concrete gate
set in QUASAR derives from it.
A topology entry is a placement: the qubits one native entangler acts on, in the order
the gate is applied to them. The length of a placement is the entangler’s arity.
Two properties set these devices apart from all-to-all or nearest-neighbor hardware. QUASAR is designed around them:
Native gate asymmetry — different physical mechanisms drive the central and the outer qubits: microwaves and radio frequency. Their native operations and costs differ.
Pulse-level scheduling — dynamical-decoupling and RF sequences realize the high-fidelity two-qubit interactions. Gates on different edges therefore cannot run in parallel.
The built-in gate sets
QUASAR ships four gate sets:
CROTGateSet —
rz,randcrot.Pulse-level description:
SimphonyBackend.
FixedCROTGateSet —
rz,sxandcrotxpi2.crotxpi2is the fixed entangler the hardware implements.Pulse-level description:
SimphonyBackend(a Simphony digital twin) andXeedQBackend(real hardware).Each rotation of the synthesis may be a U3, which takes two
sx, or a U2, which takes one.control_rotation_ansatz,target_rotation_ansatzandclosing_rotation_ansatzchoose.
CNOTGateSet —
rz,rxandcx. Most quantum-computing tooling already assumescxas native.Gate-level only: no pulse backend.
CRXGateSet —
rz,rxandcrx. Its default angle is \(\pi\). At that angle,cxandczdecompose into a singlecrxexactly.Gate-level only: no pulse backend.
Every built-in gate set takes spam_qubits, the qubits the device resets and reads out
together. The default (0,) is the central qubit alone. Targets describes
both.
The custom two-qubit rotations are Qiskit gate objects: CROTGate, CROTXGate (free-angle), CROTXPi2Gate (the fixed hardware gate) and CROTYGate.
Gate properties
A gate set can carry what each native gate costs on the device:
error — the average gate infidelity on the gate’s own qubits, as randomized benchmarking measures it;
duration — the time the gate takes, in one unit for the whole gate set;
reserved qubits — the qubits the gate keeps busy while it runs. Two gates can run at once only when their reserved qubits are disjoint.
A value is either uniform, one for every single-qubit gate and one for every entangler, or set
per gate. A per-gate value can describe one worse edge, for example. A frame change (rz)
costs nothing: no error, no time and no reserved qubit.
Errors and durations have no default. Reserved qubits have one. On CROTGateSet and
FixedCROTGateSet, a gate on a nucleus also reserves the electron. Every gate there runs after
the one before. On CNOTGateSet and CRXGateSet, a gate reserves its own qubits only.
The error-aware strategies and GateCompiler.estimate_errors read the errors. A search for the
shortest qubit mapping reads the durations and the reserved qubits.
Gate-level compilation
GateCompiler takes a gate set and a CompilerConfig. The config selects one of two paths:
Synthesis — the
quasarcppbackend decomposes the target into native gates. Configs:Qiskit path — Qiskit’s transpiler routes and translates the circuit onto the topology. Config: QiskitConfig.
Targets
A compile has one of two targets:
unitary —
GateCompiler.compile(circuit). The compiled circuit acts like the source on every input state.state preparation —
GateCompiler.compile_state(state). The compiled circuit only has to turn one start state into the target state.It is usually far shorter than the unitary, so it reaches wider registers.
compile(circuit, initial_state=...)asks the same of a circuit run from a known state.Only the two adaptive strategies compile a state.
A circuit can also ask the device to reset qubits and to read out a Pauli string.
Reset
A reset in a circuit sets a qubit to \(|0\rangle\). The device resets its spam_qubits
together: a reset of any of them resets all of them. A qubit outside the group can get
\(|0\rangle\) by a swap from a reset qubit. The compiled circuit keeps each reset, with a barrier
on either side. A custom gate set may have no spam_qubits. Then every qubit resets alone, and
a reset stays as it is.
Pauli readout
QUASAR models a population readout. Averaged over many runs, the device resolves the
population of each basis state of its spam_qubits. These populations give \(Z\) on each of the
spam_qubits, and every product of those. These are the device’s native readouts. A
single-shot readout is not modeled.
A Pauli string lists qubit 0 last, as Qiskit does (Conventions). On
three qubits, the default spam_qubits=(0,) has one native readout, \(IIZ\). With
spam_qubits=(0, 1), there are three: \(IIZ\), \(IZI\) and \(IZZ\).
A circuit can end with a request for any Pauli string, such as \(IXY\). The compiler adds the
Clifford rotation that turns the request into a native readout. It synthesizes this rotation
together with the circuit, as one unitary. Several commuting Pauli strings can be read from
the same readout, at most as many as there are spam_qubits.
The State Preparation and Pauli Readout tutorial shows all of these. The rest of this section describes the synthesis path.
Synthesis
Stages of a synthesis compile
Structure search — grow the circuit and fit it, until the strategy stops.
Compression — remove single layers the circuit does not need (adaptive strategies).
Rotation rounding — set rotation angles near chosen values to these values.
Lowering — write the result in native gates and clean it up.
Layers and levels
Layer — a rotation on each qubit of one placement, then the entangler on it. A circuit is built from layers, and its size is reported in layers.
Level — one layer on every placement: \(n - 1\) layers on a star of \(n\) qubits.
Closing layer — a rotation on every qubit, after the last entangler.
Each rotation is a U3 during the search. On FixedCROTGateSet, a rotation may be a U2
instead. The lowering then writes each rotation in the gate set’s single-qubit gates.
(a) One level on a three-qubit star, then the closing layer. \(E\) is the gate set’s entangler. A wire drawn across a gate does not take part in it. (b) A U3 or U2 rotation in the native single-qubit gates. A dashed box is a frame change.
Each candidate structure is fitted by a numerical optimizer, from several starting points. A
fixed seed makes the compile reproducible.
The four synthesis strategies
configuration |
grows by |
stops when |
returns |
|---|---|---|---|
|
one level |
the compilation error falls below |
the first level that meets it, compressed |
|
one layer, choosing its placement |
the compilation error falls below |
the first depth that meets it |
|
one level |
the overall error stops improving |
the level with the lowest overall error, compressed |
|
one layer, choosing its placement |
the overall error stops improving |
the depth with the lowest overall error |
Adaptive strategies add the same sweep over every placement at each level. They cost one fit per starting point and level.
Tree-search strategies try the placement sequences at each depth and keep the best. They can find shorter circuits on an irregular topology, at many fits per depth.
A tolerance-driven strategy fails when it cannot meet
tolerance_errorwithin its depth bound. An error-aware strategy always returns its best circuit.
Choosing a strategy
goal |
use |
|---|---|
an exact circuit, fast |
|
an exact circuit on an irregular topology |
|
the best circuit for given gate errors |
|
the same, choosing each placement |
|
a routed circuit, without synthesis |
|
The Gate Compilers tutorial runs each strategy.
Qubit mapping
The compiled circuit may place logical qubits on other physical qubits. The mapping at its
input and the mapping at its output may differ. The layout_method option of a synthesis
config chooses how the mappings are found:
value |
candidates |
syntheses on a star of \(n\) qubits |
|---|---|---|
|
logical qubit \(i\) on physical qubit \(i\) at both ends |
\(1\) |
|
every (input, output) pair |
\((n!)^2\) |
|
input first, then output |
\(2\,n!\) |
|
every pair, as |
\(n \cdot n!\) |
|
pairs with the same mapping at both ends |
\(n\) |
"diagonal_search"leaves no net permutation to follow afterwards. On a star, it chooses which logical qubit sits on the centre.An error-aware config ranks the candidates by the overall error.
AdaptiveConfigandTreeSearchConfigrank them bylayout_objective: the entangler count (the default), the overall error, or the duration. The last two need the gate properties.
The compiled circuit records both mappings on .layout. Its gates act on the physical qubits.
PulseCompiler turns them into pulses as they are, and issues a LayoutWarning when the
layout is not the identity. The Qubit Layout
tutorial compares the methods.
Errors and error-aware search
Every error QUASAR reports or accepts is an infidelity of the whole register:
against a unitary target, an average gate infidelity;
against a target state, a state infidelity.
A compiled circuit has three errors:
compilation error — how far the circuit, run without noise, sits from the target. It is computed exactly. It falls as the circuit grows.
physical error — what the gates’ own errors are expected to cost on hardware. It is a model estimate from the gate properties. It rises as the circuit grows.
overall error — the two together.
The model treats the gate errors as independent. It underestimates one case: the same coherent
error repeated with no gate between. GateCompiler.compilation_error and
GateCompiler.estimate_errors give these errors for any compiled circuit.
Error-aware search
A tolerance-driven strategy stops at the first depth that meets tolerance_error. An
error-aware strategy weighs the fit against the gate errors instead. A deeper circuit fits the
target better, but its gates add more error. The search returns the depth with the lowest
overall error. A result above tolerance_error is the expected outcome,
not a failure. With a layout_method search, the overall error also ranks the qubit mappings.
Shortening the circuit
Compression
The compress option of AdaptiveConfig and ErrorAwareAdaptiveConfig removes the layers the
finished circuit does not need. It removes one layer at a time and refits the rest.
AdaptiveConfigkeeps a removal while the circuit still meetstolerance_error.ErrorAwareAdaptiveConfigkeeps a removal when the overall error falls, even if the compilation error grows.By default, the gate set decides. Every built-in gate set compresses.
Rotation rounding
The round_single_qubit_rotations option of every synthesis config names the angles a rotation
may be rounded onto, such as \(0\) or \(\pi/2\). A rotation rounded onto \(0\) disappears.
By default, the gate set decides.
FixedCROTGateSetrounds onto \(0\) and \(\pi/2\), every other built-in gate set onto \(0\).A rounding is kept while the compilation error stays within
tolerance_error. OnErrorAwareAdaptiveConfig, it is kept while the overall error does not rise.()turns the rounding off, for a faster compile.
What a rounded rotation costs:
target |
|
|
|
|---|---|---|---|
\(0\) |
no |
no |
no |
\(\pi/2\) |
one |
one |
one |
any other angle |
one |
two |
one |
A target that saves no gate still fixes the rotation angle, for a device that runs some angles better than others.
Lowering to native gates
Each rotation is written in the gate set’s single-qubit gates, as panel (b) of the figure shows:
rz+ronCROTGateSet;rz+sxonFixedCROTGateSet;rz+rxonCNOTGateSetandCRXGateSet.
A rounded \(\pi\) rotation becomes the basis’s own \(X\).
Virtual
rzphases are merged.Consecutive single-qubit gates on one qubit are merged into one rotation.
None of these steps changes the circuit’s action.
Pulse-level compilation
For hardware execution, QUASAR lowers a native circuit to a control pulse sequence via PulseCompiler. A gate set is a gate-level description. Its pulse-level description lives in a pulse backend registered for it.
Pulse backends
Two backends exist:
SimphonyBackend— a simulator, effectively a digital twin of the device, driven by asimphonymodel.Emits microwave, RF and DDRF pulses onto the model.
Available only when the
simphonypackage is installed.
XeedQBackend— targets real XeedQ hardware. It emits pulse programs from the calibration the device reports.to_xeedq_circuitwrites a compiled circuit in the gate names of the XeedQ toolchain.from_xeedq_circuitreads such a circuit back.
Each backend is bound to one device: SimphonyBackend(model) to a model,
XeedQBackend.from_cloud_backend to the hardware it reads. PulseCompiler(gateset=..., backend=...) takes the gate set and the backend. PulseCompiler.compile returns the backend’s
own pulse representation. A gate set without a registered backend is gate-level only.
A custom backend implements AbstractPulseBackend. register_pulse_backend registers it for a gate-set type.
Native gates as pulses
Each native gate becomes one pulse sequence, chosen by the gate and its qubits. Qubit 0 is the
electron, every other qubit a nucleus.
native gate |
acts on |
pulses |
|---|---|---|
|
any qubit |
none: a frame change |
|
the electron |
one microwave pulse |
|
a nucleus |
a DDRF sequence that rotates the nucleus whatever the electron’s state |
|
the electron and a nucleus |
a DDRF sequence that rotates the nucleus conditioned on the electron’s state |
A DDRF sequence drives the nucleus with radio-frequency pulses between decoupling pulses on the electron.
The pulses follow the circuit’s gate order, one gate after another.
Pulse parameters
A backend reads every physical number from one PulseParameters object: frequencies, drive strengths and pulse durations.
XeedQBackend.from_cloud_backendreads them from the calibration the device reports.SimphonyBackend(model)takes the frequencies from the model. The durations are choices, with defaults that SimphonyBackend lists.
Benchmarking
SimphonyBackend simulates its pulses, so PulseCompiler can compare them with the ideal
gates. It gives the average gate fidelity of one native gate, of every native gate, or of a
whole native circuit. One minus a gate’s fidelity is its error, which the gate set can carry
(Gate properties). The error-aware strategies then compile for the
simulated device. XeedQBackend does not simulate its pulses, so it cannot benchmark.
The Pulse Compilers tutorial runs these steps.