Just-in-Time Kernel Creation **************************** **[1]** CUDA-Q provides a set of programming abstractions for dynamically constructing quantum kernel code at runtime. **[2]** The callable :code:`cudaq::kernel_builder` abstraction facilitates the dynamic definition of quantum kernels that can optionally be parameterized by user-defined input arguments. The :code:`kernel_builder` takes the following structure .. code-block:: cpp namespace cudaq { // Type wrapping a value in the kernel IR struct Value; template class kernel_builder { private: std::vector arguments; public: std::vector& getArguments(); std::string name() const; std::size_t getNumParams() const; Value qalloc(std::size_t nQubits = 1); Value qalloc(Value size); void h(Value& qubit); //... all other quantum operations ... // if (mz(q)) thenFunctor() void c_if(Value& result, std::function&& thenFunctor); // Invoke a predefined kernel template void call(OtherKernelBuilder&& kernelToCall, Values&... args); // General multi-control on a predefined kernel // models cudaq::control(...) template void control(OtherKernelBuilder&& kernelToControl, Value& ctrl, Values&... values); // General adjoint on a predefined kernel // models cudaq::adjoint(...) template void adjoint(OtherKernelBuilder&& kernelToAdjoint, Values&... values); // The constructed kernel is callable void operator()(Args... args); void operator()(void** argsArray); // Enable structured bindings template decltype(auto) get() { if constexpr (N == 0) return *this; else return arguments[N - 1]; } }; } /// Enable structured bindings on the kernel_builder type. /// auto [kernel, theta, phi] = std::make_kernel(); namespace std { template struct tuple_size> : std::integral_constant {}; template struct tuple_element> { using type = std::conditional_t, cudaq::QuakeValue>; }; } // namespace std namespace cudaq { kernel_builder<> make_kernel(); template kernel_builder make_kernel(); } **[3]** The structure above allows one to leverage the provided factory functions (:code:`make_kernel`) to construct an empty CUDA-Q kernel with defined argument signature. **[4]** For each type in the signature, the programmer is returned a new :code:`Value` instance which can be leveraged in the construction of the kernel. The intended mechanism for kernel creation and argument value extraction is via standard C++ `structured bindings `_. **[5]** Once the kernel is created, the programmer is free to build up the kernel expression using the exposed API. There are methods for qubit allocation, quantum operation invocation, control and adjoint synthesis, and conditional branching based on `boolean` values. Here is a simple example how one might build a CUDA-Q kernel dynamically. .. tab:: C++ .. code-block:: cpp auto kernel = cudaq::make_kernel(); auto qubits = kernel.qalloc(2); kernel.h(qubits[0]); kernel.x(qubits[0], qubits[1]); kernel.mz(qubits); // See algorithmic primitives section for more on sample auto counts = cudaq::sample(kernel); .. tab:: Python .. code-block:: python kernel = cudaq.make_kernel() qubits = kernel.qalloc(2) kernel.h(qubits[0]) kernel.cx(qubits[0], qubits[1]) kernel.mz(qubits) Here is an example demonstrating how one may build a dynamic set of CUDA-Q kernels for executing the standard Hadamard test. .. tab:: C++ .. code-block:: cpp auto [xPrep, qubitIn] = cudaq::make_kernel(); xPrep.x(qubitIn); // Compute <1|X|1> = 0 auto hadamardTest = cudaq::make_kernel(); auto q = hadamardTest.qalloc(); auto ancilla = hadamardTest.qalloc(); hadamardTest.call(xPrep, q); hadamardTest.h(ancilla); hadamardTest.control(xPrep, ancilla, q); hadamardTest.h(ancilla); hadamardTest.mz(ancilla); // See algorithmic primitives section for more on sample auto counts = cudaq::sample(hadamardTest); .. tab:: Python .. code-block:: python xPrep, qubitIn = cudaq.make_kernel(cudaq.qubit) # Compute <1|X|1> = 0 hadamardTest = cudaq.make_kernel() q = hadamardTest.qalloc() hadamardTest.call(xPrep, q) hadamardTest.h(ancilla) hadamardTest.control(xPrep, ancilla, q) hadamardTest.h(ancilla) hadamardTest.mz(ancilla) # See algorithmic primitives section for more on sample counts = cudaq.sample(hadamardTest)