5. Quantum Intrinsic Operations¶
[1] To support low-level quantum programming and build foundational quantum kernels for large-scale applications, CUDA-Q specifies the quantum intrinsic operation abstraction for device-specific single-qudit unitary operations.
[2] A quantum intrinsic operation is modeled via a standard free function in the native classical language. It has a unique instruction name and general specified function operands.
[3] 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...?, cudaq::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
). Quantum intrinsic operations return void
.
[4] 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
.
[5] The specific set of quantum intrinsic operations available to the programmer can be platform specific and provided via platform specific header files.
[6] Implementations should provide overloads to support broadcasting of single-qubit
intrinsic operations across containers of cudaq::qudit
. For example,
x(cudaq::qvector<>&)
should apply a NOT operation on all
cudaq::qubit
in the provided cudaq::qvector
.
[7] Programmers can further modify quantum intrinsic operations via
an extra specified template parameter, and CUDA-Q leverages this syntax
for synthesizing control and adjoint variants of the operation. Specifically CUDA-Q
provides the cudaq::ctrl
, and cudaq::adj
type modifiers for synthesizing control and
adjoint forms of the operation. For language bindings that do not support template
parameterization, implementations may rely on static method calls (e.g. x.ctrl(q, r)
)
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);
q, r, s = cudaq.qubit(), cudaq.qubit(), cudaq.qubit()
# Apply T operation
t(q)
# Apply Tdg operation
t.adj(q)
# Apply control hadamard operation
h.ctrl(q,r,s)
# Error, ctrl requires > 1 qubit operands
# h.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 qvector& qr) noexcept { ... }
template<typename mod = ctrl>
void NAME(qvector& 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 { ... }
bool MEASURE_OP(qubit &q) noexcept;
std::vector<bool> MEASURE_OP(qvector &q) noexcept;
double measure(cudaq::spin_op & term) noexcept { ... }
}
[1] For the default implementation of the cudaq::qubit
intrinsic operations, we let
NAME be any operation name in the set {h, x, y, z, t, s}
and ROTATION_NAME
be any
operation in {rx, ry, rz, r1 (phase)}
. Implementations may provide appropriate
function implementations using the above foundational functions to enable other
common operations (e.g. cnot -> x<cudaq::ctrl>
).
[2] 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 preceding the control
qubit
x<cudaq::ctrl>(!q, r);
x.ctrl(~q, r)
The set of gates that the official CUDA-Q implementation supports can be found in the API documentation.