Skip to content

Commit

Permalink
Fix recent failures in legacy opmath (#5742)
Browse files Browse the repository at this point in the history
- Update `Tensor.eigvals` to use `qml.math.kron` instead of `np.kron`,
fixes
https://github.com/PennyLaneAI/pennylane/actions/runs/9248166629/job/25438052473
- Adds a `new_opmath_only` fixture that skips tests not meant to work
for legacy opmath
- Mark `tests/test_pytrees.py::test_nested_pl_object` as
`new_opmath_only` (confirmed with @albi3ro)
- Mark tests in `tests/devices/default_tensor` involving `Hamiltonian`
as `new_opmath_only` (confirmed with @PietropaoloFrisoni)
  • Loading branch information
astralcai authored May 28, 2024
1 parent 59a1e05 commit c285b87
Show file tree
Hide file tree
Showing 10 changed files with 23 additions and 17 deletions.
6 changes: 4 additions & 2 deletions pennylane/operation.py
Original file line number Diff line number Diff line change
Expand Up @@ -2319,12 +2319,14 @@ def eigvals(self):
for k, g in itertools.groupby(self.obs, lambda x: x.name in standard_observables):
if k:
# Subgroup g contains only standard observables.
self._eigvals_cache = np.kron(self._eigvals_cache, pauli_eigs(len(list(g))))
self._eigvals_cache = qml.math.kron(
self._eigvals_cache, pauli_eigs(len(list(g)))
)
else:
# Subgroup g contains only non-standard observables.
for ns_ob in g:
# loop through all non-standard observables
self._eigvals_cache = np.kron(self._eigvals_cache, ns_ob.eigvals())
self._eigvals_cache = qml.math.kron(self._eigvals_cache, ns_ob.eigvals())

return self._eigvals_cache

Expand Down
6 changes: 6 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,12 @@ def use_legacy_and_new_opmath(request):
yield cm


@pytest.fixture
def new_opmath_only():
if not qml.operation.active_new_opmath():
pytest.skip("This feature only works with new opmath enabled")


#######################################################################

try:
Expand Down
2 changes: 2 additions & 0 deletions tests/devices/default_tensor/test_tensor_expval.py
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,7 @@ def test_hermitian_expectation(self, theta, phi, tol, dev):

assert np.allclose(calculated_val, reference_val, tol)

@pytest.mark.usefixtures("new_opmath_only")
def test_hamiltonian_expectation(self, theta, phi, tol, dev):
"""Tests a Hamiltonian."""

Expand Down Expand Up @@ -354,6 +355,7 @@ def test_PauliZ_hadamard_PauliY(self, theta, phi, varphi, dev, tol):
assert np.allclose(calculated_val, reference_val, tol)


@pytest.mark.usefixtures("new_opmath_only")
@pytest.mark.parametrize("theta, phi", list(zip(THETA, PHI)))
def test_multi_qubit_gates(theta, phi, dev):
"""Tests a simple circuit with multi-qubit gates."""
Expand Down
2 changes: 2 additions & 0 deletions tests/devices/default_tensor/test_tensor_var.py
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,7 @@ def test_hermitian_variance(self, theta, phi, dev):
tol = 1e-5 if dev.dtype == np.complex64 else 1e-7
assert np.allclose(calculated_val, reference_val, atol=tol, rtol=0)

@pytest.mark.usefixtures("new_opmath_only")
def test_hamiltonian_variance(self, theta, phi, dev):
"""Tests a Hamiltonian."""

Expand Down Expand Up @@ -361,6 +362,7 @@ def test_PauliZ_hadamard_PauliY(self, theta, phi, varphi, dev, tol):
assert np.allclose(calculated_val, reference_val, tol)


@pytest.mark.usefixtures("new_opmath_only")
@pytest.mark.parametrize("theta, phi", list(zip(THETA, PHI)))
def test_multi_qubit_gates(theta, phi, dev):
"""Tests a simple circuit with multi-qubit gates."""
Expand Down
4 changes: 1 addition & 3 deletions tests/devices/qubit/test_measure.py
Original file line number Diff line number Diff line change
Expand Up @@ -189,12 +189,10 @@ def qnode(t1, t2):
t1, t2 = 0.5, 1.0
assert qml.math.allclose(qnode(t1, t2), jax.jit(qnode)(t1, t2))

@pytest.mark.usefixtures("new_opmath_only")
def test_measure_identity_no_wires(self):
"""Test that measure can handle the expectation value of identity on no wires."""

if not qml.operation.active_new_opmath():
pytest.skip("Identity with no wires is not supported with legacy opmath.")

state = np.random.random([2, 2, 2])
out = measure(qml.measurements.ExpectationMP(qml.I()), state)
assert qml.math.allclose(out, 1.0)
Expand Down
4 changes: 1 addition & 3 deletions tests/devices/qubit/test_sampling.py
Original file line number Diff line number Diff line change
Expand Up @@ -517,12 +517,10 @@ def test_identity_on_no_wires(self):
[result] = measure_with_samples([mp], state, shots=qml.measurements.Shots(1))
assert qml.math.allclose(result, 1.0)

@pytest.mark.usefixtures("new_opmath_only")
def test_identity_on_no_wires_with_other_observables(self):
"""Test that measuring an identity on no wires can be used in conjunction with other measurements."""

if not qml.operation.active_new_opmath():
pytest.skip("Identity with no wires is not supported with legacy opmath.")

state = np.array([0, 1])

mps = [
Expand Down
4 changes: 1 addition & 3 deletions tests/ops/op_math/test_sum.py
Original file line number Diff line number Diff line change
Expand Up @@ -679,12 +679,10 @@ def test_queue_category(self, ops_lst, sum_method):
sum_op = sum_method(*ops_lst)
assert sum_op._queue_category is None # pylint: disable=protected-access

@pytest.mark.usefixtures("new_opmath_only")
def test_eigvals_Identity_no_wires(self):
"""Test that eigenvalues can be computed for a sum containing identity with no wires."""

if not qml.operation.active_new_opmath():
pytest.skip("Identity with no wires is not supported for legacy opmath")

op1 = qml.X(0) + 2 * qml.I()
op2 = qml.X(0) + 2 * qml.I(0)
assert qml.math.allclose(sorted(op1.eigvals()), sorted(op2.eigvals()))
Expand Down
4 changes: 1 addition & 3 deletions tests/pauli/grouping/test_pauli_group_observables.py
Original file line number Diff line number Diff line change
Expand Up @@ -466,13 +466,11 @@ def test_no_observables_with_wires(self):
assert groups == [observables]
assert coeffs == [[1, 2]]

@pytest.mark.usefixtures("new_opmath_only")
def test_observables_on_no_wires_coeffs(self):
"""Test that observables on no wires are stuck in the first group and
coefficients are tracked when provided."""

if not qml.operation.active_new_opmath():
pytest.skip("Identity with no wires is not supported with legacy opmath.")

observables = [
qml.X(0),
qml.Z(0),
Expand Down
4 changes: 4 additions & 0 deletions tests/test_pytrees.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@
"""
Tests for the pennylane pytrees module
"""

import pytest

import pennylane as qml
from pennylane.pytrees import PyTreeStructure, flatten, leaf, register_pytree, unflatten

Expand Down Expand Up @@ -100,6 +103,7 @@ def test_dict():
assert new_x == {"a": 5, "b": {"c": 6, "d": 7}}


@pytest.mark.usefixtures("new_opmath_only")
def test_nested_pl_object():
"""Test that we can flatten and unflatten nested pennylane object."""

Expand Down
4 changes: 1 addition & 3 deletions tests/test_qnode_legacy.py
Original file line number Diff line number Diff line change
Expand Up @@ -1876,13 +1876,11 @@ def circuit():

assert len(tapes) == 2

@pytest.mark.usefixtures("new_opmath_only")
@pytest.mark.parametrize("grouping", [True, False])
def test_multiple_hamiltonian_expansion_finite_shots(self, grouping):
"""Test that multiple Hamiltonians works correctly (sum_expand should be used)"""

if not qml.operation.active_new_opmath():
pytest.skip("expval of the legacy Hamiltonian does not support finite shots.")

dev = qml.device("default.qubit.legacy", wires=3, shots=50000)

obs = [qml.PauliX(0), qml.PauliX(0) @ qml.PauliZ(1), qml.PauliZ(0) @ qml.PauliZ(1)]
Expand Down

0 comments on commit c285b87

Please sign in to comment.