Skip to content

Commit c0222c0

Browse files
lillian542ringo-but-quantum
authored andcommitted
Remove Tensor and Hamiltonian in python frontend for lightning (#994)
We are removing the Tensor and Hamiltonian classes, so we should remove their (minimal) remaining references in the Lightning python code. Blocks running CI for the [PL PR legacy_opmath, since every test that uses `lightning.qubit` fails with Tensor removed. --------- Co-authored-by: ringo-but-quantum <[email protected]>
1 parent e2e116b commit c0222c0

18 files changed

+27
-121
lines changed

.github/CHANGELOG.md

+3
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@
1313

1414
### Breaking changes
1515

16+
* Handling for the legacy operator arithmetic (the `Hamiltonian` and `Tensor` classes in PennyLane) is removed.
17+
[(#994)](https://github.com/PennyLaneAI/pennylane-lightning/pull/994)
18+
1619
### Improvements
1720

1821
* Unify excitation gates memory layout to row-major for both LGPU and LT.

pennylane_lightning/core/_measurements_base.py

+6-28
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@
3232
StateMeasurement,
3333
VarianceMP,
3434
)
35-
from pennylane.ops import Hamiltonian, SparseHamiltonian, Sum
35+
from pennylane.ops import SparseHamiltonian, Sum
3636
from pennylane.tape import QuantumScript
3737
from pennylane.typing import Result, TensorLike
3838
from pennylane.wires import Wires
@@ -117,7 +117,7 @@ def expval(self, measurementprocess: MeasurementProcess):
117117
)
118118

119119
if (
120-
isinstance(measurementprocess.obs, (qml.ops.Hamiltonian, qml.Hermitian))
120+
isinstance(measurementprocess.obs, qml.Hermitian)
121121
or (measurementprocess.obs.arithmetic_depth > 0)
122122
or isinstance(measurementprocess.obs.name, List)
123123
):
@@ -176,7 +176,7 @@ def var(self, measurementprocess: MeasurementProcess):
176176
)
177177

178178
if (
179-
isinstance(measurementprocess.obs, (qml.ops.Hamiltonian, qml.Hermitian))
179+
isinstance(measurementprocess.obs, qml.Hermitian)
180180
or (measurementprocess.obs.arithmetic_depth > 0)
181181
or isinstance(measurementprocess.obs.name, List)
182182
):
@@ -307,15 +307,13 @@ def measure_with_samples(
307307
raise TypeError(
308308
"ExpectationMP/VarianceMP(SparseHamiltonian) cannot be computed with samples."
309309
)
310-
if isinstance(group[0], VarianceMP) and isinstance(group[0].obs, (Hamiltonian, Sum)):
311-
raise TypeError("VarianceMP(Hamiltonian/Sum) cannot be computed with samples.")
310+
if isinstance(group[0], VarianceMP) and isinstance(group[0].obs, Sum):
311+
raise TypeError("VarianceMP(Sum) cannot be computed with samples.")
312312
if isinstance(group[0], (ClassicalShadowMP, ShadowExpvalMP)):
313313
raise TypeError(
314314
"ExpectationMP(ClassicalShadowMP, ShadowExpvalMP) cannot be computed with samples."
315315
)
316-
if isinstance(group[0], ExpectationMP) and isinstance(group[0].obs, Hamiltonian):
317-
all_res.extend(self._measure_hamiltonian_with_samples(group, shots))
318-
elif isinstance(group[0], ExpectationMP) and isinstance(group[0].obs, Sum):
316+
if isinstance(group[0], ExpectationMP) and isinstance(group[0].obs, Sum):
319317
all_res.extend(self._measure_sum_with_samples(group, shots))
320318
else:
321319
all_res.extend(self._measure_with_samples_diagonalizing_gates(group, shots))
@@ -372,26 +370,6 @@ def _measure_with_samples_diagonalizing_gates(
372370
TensorLike[Any]: Sample measurement results
373371
"""
374372

375-
def _measure_hamiltonian_with_samples(
376-
self,
377-
mp: List[SampleMeasurement],
378-
shots: Shots,
379-
):
380-
# the list contains only one element based on how we group measurements
381-
mp = mp[0]
382-
383-
# if the measurement process involves a Hamiltonian, measure each
384-
# of the terms separately and sum
385-
def _sum_for_single_shot(s):
386-
results = self.measure_with_samples(
387-
[ExpectationMP(t) for t in mp.obs.terms()[1]],
388-
s,
389-
)
390-
return sum(c * res for c, res in zip(mp.obs.terms()[0], results))
391-
392-
unsqueezed_results = tuple(_sum_for_single_shot(type(shots)(s)) for s in shots)
393-
return [unsqueezed_results] if shots.has_partitioned_shots else [unsqueezed_results[0]]
394-
395373
def _measure_sum_with_samples(
396374
self,
397375
mp: List[SampleMeasurement],

pennylane_lightning/core/_serialize.py

+4-8
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,7 @@
3333
matrix,
3434
)
3535
from pennylane.math import unwrap
36-
from pennylane.operation import Tensor
37-
from pennylane.ops import Hamiltonian, LinearCombination, Prod, SProd, Sum
36+
from pennylane.ops import LinearCombination, Prod, SProd, Sum
3837
from pennylane.tape import QuantumTape
3938

4039
NAMED_OBS = (Identity, PauliX, PauliY, PauliZ, Hadamard)
@@ -211,8 +210,7 @@ def _hermitian_ob(self, observable, wires_map: dict = None):
211210

212211
def _tensor_ob(self, observable, wires_map: dict = None):
213212
"""Serialize a tensor observable"""
214-
obs = observable.obs if isinstance(observable, Tensor) else observable.operands
215-
return self.tensor_obs([self._ob(o, wires_map) for o in obs])
213+
return self.tensor_obs([self._ob(o, wires_map) for o in observable.operands])
216214

217215
def _chunk_ham_terms(self, coeffs, ops, split_num: int = 1) -> List:
218216
"Create split_num sub-Hamiltonians from a single high term-count Hamiltonian"
@@ -262,7 +260,7 @@ def _sparse_hamiltonian(self, observable, wires_map: dict = None):
262260
"""
263261

264262
if self._use_mpi:
265-
Hmat = Hamiltonian([1.0], [Identity(0)]).sparse_matrix()
263+
Hmat = Identity(0).sparse_matrix()
266264
H_sparse = SparseHamiltonian(Hmat, wires=range(1))
267265
spm = H_sparse.sparse_matrix()
268266
# Only root 0 needs the overall sparse matrix data
@@ -323,11 +321,9 @@ def _ob(self, observable, wires_map: dict = None):
323321
"""Serialize a :class:`pennylane.operation.Observable` into an Observable."""
324322
if isinstance(observable, NAMED_OBS):
325323
return self._named_obs(observable, wires_map)
326-
if isinstance(observable, Hamiltonian):
327-
return self._hamiltonian(observable, wires_map)
328324
if observable.pauli_rep is not None:
329325
return self._pauli_sentence(observable.pauli_rep, wires_map)
330-
if isinstance(observable, (Tensor, Prod)):
326+
if isinstance(observable, Prod):
331327
if isinstance(observable, Prod) and observable.has_overlapping_wires:
332328
return self._hermitian_ob(observable, wires_map)
333329
return self._tensor_ob(observable, wires_map)

pennylane_lightning/core/_version.py

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

19-
__version__ = "0.40.0-dev9"
19+
20+
__version__ = "0.40.0-dev8"

pennylane_lightning/core/lightning_base.py

+4-12
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
from pennylane import BasisState, StatePrep
2525
from pennylane.devices import QubitDevice
2626
from pennylane.measurements import Expectation, MeasurementProcess, State
27-
from pennylane.operation import Operation, Tensor
27+
from pennylane.operation import Operation
2828
from pennylane.ops import Prod, Projector, SProd, Sum
2929
from pennylane.wires import Wires
3030

@@ -178,9 +178,7 @@ def probability(self, wires=None, shot_range=None, bin_size=None):
178178
def _get_diagonalizing_gates(self, circuit: qml.tape.QuantumTape) -> List[Operation]:
179179
# pylint: disable=no-member, protected-access
180180
def skip_diagonalizing(obs):
181-
return isinstance(obs, qml.Hamiltonian) or (
182-
isinstance(obs, qml.ops.Sum) and obs._pauli_rep is not None
183-
)
181+
return isinstance(obs, qml.ops.Sum) and obs._pauli_rep is not None
184182

185183
meas_filtered = list(
186184
filter(lambda m: m.obs is None or not skip_diagonalizing(m.obs), circuit.measurements)
@@ -319,18 +317,12 @@ def _assert_adjdiff_no_projectors(observable):
319317
Raises:
320318
~pennylane.QuantumFunctionError: if a ``Projector`` is found.
321319
"""
322-
if isinstance(observable, Tensor):
323-
if any(isinstance(o, Projector) for o in observable.non_identity_obs):
324-
raise qml.QuantumFunctionError(
325-
"Adjoint differentiation method does not support the Projector observable"
326-
)
327-
328-
elif isinstance(observable, Projector):
320+
if isinstance(observable, Projector):
329321
raise qml.QuantumFunctionError(
330322
"Adjoint differentiation method does not support the Projector observable"
331323
)
332324

333-
elif isinstance(observable, SProd):
325+
if isinstance(observable, SProd):
334326
LightningBase._assert_adjdiff_no_projectors(observable.base)
335327

336328
elif isinstance(observable, (Sum, Prod)):

pennylane_lightning/lightning_gpu/lightning_gpu.py

+1-7
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@
4242
validate_observables,
4343
)
4444
from pennylane.measurements import MidMeasureMP
45-
from pennylane.operation import DecompositionUndefinedError, Operator, Tensor
45+
from pennylane.operation import DecompositionUndefinedError, Operator
4646
from pennylane.ops import Prod, SProd, Sum
4747
from pennylane.tape import QuantumScript
4848
from pennylane.transforms.core import TransformProgram
@@ -169,7 +169,6 @@
169169
"PauliZ",
170170
"Hadamard",
171171
"SparseHamiltonian",
172-
"Hamiltonian",
173172
"LinearCombination",
174173
"Hermitian",
175174
"Identity",
@@ -204,11 +203,6 @@ def adjoint_observables(obs: Operator) -> bool:
204203
if isinstance(obs, qml.Projector):
205204
return False
206205

207-
if isinstance(obs, Tensor):
208-
if any(isinstance(o, qml.Projector) for o in obs.non_identity_obs):
209-
return False
210-
return True
211-
212206
if isinstance(obs, SProd):
213207
return adjoint_observables(obs.base)
214208

pennylane_lightning/lightning_kokkos/lightning_kokkos.py

+1-7
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@
3838
validate_observables,
3939
)
4040
from pennylane.measurements import MidMeasureMP
41-
from pennylane.operation import DecompositionUndefinedError, Operator, Tensor
41+
from pennylane.operation import DecompositionUndefinedError, Operator
4242
from pennylane.ops import Prod, SProd, Sum
4343
from pennylane.tape import QuantumScript
4444
from pennylane.transforms.core import TransformProgram
@@ -138,7 +138,6 @@
138138
"Identity",
139139
"Projector",
140140
"SparseHamiltonian",
141-
"Hamiltonian",
142141
"LinearCombination",
143142
"Sum",
144143
"SProd",
@@ -174,11 +173,6 @@ def adjoint_observables(obs: Operator) -> bool:
174173
if isinstance(obs, qml.Projector):
175174
return False
176175

177-
if isinstance(obs, Tensor):
178-
if any(isinstance(o, qml.Projector) for o in obs.non_identity_obs):
179-
return False
180-
return True
181-
182176
if isinstance(obs, SProd):
183177
return adjoint_observables(obs.base)
184178

pennylane_lightning/lightning_qubit/lightning_qubit.py

+1-7
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@
3838
validate_observables,
3939
)
4040
from pennylane.measurements import MidMeasureMP
41-
from pennylane.operation import DecompositionUndefinedError, Operator, Tensor
41+
from pennylane.operation import DecompositionUndefinedError, Operator
4242
from pennylane.ops import Prod, SProd, Sum
4343
from pennylane.tape import QuantumScript
4444
from pennylane.transforms.core import TransformProgram
@@ -159,7 +159,6 @@
159159
"Identity",
160160
"Projector",
161161
"SparseHamiltonian",
162-
"Hamiltonian",
163162
"LinearCombination",
164163
"Sum",
165164
"SProd",
@@ -200,11 +199,6 @@ def adjoint_observables(obs: Operator) -> bool:
200199
if isinstance(obs, qml.Projector):
201200
return False
202201

203-
if isinstance(obs, Tensor):
204-
if any(isinstance(o, qml.Projector) for o in obs.non_identity_obs):
205-
return False
206-
return True
207-
208202
if isinstance(obs, SProd):
209203
return adjoint_observables(obs.base)
210204

pennylane_lightning/lightning_tensor/lightning_tensor.py

-1
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,6 @@
154154
"Hadamard",
155155
"Hermitian",
156156
"Identity",
157-
"Hamiltonian",
158157
"LinearCombination",
159158
"Sum",
160159
"SProd",

tests/conftest.py

-28
Original file line numberDiff line numberDiff line change
@@ -206,34 +206,6 @@ def _statevector(num_wires):
206206
return _statevector
207207

208208

209-
#######################################################################
210-
# Fixtures for testing under new and old opmath
211-
212-
213-
@pytest.fixture(scope="function")
214-
def use_legacy_opmath():
215-
with qml.operation.disable_new_opmath_cm() as cm:
216-
yield cm
217-
218-
219-
@pytest.fixture(scope="function")
220-
def use_new_opmath():
221-
with qml.operation.enable_new_opmath_cm() as cm:
222-
yield cm
223-
224-
225-
@pytest.fixture(
226-
params=[qml.operation.disable_new_opmath_cm, qml.operation.enable_new_opmath_cm],
227-
scope="function",
228-
)
229-
def use_legacy_and_new_opmath(request):
230-
with request.param() as cm:
231-
yield cm
232-
233-
234-
#######################################################################
235-
236-
237209
def validate_counts(shots, results1, results2):
238210
"""Compares two counts.
239211

tests/lightning_qubit/test_adjoint_jacobian_class.py

-2
Original file line numberDiff line numberDiff line change
@@ -281,7 +281,6 @@ def test_multiple_rx_gradient_expval_hermitian(self, tol, lightning_sv):
281281

282282
assert np.allclose(expected, result, atol=tol, rtol=0)
283283

284-
@pytest.mark.usefixtures("use_legacy_and_new_opmath")
285284
def test_multiple_rx_gradient_expval_hamiltonian(self, tol, lightning_sv):
286285
"""Tests that the gradient of multiple RX gates in a circuit yields the correct result
287286
with Hermitian observable
@@ -392,7 +391,6 @@ def calculate_vjp(statevector, tape, vector):
392391

393392
return LightningAdjointJacobian(statevector).calculate_vjp(tape, vector)
394393

395-
@pytest.mark.usefixtures("use_legacy_and_new_opmath")
396394
def test_multiple_measurements(self, tol, lightning_sv):
397395
"""Tests provides correct answer when provided multiple measurements."""
398396
x, y, z = [0.5, 0.3, -0.7]

tests/lightning_qubit/test_jacobian_method.py

-1
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,6 @@ def process_and_execute(statevector, tape, dy, execute_and_derivatives=False):
139139
jac = device.vjp(tape, dy, statevector)
140140
return results, jac
141141

142-
@pytest.mark.usefixtures("use_legacy_and_new_opmath")
143142
@pytest.mark.parametrize("theta, phi", list(zip(THETA, PHI)))
144143
@pytest.mark.parametrize(
145144
"obs",

tests/new_api/test_device.py

+3-7
Original file line numberDiff line numberDiff line change
@@ -132,12 +132,12 @@ def test_accepted_observables(self):
132132
@pytest.mark.parametrize(
133133
"obs, expected",
134134
[
135-
(qml.operation.Tensor(qml.Projector([0], 0), qml.PauliZ(1)), False),
135+
(qml.prod(qml.Projector([0], 0), qml.PauliZ(1)), False),
136136
(qml.prod(qml.Projector([0], 0), qml.PauliZ(1)), False),
137137
(qml.s_prod(1.5, qml.Projector([0], 0)), False),
138138
(qml.sum(qml.Projector([0], 0), qml.Hadamard(1)), False),
139139
(qml.sum(qml.prod(qml.Projector([0], 0), qml.Y(1)), qml.PauliX(1)), False),
140-
(qml.operation.Tensor(qml.Y(0), qml.Z(1)), True),
140+
(qml.prod(qml.Y(0), qml.Z(1)), True),
141141
(qml.prod(qml.Y(0), qml.PauliZ(1)), True),
142142
(qml.s_prod(1.5, qml.Y(1)), True),
143143
(qml.sum(qml.Y(1), qml.Hadamard(1)), True),
@@ -429,7 +429,6 @@ def test_preprocess_state_prep_middle_op_decomposition(self, op, decomp_depth):
429429
)
430430
assert qml.equal(new_tape, expected_tape)
431431

432-
@pytest.mark.usefixtures("use_legacy_and_new_opmath")
433432
@pytest.mark.parametrize("theta, phi", list(zip(THETA, PHI)))
434433
@pytest.mark.parametrize(
435434
"mp",
@@ -443,7 +442,7 @@ def test_preprocess_state_prep_middle_op_decomposition(self, op, decomp_depth):
443442
qml.expval(qml.Hamiltonian([-0.5, 1.5], [qml.Y(1), qml.X(1)])),
444443
qml.expval(2.5 * qml.Z(0)),
445444
qml.expval(qml.Z(0) @ qml.X(1)),
446-
qml.expval(qml.operation.Tensor(qml.Z(0), qml.X(1))),
445+
qml.expval(qml.prod(qml.Z(0), qml.X(1))),
447446
qml.expval(
448447
qml.SparseHamiltonian(
449448
qml.Hamiltonian([-1.0, 1.5], [qml.Z(1), qml.X(1)]).sparse_matrix(
@@ -483,7 +482,6 @@ def test_execute_single_measurement(self, theta, phi, mp, dev):
483482
expected = self.calculate_reference(qs)[0]
484483
assert np.allclose(res, expected)
485484

486-
@pytest.mark.usefixtures("use_legacy_and_new_opmath")
487485
@pytest.mark.parametrize("theta, phi", list(zip(THETA, PHI)))
488486
@pytest.mark.parametrize(
489487
"mp1",
@@ -652,7 +650,6 @@ def test_supports_derivatives(self, dev, config, tape, expected, batch_obs):
652650
"""Test that supports_derivative returns the correct boolean value."""
653651
assert dev.supports_derivatives(config, tape) == expected
654652

655-
@pytest.mark.usefixtures("use_legacy_and_new_opmath")
656653
@pytest.mark.parametrize("theta, phi", list(zip(THETA, PHI)))
657654
@pytest.mark.parametrize(
658655
"obs",
@@ -1038,7 +1035,6 @@ def test_supports_vjp(self, dev, config, tape, expected, batch_obs):
10381035
"""Test that supports_vjp returns the correct boolean value."""
10391036
assert dev.supports_vjp(config, tape) == expected
10401037

1041-
@pytest.mark.usefixtures("use_legacy_and_new_opmath")
10421038
@pytest.mark.parametrize("theta, phi", list(zip(THETA, PHI)))
10431039
@pytest.mark.parametrize(
10441040
"obs",

0 commit comments

Comments
 (0)