7. Sub-circuit Synthesis¶
[1] Execution of pure device quantum kernels can be modified via explicit intrinsic library calls that provide multi-qubit controlled application and adjoint, or circuit reversal, semantics.
[2] CUDA-Q defines the following library functions for enabling multi-controlled and adjoint operations on a general pure device kernel:
template <typename QuantumKernel, typename... Args>
void control(QuantumKernel &&kernel,
cudaq::qubit& ctrl_qubit, Args &... args);
template <typename QuantumKernel, typename QuantumRegister, typename... Args>
requires(std::ranges::range<QuantumRegister>)
void control(QuantumKernel &&kernel,
QuantumRegister& ctrl_qubits, Args &... args);
template <typename QuantumKernel, typename... Args>
void adjoint(QuantumKernel &&kernel, Args &... args);
These functions can be leveraged in quantum kernel code in the following way
__qpu__ void x_gate(cudaq::qubit& q) { x(q); }
struct kernel {
void operator() () __qpu__ {
cudaq::qarray<3> q;
...
// Create Toffoli gate
auto ctrl_bits = q.front(2);
cudaq::control(x_gate, ctrl_bits, q[2]);
...
}
};
void rx_and_h_gate(double x, cudaq::qubit& q) __qpu__ { rx(x,q); h(q); }
__qpu__ kernel(int N) {
cudaq::qvector q(N);
...
// apply h(q[2]); rx(-pi, q[2]);
cudaq::adjoint(rx_and_h_gate{}, M_PI, q[2]);
...
}
@cudaq.kernel()
def x_gate(q : cudaq.qubit):
x(q)
@cudaq.kernel()
def kernelTestControl():
q = cudaq.qvector(3)
...
ctrl_bits = q.front(2)
cudaq.control(x_gate, ctrl_bits, q[2])
...
@cudaq.kernel()
def rx_and_h_gate(x : float, q : cudaq.qubit):
rx(x, q)
h(q)
@cudaq.kernel()
def kernelTestAdjoint(N : int):
q = cudaq.qvector(N)
...
# apply h(q[2]); rx(-pi, q[2])
cudaq.adjoint(rx_and_h_gate, np.pi, q[2])
...
[3] The cudaq::control(...)
function takes as input an instantiated pure
device quantum kernel, a std::range of control qubits (cudaq::qvector
or cudaq::qview
), and the remaining arguments for the kernel itself.
[4] Compiler implementations are free to synthesize multi-controlled operations
using any pertinent synthesis strategy available. Qubits may be aggregated into
a range of control qubits with or without the use of the operator!
negated polarity operator.
cudaq::control(kernel{}, {qubit0, !qubit1}, kernel_arg);
cudaq.control(kernel, [qubit0, ~qubit1], kernel_arg)
[5] The cudaq::adjoint(...)
function takes as input an
pure device quantum kernel and the remaining arguments for the kernel.