Skip to content

Commit 356bdce

Browse files
Add LTensor Hermitian wire length check (#806)
### Before submitting Please complete the following checklist when submitting a PR: - [ ] 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`. - [ ] 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:** As of cutensornet-v24.03.0, only 1-wire Hermitian observables can be correctly supported. This limit was captured by previous python unit tests. This PR add Hermitian obs wires check in both python and C++ layers and updates the python unit tests to ensure this limit can be captured. [SC-69138] **Description of the Change:** **Benefits:** **Possible Drawbacks:** **Related GitHub Issues:** --------- Co-authored-by: ringo-but-quantum <[email protected]>
1 parent c81ce7d commit 356bdce

File tree

9 files changed

+237
-57
lines changed

9 files changed

+237
-57
lines changed

.github/CHANGELOG.md

+3
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,9 @@
3737

3838
### Bug fixes
3939

40+
* Check for the number of wires for Hermitian observables in Lightning-Tensor. Only 1-wire Hermitian observables are supported as of `cuTensorNet-v24.03.0`.
41+
[(#806)](https://github.com/PennyLaneAI/pennylane-lightning/pull/806)
42+
4043
* Set `PL_BACKEND` for the entire `build-wheel-lightning-gpu` Docker-build stage to properly build the Lightning-GPU wheel.
4144
[(#791)](https://github.com/PennyLaneAI/pennylane-lightning/pull/791)
4245

pennylane_lightning/core/_serialize.py

+2
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,8 @@ def _hermitian_ob(self, observable, wires_map: dict = None):
205205
"""Serializes a Hermitian observable"""
206206

207207
wires = [wires_map[w] for w in observable.wires] if wires_map else observable.wires.tolist()
208+
if self.device_name == "lightning.tensor" and len(wires) > 1:
209+
raise ValueError("The number of Hermitian observables target wires should be 1.")
208210
return self.hermitian_obs(matrix(observable).ravel().astype(self.ctype), wires)
209211

210212
def _tensor_ob(self, observable, wires_map: dict = None):

pennylane_lightning/core/_version.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -16,4 +16,4 @@
1616
Version number (major.minor.patch[-label])
1717
"""
1818

19-
__version__ = "0.38.0-dev10"
19+
__version__ = "0.38.0-dev11"

pennylane_lightning/core/src/simulators/lightning_tensor/tncuda/observables/ObservablesTNCuda.hpp

+3
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020

2121
#include "Constant.hpp"
2222
#include "ConstantUtil.hpp" // lookup
23+
#include "Error.hpp"
2324
#include "Util.hpp"
2425

2526
#include "cuda_helpers.hpp"
@@ -241,6 +242,8 @@ class HermitianObsTNCuda : public ObservableTNCuda<TensorNetT> {
241242
*/
242243
HermitianObsTNCuda(MatrixT matrix, std::vector<std::size_t> wires)
243244
: matrix_{std::move(matrix)}, wires_{std::move(wires)} {
245+
PL_ABORT_IF(wires_.size() != 1, "The number of Hermitian target wires "
246+
"must be 1 for Lightning-Tensor.");
244247
PL_ASSERT(matrix_.size() == Pennylane::Util::exp2(2 * wires_.size()));
245248
BaseType::coeffs_.emplace_back(PrecisionT{1.0});
246249
BaseType::numTensors_.emplace_back(std::size_t{1});

pennylane_lightning/core/src/simulators/lightning_tensor/tncuda/observables/tests/Test_Observables_TNCuda.cpp

+9-7
Original file line numberDiff line numberDiff line change
@@ -86,16 +86,18 @@ TEMPLATE_TEST_CASE("[Hermitian]", "[Observables]", float, double) {
8686
using HermitianObsT = HermitianObsTNCuda<TensorNetT>;
8787

8888
SECTION("HermitianObs only accepts correct arguments") {
89-
auto ob1 =
90-
HermitianObsT{std::vector<ComplexT>{0.0, 0.0, 0.0, 0.0}, {0}};
91-
auto ob2 = HermitianObsT{std::vector<ComplexT>(16, ComplexT{}), {0, 1}};
9289
REQUIRE_THROWS_AS(
9390
HermitianObsT(std::vector<ComplexT>{0.0, 0.0, 0.0}, {0}),
9491
LightningException);
9592
REQUIRE_THROWS_AS(
9693
HermitianObsT(std::vector<ComplexT>{0.0, 0.0, 0.0, 0.0, 0.0},
9794
{0, 1}),
9895
LightningException);
96+
REQUIRE_THROWS_WITH(
97+
HermitianObsT(std::vector<ComplexT>(16, ComplexT{0.0, 0.0}),
98+
{0, 1}),
99+
Catch::Matchers::Contains("The number of Hermitian target wires "
100+
"must be 1 for Lightning-Tensor."));
99101
}
100102

101103
SECTION("Test get obs name") {
@@ -153,8 +155,8 @@ TEMPLATE_TEST_CASE("[TensorProd]", "[Observables]", float, double) {
153155

154156
SECTION("Overlapping wires throw an exception") {
155157
auto ob1 = std::make_shared<HermitianObsT>(
156-
std::vector<ComplexT>(16, ComplexT{0.0, 0.0}),
157-
std::vector<std::size_t>{0, 1});
158+
std::vector<ComplexT>(4, ComplexT{0.0, 0.0}),
159+
std::vector<std::size_t>{1});
158160
auto ob2_1 = std::make_shared<NamedObsT>(
159161
"PauliX", std::vector<std::size_t>{1});
160162
auto ob2_2 = std::make_shared<NamedObsT>(
@@ -167,8 +169,8 @@ TEMPLATE_TEST_CASE("[TensorProd]", "[Observables]", float, double) {
167169

168170
SECTION("Constructing an observable with non-overlapping wires ") {
169171
auto ob1 = std::make_shared<HermitianObsT>(
170-
std::vector<ComplexT>(16, ComplexT{0.0, 0.0}),
171-
std::vector<std::size_t>{0, 1});
172+
std::vector<ComplexT>(4, ComplexT{0.0, 0.0}),
173+
std::vector<std::size_t>{1});
172174
auto ob2_1 = std::make_shared<NamedObsT>(
173175
"PauliX", std::vector<std::size_t>{2});
174176
auto ob2_2 = std::make_shared<NamedObsT>(

pennylane_lightning/lightning_tensor/_measurements.py

+4
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,10 @@ def expval(self, measurementprocess: MeasurementProcess):
7474
if isinstance(measurementprocess.obs, qml.SparseHamiltonian):
7575
raise NotImplementedError("Sparse Hamiltonians are not supported.")
7676

77+
if isinstance(measurementprocess.obs, qml.Hermitian):
78+
if len(measurementprocess.obs.wires) > 1:
79+
raise ValueError("The number of Hermitian observables target wires should be 1.")
80+
7781
ob_serialized = QuantumScriptSerializer(
7882
self._tensornet.device_name, self.dtype == np.complex64
7983
)._ob(measurementprocess.obs)

tests/test_expval.py

+15-3
Original file line numberDiff line numberDiff line change
@@ -163,7 +163,7 @@ def circuit():
163163
circ_def = qml.QNode(circuit, dev_def)
164164
assert np.allclose(circ(), circ_def(), tol)
165165

166-
@pytest.mark.parametrize("n_wires", range(1, 7 if device_name != "lightning.tensor" else 5))
166+
@pytest.mark.parametrize("n_wires", range(1, 7))
167167
def test_hermitian_expectation(self, n_wires, theta, phi, qubit_device, tol):
168168
"""Test that Hermitian expectation value is correct"""
169169
n_qubits = 7
@@ -185,14 +185,26 @@ def test_hermitian_expectation(self, n_wires, theta, phi, qubit_device, tol):
185185
def circuit():
186186
if device_name != "lightning.tensor":
187187
qml.StatePrep(init_state, wires=range(n_qubits))
188-
qml.RY(theta, wires=[0])
188+
qml.RX(theta, wires=[0])
189189
qml.RY(phi, wires=[1])
190+
qml.RX(theta, wires=[2])
191+
qml.RY(phi, wires=[3])
192+
qml.RX(theta, wires=[4])
193+
qml.RY(phi, wires=[5])
194+
qml.RX(theta, wires=[6])
190195
qml.CNOT(wires=[0, 1])
191196
return qml.expval(obs)
192197

193198
circ = qml.QNode(circuit, dev)
194199
circ_def = qml.QNode(circuit, dev_def)
195-
assert np.allclose(circ(), circ_def(), tol)
200+
if device_name == "lightning.tensor" and n_wires > 1:
201+
with pytest.raises(
202+
ValueError,
203+
match="The number of Hermitian observables target wires should be 1.",
204+
):
205+
assert np.allclose(circ(), circ_def(), tol)
206+
else:
207+
assert np.allclose(circ(), circ_def(), tol)
196208

197209

198210
@pytest.mark.parametrize(

tests/test_measurements.py

+12-8
Original file line numberDiff line numberDiff line change
@@ -311,14 +311,18 @@ def circuit():
311311
(
312312
[
313313
qml.PauliX(0) @ qml.PauliZ(1),
314-
qml.Hermitian(
315-
[
316-
[1.0, 0.0, 0.0, 0.0],
317-
[0.0, 3.0, 0.0, 0.0],
318-
[0.0, 0.0, -1.0, 1.0],
319-
[0.0, 0.0, 1.0, -2.0],
320-
],
321-
wires=[0, 1],
314+
(
315+
qml.Hermitian(
316+
[
317+
[1.0, 0.0, 0.0, 0.0],
318+
[0.0, 3.0, 0.0, 0.0],
319+
[0.0, 0.0, -1.0, 1.0],
320+
[0.0, 0.0, 1.0, -2.0],
321+
],
322+
wires=[0, 1],
323+
)
324+
if device_name != "lightning.tensor"
325+
else qml.Hermitian([[1.0, 0.0], [0.0, 1.0]], wires=[0])
322326
),
323327
],
324328
[0.3, 1.0],

0 commit comments

Comments
 (0)