Allocating and Using Quantum Memory in CUDA Quantum

CUDA Quantum provides a quantum memory model that enables one to think about general qudits of information, dynamic and static registers of those qudits, and whether those registers are owning or non-owning. The latter point very much follows the same pattern one sees in modern C++, where we have generic owning container types like the std::vector<T> and std::array<T>, as well as non-owning container types like the std::span<T>.

To this end, CUDA Quantum defines a non-copyable unit of quantum information, the cudaq::qudit<Levels> template type. Because it is non-copyable, instances of this type cannot be passed by value and must always be passed by reference, in an effort to avoid copying, or cloning, the underlying quantum information.

Note

Thus far (as of this beta release), the majority of CUDA Quantum development work has focused on cudaq::qudit<2> (which we typedef as cudaq::qubit) but the demonstrations and discussions that follow are meant to be general on qudits.

The CUDA Quantum quantum memory container types are the cudaq::qreg<NQudits = dyn, Levels> and the cudaq::qspan<NQudits = dyn, Levels>, representing owning and non-owning semantics, respectively. Notice that the first template parameter represents the number of qudits contained and is defaulted to a special symbol in CUDA Quantum (dyn) indicating that this is a runtime-known, dynamic register of qudits, very much akin to something like std::vector<T> v(N) in classical C++. One can alternatively specify this template parameter to pick up std::array<T, N> a like semantics whereby the size of the register is known at compile time. The trade-off here is important - there may be quantum circuit optimizations that can be enabled at compile time for CUDA Quantum kernels solely employing compile-time-known cudaq::qreg allocations.

These quantum memory types are specifically designed to throw compile-time errors when they are incorrectly used. An example of this for quantum memory and its underlying ownership model can be seen in this snippet

__qpu__ void fooBad(cudaq::qubit q) { ... };
__qpu__ void fooGood(cudaq::qubit& q) { ... };
__qpu__ void barBad(cudaq::qreg<> q) { ... };
__qpu__ void barGood(cudaq::qreg<>& q) { ... };
__qpu__ void barGoodWithSpan(cudaq::qspan q) { ... };

struct myEntryPointKernel {
  void operator()(int runtimeKnownInteger) __qpu__ {
    // Allocate array-like compile-time-known
    // register of 2 qubits. Owns the qubits.
    cudaq::qreg<2> a;
    // fooBad (a[0]); // Compile Error, cannot pass qubits by value (no copy)
    // auto alias = a[0]; // Compile Error, cannot copy (auto defaults to by-value)
    auto& alias = a[0]; // Must alias by reference
    fooGood (a[0]); // Can pass by reference, no copy

    // barBad(a); // Compile Error, cannot pass qreg by value
    barGood(a); // Can pass by reference, no copy

    // Allocate vector-like register of qubits
    // Owns the qubits
    cudaq::qreg b(runtimeKnownInteger);

    // Get the front 2 qubits, which returns
    // a cudaq::span<>, it does not own the qubits.
    auto sub_view = b.front(2);

    // cudaq::span is non-owning, it can be passed by value
    barGoodWithSpan(sub_view);

    // cudaq::qreg can also be passed to a kernel that accepts a span
    barGoodWithSpan(a);

    // Front with no size provided will
    // return a reference to the first qubit.
    // You must define this as a reference variable.
    auto& frontQubit = b.front();

    // a, b go out of scope, qubits deallocated
    // returned to infinite global register of qubits
    // NOTE automated uncomputation not currently implemented.
  }
};

cudaq::qubit and cudaq::qreg types are owning types and therefore cannot be passed by value to invoked pure quantum device kernels. In order to share allocated registers with other quantum function calls, one must pass by reference or define the invoked kernel to take the qubits as a cudaq::span. Note that all slicing operations intended to extract a sub-register from a given cudaq::qreg will return a non-owning qspan.