Operators¶
The operator (Operator) is the core abstraction for quantum operations in SparQ. Unlike traditional quantum circuit simulators, which describe quantum algorithms as sequences of gates, SparQ abstracts every transformation as a callable object (a C++ functor), separating the definition of an operator from the objects it acts upon.
The Two-Phase Model¶
SparQ operators follow a two-phase model of construction → application:
Construction phase — specify which registers the operator acts on, together with the relevant parameters. No quantum state is involved in this phase.
Application phase — apply the constructed operator to a
SparseState, completing the quantum state transformation.
import pysparq as ps
ps.System.clear()
ps.System.add_register("a", ps.UnsignedInteger, 4)
ps.System.add_register("b", ps.UnsignedInteger, 4)
ps.System.add_register("result", ps.UnsignedInteger, 4)
state = ps.SparseState()
# Phase 1: construction — specify registers and parameters
add_op = ps.Add_UInt_UInt("a", "b", "result")
# Phase 2: application — act on the quantum state
add_op(state)
The benefits of this separation:
Reuse: the same operator object can be applied to different quantum states repeatedly, without reconstructing it
Composition: operators can carry conditions (conditioned_by), so the control logic can be adjusted flexibly after construction
Reversibility: a constructed operator supports the
dag()method to perform the inverse operation
Construction Parameters¶
Construction parameters determine the behavior of an operator and usually fall into the following categories:
Register Identifiers¶
Almost every operator needs to specify its target registers. This can be done with a name string or an integer index:
# By name (recommended)
op = ps.Add_UInt_UInt("a", "b", "result")
# By index
op = ps.Add_UInt_UInt(0, 1, 2)
Constant Parameters¶
Some operators accept classical constants as parameters, for example the addend in an addition or the multiplier in a multiplication:
# Constant addition: specify the constant 7
ps.Add_ConstUInt_InPlace("counter", 7)
# Constant multiplication: specify the multiplier 3
ps.Mult_UInt_ConstUInt("input", 3, "result")
Hyperparameters¶
Some advanced operators require algorithm-level hyperparameters, such as \(\kappa\) (condition number) and \(\epsilon\) (precision) in Hamiltonian simulation, or the step-size parameter \(s\) in QDA.
Bit Indices and Angles¶
Single-qubit gates need to specify which bit of the register they act on, and rotation gates need the rotation angle:
# Apply an X gate to bit 0 of register "q"
ps.X_Bool("q", 0)
# Rotate by π/4 around the X axis
ps.RX_Bool("q", 0, np.pi / 4)
Applying Operators¶
Direct Application¶
After construction, the function-call syntax op(state) applies the operator to a SparseState:
op = ps.Hadamard_Int("reg", 4)
op(state)
Inverse Application¶
For operators derived from BaseOperator, use dag() to perform the inverse transformation:
op = ps.ShiftLeft_InPlace("reg", 2)
op(state) # shift left by 2 bits
op.dag(state) # shift right by 2 bits (undo)
For SelfAdjointOperator, calling op(state) again is the inverse operation (\(U^\dagger = U\)).
Conditional Application¶
All operators support chained conditional methods to implement controlled operations:
op = ps.Add_UInt_UInt("a", "b", "result")
# Execute only when "ctrl" is non-zero
op.conditioned_by_nonzeros("ctrl")(state)
# Stacking multiple conditions
op.conditioned_by_nonzeros(["ctrl1", "ctrl2"])(state)
Operator Categories¶
SparQ’s operators are divided into the following major categories by function:
Category |
Description |
Header file |
|---|---|---|
Quantum arithmetic |
Register-level operations such as addition, multiplication, shifting, and comparison |
|
Basic quantum gates |
Pauli gates, phase gates, rotation gates, general-purpose gates |
|
Hadamard operations |
Hadamard transforms over integers / booleans / partial qubits |
|
QFT |
Quantum Fourier transform and its inverse |
|
QRAM operators |
Quantum random access memory load operations |
|
Conditional rotation |
Rotating qubits conditioned on register values |
|
Phase and reflection |
Conditional phase flips, global phase, Grover reflection |
|
Rotation and state preparation |
Unitary rotations of arbitrary dimension, quantum state preparation |
|
System operations |
Register stack management, zero-amplitude cleanup, system split/merge |
|
Partial trace |
Measurement, selective collapse, range collapse |
|
Sorting |
Sorting basis states by key value, amplitude, and other dimensions |
|
Dark magic operations |
Unsafe operations that directly modify the quantum state |
|
Debugging tools |
Normalization checks, NaN detection, state printing |
|
For the detailed API and usage of each category of operators, see the Operator Reference section.
Concept: Operators vs. Flow-Control Classes¶
SparQ’s codebase distinguishes between two concepts at different levels:
Operators inherit from BaseOperator or SelfAdjointOperator and directly manipulate the quantum state (SparseState); they are closely tied to quantum algorithms and quantum circuits. Examples include CondRot_Fixed_Bool, T (the state-preparation operator in CKS), and SparseMatrixOracle1.
Flow-control classes (Flow-Control / Algorithm Classes) do not manipulate the quantum state directly; instead they hold registers, orchestrate operator execution, and manage state initialization and iteration. Their responsibility is to test and verify whether a quantum procedure is correct, not to implement quantum gates. For example:
QuantumWalkNSteps(CKS): manages multi-step quantum walks, register creation, and environment initializationLCU_Container(CKS): manages the Chebyshev LCU iteration loopWalkS(QDA): coordinates block encoding and state preparation in the QDA algorithm
The point of this distinction: operators are the minimal composable, testable units, while flow-control classes are the glue that chains operators together for a particular algorithmic workflow. Flow-control classes should not inherit from the Operator base class, because their responsibility is orchestration rather than application.