Skip to content

Commit

Permalink
Unify error message for better Catalyst support (#577)
Browse files Browse the repository at this point in the history
* intial commit

* Auto update version

* add changelog and make format

* Trigger CI

* quick fix

* add more unit tests for tensorProd

* add more unit tests

* call method in baseclass with static_cast

* test for kokkos as well

* fix

* add return type for var(obs, shot)

* add return more type

* add debug information for catalyst check

* add debug info in applyInPlace TensorProd

* Update support: remove debugging info, raise an error for TP constructor corner cases

* Auto update version

* Update format

* Apply suggestions from code review

* make format

* make format clang-tidy

---------

Co-authored-by: Dev version update bot <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Ali Asadi <ali@xanadu.ai>
  • Loading branch information
3 people committed Dec 14, 2023
1 parent 5427421 commit 8101e42
Show file tree
Hide file tree
Showing 6 changed files with 166 additions and 59 deletions.
11 changes: 7 additions & 4 deletions .github/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@
[(#576)](https://github.com/PennyLaneAI/pennylane-lightning/pull/576)

* Shot-noise related methods now accommodate observable objects with arbitrary eigenvalues. Add a Kronecker product method for two diagonal matrices.
[(#570)](https://github.com/PennyLaneAI/pennylane-lightning/pull/570)
[(#570)](https://github.com/PennyLaneAI/pennylane-lightning/pull/570)

* Add shot-noise support for probs in the C++ layer. Probabilities are calculated from generated samples. All Lightning backends support this feature. Please note that target wires should be sorted in ascending manner.
[(#568)](https://github.com/PennyLaneAI/pennylane-lightning/pull/568)
[(#568)](https://github.com/PennyLaneAI/pennylane-lightning/pull/568)

* Add `LM` kernels to apply arbitrary controlled operations efficiently.
[(#516)](https://github.com/PennyLaneAI/pennylane-lightning/pull/516)
Expand All @@ -30,6 +30,9 @@

### Improvements

* Unify error messages of shot measurement related unsupported observables to better Catalyst.
[(#577)](https://github.com/PennyLaneAI/pennylane-lightning/pull/577)

* Add configuration files to improve compatibility with Catalyst.
[(#566)](https://github.com/PennyLaneAI/pennylane-lightning/pull/566)

Expand Down Expand Up @@ -60,7 +63,7 @@
[(#563)](https://github.com/PennyLaneAI/pennylane-lightning/pull/563)

* Add OpenGraph social preview for Lightning docs.
[(#574)](https://github.com/PennyLaneAI/pennylane-lightning/pull/574)
[(#574)](https://github.com/PennyLaneAI/pennylane-lightning/pull/574)

### Bug fixes

Expand All @@ -83,7 +86,7 @@

This release contains contributions from (in alphabetical order):

Isaac De Vlugt, Amintor Dusko, Vincent Michaud-Rioux, Erick Ochoa Lopez, Lee James O'Riordan, Shuli Shu
Ali Asadi, Isaac De Vlugt, Amintor Dusko, Vincent Michaud-Rioux, Erick Ochoa Lopez, Lee James O'Riordan, Shuli Shu

---

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.34.0-dev19"
__version__ = "0.34.0-dev20"
58 changes: 29 additions & 29 deletions pennylane_lightning/core/src/measurements/MeasurementsBase.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -133,18 +133,9 @@ template <class StateVectorT, class Derived> class MeasurementsBase {
PrecisionT result{0.0};

if (obs.getObsName().find("SparseHamiltonian") != std::string::npos) {
// SparseHamiltonian does not support samples in pennylane.
PL_ABORT("For SparseHamiltonian Observables, expval calculation is "
"not supported by shots");
} else if (obs.getObsName().find("Hermitian") != std::string::npos) {
// TODO support. This support requires an additional method to solve
// eigenpair and unitary matrices, and the results of eigenpair and
// unitary matrices data need to be added to the Hermitian class and
// public methods are need to access eigen values. Note the
// assumption that eigen values are -1 and 1 in the
// `measurement_with_sample` method should be updated as well.
PL_ABORT("For Hermitian Observables, expval calculation is not "
"supported by shots");
// SparseHamiltonian does not support shot measurement in pennylane.
PL_ABORT("SparseHamiltonian observables do not support shot "
"measurement.");
} else if (obs.getObsName().find("Hamiltonian") != std::string::npos) {
auto coeffs = obs.getCoeffs();
auto obsTerms = obs.getObs();
Expand Down Expand Up @@ -174,7 +165,8 @@ template <class StateVectorT, class Derived> class MeasurementsBase {
*/
auto measure_with_samples(const Observable<StateVectorT> &obs,
const size_t &num_shots,
const std::vector<size_t> &shot_range) {
const std::vector<size_t> &shot_range)
-> std::vector<PrecisionT> {
const size_t num_qubits = _statevector.getTotalNumQubits();
std::vector<size_t> obs_wires;
std::vector<std::vector<PrecisionT>> eigenValues;
Expand Down Expand Up @@ -214,8 +206,25 @@ template <class StateVectorT, class Derived> class MeasurementsBase {
*
* @return Variance of the given observable.
*/
auto var(const Observable<StateVectorT> &obs, const size_t &num_shots) {
if (obs.getObsName().find("Hamiltonian") == std::string::npos) {
auto var(const Observable<StateVectorT> &obs, const size_t &num_shots)
-> PrecisionT {
PrecisionT result{0.0};
if (obs.getObsName().find("SparseHamiltonian") != std::string::npos) {
// SparseHamiltonian does not support shot measurement in pennylane.
PL_ABORT("SparseHamiltonian observables do not support shot "
"measurement.");
} else if (obs.getObsName().find("Hamiltonian") != std::string::npos) {
// Branch for Hamiltonian observables
auto coeffs = obs.getCoeffs();
auto obs_terms = obs.getObs();

size_t obs_term_idx = 0;
for (const auto &coeff : coeffs) {
result +=
coeff * coeff * var(*obs_terms[obs_term_idx], num_shots);
obs_term_idx++;
}
} else {
auto obs_samples = measure_with_samples(obs, num_shots, {});
auto square_mean =
std::accumulate(obs_samples.begin(), obs_samples.end(), 0.0) /
Expand All @@ -226,18 +235,7 @@ template <class StateVectorT, class Derived> class MeasurementsBase {
return acc + element * element;
}) /
obs_samples.size();
PrecisionT result = mean_square - square_mean * square_mean;
return result;
}
// Branch for Hamiltonian observables
auto coeffs = obs.getCoeffs();
auto obs_terms = obs.getObs();

PrecisionT result{0.0};
size_t obs_term_idx = 0;
for (const auto &coeff : coeffs) {
result += coeff * coeff * var(*obs_terms[obs_term_idx], num_shots);
obs_term_idx++;
result = mean_square - square_mean * square_mean;
}
return result;
}
Expand All @@ -252,7 +250,8 @@ template <class StateVectorT, class Derived> class MeasurementsBase {
* @return Floating point std::vector with probabilities.
* The basis columns are rearranged according to wires.
*/
auto probs(const Observable<StateVectorT> &obs, size_t num_shots = 0) {
auto probs(const Observable<StateVectorT> &obs, size_t num_shots = 0)
-> std::vector<PrecisionT> {
PL_ABORT_IF(
obs.getObsName().find("Hamiltonian") != std::string::npos,
"Hamiltonian and Sparse Hamiltonian do not support samples().");
Expand Down Expand Up @@ -443,7 +442,8 @@ template <class StateVectorT, class Derived> class MeasurementsBase {
const size_t &num_shots,
const std::vector<size_t> &shot_range,
std::vector<size_t> &obs_wires,
std::vector<std::vector<PrecisionT>> &eigenValues) {
std::vector<std::vector<PrecisionT>> &eigenValues)
-> std::vector<size_t> {
const size_t num_qubits = _statevector.getTotalNumQubits();
std::vector<size_t> samples;
if constexpr (std::is_same_v<
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -597,7 +597,7 @@ template <typename TypeList> void testHermitianObsExpvalShot() {
REQUIRE_THROWS_WITH(
Measurer.expval(obs, num_shots, shots_range),
Catch::Matchers::Contains(
"expval calculation is not supported by shots"));
"Hermitian observables do not support shot measurement."));
REQUIRE(obs.getCoeffs().size() == 0);
}

Expand Down Expand Up @@ -943,6 +943,43 @@ template <typename TypeList> void testTensorProdObsVarShot() {
REQUIRE(expected == Approx(result).margin(5e-2));
}

DYNAMIC_SECTION(" full wires"
<< StateVectorToName<StateVectorT>::name) {
size_t num_shots = 10000;
auto X2 = std::make_shared<NamedObs<StateVectorT>>(
"PauliX", std::vector<size_t>{2});
auto Y1 = std::make_shared<NamedObs<StateVectorT>>(
"PauliY", std::vector<size_t>{1});
auto Z0 = std::make_shared<NamedObs<StateVectorT>>(
"PauliZ", std::vector<size_t>{0});
auto obs = TensorProdObs<StateVectorT>::create({X2, Y1, Z0});
auto expected = Measurer.var(*obs);
auto result = Measurer.var(*obs, num_shots);
REQUIRE(expected == Approx(result).margin(5e-2));
}

DYNAMIC_SECTION(" full wires with apply operations"
<< StateVectorToName<StateVectorT>::name) {
size_t num_shots = 10000;
auto X2 = std::make_shared<NamedObs<StateVectorT>>(
"PauliX", std::vector<size_t>{2});
auto Y1 = std::make_shared<NamedObs<StateVectorT>>(
"PauliY", std::vector<size_t>{1});
auto Z0 = std::make_shared<NamedObs<StateVectorT>>(
"PauliZ", std::vector<size_t>{0});
auto obs = TensorProdObs<StateVectorT>::create({X2, Y1, Z0});

statevector.applyOperations({"Hadamard", "PauliZ", "S", "Hadamard"},
{{0}, {1}, {2}, {2}},
{false, false, false, false});

Measurements<StateVectorT> Measurer0(statevector);

auto expected = Measurer0.var(*obs);
auto result = Measurer0.var(*obs, num_shots);
REQUIRE(expected == Approx(result).margin(5e-2));
}

DYNAMIC_SECTION(" With Identity"
<< StateVectorToName<StateVectorT>::name) {
size_t num_shots = 10000;
Expand Down Expand Up @@ -1328,7 +1365,7 @@ TEST_CASE("Var Shot - HamiltonianObs ", "[MeasurementsBase][Observables]") {
}
}

template <typename TypeList> void testSparseHObsExpvalShot() {
template <typename TypeList> void testSparseHObsMeasureShot() {
if constexpr (!std::is_same_v<TypeList, void>) {
using StateVectorT = typename TypeList::Type;
using ComplexT = typename StateVectorT::ComplexT;
Expand All @@ -1348,22 +1385,32 @@ template <typename TypeList> void testSparseHObsExpvalShot() {
ComplexT{1.0, 0.0}, ComplexT{1.0, 0.0}},
{7, 6, 5, 4, 3, 2, 1, 0}, {0, 1, 2, 3, 4, 5, 6, 7, 8}, {0, 1, 2});

DYNAMIC_SECTION("Failed for SparseH "
DYNAMIC_SECTION("Failed for expval "
<< StateVectorToName<StateVectorT>::name) {
size_t num_shots = 1000;
std::vector<size_t> shots_range = {};
REQUIRE_THROWS_WITH(
Measurer.expval(*sparseH, num_shots, shots_range),
Catch::Matchers::Contains(
"expval calculation is not supported by shots"));
Catch::Matchers::Contains("SparseHamiltonian observables do "
"not support shot measurement."));
}

DYNAMIC_SECTION("Failed for var "
<< StateVectorToName<StateVectorT>::name) {
size_t num_shots = 1000;
std::vector<size_t> shots_range = {};
REQUIRE_THROWS_WITH(
Measurer.var(*sparseH, num_shots),
Catch::Matchers::Contains("SparseHamiltonian observables do "
"not support shot measurement."));
}

testSparseHObsExpvalShot<typename TypeList::Next>();
testSparseHObsMeasureShot<typename TypeList::Next>();
}
}

TEST_CASE("Expval Shot - SparseHObs ", "[MeasurementsBase][Observables]") {
TEST_CASE("Measure Shot - SparseHObs ", "[MeasurementsBase][Observables]") {
if constexpr (BACKEND_FOUND) {
testSparseHObsExpvalShot<TestStateVectorBackends>();
testSparseHObsMeasureShot<TestStateVectorBackends>();
}
}
37 changes: 26 additions & 11 deletions pennylane_lightning/core/src/observables/Observables.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -189,9 +189,7 @@ class NamedObsBase : public Observable<StateVectorT> {
} else if (obs_name_ == "PauliZ") {
} else if (obs_name_ == "Identity") {
} else {
PL_ABORT("Provided NamedObs does not supported for shots "
"calculation. Supported NamedObs are PauliX, PauliY, "
"PauliZ, Identity and Hadamard.");
PL_ABORT("Provided NamedObs does not support shot measurement.");
}

if (obs_name_ == "Identity") {
Expand Down Expand Up @@ -257,8 +255,11 @@ class HermitianObsBase : public Observable<StateVectorT> {
[[maybe_unused]] StateVectorT &sv,
[[maybe_unused]] std::vector<std::vector<PrecisionT>> &eigenValues,
[[maybe_unused]] std::vector<size_t> &ob_wires) const override {
PL_ABORT("Hermitian observables do not support applyInPlaceShots "
"method.");
// TODO support. This support requires an additional method to solve
// eigenpair and unitary matrices, and the results of eigenpair and
// unitary matrices data need to be added to the Hermitian class and
// public methods are need to access eigen values.
PL_ABORT("Hermitian observables do not support shot measurement.");
}
};

Expand Down Expand Up @@ -300,8 +301,15 @@ class TensorProdObsBase : public Observable<StateVectorT> {
*/
template <typename... Ts>
explicit TensorProdObsBase(Ts &&...arg) : obs_{std::forward<Ts>(arg)...} {
std::unordered_set<size_t> wires;
if (obs_.size() == 1 &&
obs_[0]->getObsName().find('@') != std::string::npos) {
// This would prevent the misuse of this constructor for creating
// TensorProdObsBase(TensorProdObsBase).
PL_ABORT("A new TensorProdObsBase observable cannot be created "
"from a single TensorProdObsBase.");
}

std::unordered_set<size_t> wires;
for (const auto &ob : obs_) {
const auto ob_wires = ob->getWires();
for (const auto wire : ob_wires) {
Expand Down Expand Up @@ -381,6 +389,14 @@ class TensorProdObsBase : public Observable<StateVectorT> {
void applyInPlaceShots(StateVectorT &sv,
std::vector<std::vector<PrecisionT>> &eigenValues,
std::vector<size_t> &ob_wires) const override {
for (const auto &ob : obs_) {
if (ob->getObsName().find("Hamiltonian") != std::string::npos) {
PL_ABORT("Hamiltonian observables as a term of an TensorProd "
"observable do not "
"support shot measurement.");
}
}

eigenValues.clear();
ob_wires.clear();
for (const auto &ob : obs_) {
Expand Down Expand Up @@ -481,8 +497,8 @@ class HamiltonianBase : public Observable<StateVectorT> {
[[maybe_unused]] StateVectorT &sv,
[[maybe_unused]] std::vector<std::vector<PrecisionT>> &eigenValues,
[[maybe_unused]] std::vector<size_t> &ob_wires) const override {
PL_ABORT(
"Hamiltonian observables do not support the applyInPlaceShots");
PL_ABORT("Hamiltonian observables as a term of an observable do not "
"support shot measurement.");
}

[[nodiscard]] auto getWires() const -> std::vector<size_t> override {
Expand Down Expand Up @@ -614,9 +630,8 @@ class SparseHamiltonianBase : public Observable<StateVectorT> {
[[maybe_unused]] StateVectorT &sv,
[[maybe_unused]] std::vector<std::vector<PrecisionT>> &eigenValues,
[[maybe_unused]] std::vector<size_t> &ob_wires) const override {
PL_ABORT("SparseHamiltonian observables do not support the "
"applyInPlaceShots "
"method.");
PL_ABORT(
"SparseHamiltonian observables do not support shot measurement.");
}

[[nodiscard]] auto getObsName() const -> std::string override {
Expand Down
Loading

0 comments on commit 8101e42

Please sign in to comment.