From 001fd78977c5e8bd11f00f7b31731f14e87407c7 Mon Sep 17 00:00:00 2001 From: "Lee J. O'Riordan" Date: Wed, 15 Feb 2023 08:54:38 -0500 Subject: [PATCH 01/18] Fix wire ordering for sparseH --- pennylane_lightning/lightning_qubit.py | 5 ++++- tests/test_measures_sparse.py | 8 +++----- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/pennylane_lightning/lightning_qubit.py b/pennylane_lightning/lightning_qubit.py index 73c4c8e518..296b480697 100644 --- a/pennylane_lightning/lightning_qubit.py +++ b/pennylane_lightning/lightning_qubit.py @@ -814,7 +814,10 @@ def expval(self, observable, shot_range=None, bin_size=None): if observable.name == "SparseHamiltonian": if Kokkos_info()["USE_KOKKOS"] == True: # converting COO to CSR sparse representation. - CSR_SparseHamiltonian = observable.data[0].tocsr(copy=False) + + CSR_SparseHamiltonian = observable.sparse_matrix(wire_order=self.wires).tocsr( + copy=False + ) return M.expval( CSR_SparseHamiltonian.indptr, CSR_SparseHamiltonian.indices, diff --git a/tests/test_measures_sparse.py b/tests/test_measures_sparse.py index 30226f86f9..27c65ac14c 100644 --- a/tests/test_measures_sparse.py +++ b/tests/test_measures_sparse.py @@ -45,9 +45,7 @@ def circuit(): qml.RY(-0.2, wires=[1]) return qml.expval( qml.SparseHamiltonian( - qml.utils.sparse_hamiltonian( - qml.Hamiltonian([1], [qml.PauliX(0) @ qml.Identity(1)]) - ), + qml.Hamiltonian([1], [qml.PauliX(0) @ qml.Identity(1)]).sparse_matrix(), wires=[0, 1], ) ) @@ -98,7 +96,7 @@ class TestSparseExpvalQChem: symbols, geometry, ) - H_sparse = qml.utils.sparse_hamiltonian(H) + H_sparse = H.sparse_matrix() active_electrons = 1 @@ -114,7 +112,7 @@ def dev(self, request): @pytest.mark.parametrize( "qubits, wires, H_sparse, hf_state, excitations", [ - [qubits, np.arange(qubits), H_sparse, hf_state, excitations], + [qubits, range(qubits), H_sparse, hf_state, excitations], [ qubits, np.random.permutation(np.arange(qubits)), From 10d566dcfb5f44732e0fbd764f40080d00eb2a84 Mon Sep 17 00:00:00 2001 From: Dev version update bot Date: Wed, 15 Feb 2023 13:59:53 +0000 Subject: [PATCH 02/18] Auto update version --- pennylane_lightning/_version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pennylane_lightning/_version.py b/pennylane_lightning/_version.py index e0b04f640b..e7b9c36e20 100644 --- a/pennylane_lightning/_version.py +++ b/pennylane_lightning/_version.py @@ -16,4 +16,4 @@ Version number (major.minor.patch[-label]) """ -__version__ = "0.29.0-dev9" +__version__ = "0.29.0-dev10" From e0762dca71769eec99f8c42da0c491b4e6b70563 Mon Sep 17 00:00:00 2001 From: "Lee J. O'Riordan" Date: Fri, 17 Feb 2023 13:52:18 -0500 Subject: [PATCH 03/18] Add offload support to C++ for variance Observables --- pennylane_lightning/src/bindings/Bindings.cpp | 34 +++++++++-- .../src/simulator/Measures.hpp | 57 +++++++++++++++++++ 2 files changed, 87 insertions(+), 4 deletions(-) diff --git a/pennylane_lightning/src/bindings/Bindings.cpp b/pennylane_lightning/src/bindings/Bindings.cpp index 92caecefe9..9f643119fd 100644 --- a/pennylane_lightning/src/bindings/Bindings.cpp +++ b/pennylane_lightning/src/bindings/Bindings.cpp @@ -134,10 +134,36 @@ void lightning_class_bindings(py::module_ &m) { strides /* strides for each axis */ )); }) - .def("var", [](Measures &M, const std::string &operation, - const std::vector &wires) { - return M.var(operation, wires); - }); + .def("var", + [](Measures &M, const std::string &operation, + const std::vector &wires) { + return M.var(operation, wires); + }) + .def("var", + static_cast::*)( + const std::string &, const std::vector &)>( + &Measures::var), + "Variance of an operation by name.") + .def( + "var", + [](Measures &M, + const std::shared_ptr> &ob) { + return M.var(*ob); + }, + "Variance of an operation object.") + .def( + "var", + [](Measures &M, const np_arr_sparse_ind row_map, + const np_arr_sparse_ind entries, const np_arr_c values) { + return M.var( + static_cast(row_map.request().ptr), + static_cast(row_map.request().size), + static_cast(entries.request().ptr), + static_cast *>( + values.request().ptr), + static_cast(values.request().size)); + }, + "Expected value of a sparse Hamiltonian."); } template diff --git a/pennylane_lightning/src/simulator/Measures.hpp b/pennylane_lightning/src/simulator/Measures.hpp index be7f86fe36..94ead0b6b3 100644 --- a/pennylane_lightning/src/simulator/Measures.hpp +++ b/pennylane_lightning/src/simulator/Measures.hpp @@ -232,6 +232,26 @@ class Measures { return std::real(inner_prod); } + /** + * @brief Variance value for a general Observable + * + * @param ob Observable + */ + auto var(const Observable &ob) -> fp_t { + // Copying the original state vector, for the application of the + // observable operator. + StateVectorManagedCPU op_sv(original_statevector); + ob.applyInPlace(op_sv); + + const fp_t mean_square = std::real(Util::innerProdC( + op_sv.getData(), op_sv.getData(), op_sv.getLength())); + const fp_t squared_mean = static_cast(std::pow( + std::real(Util::innerProdC(original_statevector.getData(), + op_sv.getData(), op_sv.getLength())), + 2)); + return (mean_square - squared_mean); + } + /** * @brief Variance of an observable. * @@ -309,6 +329,43 @@ class Measures { return expected_value_list; }; + /** + * @brief Variance of a Sparse Hamiltonian. + * + * @tparam index_type integer type used as indices of the sparse matrix. + * @param row_map_ptr row_map array pointer. + * The j element encodes the number of non-zeros above + * row j. + * @param row_map_size row_map array size. + * @param entries_ptr pointer to an array with column indices of the + * non-zero elements. + * @param values_ptr pointer to an array with the non-zero elements. + * @param numNNZ number of non-zero elements. + * @return fp_t Variance value. + */ + template + fp_t var(const index_type *row_map_ptr, const index_type row_map_size, + const index_type *entries_ptr, const CFP_t *values_ptr, + const index_type numNNZ) { + PL_ABORT_IF( + (original_statevector.getLength() != (size_t(row_map_size) - 1)), + "Statevector and Hamiltonian have incompatible sizes."); + auto operator_vector = Util::apply_Sparse_Matrix( + original_statevector.getData(), + static_cast(original_statevector.getLength()), + row_map_ptr, row_map_size, entries_ptr, values_ptr, numNNZ); + + const fp_t mean_square = std::real(Util::innerProdC( + operator_vector.getData(), operator_vector.getData(), + operator_vector.getLength())); + const fp_t squared_mean = static_cast( + std::pow(std::real(Util::innerProdC(operator_vector.getData(), + original_statevector.getData(), + operator_vector.getLength())), + 2)); + return (mean_square - squared_mean); + }; + /** * @brief Generate samples using the alias method. * Reference: https://en.wikipedia.org/wiki/Alias_method From a7b23fede164acaffd88b0e273f1afd65ec4f132 Mon Sep 17 00:00:00 2001 From: "Lee J. O'Riordan" Date: Fri, 17 Feb 2023 13:54:35 -0500 Subject: [PATCH 04/18] Ensure Hamiltonians in Tensor products are appropriately decomposed to C++ supported ops --- pennylane_lightning/_serialize.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/pennylane_lightning/_serialize.py b/pennylane_lightning/_serialize.py index 0338b7c240..a506b47114 100644 --- a/pennylane_lightning/_serialize.py +++ b/pennylane_lightning/_serialize.py @@ -21,6 +21,7 @@ BasisState, Hadamard, Projector, + Hamiltonian, QubitStateVector, Rot, ) @@ -65,10 +66,13 @@ def _obs_has_kernel(ob: Observable) -> bool: """ if is_pauli_word(ob): return True - if isinstance(ob, (Hadamard, Projector)): + if isinstance(ob, (Hadamard)): return True + if isinstance(ob, Hamiltonian): + return all(_obs_has_kernel(o) for o in ob.ops) if isinstance(ob, Tensor): return all(_obs_has_kernel(o) for o in ob.obs) + return False From de5dc54a89fc2de0465a87d30b68a6ed7540a7e6 Mon Sep 17 00:00:00 2001 From: "Lee J. O'Riordan" Date: Fri, 17 Feb 2023 13:56:00 -0500 Subject: [PATCH 05/18] Avoid tensor-product observables falling-back to _qubit_device definitions due to differences in chosen basis --- pennylane_lightning/lightning_qubit.py | 46 +++++++++++++++++++------- 1 file changed, 34 insertions(+), 12 deletions(-) diff --git a/pennylane_lightning/lightning_qubit.py b/pennylane_lightning/lightning_qubit.py index 296b480697..1381370cda 100644 --- a/pennylane_lightning/lightning_qubit.py +++ b/pennylane_lightning/lightning_qubit.py @@ -789,15 +789,10 @@ def expval(self, observable, shot_range=None, bin_size=None): Returns: Expectation value of the observable """ - if ( - (observable.arithmetic_depth > 0) - or isinstance(observable.name, List) - or observable.name - in [ - "Identity", - "Projector", - ] - ): + if observable.name in [ + "Identity", + "Projector", + ]: return super().expval(observable, shot_range=shot_range, bin_size=bin_size) if self.shots is not None: @@ -827,7 +822,11 @@ def expval(self, observable, shot_range=None, bin_size=None): "The expval of a SparseHamiltonian requires Kokkos and Kokkos Kernels." ) - if observable.name in ["Hamiltonian", "Hermitian"]: + if ( + observable.name in ["Hamiltonian", "Hermitian"] + or (observable.arithmetic_depth > 0) + or isinstance(observable.name, List) + ): ob_serialized = _serialize_ob(observable, self.wire_map, use_csingle=self.use_csingle) return M.expval(ob_serialized) @@ -850,10 +849,9 @@ def var(self, observable, shot_range=None, bin_size=None): Returns: Variance of the observable """ - if isinstance(observable.name, List) or observable.name in [ + if observable.name in [ "Identity", "Projector", - "Hermitian", ]: return super().var(observable, shot_range=shot_range, bin_size=bin_size) @@ -869,6 +867,30 @@ def var(self, observable, shot_range=None, bin_size=None): state_vector = StateVectorC64(ket) if self.use_csingle else StateVectorC128(ket) M = MeasuresC64(state_vector) if self.use_csingle else MeasuresC128(state_vector) + if observable.name == "SparseHamiltonian": + if Kokkos_info()["USE_KOKKOS"] == True: + # converting COO to CSR sparse representation. + + CSR_SparseHamiltonian = observable.sparse_matrix(wire_order=self.wires).tocsr( + copy=False + ) + return M.var( + CSR_SparseHamiltonian.indptr, + CSR_SparseHamiltonian.indices, + CSR_SparseHamiltonian.data, + ) + raise NotImplementedError( + "The expval of a SparseHamiltonian requires Kokkos and Kokkos Kernels." + ) + + if ( + observable.name in ["Hamiltonian", "Hermitian"] + or (observable.arithmetic_depth > 0) + or isinstance(observable.name, List) + ): + ob_serialized = _serialize_ob(observable, self.wire_map, use_csingle=self.use_csingle) + return M.var(ob_serialized) + # translate to wire labels used by device observable_wires = self.map_wires(observable.wires) From 3f2b34eeb271edfc63f2e27be18494d55c4363af Mon Sep 17 00:00:00 2001 From: "Lee J. O'Riordan" Date: Fri, 17 Feb 2023 13:56:31 -0500 Subject: [PATCH 06/18] Ensure TP of Hamiltonians are validated appropriately --- tests/test_serialize.py | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/tests/test_serialize.py b/tests/test_serialize.py index 7ec05ea5e7..c8aa9428c4 100644 --- a/tests/test_serialize.py +++ b/tests/test_serialize.py @@ -61,29 +61,33 @@ def test_hadamard(self): o = qml.Hadamard(0) assert _obs_has_kernel(o) - def test_projector(self): - """Tests if return is true for a Projector observable""" - o = qml.Projector([0], wires=0) - assert _obs_has_kernel(o) - def test_hermitian(self): """Tests if return is false for a Hermitian observable""" o = qml.Hermitian(np.eye(2), wires=0) assert not _obs_has_kernel(o) def test_tensor_product_of_valid_terms(self): - """Tests if return is true for a tensor product of Pauli, Hadamard, and Projector terms""" - o = qml.PauliZ(0) @ qml.Hadamard(1) @ qml.Projector([0], wires=2) + """Tests if return is true for a tensor product of Pauli, Hadamard, and Hamiltonian terms""" + o = qml.PauliZ(0) @ qml.Hadamard(1) @ (0.1 * (qml.PauliZ(2) + qml.PauliX(3))) assert _obs_has_kernel(o) def test_tensor_product_of_invalid_terms(self): """Tests if return is false for a tensor product of Hermitian terms""" - o = qml.Hermitian(np.eye(2), wires=0) @ qml.Hermitian(np.eye(2), wires=1) + o = ( + qml.Hermitian(np.eye(2), wires=0) + @ qml.Hermitian(np.eye(2), wires=1) + @ qml.Projector([0], wires=2) + ) assert not _obs_has_kernel(o) def test_tensor_product_of_mixed_terms(self): """Tests if return is false for a tensor product of valid and invalid terms""" - o = qml.PauliZ(0) @ qml.Hermitian(np.eye(2), wires=1) + o = qml.PauliZ(0) @ qml.Hermitian(np.eye(2), wires=1) @ qml.Projector([0], wires=2) + assert not _obs_has_kernel(o) + + def test_projector(self): + """Tests if return is false for a Projector observable""" + o = qml.Projector([0], wires=0) assert not _obs_has_kernel(o) From f67138deb69dd1eb21fc8ebc865927b5965baca5 Mon Sep 17 00:00:00 2001 From: "Lee J. O'Riordan" Date: Fri, 17 Feb 2023 14:51:01 -0500 Subject: [PATCH 07/18] Fix vector caling convention --- pennylane_lightning/src/simulator/Measures.hpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/pennylane_lightning/src/simulator/Measures.hpp b/pennylane_lightning/src/simulator/Measures.hpp index 94ead0b6b3..72d899bcb6 100644 --- a/pennylane_lightning/src/simulator/Measures.hpp +++ b/pennylane_lightning/src/simulator/Measures.hpp @@ -355,13 +355,13 @@ class Measures { static_cast(original_statevector.getLength()), row_map_ptr, row_map_size, entries_ptr, values_ptr, numNNZ); - const fp_t mean_square = std::real(Util::innerProdC( - operator_vector.getData(), operator_vector.getData(), - operator_vector.getLength())); + const fp_t mean_square = std::real( + Util::innerProdC(operator_vector.data(), operator_vector.data(), + operator_vector.getLength())); const fp_t squared_mean = static_cast( - std::pow(std::real(Util::innerProdC(operator_vector.getData(), + std::pow(std::real(Util::innerProdC(operator_vector.data(), original_statevector.getData(), - operator_vector.getLength())), + operator_vector.length())), 2)); return (mean_square - squared_mean); }; From 62af9053536c69fafac7cd287d0ac34fb9f9580f Mon Sep 17 00:00:00 2001 From: "Lee J. O'Riordan" Date: Fri, 17 Feb 2023 14:53:16 -0500 Subject: [PATCH 08/18] Fix vector caling convention again --- pennylane_lightning/src/simulator/Measures.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pennylane_lightning/src/simulator/Measures.hpp b/pennylane_lightning/src/simulator/Measures.hpp index 72d899bcb6..f5396292e5 100644 --- a/pennylane_lightning/src/simulator/Measures.hpp +++ b/pennylane_lightning/src/simulator/Measures.hpp @@ -357,11 +357,11 @@ class Measures { const fp_t mean_square = std::real( Util::innerProdC(operator_vector.data(), operator_vector.data(), - operator_vector.getLength())); + operator_vector.size())); const fp_t squared_mean = static_cast( std::pow(std::real(Util::innerProdC(operator_vector.data(), original_statevector.getData(), - operator_vector.length())), + operator_vector.size())), 2)); return (mean_square - squared_mean); }; From 5ada0605ab46d994ec3638ad42a8225d122564ed Mon Sep 17 00:00:00 2001 From: "Lee J. O'Riordan" Date: Tue, 21 Feb 2023 12:57:09 -0500 Subject: [PATCH 09/18] Ensure out-of-order probs raise error currently --- pennylane_lightning/lightning_qubit.py | 6 +++ pennylane_lightning/src/bindings/Bindings.cpp | 7 +-- .../src/simulator/Measures.hpp | 4 +- tests/test_measures.py | 54 +++++++++++++++++-- 4 files changed, 62 insertions(+), 9 deletions(-) diff --git a/pennylane_lightning/lightning_qubit.py b/pennylane_lightning/lightning_qubit.py index 1381370cda..16752df0b5 100644 --- a/pennylane_lightning/lightning_qubit.py +++ b/pennylane_lightning/lightning_qubit.py @@ -744,6 +744,11 @@ def probability(self, wires=None, shot_range=None, bin_size=None): Returns: array[float]: list of the probabilities """ + if wires and len(wires) > 1 and (not np.all(list(wires)[:-1] <= list(wires)[1:])): + raise RuntimeError( + "Lightning does not currently support out-of-order indices for probabilities" + ) + if self.shots is not None: return self.estimate_probability(wires=wires, shot_range=shot_range, bin_size=bin_size) @@ -756,6 +761,7 @@ def probability(self, wires=None, shot_range=None, bin_size=None): # To support np.complex64 based on the type of self._state dtype = self._state.dtype ket = np.ravel(self._state) + state_vector = StateVectorC64(ket) if self.use_csingle else StateVectorC128(ket) M = MeasuresC64(state_vector) if self.use_csingle else MeasuresC128(state_vector) return M.probs(device_wires) diff --git a/pennylane_lightning/src/bindings/Bindings.cpp b/pennylane_lightning/src/bindings/Bindings.cpp index 9f643119fd..a664ed0bf6 100644 --- a/pennylane_lightning/src/bindings/Bindings.cpp +++ b/pennylane_lightning/src/bindings/Bindings.cpp @@ -87,11 +87,12 @@ void lightning_class_bindings(py::module_ &m) { .def(py::init &>()) .def("probs", [](Measures &M, const std::vector &wires) { - if (wires.empty()) { - return py::array_t(py::cast(M.probs())); - } return py::array_t(py::cast(M.probs(wires))); }) + .def("probs", + [](Measures &M) { + return py::array_t(py::cast(M.probs())); + }) .def("expval", static_cast::*)( const std::string &, const std::vector &)>( diff --git a/pennylane_lightning/src/simulator/Measures.hpp b/pennylane_lightning/src/simulator/Measures.hpp index f5396292e5..c0bf75fad5 100644 --- a/pennylane_lightning/src/simulator/Measures.hpp +++ b/pennylane_lightning/src/simulator/Measures.hpp @@ -80,7 +80,9 @@ class Measures { * @return Floating point std::vector with probabilities. * The basis columns are rearranged according to wires. */ - std::vector probs(const std::vector &wires) { + std::vector + probs(const std::vector &wires, + [[maybe_unused]] const std::vector &device_wires = {}) { // Determining index that would sort the vector. // This information is needed later. const auto sorted_ind_wires = Util::sorting_indices(wires); diff --git a/tests/test_measures.py b/tests/test_measures.py index afc2f1bc2e..beb1cc2af8 100644 --- a/tests/test_measures.py +++ b/tests/test_measures.py @@ -97,7 +97,6 @@ def circuit(): "cases", [ [[0, 1], [0.9165164490394898, 0.0, 0.08348355096051052, 0.0]], - [[1, 0], [0.9165164490394898, 0.08348355096051052, 0.0, 0.0]], [0, [0.9165164490394898, 0.08348355096051052]], [[0], [0.9165164490394898, 0.08348355096051052]], ], @@ -116,6 +115,31 @@ def circuit(): assert np.allclose(circuit(), cases[1], atol=tol, rtol=0) + @pytest.mark.parametrize( + "cases", + [ + [[1, 0], [0.9165164490394898, 0.08348355096051052, 0.0, 0.0]], + [["a", "0"], [0.9165164490394898, 0.08348355096051052]], + ], + ) + def test_fail_probs_tape_unordered_wires(self, cases, tol, dev): + """Test probs with a circuit on wires=[0]""" + + x, y, z = [0.5, 0.3, -0.7] + + @qml.qnode(dev) + def circuit(): + qml.RX(0.4, wires=[0]) + qml.Rot(x, y, z, wires=[0]) + qml.RY(-0.2, wires=[0]) + return qml.probs(wires=cases[0]) + + with pytest.raises( + RuntimeError, + match="Lightning does not currently support out-of-order indices for probabilities", + ): + assert np.allclose(circuit(), cases[1], atol=tol, rtol=0) + @pytest.mark.parametrize( "cases", [ @@ -128,6 +152,24 @@ def circuit(): 0.0013668981445561978, ], ], + [0, [0.938791280945186, 0.061208719054813635]], + [[0], [0.938791280945186, 0.061208719054813635]], + ], + ) + def test_probs_tape_wire01(self, cases, tol, dev): + """Test probs with a circuit on wires=[0,1]""" + + @qml.qnode(dev) + def circuit(): + qml.RX(0.5, wires=[0]) + qml.RY(0.3, wires=[1]) + return qml.probs(wires=cases[0]) + + assert np.allclose(circuit(), cases[1], atol=tol, rtol=0) + + @pytest.mark.parametrize( + "cases", + [ [ [1, 0], [ @@ -137,11 +179,9 @@ def circuit(): 0.0013668981445561978, ], ], - [0, [0.938791280945186, 0.061208719054813635]], - [[0], [0.938791280945186, 0.061208719054813635]], ], ) - def test_probs_tape_wire01(self, cases, tol, dev): + def test_fail_probs_tape_wire01(self, cases, tol, dev): """Test probs with a circuit on wires=[0,1]""" @qml.qnode(dev) @@ -150,7 +190,11 @@ def circuit(): qml.RY(0.3, wires=[1]) return qml.probs(wires=cases[0]) - assert np.allclose(circuit(), cases[1], atol=tol, rtol=0) + with pytest.raises( + RuntimeError, + match="Lightning does not currently support out-of-order indices for probabilities", + ): + assert np.allclose(circuit(), cases[1], atol=tol, rtol=0) class TestExpval: From fb6e2b923584a9e156fa6378e38af882e98a8b2b Mon Sep 17 00:00:00 2001 From: "Lee J. O'Riordan" Date: Tue, 21 Feb 2023 13:02:11 -0500 Subject: [PATCH 10/18] Update changelog [skip_ci] --- .github/CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/CHANGELOG.md b/.github/CHANGELOG.md index 2c7e8d5883..ab0157ce39 100644 --- a/.github/CHANGELOG.md +++ b/.github/CHANGELOG.md @@ -28,6 +28,9 @@ ### Bug fixes +* Ensure error raised when asking for out of order marginal probabilities. Prevents the return of incorrect results. +[(#416)](https://github.com/PennyLaneAI/pennylane-lightning/pull/416) + * Fix Github shields in README. [(#402)](https://github.com/PennyLaneAI/pennylane-lightning/pull/402) From 7a62bc6a12b8f4be2ad5bd34ae469a1c19087e08 Mon Sep 17 00:00:00 2001 From: "Lee J. O'Riordan" Date: Tue, 21 Feb 2023 13:12:38 -0500 Subject: [PATCH 11/18] Trigger CI From 34790c1e9784505e032ba9cfe10e9701e2ce26cb Mon Sep 17 00:00:00 2001 From: Lee James O'Riordan Date: Tue, 21 Feb 2023 13:44:00 -0500 Subject: [PATCH 12/18] Update pennylane_lightning/lightning_qubit.py Co-authored-by: Amintor Dusko <87949283+AmintorDusko@users.noreply.github.com> --- pennylane_lightning/lightning_qubit.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pennylane_lightning/lightning_qubit.py b/pennylane_lightning/lightning_qubit.py index 16752df0b5..40f4b4a610 100644 --- a/pennylane_lightning/lightning_qubit.py +++ b/pennylane_lightning/lightning_qubit.py @@ -814,7 +814,7 @@ def expval(self, observable, shot_range=None, bin_size=None): M = MeasuresC64(state_vector) if self.use_csingle else MeasuresC128(state_vector) if observable.name == "SparseHamiltonian": if Kokkos_info()["USE_KOKKOS"] == True: - # converting COO to CSR sparse representation. + # ensuring CSR sparse representation. CSR_SparseHamiltonian = observable.sparse_matrix(wire_order=self.wires).tocsr( copy=False From 652515a2e308b7bd481b661ac9f02b7c7e294d18 Mon Sep 17 00:00:00 2001 From: Lee James O'Riordan Date: Tue, 21 Feb 2023 13:44:10 -0500 Subject: [PATCH 13/18] Update pennylane_lightning/lightning_qubit.py Co-authored-by: Amintor Dusko <87949283+AmintorDusko@users.noreply.github.com> --- pennylane_lightning/lightning_qubit.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pennylane_lightning/lightning_qubit.py b/pennylane_lightning/lightning_qubit.py index 40f4b4a610..fcb696f202 100644 --- a/pennylane_lightning/lightning_qubit.py +++ b/pennylane_lightning/lightning_qubit.py @@ -875,7 +875,7 @@ def var(self, observable, shot_range=None, bin_size=None): if observable.name == "SparseHamiltonian": if Kokkos_info()["USE_KOKKOS"] == True: - # converting COO to CSR sparse representation. + # ensuring CSR sparse representation. CSR_SparseHamiltonian = observable.sparse_matrix(wire_order=self.wires).tocsr( copy=False From cb1b62d205bf26ba26ae091769054c2bea91524c Mon Sep 17 00:00:00 2001 From: Lee James O'Riordan Date: Tue, 21 Feb 2023 13:44:17 -0500 Subject: [PATCH 14/18] Update tests/test_measures.py Co-authored-by: Amintor Dusko <87949283+AmintorDusko@users.noreply.github.com> --- tests/test_measures.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_measures.py b/tests/test_measures.py index beb1cc2af8..4542792a37 100644 --- a/tests/test_measures.py +++ b/tests/test_measures.py @@ -119,7 +119,7 @@ def circuit(): "cases", [ [[1, 0], [0.9165164490394898, 0.08348355096051052, 0.0, 0.0]], - [["a", "0"], [0.9165164490394898, 0.08348355096051052]], + [["a", "0"], [0.9165164490394898, 0.08348355096051052, 0.0, 0.0]], ], ) def test_fail_probs_tape_unordered_wires(self, cases, tol, dev): From 1a8f3b58204977c399edf82686769e2167c14947 Mon Sep 17 00:00:00 2001 From: "Lee J. O'Riordan" Date: Tue, 21 Feb 2023 15:02:44 -0500 Subject: [PATCH 15/18] Move probs forbid statement check --- pennylane_lightning/lightning_qubit.py | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/pennylane_lightning/lightning_qubit.py b/pennylane_lightning/lightning_qubit.py index 16752df0b5..3aed7e5b7a 100644 --- a/pennylane_lightning/lightning_qubit.py +++ b/pennylane_lightning/lightning_qubit.py @@ -744,11 +744,6 @@ def probability(self, wires=None, shot_range=None, bin_size=None): Returns: array[float]: list of the probabilities """ - if wires and len(wires) > 1 and (not np.all(list(wires)[:-1] <= list(wires)[1:])): - raise RuntimeError( - "Lightning does not currently support out-of-order indices for probabilities" - ) - if self.shots is not None: return self.estimate_probability(wires=wires, shot_range=shot_range, bin_size=bin_size) @@ -758,6 +753,15 @@ def probability(self, wires=None, shot_range=None, bin_size=None): # translate to wire labels used by device device_wires = self.map_wires(wires) + if ( + device_wires + and len(device_wires) > 1 + and (not np.all(list(device_wires)[:-1] <= list(device_wires)[1:])) + ): + raise RuntimeError( + "Lightning does not currently support out-of-order indices for probabilities" + ) + # To support np.complex64 based on the type of self._state dtype = self._state.dtype ket = np.ravel(self._state) From a1615130ca37bcb27679fb0d507080d87b6f4ee9 Mon Sep 17 00:00:00 2001 From: "Lee J. O'Riordan" Date: Tue, 21 Feb 2023 15:15:30 -0500 Subject: [PATCH 16/18] Remove redundant wire test --- tests/test_measures.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/test_measures.py b/tests/test_measures.py index 4542792a37..a78eadcc83 100644 --- a/tests/test_measures.py +++ b/tests/test_measures.py @@ -119,7 +119,6 @@ def circuit(): "cases", [ [[1, 0], [0.9165164490394898, 0.08348355096051052, 0.0, 0.0]], - [["a", "0"], [0.9165164490394898, 0.08348355096051052, 0.0, 0.0]], ], ) def test_fail_probs_tape_unordered_wires(self, cases, tol, dev): From 372a9afc0fd26a4fbbc21668c45cfd370b04c827 Mon Sep 17 00:00:00 2001 From: "Lee J. O'Riordan" Date: Tue, 21 Feb 2023 17:16:50 -0500 Subject: [PATCH 17/18] Ensure np arrays used for wire validation --- pennylane_lightning/lightning_qubit.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pennylane_lightning/lightning_qubit.py b/pennylane_lightning/lightning_qubit.py index 2767236a68..4f72bd77f4 100644 --- a/pennylane_lightning/lightning_qubit.py +++ b/pennylane_lightning/lightning_qubit.py @@ -756,7 +756,7 @@ def probability(self, wires=None, shot_range=None, bin_size=None): if ( device_wires and len(device_wires) > 1 - and (not np.all(list(device_wires)[:-1] <= list(device_wires)[1:])) + and (not np.all(np.array(device_wires)[:-1] <= np.array(device_wires)[1:])) ): raise RuntimeError( "Lightning does not currently support out-of-order indices for probabilities" From 675e35ed2d41dbe1b4a91cc7d0a04bc3ef2a8274 Mon Sep 17 00:00:00 2001 From: Matthew Silverman Date: Tue, 21 Feb 2023 17:20:00 -0500 Subject: [PATCH 18/18] add test to verify order must only match device (#419) --- tests/test_measures.py | 32 ++++++++++++++++++++++++++++---- 1 file changed, 28 insertions(+), 4 deletions(-) diff --git a/tests/test_measures.py b/tests/test_measures.py index a78eadcc83..16108d6d06 100644 --- a/tests/test_measures.py +++ b/tests/test_measures.py @@ -118,13 +118,15 @@ def circuit(): @pytest.mark.parametrize( "cases", [ - [[1, 0], [0.9165164490394898, 0.08348355096051052, 0.0, 0.0]], + [[0, 1], [1, 0]], + [[1, 0], [0, 1]], ], ) - def test_fail_probs_tape_unordered_wires(self, cases, tol, dev): - """Test probs with a circuit on wires=[0]""" + def test_fail_probs_tape_unordered_wires(self, cases, tol): + """Test probs with a circuit on wires=[0] fails for out-of-order wires passed to probs.""" x, y, z = [0.5, 0.3, -0.7] + dev = qml.device("lightning.qubit", wires=cases[1]) @qml.qnode(dev) def circuit(): @@ -137,7 +139,29 @@ def circuit(): RuntimeError, match="Lightning does not currently support out-of-order indices for probabilities", ): - assert np.allclose(circuit(), cases[1], atol=tol, rtol=0) + _ = circuit() + + @pytest.mark.parametrize( + "cases", + [ + [[1, 0], [1, 0], [0.9165164490394898, 0.08348355096051052, 0.0, 0.0]], + [[2, 0], [2, 0, 1], [0.9165164490394898, 0.08348355096051052, 0.0, 0.0]], + ], + ) + def test_probs_matching_device_wire_order(self, cases, tol): + """Test probs with a circuit on wires=[0] passes if wires are sorted wrt device wires.""" + + x, y, z = [0.5, 0.3, -0.7] + dev = qml.device("lightning.qubit", wires=cases[1]) + + @qml.qnode(dev) + def circuit(): + qml.RX(0.4, wires=[0]) + qml.Rot(x, y, z, wires=[0]) + qml.RY(-0.2, wires=[0]) + return qml.probs(wires=cases[0]) + + assert np.allclose(circuit(), cases[2], atol=tol, rtol=0) @pytest.mark.parametrize( "cases",