Skip to content

Commit

Permalink
LGPU simulator support controlled ops (#1005)
Browse files Browse the repository at this point in the history
### Before submitting

Please complete the following checklist when submitting a PR:

- [X] All new features must include a unit test.
If you've fixed a bug or added code that should be tested, add a test to
the
      [`tests`](../tests) directory!

- [ ] All new functions and code must be clearly commented and
documented.
If you do make documentation changes, make sure that the docs build and
      render correctly by running `make docs`.

- [X] Ensure that the test suite passes, by running `make test`.

- [X] Add a new entry to the `.github/CHANGELOG.md` file, summarizing
the
      change, and including a link back to the PR.

- [X] Ensure that code is properly formatted by running `make format`. 

When all the above are checked, delete everything above the dashed
line and fill in the pull request template.


------------------------------------------------------------------------------------------------------------

**Context:**
This PR updates the Lightning GPU simulator for Catalyst to support
controlled gates and matrices.

**Description of the Change:**

**Benefits:**

**Possible Drawbacks:**

**Related GitHub Issues:**

[sc-79061]

---------

Co-authored-by: ringo-but-quantum <[email protected]>
  • Loading branch information
josephleekl and ringo-but-quantum authored Nov 27, 2024
1 parent 1177ae9 commit 8a56a99
Show file tree
Hide file tree
Showing 4 changed files with 116 additions and 14 deletions.
3 changes: 3 additions & 0 deletions .github/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@

### Improvements

* Enable N-controlled gate and matrix support to `lightning.gpu` simulator for Catalyst.
[(#1005)](https://github.com/PennyLaneAI/pennylane-lightning/pull/1005)

* Reverse Lightning Qubit generators vector insertion order.
[(#1009)](https://github.com/PennyLaneAI/pennylane-lightning/pull/1009)

Expand Down
2 changes: 1 addition & 1 deletion pennylane_lightning/core/_version.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,4 @@
Version number (major.minor.patch[-label])
"""

__version__ = "0.40.0-dev19"
__version__ = "0.40.0-dev20"
Original file line number Diff line number Diff line change
Expand Up @@ -157,25 +157,31 @@ void LightningGPUSimulator::NamedOperation(
const std::vector<QubitIdType> &wires, bool inverse,
const std::vector<QubitIdType> &controlled_wires,
const std::vector<bool> &controlled_values) {
RT_FAIL_IF(!controlled_wires.empty() || !controlled_values.empty(),
"LightningGPU does not support native quantum control.");

// Check the validity of number of qubits and parameters
RT_FAIL_IF(controlled_wires.size() != controlled_values.size(),
"Controlled wires/values size mismatch");
RT_FAIL_IF(!isValidQubits(wires), "Given wires do not refer to qubits");
RT_FAIL_IF(!isValidQubits(controlled_wires),
"Given controlled wires do not refer to qubits");

// Convert wires to device wires
auto &&dev_wires = getDeviceWires(wires);
auto &&dev_controlled_wires = getDeviceWires(controlled_wires);

// Update the state-vector
this->device_sv->applyOperation(name, dev_wires, inverse, params);
if (controlled_wires.empty()) {
this->device_sv->applyOperation(name, dev_wires, inverse, params);
} else {
this->device_sv->applyOperation(name, dev_controlled_wires,
controlled_values, dev_wires, inverse,
params);
}

// Update tape caching if required
if (this->tape_recording) {
this->cache_manager.addOperation(name, params, dev_wires, inverse, {},
{/*controlled_wires*/},
{/*controlled_values*/});
dev_controlled_wires,
controlled_values);
}
}

Expand All @@ -184,23 +190,29 @@ void LightningGPUSimulator::MatrixOperation(
const std::vector<QubitIdType> &wires, bool inverse,
const std::vector<QubitIdType> &controlled_wires,
const std::vector<bool> &controlled_values) {
// TODO: Remove when controlled wires API is supported
RT_FAIL_IF(!controlled_wires.empty() || !controlled_values.empty(),
"LightningGPU device does not support native quantum control.");
RT_FAIL_IF(controlled_wires.size() != controlled_values.size(),
"Controlled wires/values size mismatch");
RT_FAIL_IF(!isValidQubits(wires), "Given wires do not refer to qubits");
RT_FAIL_IF(!isValidQubits(controlled_wires),
"Given controlled wires do not refer to qubits");

// Convert wires to device wires
auto &&dev_wires = getDeviceWires(wires);

this->device_sv->applyMatrix(matrix, dev_wires, inverse);
auto &&dev_controlled_wires = getDeviceWires(controlled_wires);

if (controlled_wires.empty()) {
this->device_sv->applyMatrix(matrix, dev_wires, inverse);
} else {
this->device_sv->applyOperation("matrix", dev_controlled_wires,
controlled_values, dev_wires, inverse,
{}, matrix);
}

// Update tape caching if required
if (this->tape_recording) {
this->cache_manager.addOperation("QubitUnitary", {}, dev_wires, inverse,
matrix, {/*controlled_wires*/},
{/*controlled_values*/});
matrix, dev_controlled_wires,
controlled_values);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -678,6 +678,93 @@ TEST_CASE("LightningGPUSimulator::GateSet", "[GateSet]") {
REQUIRE(LGPUsim->CacheManagerInfo() == expected);
}

SECTION("Controlled Pauli-X and RX") {
std::unique_ptr<LGPUSimulator> LGPUsim =
std::make_unique<LGPUSimulator>();

constexpr std::size_t n_qubits = 2;
std::vector<intptr_t> Qs;
Qs.reserve(n_qubits);

for (std::size_t i = 0; i < n_qubits; i++) {
Qs[i] = LGPUsim->AllocateQubit();
}

LGPUsim->NamedOperation("Hadamard", {}, {Qs[0]}, false);
LGPUsim->NamedOperation("PauliX", {}, {Qs[1]}, false, {Qs[0]}, {true});
LGPUsim->NamedOperation("RX", {M_PI_2}, {Qs[1]}, false, {Qs[0]},
{true});

std::vector<std::complex<double>> state(1U << LGPUsim->GetNumQubits());
DataView<std::complex<double>, 1> view(state);
LGPUsim->State(view);

CHECK(
state.at(0) ==
PLApproxComplex(std::complex<double>{M_SQRT1_2, 0}).epsilon(1e-5));
CHECK(state.at(1) ==
PLApproxComplex(std::complex<double>{0, 0}).epsilon(1e-5));
CHECK(state.at(2) ==
PLApproxComplex(std::complex<double>{0, -0.5}).epsilon(1e-5));
CHECK(state.at(3) ==
PLApproxComplex(std::complex<double>{0.5, 0.0}).epsilon(1e-5));
}

SECTION("Hadamard, CNOT and Matrix") {
std::unique_ptr<LGPUSimulator> LGPUsim =
std::make_unique<LGPUSimulator>();

constexpr std::size_t n_qubits = 2;
std::vector<intptr_t> Qs = LGPUsim->AllocateQubits(n_qubits);

LGPUsim->NamedOperation("Hadamard", {}, {Qs[0]}, false);
LGPUsim->NamedOperation("CNOT", {}, {Qs[0], Qs[1]}, false);

const std::vector<intptr_t> wires = {Qs[1]};
const std::vector<intptr_t> controlled_wires = {Qs[0]};
const std::vector<bool> controlled_values = {true};
std::vector<std::complex<double>> matrix{
{-0.6709485262524046, -0.6304426335363695},
{-0.14885403153998722, 0.3608498832392019},
{-0.2376311670004963, 0.3096798175687841},
{-0.8818365947322423, -0.26456390390903695},
};
LGPUsim->MatrixOperation(matrix, wires, false, controlled_wires,
controlled_values);

std::vector<std::complex<double>> state(1U << LGPUsim->GetNumQubits());
DataView<std::complex<double>, 1> view(state);
LGPUsim->State(view);

// calculated by pennylane.
CHECK(state[0] == PLApproxComplex(std::complex<double>{0.70710678, 0.0})
.epsilon(1e-5));
CHECK(state[1] ==
PLApproxComplex(std::complex<double>{0.0, 0.0}).epsilon(1e-5));
CHECK(state[2] ==
PLApproxComplex(std::complex<double>{-0.1052557, 0.2551594})
.epsilon(1e-5));
CHECK(state[3] ==
PLApproxComplex(std::complex<double>{-0.62355264, -0.187075})
.epsilon(1e-5));
}

SECTION("Mismatch controlled wires") {
std::unique_ptr<LGPUSimulator> LGPUsim =
std::make_unique<LGPUSimulator>();
constexpr std::size_t n_qubits = 2;
std::vector<intptr_t> Qs = LGPUsim->AllocateQubits(n_qubits);

REQUIRE_THROWS_WITH(
LGPUsim->NamedOperation("Hadamard", {}, {Qs[0]}, false, {Qs[1]},
{}),
Catch::Contains("Controlled wires/values size mismatch"));
std::vector<std::complex<double>> matrix(4, {0.0, 0.0});
REQUIRE_THROWS_WITH(
LGPUsim->MatrixOperation(matrix, {Qs[0]}, false, {Qs[1]}, {}),
Catch::Contains("Controlled wires/values size mismatch"));
}

SECTION("Test setStateVector") {
std::unique_ptr<LGPUSimulator> LGPUsim =
std::make_unique<LGPUSimulator>();
Expand Down

0 comments on commit 8a56a99

Please sign in to comment.