diff --git a/.github/workflows/install_deps/action.yml b/.github/workflows/install_deps/action.yml index 809358ce745..99b77dc8157 100644 --- a/.github/workflows/install_deps/action.yml +++ b/.github/workflows/install_deps/action.yml @@ -15,7 +15,7 @@ inputs: jax_version: description: The version of JAX to install for any job that requires JAX required: false - default: 0.4.23 + default: '0.4.23' install_tensorflow: description: Indicate if TensorFlow should be installed or not required: false @@ -23,7 +23,7 @@ inputs: tensorflow_version: description: The version of TensorFlow to install for any job that requires TensorFlow required: false - default: 2.16.0 + default: '2.16.0' install_pytorch: description: Indicate if PyTorch should be installed or not required: false @@ -31,7 +31,7 @@ inputs: pytorch_version: description: The version of PyTorch to install for any job that requires PyTorch required: false - default: 2.3.0 + default: '2.3.0' install_pennylane_lightning_master: description: Indicate if PennyLane-Lightning should be installed from the master branch required: false diff --git a/.gitignore b/.gitignore index fc69a5281fd..d9da3038c69 100644 --- a/.gitignore +++ b/.gitignore @@ -27,3 +27,5 @@ config.toml qml_debug.log datasets/* .benchmarks/* +*.h5 +*.hdf5 diff --git a/doc/releases/changelog-dev.md b/doc/releases/changelog-dev.md index 7adc25a6363..783f0629b61 100644 --- a/doc/releases/changelog-dev.md +++ b/doc/releases/changelog-dev.md @@ -6,6 +6,9 @@

Improvements 🛠

+* PennyLane is now compatible with NumPy 2.0. + [(#6061)](https://github.com/PennyLaneAI/pennylane/pull/6061) + * `qml.qchem.excitations` now optionally returns fermionic operators. [(#6171)](https://github.com/PennyLaneAI/pennylane/pull/6171) @@ -127,6 +130,8 @@ This release contains contributions from (in alphabetical order): Guillermo Alonso, Utkarsh Azad, Lillian M. A. Frederiksen, +Pietropaolo Frisoni, +Emiliano Godinez, Christina Lee, William Maxwell, Lee J. O'Riordan, diff --git a/pennylane/numpy/random.py b/pennylane/numpy/random.py index eae1511cf4f..12a00e798a5 100644 --- a/pennylane/numpy/random.py +++ b/pennylane/numpy/random.py @@ -16,9 +16,10 @@ it works with the PennyLane :class:`~.tensor` class. """ -from autograd.numpy import random as _random +# isort: skip_file from numpy import __version__ as np_version from numpy.random import MT19937, PCG64, SFC64, Philox # pylint: disable=unused-import +from autograd.numpy import random as _random from packaging.specifiers import SpecifierSet from packaging.version import Version @@ -26,8 +27,8 @@ wrap_arrays(_random.__dict__, globals()) - if Version(np_version) in SpecifierSet(">=0.17.0"): + # pylint: disable=too-few-public-methods # pylint: disable=missing-class-docstring class Generator(_random.Generator): diff --git a/requirements-ci.txt b/requirements-ci.txt index 083beaae25f..d552b95e904 100644 --- a/requirements-ci.txt +++ b/requirements-ci.txt @@ -1,5 +1,5 @@ numpy -scipy<1.13.0 +scipy<=1.13.0 cvxpy cvxopt networkx diff --git a/setup.py b/setup.py index e13673fb1fa..41ae9775027 100644 --- a/setup.py +++ b/setup.py @@ -21,7 +21,7 @@ version = f.readlines()[-1].split()[-1].strip("\"'") requirements = [ - "numpy<2.0", + "numpy<=2.0", "scipy", "networkx", "rustworkx>=0.14.0", diff --git a/tests/data/attributes/operator/test_operator.py b/tests/data/attributes/operator/test_operator.py index 83e0c658ab6..8079b720f89 100644 --- a/tests/data/attributes/operator/test_operator.py +++ b/tests/data/attributes/operator/test_operator.py @@ -174,6 +174,7 @@ def test_value_init(self, attribute_cls, op_in): """Test that a DatasetOperator can be value-initialized from an operator, and that the deserialized operator is equivalent.""" + if not qml.operation.active_new_opmath() and isinstance(op_in, qml.ops.LinearCombination): op_in = qml.operation.convert_to_legacy_H(op_in) @@ -183,7 +184,8 @@ def test_value_init(self, attribute_cls, op_in): assert dset_op.info["py_type"] == get_type_str(type(op_in)) op_out = dset_op.get_value() - assert repr(op_out) == repr(op_in) + with np.printoptions(legacy="1.21"): + assert repr(op_out) == repr(op_in) assert op_in.data == op_out.data @pytest.mark.parametrize( @@ -199,6 +201,7 @@ def test_bind_init(self, attribute_cls, op_in): """Test that a DatasetOperator can be bind-initialized from an operator, and that the deserialized operator is equivalent.""" + if not qml.operation.active_new_opmath() and isinstance(op_in, qml.ops.LinearCombination): op_in = qml.operation.convert_to_legacy_H(op_in) @@ -210,10 +213,12 @@ def test_bind_init(self, attribute_cls, op_in): assert dset_op.info["py_type"] == get_type_str(type(op_in)) op_out = dset_op.get_value() - assert repr(op_out) == repr(op_in) + with np.printoptions(legacy="1.21"): + assert repr(op_out) == repr(op_in) assert op_in.data == op_out.data assert op_in.wires == op_out.wires - assert repr(op_in) == repr(op_out) + with np.printoptions(legacy="1.21"): + assert repr(op_in) == repr(op_out) @pytest.mark.parametrize("attribute_cls", [DatasetOperator, DatasetPyTree]) diff --git a/tests/data/attributes/test_dict.py b/tests/data/attributes/test_dict.py index 6bf6e202fd6..3e3a2a9d4b2 100644 --- a/tests/data/attributes/test_dict.py +++ b/tests/data/attributes/test_dict.py @@ -15,6 +15,7 @@ Tests for the ``DatasetDict`` attribute type. """ +import numpy as np import pytest from pennylane.data.attributes import DatasetDict @@ -45,7 +46,8 @@ def test_value_init(self, value): assert dset_dict.info.py_type == "dict" assert dset_dict.bind.keys() == value.keys() assert len(dset_dict) == len(value) - assert repr(value) == repr(dset_dict) + with np.printoptions(legacy="1.21"): + assert repr(value) == repr(dset_dict) @pytest.mark.parametrize( "value", [{"a": 1, "b": 2}, {}, {"a": 1, "b": {"x": "y", "z": [1, 2]}}] @@ -93,7 +95,8 @@ def test_copy(self, value): assert builtin_dict.keys() == value.keys() assert len(builtin_dict) == len(value) - assert repr(builtin_dict) == repr(value) + with np.printoptions(legacy="1.21"): + assert repr(builtin_dict) == repr(value) @pytest.mark.parametrize( "value", [{"a": 1, "b": 2}, {}, {"a": 1, "b": {"x": "y", "z": [1, 2]}}] @@ -121,4 +124,5 @@ def test_equality_same_length(self): ) def test_string_conversion(self, value): dset_dict = DatasetDict(value) - assert str(dset_dict) == str(value) + with np.printoptions(legacy="1.21"): + assert str(dset_dict) == str(value) diff --git a/tests/data/attributes/test_list.py b/tests/data/attributes/test_list.py index eef27057616..2f4c937d178 100644 --- a/tests/data/attributes/test_list.py +++ b/tests/data/attributes/test_list.py @@ -18,6 +18,7 @@ from itertools import combinations +import numpy as np import pytest from pennylane.data import DatasetList @@ -56,8 +57,9 @@ def test_value_init(self, input_type, value): lst = DatasetList(input_type(value)) assert lst == value - assert repr(lst) == repr(value) assert len(lst) == len(value) + with np.printoptions(legacy="1.21"): + assert repr(lst) == repr(value) @pytest.mark.parametrize("input_type", (list, tuple)) @pytest.mark.parametrize("value", [[], [1], [1, 2, 3], ["a", "b", "c"], [{"a": 1}]]) @@ -148,12 +150,14 @@ def test_setitem_out_of_range(self, index): @pytest.mark.parametrize("value", [[], [1], [1, 2, 3], ["a", "b", "c"], [{"a": 1}]]) def test_copy(self, input_type, value): """Test that a `DatasetList` can be copied.""" + ds = DatasetList(input_type(value)) ds_copy = ds.copy() assert ds_copy == value - assert repr(ds_copy) == repr(value) assert len(ds_copy) == len(value) + with np.printoptions(legacy="1.21"): + assert repr(ds_copy) == repr(value) @pytest.mark.parametrize("input_type", (list, tuple)) @pytest.mark.parametrize("value", [[], [1], [1, 2, 3], ["a", "b", "c"], [{"a": 1}]]) @@ -169,8 +173,10 @@ def test_equality(self, input_type, value): @pytest.mark.parametrize("value", [[], [1], [1, 2, 3], ["a", "b", "c"], [{"a": 1}]]) def test_string_conversion(self, value): """Test that a `DatasetList` is converted to a string correctly.""" + dset_dict = DatasetList(value) - assert str(dset_dict) == str(value) + with np.printoptions(legacy="1.21"): + assert str(dset_dict) == str(value) @pytest.mark.parametrize("value", [[1], [1, 2, 3], ["a", "b", "c"], [{"a": 1}]]) def test_deleting_elements(self, value): diff --git a/tests/data/base/test_attribute.py b/tests/data/base/test_attribute.py index d38249c1672..da500db48e5 100644 --- a/tests/data/base/test_attribute.py +++ b/tests/data/base/test_attribute.py @@ -285,8 +285,8 @@ def test_bind_init_from_other_bind(self): ) def test_repr(self, val, attribute_type): """Test that __repr__ has the expected format.""" - - assert repr(attribute(val)) == f"{attribute_type.__name__}({repr(val)})" + with np.printoptions(legacy="1.21"): + assert repr(attribute(val)) == f"{attribute_type.__name__}({repr(val)})" @pytest.mark.parametrize( "val", diff --git a/tests/devices/qubit/test_measure.py b/tests/devices/qubit/test_measure.py index d0c618311cf..47e4d8c2a31 100644 --- a/tests/devices/qubit/test_measure.py +++ b/tests/devices/qubit/test_measure.py @@ -302,7 +302,7 @@ class TestNaNMeasurements: def test_nan_float_result(self, mp, interface): """Test that the result of circuits with 0 probability postselections is NaN with the expected shape.""" - state = qml.math.full((2, 2), np.NaN, like=interface) + state = qml.math.full((2, 2), np.nan, like=interface) res = measure(mp, state, is_state_batched=False) assert qml.math.ndim(res) == 0 @@ -339,7 +339,7 @@ def test_nan_float_result(self, mp, interface): def test_nan_float_result_jax(self, mp, use_jit): """Test that the result of circuits with 0 probability postselections is NaN with the expected shape.""" - state = qml.math.full((2, 2), np.NaN, like="jax") + state = qml.math.full((2, 2), np.nan, like="jax") if use_jit: import jax @@ -360,7 +360,7 @@ def test_nan_float_result_jax(self, mp, use_jit): def test_nan_probs(self, mp, interface): """Test that the result of circuits with 0 probability postselections is NaN with the expected shape.""" - state = qml.math.full((2, 2), np.NaN, like=interface) + state = qml.math.full((2, 2), np.nan, like=interface) res = measure(mp, state, is_state_batched=False) assert qml.math.shape(res) == (2 ** len(mp.wires),) @@ -375,7 +375,7 @@ def test_nan_probs(self, mp, interface): def test_nan_probs_jax(self, mp, use_jit): """Test that the result of circuits with 0 probability postselections is NaN with the expected shape.""" - state = qml.math.full((2, 2), np.NaN, like="jax") + state = qml.math.full((2, 2), np.nan, like="jax") if use_jit: import jax diff --git a/tests/devices/qubit/test_sampling.py b/tests/devices/qubit/test_sampling.py index 4174ed63aae..e36c69c26a3 100644 --- a/tests/devices/qubit/test_sampling.py +++ b/tests/devices/qubit/test_sampling.py @@ -591,7 +591,7 @@ def test_only_catch_nan_errors(self, shots): mp = qml.expval(qml.PauliZ(0)) _shots = Shots(shots) - with pytest.raises(ValueError, match="probabilities do not sum to 1"): + with pytest.raises(ValueError, match=r"(?i)probabilities do not sum to 1"): _ = measure_with_samples([mp], state, _shots) @pytest.mark.all_interfaces @@ -619,7 +619,7 @@ def test_only_catch_nan_errors(self, shots): def test_nan_float_result(self, mp, interface, shots): """Test that the result of circuits with 0 probability postselections is NaN with the expected shape.""" - state = qml.math.full((2, 2), np.NaN, like=interface) + state = qml.math.full((2, 2), np.nan, like=interface) res = measure_with_samples((mp,), state, _FlexShots(shots), is_state_batched=False) if not isinstance(shots, list): @@ -646,7 +646,7 @@ def test_nan_float_result(self, mp, interface, shots): def test_nan_samples(self, mp, interface, shots): """Test that the result of circuits with 0 probability postselections is NaN with the expected shape.""" - state = qml.math.full((2, 2), np.NaN, like=interface) + state = qml.math.full((2, 2), np.nan, like=interface) res = measure_with_samples((mp,), state, _FlexShots(shots), is_state_batched=False) if not isinstance(shots, list): @@ -672,7 +672,7 @@ def test_nan_samples(self, mp, interface, shots): def test_nan_classical_shadows(self, interface, shots): """Test that classical_shadows returns an empty array when the state has NaN values""" - state = qml.math.full((2, 2), np.NaN, like=interface) + state = qml.math.full((2, 2), np.nan, like=interface) res = measure_with_samples( (qml.classical_shadow([0]),), state, _FlexShots(shots), is_state_batched=False ) @@ -699,7 +699,7 @@ def test_nan_classical_shadows(self, interface, shots): def test_nan_shadow_expval(self, H, interface, shots): """Test that shadow_expval returns an empty array when the state has NaN values""" - state = qml.math.full((2, 2), np.NaN, like=interface) + state = qml.math.full((2, 2), np.nan, like=interface) res = measure_with_samples( (qml.shadow_expval(H),), state, _FlexShots(shots), is_state_batched=False ) @@ -757,7 +757,7 @@ def test_sample_state_renorm_error(self, interface): """Test that renormalization does not occur if the error is too large.""" state = qml.math.array(two_qubit_state_not_normalized, like=interface) - with pytest.raises(ValueError, match="probabilities do not sum to 1"): + with pytest.raises(ValueError, match=r"(?i)probabilities do not sum to 1"): _ = sample_state(state, 10) @pytest.mark.all_interfaces @@ -775,7 +775,7 @@ def test_sample_batched_state_renorm_error(self, interface): """Test that renormalization does not occur if the error is too large.""" state = qml.math.array(batched_state_not_normalized, like=interface) - with pytest.raises(ValueError, match="probabilities do not sum to 1"): + with pytest.raises(ValueError, match=r"(?i)probabilities do not sum to 1"): _ = sample_state(state, 10, is_state_batched=True) diff --git a/tests/devices/qutrit_mixed/test_qutrit_mixed_sampling.py b/tests/devices/qutrit_mixed/test_qutrit_mixed_sampling.py index eb3383ed5a6..ecd2fbbcca8 100644 --- a/tests/devices/qutrit_mixed/test_qutrit_mixed_sampling.py +++ b/tests/devices/qutrit_mixed/test_qutrit_mixed_sampling.py @@ -402,7 +402,7 @@ def test_only_catch_nan_errors(self, shots): mp = qml.sample(wires=range(2)) _shots = Shots(shots) - with pytest.raises(ValueError, match="probabilities do not sum to 1"): + with pytest.raises(ValueError, match=r"(?i)probabilities do not sum to 1"): _ = measure_with_samples(mp, state, _shots) @pytest.mark.parametrize("mp", [qml.probs(0), qml.probs(op=qml.GellMann(0, 1))]) diff --git a/tests/devices/test_default_qubit_legacy.py b/tests/devices/test_default_qubit_legacy.py index 11ca082441c..9b67d18b5c6 100644 --- a/tests/devices/test_default_qubit_legacy.py +++ b/tests/devices/test_default_qubit_legacy.py @@ -18,6 +18,7 @@ # pylint: disable=protected-access,cell-var-from-loop import cmath import math +from importlib.metadata import version import pytest @@ -628,7 +629,8 @@ def test_apply_global_phase(self, qubit_device_3_wires, tol, wire, input_state): expected_output = np.array(input_state) * np.exp(-1j * phase) assert np.allclose(qubit_device_3_wires._state, np.array(expected_output), atol=tol, rtol=0) - assert qubit_device_3_wires._state.dtype == qubit_device_3_wires.C_DTYPE + if version("numpy") < "2.0.0": + assert qubit_device_3_wires._state.dtype == qubit_device_3_wires.C_DTYPE def test_apply_errors_qubit_state_vector(self, qubit_device_2_wires): """Test that apply fails for incorrect state preparation, and > 2 qubit gates""" @@ -650,26 +652,26 @@ def test_apply_errors_qubit_state_vector(self, qubit_device_2_wires): ) def test_apply_errors_basis_state(self, qubit_device_2_wires): - - with pytest.raises( - ValueError, match=r"Basis state must only consist of 0s and 1s; got \[-0\.2, 4\.2\]" - ): - qubit_device_2_wires.apply([qml.BasisState(np.array([-0.2, 4.2]), wires=[0, 1])]) - - with pytest.raises( - ValueError, match=r"State must be of length 1; got length 2 \(state=\[0 1\]\)\." - ): - qubit_device_2_wires.apply([qml.BasisState(np.array([0, 1]), wires=[0])]) - - with pytest.raises( - qml.DeviceError, - match="Operation BasisState cannot be used after other Operations have already been applied " - "on a default.qubit.legacy device.", - ): - qubit_device_2_wires.reset() - qubit_device_2_wires.apply( - [qml.RZ(0.5, wires=[0]), qml.BasisState(np.array([1, 1]), wires=[0, 1])] - ) + with np.printoptions(legacy="1.21"): + with pytest.raises( + ValueError, match=r"Basis state must only consist of 0s and 1s; got \[-0\.2, 4\.2\]" + ): + qubit_device_2_wires.apply([qml.BasisState(np.array([-0.2, 4.2]), wires=[0, 1])]) + + with pytest.raises( + ValueError, match=r"State must be of length 1; got length 2 \(state=\[0 1\]\)\." + ): + qubit_device_2_wires.apply([qml.BasisState(np.array([0, 1]), wires=[0])]) + + with pytest.raises( + qml.DeviceError, + match="Operation BasisState cannot be used after other Operations have already been applied " + "on a default.qubit.legacy device.", + ): + qubit_device_2_wires.reset() + qubit_device_2_wires.apply( + [qml.RZ(0.5, wires=[0]), qml.BasisState(np.array([1, 1]), wires=[0, 1])] + ) class TestExpval: diff --git a/tests/devices/test_qubit_device.py b/tests/devices/test_qubit_device.py index 9edc522a408..8f50bb65329 100644 --- a/tests/devices/test_qubit_device.py +++ b/tests/devices/test_qubit_device.py @@ -1605,9 +1605,9 @@ def test_samples_to_counts_with_nan(self): # imitate hardware return with NaNs (requires dtype float) samples = qml.math.cast_like(samples, np.array([1.2])) - samples[0][0] = np.NaN - samples[17][1] = np.NaN - samples[850][0] = np.NaN + samples[0][0] = np.nan + samples[17][1] = np.nan + samples[850][0] = np.nan result = device._samples_to_counts(samples, mp=qml.measurements.CountsMP(), num_wires=2) diff --git a/tests/measurements/test_counts.py b/tests/measurements/test_counts.py index 3f3badb5c0e..08da35015c9 100644 --- a/tests/measurements/test_counts.py +++ b/tests/measurements/test_counts.py @@ -135,9 +135,9 @@ def test_counts_with_nan_samples(self): rng = np.random.default_rng(123) samples = rng.choice([0, 1], size=(shots, 2)).astype(np.float64) - samples[0][0] = np.NaN - samples[17][1] = np.NaN - samples[850][0] = np.NaN + samples[0][0] = np.nan + samples[17][1] = np.nan + samples[850][0] = np.nan result = qml.counts(wires=[0, 1]).process_samples(samples, wire_order=[0, 1]) diff --git a/tests/templates/test_subroutines/test_prepselprep.py b/tests/templates/test_subroutines/test_prepselprep.py index 95e7f771ef7..7e3b7966c28 100644 --- a/tests/templates/test_subroutines/test_prepselprep.py +++ b/tests/templates/test_subroutines/test_prepselprep.py @@ -47,13 +47,16 @@ def test_standard_checks(lcu, control): def test_repr(): """Test the repr method.""" + lcu = qml.dot([0.25, 0.75], [qml.Z(2), qml.X(1) @ qml.X(2)]) control = [0] op = qml.PrepSelPrep(lcu, control) - assert ( - repr(op) == "PrepSelPrep(coeffs=(0.25, 0.75), ops=(Z(2), X(1) @ X(2)), control=Wires([0]))" - ) + with np.printoptions(legacy="1.21"): + assert ( + repr(op) + == "PrepSelPrep(coeffs=(0.25, 0.75), ops=(Z(2), X(1) @ X(2)), control=Wires([0]))" + ) def _get_new_terms(lcu):