3. Quantum Types

This language extension provides certain fundamental types that are pertinent to quantum-classical computing. These types are provided via defined library types in the cudaq namespace.

3.1. cudaq::qudit<Levels>

The cudaq::qudit models a \(D\)-level unit of quantum information. The state of this system (for \(N\) qudits) can be described by a \(D\)N-dimensional vector in Hilbert space with the absolute square of all elements summing to 1. The cudaq::qudit encapsulates a unique std::size_t modeling the index of the qudit in the underlying quantum memory space (assuming an infinite register of available qudits). To adhere to the no-cloning theorem of quantum mechanics, the cudaq::qudit is non-copyable and non-movable. Therefore, all cudaq::qudit instances must be passed by reference, and the no-cloning theorem is satisfied at compile-time. cudaq::qudit instances can only be allocated within CUDA Quantum quantum kernel code and can never be allocated from classical host code.

The cudaq::qudit takes on the following structure

template <std::size_t Levels>
class qudit {
  protected:
    const std::size_t idx = 0;
  public:
    qudit();
    qudit(const qudit&) = delete;
    qudit(qudit &&) = delete;
    std::size_t id() const;
    static constexpr std::size_t n_levels() { return Levels; }
};

3.2. cudaq::qubit

The specification provides a primitive cudaq::qubit type which models a single quantum bit (2-level) in the discrete quantum memory space. cudaq::qubit is an alias for cudaq::qudit<2>

namespace cudaq {
  using qubit = qudit<2>;
}

The qubit type can only be allocated within quantum kernel code and cannot be instantiated in host classical code. All instantiated cudaq::qubit instances start in the |0> computational basis state and serve as the primary input argument for quantum intrinsic operations modeling typical logical quantum gate operations.

{
  // Allocate a qubit in the |0> state
  cudaq::qubit q;
  // Put the qubit in a superposition of |0> and |1>
  h(q); // cudaq::h == hadamard, ADL leveraged
  printf("ID = %lu\n", q.id()); // prints 0
  cudaq::qubit r;
  printf("ID = %lu\n", r.id()); // prints 1
  // qubit out of scope, implicit deallocation
}
cudaq::qubit q;
printf("ID = %lu\n", q.id()); // prints 0 (previous deallocated)

3.3. Quantum Containers

CUDA Quantum provides abstractions for dealing with groups of cudaq::qudit instances in the form of familiar, std-like C++ containers. The underlying connectivity of the cudaq::qudit instances stored in these containers is opaque to the programmer and any logical-to-physical program connectivity mapping should be done by compiler implementations.

3.3.1. cudaq::qspan<N, Levels> (Deprecated. Use cudaq::qview<Levels> instead.)

cudaq::qspan is a non-owning reference to a part of the discrete quantum memory space. It’s a std::span-like C++ range of cudaq::qudit (see C++ span). It does not own its elements. It takes a single template parameter indicating the levels for the underlying qudits that it stores. This parameter defaults to 2 for qubits. It takes on the following structure:

namespace cudaq {
  template <std::size_t Levels = 2>
  class qspan {
    private:
      std::span<qudit<Levels>> qubits;
    public:
      // Construct a span that refers to the qudits in `other`.
      qspan(std::ranges::range<qudit<Levels>> auto& other);
      qspan(qspan const& other);

      // Iterator interface.
      auto begin();
      auto end();

      // Returns the qudit at `idx`.
      qudit<Levels>& operator[](const std::size_t idx);

      // Returns the `[0, count)` qudits.
      qspan<Levels> front(std::size_t count);
      // Returns the first qudit.
      qudit<Levels>& front();
      // Returns the `[count, size())` qudits.
      qspan<Levels> back(std::size_t count);
      // Returns the last qudit.
      qudit<Levels>& back();

      // Returns the `[start, start+count)` qudits.
      qspan<Levels>
      slice(std::size_t start, std::size_t count);

      // Returns the number of contained qudits.
      std::size_t size() const;
  };
}

3.3.2. cudaq::qreg<N, Levels> (Deprecated. Use cudaq::qvector<Levels> instead.)

cudaq::qreg<N, Levels> models a register of the discrete quantum memory space - a C++ container of cudaq::qudit. As a container, it owns its elements and their storage. qreg<dyn, Levels> is a dynamically allocated container (std::vector-like, see C++ vector). cudaq::qreg<N, Levels> (where N is an integral constant) is a statically allocated container (std::array-like, see array). Its template parameters default to dynamic allocation and cudaq::qudit<2>.

namespace cudaq {
  template <std::size_t N = dyn, std::size_t Levels = 2>
  class qreg {
    private:
      std::conditional_t<
        N == dyn,
        std::vector<qudit<Levels>>,
        std::array<qudit<Levels>, N>
      > qudits;
    public:
      // Construct a qreg with `size` qudits in the |0> state.
      qreg(std::size_t size) requires (N == dyn);
      qreg(qreg const&) = delete;

      // Iterator interface.
      auto begin();
      auto end();

      // Returns the qudit at `idx`.
      qudit<Levels>& operator[](const std::size_t idx);

      // Returns the `[0, count)` qudits.
      qspan<dyn, Levels> front(std::size_t count);
      // Returns the first qudit.
      qudit<Levels>& front();
      // Returns the `[count, size())` qudits.
      qspan<dyn, Levels> back(std::size_t count);
      // Returns the last qudit.
      qudit<Levels>& back();

      // Returns the `[start, start+count)` qudits.
      qspan<dyn, Levels>
      slice(std::size_t start, std::size_t count);

      // Returns the number of contained qudits.
      std::size_t size() const;

      // Destroys all contained qudits. Postcondition: `size() == 0`.
      void clear();
  };
}

qreg instances can only be instantiated from within quantum kernels, they cannot be instantiated in host code. All qubits in the qreg start in the |0> computational basis state.

// Allocate 20 qubits, std::vector-like semantics
cudaq::qreg q(20);
auto first = q.front();
auto first_5 = q.front(5);
auto last = q.back();
for (int i = 0; i < q.size(); i++) {
  ... do something with q[i] ...
}
for (auto & qb : q) {
  ... do something with qb ...
}

// std::array-like semantics
cudaq::qreg<5> fiveCompileTimeQubits;