Skip to content

Commit

Permalink
Update NullQubit Allocate and Release functionalities (#1210)
Browse files Browse the repository at this point in the history
**Context:** Running more complex workflows with Catalyzed NullQubit
requires more structure than expected for the NullQubit class.

**Description of the Change:** This PR will expand NullQubit's Allocate
and Release Qubit functionalities.


[sc-76168]
  • Loading branch information
AmintorDusko authored Oct 16, 2024
1 parent 848522f commit 1e7914a
Show file tree
Hide file tree
Showing 4 changed files with 65 additions and 14 deletions.
11 changes: 8 additions & 3 deletions doc/releases/changelog-dev.md
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@
Available MLIR passes are now documented and available within the
[catalyst.passes module documentation](https://docs.pennylane.ai/projects/catalyst/en/stable/code/__init__.html#module-catalyst.passes).

* A peephole merge rotations pass is now available in MLIR. It can be added to `catalyst.passes.pipeline`, or the
* A peephole merge rotations pass is now available in MLIR. It can be added to `catalyst.passes.pipeline`, or the
Python function `catalyst.passes.merge_rotations` can be directly called on a `QNode`.
[(#1162)](https://github.com/PennyLaneAI/catalyst/pull/1162)
[(#1206)](https://github.com/PennyLaneAI/catalyst/pull/1206)
Expand All @@ -144,7 +144,7 @@

```python
from catalys.passes import merge_rotations

@qjit
@merge_rotations
@qml.qnode(qml.device("lightning.qubit", wires=1))
Expand Down Expand Up @@ -187,6 +187,9 @@

<h3>Improvements</h3>

* Implement a Catalyst runtime plugin that mocks out all functions in the QuantumDevice interface.
[(#1179)](https://github.com/PennyLaneAI/catalyst/pull/1179)

* Scalar tensors are eliminated from control flow operations in the program, and are replaced with
bare scalars instead. This improves compilation time and memory usage at runtime by avoiding heap
allocations and reducing the amount of instructions.
Expand Down Expand Up @@ -270,7 +273,8 @@

<h3>Bug fixes</h3>

* Resolve a bug where `mitigate_with_zne` does not work properly with shots and devices

* Resolve a bug where `mitigate_with_zne` does not work properly with shots and devices
supporting only Counts and Samples (e.g. Qrack). (transform: `measurements_from_sample`).
[(#1165)](https://github.com/PennyLaneAI/catalyst/pull/1165)

Expand Down Expand Up @@ -338,6 +342,7 @@

This release contains contributions from (in alphabetical order):

Amintor Dusko,
Joey Carter,
Spencer Comin,
Lillian M.A. Frederiksen,
Expand Down
8 changes: 5 additions & 3 deletions runtime/lib/backend/null_qubit/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,10 @@ add_library(rtd_null_qubit SHARED NullQubit.cpp)
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

target_include_directories(rtd_null_qubit PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}
${runtime_includes}
)
target_include_directories(rtd_null_qubit PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}
${runtime_includes}
${backend_includes}
)

set_property(TARGET rtd_null_qubit PROPERTY POSITION_INDEPENDENT_CODE ON)
35 changes: 30 additions & 5 deletions runtime/lib/backend/null_qubit/NullQubit.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

#pragma once

#include <algorithm> // generate_n
#include <complex>
#include <memory>
#include <optional>
Expand All @@ -22,6 +23,7 @@

#include "DataView.hpp"
#include "QuantumDevice.hpp"
#include "QubitManager.hpp"
#include "Types.h"

namespace Catalyst::Runtime::Devices {
Expand Down Expand Up @@ -51,7 +53,11 @@ struct NullQubit final : public Catalyst::Runtime::QuantumDevice {
*
* @return `QubitIdType`
*/
auto AllocateQubit() -> QubitIdType { return 0; }
auto AllocateQubit() -> QubitIdType
{
num_qubits_++; // next_id
return this->qubit_manager.Allocate(num_qubits_);
}

/**
* @brief Allocate a vector of qubits.
Expand All @@ -62,25 +68,40 @@ struct NullQubit final : public Catalyst::Runtime::QuantumDevice {
*/
auto AllocateQubits(size_t num_qubits) -> std::vector<QubitIdType>
{
return std::vector<QubitIdType>(num_qubits, 0);
if (!num_qubits) {
return {};
}
std::vector<QubitIdType> result(num_qubits);
std::generate_n(result.begin(), num_qubits, [this]() { return AllocateQubit(); });
return result;
}

/**
* @brief Doesn't Release a qubit.
*/
void ReleaseQubit(QubitIdType) {}
void ReleaseQubit(QubitIdType q)
{
if (!num_qubits_) {
num_qubits_--;
this->qubit_manager.Release(q);
}
}

/**
* @brief Doesn't Release all qubits.
*/
void ReleaseAllQubits() {}
void ReleaseAllQubits()
{
num_qubits_ = 0;
this->qubit_manager.ReleaseAll();
}

/**
* @brief Doesn't Get the number of allocated qubits.
*
* @return `size_t`
*/
[[nodiscard]] auto GetNumQubits() const -> size_t { return 0; }
[[nodiscard]] auto GetNumQubits() const -> size_t { return num_qubits_; }

/**
* @brief Doesn't Set the number of device shots.
Expand Down Expand Up @@ -295,5 +316,9 @@ struct NullQubit final : public Catalyst::Runtime::QuantumDevice {
{
return {0, 0, 0, {}, {}};
}

private:
std::size_t num_qubits_{0};
Catalyst::Runtime::QubitManager<QubitIdType, size_t> qubit_manager{};
};
} // namespace Catalyst::Runtime::Devices
25 changes: 22 additions & 3 deletions runtime/tests/Test_NullQubit.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#include "ExecutionContext.hpp"
#include "NullQubit.hpp"
#include "QuantumDevice.hpp"
#include "QubitManager.hpp"
#include "RuntimeCAPI.h"

#include "TestUtils.hpp"
Expand Down Expand Up @@ -194,7 +195,7 @@ TEST_CASE("NullQubit (no) Basis vector", "[NullQubit]")
DataView<std::complex<double>, 1> view(state);
sim->State(view);

CHECK(view.size() == 1);
CHECK(view.size() == 8);
CHECK(view(0).real() == Approx(0.0).epsilon(1e-5));
CHECK(view(0).imag() == Approx(0.0).epsilon(1e-5));
}
Expand All @@ -213,11 +214,29 @@ TEST_CASE("test AllocateQubits", "[NullQubit]")
DataView<std::complex<double>, 1> view(state);
sim->State(view);

CHECK(state.size() == 1);
CHECK(state.size() == 4);
CHECK(state[0].real() == Approx(0.0).epsilon(1e-5));
CHECK(state[0].imag() == Approx(0.0).epsilon(1e-5));
}

TEST_CASE("test AllocateQubits generates a proper std::vector<QubitIdType>", "[NullQubit]")
{
std::size_t num_qubits = 4;
std::unique_ptr<NullQubit> sim = std::make_unique<NullQubit>();

CHECK(sim->AllocateQubits(0).size() == 0);

auto q_vec = sim->AllocateQubits(num_qubits);

QubitManager qm = QubitManager();
std::vector<QubitIdType> result(num_qubits);
std::generate_n(result.begin(), num_qubits, [&, n = 0]() mutable { return qm.Allocate(n++); });

for (std::size_t nn = 0; nn < num_qubits; nn++) {
CHECK(q_vec[nn] == result[nn]);
}
}

TEST_CASE("Mix Gate test R(X,Y,Z) num_qubits=4", "[NullQubit]")
{
std::unique_ptr<NullQubit> sim = std::make_unique<NullQubit>();
Expand All @@ -234,7 +253,7 @@ TEST_CASE("Mix Gate test R(X,Y,Z) num_qubits=4", "[NullQubit]")
DataView<std::complex<double>, 1> view(state);
sim->State(view);

CHECK(view.size() == 1);
CHECK(view.size() == 16);
CHECK(view(0).real() == Approx(0.0).epsilon(1e-5));
CHECK(view(0).imag() == Approx(0.0).epsilon(1e-5));
}
Expand Down

0 comments on commit 1e7914a

Please sign in to comment.