5. Quantum Intrinsic Operations¶
In an effort to support low-level quantum programming and build foundational
quantum kernels for large-scale applications, CUDA Quantum defines the quantum
intrinsic operation as an abstraction
for device-specific single-qudit unitary operations. A quantum intrinsic
operation is modeled via a standard C++ function with a unique instruction name and
general function operands. These operands can model classical rotation
parameters or units of quantum information (e.g. the cudaq::qudit
).
The syntax for these operations is
void INST_NAME( PARAM...?, qudit<N>&...);
where INST_NAME
is the name of the instruction, qudit<N>&...
indicates one many
cudaq::qudit
instances, and PARAM...?
indicates optional parameters of
floating point type (e.g. double
, float
). All intrinsic operations should
start with a base declaration targeting a single cudaq::qudit
, and overloads
should be provided that take more than one cudaq::qudit
instances to model the application
of that instruction on all provided cudaq::qudits
, e.g. void x(cudaq::qubit&)
and
x(cudaq::qubit&, cudaq::qubit&, cudaq::qubit&)
, modeling the NOT operation on a single
cudaq::qubit
or on multiple cudaq::qubit
.
The specific set of quantum intrinsic operations available to the programmer
will be platform specific, e.g. the standard Clifford+T gate set on
cudaq::qubit
versus a continuous variable (photonic) gate set on
cudaq::qudit<N>
.
Implementations should provide overloads to support broadcasting of an
operation across a register of cudaq::qudit
, e.g. x(cudaq::qreg<>&)
to apply a NOT operation on all cudaq::qubit
in the provided cudaq::qreg
.
Programmers can further modify quantum intrinsic operations via an extra specified template parameter, and CUDA Quantum leverages this syntax for synthesizing control and adjoint variants of the operation. Here is an example of how one might modify an intrinsic operation for multi-control and adjoint operations.
cudaq::qubit q, r, s;
// Apply T operation
t(q);
// Apply Tdg operation
t<cudaq::adj>(q);
// Apply control hadamard operation
h<cudaq::ctrl>(q,r,s);
// Error, ctrl requires > 1 qubit operands
// h<cudaq::ctrl>(r);
5.1. Operations on cudaq::qubit
¶
The default set of quantum intrinsic operations for the
cudaq::qubit
type is as follows:
namespace cudaq {
struct base;
struct ctrl;
struct adj;
// Single qubit operations, ctrl / adj variants, and broadcasting
template<typename mod = base, typename... QubitArgs>
void NAME(QubitArgs&... args) noexcept { ... }
template<typename mod = base>
void NAME(const qreg& qr) noexcept { ... }
template<typename mod = ctrl>
void NAME(qreg& ctrls, qubit& target) noexcept { ... }
// Single qubit rotation operations and ctrl / adj variants
template <typename mod = base, typename ScalarAngle, typename... QubitArgs>
void ROTATION_NAME(ScalarAngle angle, QubitArgs &...args) noexcept { ... }
// General swap with control variants
// must take at least 2 qubits
template<typename... QubitArgs>
void swap(QubitArgs&... args) { ... }
bool MEASURE_OP(qubit &q) noexcept;
std::vector<bool> MEASURE_OP(qreg &q) noexcept;
double measure(cudaq::spin_op & term) noexcept { ... }
}
For the default implementation of the cudaq::qubit
intrinsic operations, we
let NAME
be any operation name in the set {x, y, z, h, t, s}
and ROTATION_NAME
be any operation in {rx, ry, rz, r1 (phase)}
.
Measurements (MEASURE_OP
) can be general qubit measurements in the x, y, or z
direction (mx, my, mz
).
Implementations may provide appropriate function implementations using the
above foundational functions to enable other common operations
(e.g. cnot
-> x<ctrl>
).
Control qubits can be specified with positive or negative polarity. By this we mean
that a control qubit can specify that a target operation is applied if the control
qubit state is a |0>
(positive polarity) or |1>
(negative polarity).
By default all control qubits are assumed to convey positive polarity.
The syntax for negating the polarity is the not operator preceeding the
control qubit (e.g., x<cudaq::ctrl>(!q, r)
,
for cudaq::qubits
q
and r
). Negating the polarity of
control qubits is supported in swap
and the gates in sets NAME
or ROTATION_NAME
. The negate notation is only supported on control
qubits and not target qubits. So negating either of the target qubits in the
swap
operation is not allowed.