Multi-control Synthesis¶
Now let’s take a look at how CUDA Quantum allows one to control a general unitary on an arbitrary number of control qubits.
Our first option is to describe our general unitary by another pre-defined CUDA Quantum kernel.
# A kernel that performs an X-gate on a provided qubit.
@cudaq.kernel
def x_kernel(qubit: cudaq.qubit):
x(qubit)
# A kernel that will call `x_kernel` as a controlled operation.
@cudaq.kernel
def kernel():
control_vector = cudaq.qvector(2)
target = cudaq.qubit()
x(control_vector)
x(target)
x(control_vector[1])
cudaq.control(x_kernel, control_vector, target)
results = cudaq.sample(kernel)
print(results)
Alternatively, one may pass multiple arguments for control qubits or vectors to any controlled operation.
@cudaq.kernel
def kernel():
qvector = cudaq.qvector(3)
x(qvector)
x(qvector[1])
x.ctrl([qvector[0], qvector[1]], qvector[2])
mz(qvector)
results = cudaq.sample(kernel)
print(results)
For this scenario, our general unitary can be described by another pre-defined CUDA Quantum kernel expression.
// Compile and run with:
// ```
// nvq++ multi_controlled_operations.cpp -o ccnot.x && ./ccnot.x
// ```
#include <cudaq.h>
#include <cudaq/algorithm.h>
// Here we demonstrate how one might apply multi-controlled
// operations on a general CUDA Quantum kernel.
struct ApplyX {
void operator()(cudaq::qubit &q) __qpu__ { x(q); }
};
struct ccnot_test {
// constrain the signature of the incoming kernel
void operator()(cudaq::takes_qubit auto &&apply_x) __qpu__ {
cudaq::qvector qs(3);
x(qs);
x(qs[1]);
// Control U (apply_x) on the first two qubits of
// the allocated register.
cudaq::control(apply_x, qs.front(2), qs[2]);
mz(qs);
}
};
int main() {
// We can achieve the same thing as above via
// a lambda expression.
auto ccnot = []() __qpu__ {
cudaq::qvector q(3);
x(q);
x(q[1]);
x<cudaq::ctrl>(q[0], q[1], q[2]);
mz(q);
};
auto counts = cudaq::sample(ccnot);
// Fine grain access to the bits and counts
for (auto &[bits, count] : counts) {
printf("Observed: %s, %lu\n", bits.data(), count);
}
auto counts2 = cudaq::sample(ccnot_test{}, ApplyX{});
// Fine grain access to the bits and counts
for (auto &[bits, count] : counts2) {
printf("Observed: %s, %lu\n", bits.data(), count);
}
}