Skip to content

Commit

Permalink
Daily rc sync to master (#5635)
Browse files Browse the repository at this point in the history
Automatic sync from the release candidate to master during a feature
freeze.

---------

Co-authored-by: Mudit Pandey <mudit.pandey@xanadu.ai>
Co-authored-by: Astral Cai <astral.cai@xanadu.ai>
Co-authored-by: Vincent Michaud-Rioux <vincentm@nanoacademic.com>
Co-authored-by: David Wierichs <david.wierichs@xanadu.ai>
Co-authored-by: lillian542 <38584660+lillian542@users.noreply.github.com>
Co-authored-by: Pietropaolo Frisoni <pietropaolo.frisoni@xanadu.ai>
Co-authored-by: Christina Lee <christina@xanadu.ai>
Co-authored-by: GitHub Actions Bot <>
  • Loading branch information
8 people authored May 3, 2024
1 parent b556393 commit 465d337
Show file tree
Hide file tree
Showing 28 changed files with 140 additions and 56 deletions.
2 changes: 1 addition & 1 deletion doc/introduction/compiling_circuits.rst
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ RX(11.336370614359172, wires=[0])
>>> qml.simplify(qml.ops.Pow(qml.RX(1, 0), 3))
RX(3.0, wires=[0])
>>> qml.simplify(qml.sum(qml.Y(3), qml.Y(3)))
2 * Y(3)
2.0 * Y(3)
>>> qml.simplify(qml.RX(1, 0) @ qml.RX(1, 0))
RX(2.0, wires=[0])
>>> qml.simplify(qml.prod(qml.X(0), qml.Z(0)))
Expand Down
2 changes: 1 addition & 1 deletion doc/introduction/inspecting_circuits.rst
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,7 @@ or to check whether two gates causally influence each other.
Internally, the :class:`~pennylane.CircuitGraph` class constructs a ``rustworkx`` graph object.

>>> type(g.graph)
<class 'rustworkx.PyDiGraph'>
rustworkx.PyDiGraph

There is no edge between the ``Hadamard`` and the first ``CNOT``, but between consecutive ``CNOT`` gates:

Expand Down
8 changes: 8 additions & 0 deletions doc/releases/changelog-0.36.0.md
Original file line number Diff line number Diff line change
Expand Up @@ -551,6 +551,10 @@

<h3>Bug fixes 🐛</h3>

* Improves the error message for setting shots on the new device interface, or trying to access a property
that no longer exists.
[(#5616)](https://github.com/PennyLaneAI/pennylane/pull/5616)

* Fixed a bug where `qml.draw` and `qml.draw_mpl` incorrectly raised errors for circuits collecting statistics on mid-circuit measurements
while using `qml.defer_measurements`.
[(#5610)](https://github.com/PennyLaneAI/pennylane/pull/5610)
Expand Down Expand Up @@ -677,6 +681,10 @@
* `qml.equal` can now be used with sums and products that contain operators on no wires like `I` and `GlobalPhase`.
[(#5562)](https://github.com/PennyLaneAI/pennylane/pull/5562)

* `CompositeOp.has_diagonalizing_gates` now does a more complete check of the base operators to ensure consistency
between `op.has_diagonalzing_gates` and `op.diagonalizing_gates()`
[(#5603)](https://github.com/PennyLaneAI/pennylane/pull/5603)

<h3>Contributors ✍️</h3>

This release contains contributions from (in alphabetical order):
Expand Down
18 changes: 18 additions & 0 deletions pennylane/devices/device_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,14 @@ def __repr__(self):
details = f"({', '.join(details)}) " if details else ""
return f"<{self.name} device {details}at {hex(id(self))}>"

def __getattr__(self, key):
raise AttributeError(
f"{type(self).__name__} has no attribute '{key}'."
" You may be looking for a property or method present in the legacy device interface."
f" Please consult the {type(self).__name__} documentation for an updated list of public"
" properties and methods."
)

@property
def shots(self) -> Shots:
"""Default shots for execution workflows containing this device.
Expand All @@ -186,6 +194,16 @@ def shots(self) -> Shots:
"""
return self._shots

@shots.setter
def shots(self, _):
raise AttributeError(
(
"Shots can no longer be set on a device instance. "
"You can set shots on a call to a QNode, on individual tapes, or "
"create a new device instance instead."
)
)

@property
def wires(self) -> Wires:
"""The device wires.
Expand Down
17 changes: 7 additions & 10 deletions pennylane/devices/preprocess.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@

import pennylane as qml
from pennylane import DeviceError, Snapshot, transform
from pennylane.measurements import MeasurementProcess, SampleMeasurement, StateMeasurement
from pennylane.measurements import SampleMeasurement, StateMeasurement
from pennylane.operation import StatePrepBase, Tensor
from pennylane.typing import Result, ResultBatch
from pennylane.wires import WireError
Expand Down Expand Up @@ -230,16 +230,13 @@ def validate_adjoint_trainable_params(
"Differentiating with respect to the input parameters of state-prep operations "
"is not supported with the adjoint differentiation method."
)
for k in tape.trainable_params:
mp_or_op = tape[tape._par_info[k]["op_idx"]]
if isinstance(mp_or_op, MeasurementProcess):
for m in tape.measurements:
if m.obs and qml.operation.is_trainable(m.obs):
warnings.warn(
"Differentiating with respect to the input parameters of "
f"{mp_or_op.obs.name} is not supported with the "
"adjoint differentiation method. Gradients are computed "
"only with regards to the trainable parameters of the circuit.\n\n Mark "
"the parameters of the measured observables as non-trainable "
"to silence this warning.",
f"Differentiating with respect to the input parameters of {m.obs.name} "
"is not supported with the adjoint differentiation method. Gradients are computed "
"only with regards to the trainable parameters of the circuit.\n\n Mark the "
"parameters of the measured observables as non-trainable to silence this warning.",
UserWarning,
)
return (tape,), null_postprocessing
Expand Down
9 changes: 9 additions & 0 deletions pennylane/math/fidelity.py
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,15 @@ def fidelity(state0, state1, check_state=False, c_dtype="complex128"):
if qml.math.shape(state0)[-1] != qml.math.shape(state1)[-1]:
raise qml.QuantumFunctionError("The two states must have the same number of wires.")

batch_size0 = qml.math.shape(state0)[0] if qml.math.ndim(state0) > 2 else None
batch_size1 = qml.math.shape(state1)[0] if qml.math.ndim(state1) > 2 else None

if qml.math.get_interface(state0) == "jax" or qml.math.get_interface(state1) == "jax":
if batch_size0 and not batch_size1:
state1 = qml.math.broadcast_to(state1, (batch_size0, *qml.math.shape(state1)))
elif not batch_size0 and batch_size1:
state0 = qml.math.broadcast_to(state0, (batch_size1, *qml.math.shape(state0)))

# Two mixed states
_register_vjp(state0, state1)
fid = qml.math.compute_fidelity(state0, state1)
Expand Down
9 changes: 8 additions & 1 deletion pennylane/ops/functions/dot.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,8 @@ def dot(
Args:
coeffs (Sequence[float, Callable]): sequence containing the coefficients of the linear combination
ops (Sequence[Operator]): sequence containing the operators of the linear combination
ops (Sequence[Operator, PauliWord, PauliSentence]): sequence containing the operators of the linear combination.
Can also be ``PauliWord`` or ``PauliSentence`` instances.
pauli (bool, optional): If ``True``, a :class:`~.PauliSentence`
operator is used to represent the linear combination. If False, a :class:`Sum` operator
is returned. Defaults to ``False``. Note that when ``ops`` consists solely of ``PauliWord``
Expand Down Expand Up @@ -136,6 +137,12 @@ def dot(
if len(coeffs) == 0 and len(ops) == 0:
raise ValueError("Cannot compute the dot product of an empty sequence.")

for t in (Operator, PauliWord, PauliSentence):
if isinstance(ops, t):
raise ValueError(
f"ops must be an Iterable of {t.__name__}'s, not a {t.__name__} itself."
)

if any(callable(c) for c in coeffs):
return ParametrizedHamiltonian(coeffs, ops)

Expand Down
5 changes: 5 additions & 0 deletions pennylane/ops/op_math/composite.py
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,11 @@ def eigendecomposition(self):
@property
def has_diagonalizing_gates(self):
if self.has_overlapping_wires:
for ops in self.overlapping_ops:
# if any of the single ops doesn't have diagonalizing gates, the overall operator doesn't either
if len(ops) == 1 and not ops[0].has_diagonalizing_gates:
return False
# the lists of ops with multiple operators can be handled if there is a matrix
return self.has_matrix

return all(op.has_diagonalizing_gates for op in self)
Expand Down
4 changes: 4 additions & 0 deletions pennylane/ops/op_math/linear_combination.py
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,10 @@ def __init__(
_pauli_rep=None,
id=None,
):
if isinstance(observables, Operator):
raise ValueError(
"observables must be an Iterable of Operator's, and not an Operator itself."
)
if qml.math.shape(coeffs)[0] != len(observables):
raise ValueError(
"Could not create valid LinearCombination; "
Expand Down
22 changes: 13 additions & 9 deletions pennylane/pauli/dla/lie_closure.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,9 +63,9 @@ def lie_closure(
A first round of commutators between all elements yields:
>>> qml.commutator(X(0) @ X(1), Z(0))
-2j * (X(1) @ Y(0))
-2j * (Y(0) @ X(1))
>>> qml.commutator(X(0) @ X(1), Z(1))
-2j * (Y(1) @ X(0))
-2j * (X(0) @ Y(1))
A next round of commutators between all elements further yields the new operator ``Y(0) @ Y(1)``.
Expand All @@ -81,9 +81,9 @@ def lie_closure(
[X(1) @ X(0),
Z(0),
Z(1),
-1.0 * (X(1) @ Y(0)),
-1.0 * (Y(1) @ X(0)),
-1.0 * (Y(1) @ Y(0))]
-1.0 * (Y(0) @ X(1)),
-1.0 * (X(0) @ Y(1)),
-1.0 * (Y(0) @ Y(1))]
Note that we normalize by removing the factors of :math:`2i`, though minus signs are left intact.
Expand Down Expand Up @@ -305,10 +305,14 @@ def add(self, other, tol=1e-15):

for ps in other:
# TODO: Potential speed-up by computing the maximal linear independent set for all current basis vectors + other, essentially algorithm1 in https://arxiv.org/abs/1012.5256
self._M, self._pw_to_idx, self._rank, self._num_pw, is_independent = (
self._check_independence(
self._M, ps, self._pw_to_idx, self._rank, self._num_pw, tol
)
(
self._M,
self._pw_to_idx,
self._rank,
self._num_pw,
is_independent,
) = self._check_independence(
self._M, ps, self._pw_to_idx, self._rank, self._num_pw, tol
)
if is_independent:
self._basis.append(ps)
Expand Down
2 changes: 1 addition & 1 deletion pennylane/pauli/dla/structure_constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ def structure_constants(
>>> gens += [Z(i) for i in range(n)]
>>> dla = qml.lie_closure(gens)
>>> print(dla)
[X(1) @ X(0), Z(0), Z(1), -1.0 * (X(1) @ Y(0)), -1.0 * (Y(1) @ X(0)), -1.0 * (Y(1) @ Y(0))]
[X(0) @ X(1), Z(0), Z(1), -1.0 * (Y(0) @ X(1)), -1.0 * (X(0) @ Y(1)), -1.0 * (Y(0) @ Y(1))]
The dimension of the DLA is :math:`d = 6`. Hence, the structure constants have shape ``(6, 6, 6)``.
Expand Down
2 changes: 1 addition & 1 deletion pennylane/qaoa/cycle.py
Original file line number Diff line number Diff line change
Expand Up @@ -347,7 +347,7 @@ def loss_hamiltonian(graph: Union[nx.Graph, rx.PyGraph, rx.PyDiGraph]) -> qml.op
)
>>> import rustworkx as rx
>>> g = rx.generators.directed_mesh_graph(3)
>>> g = rx.generators.directed_mesh_graph(3, [0, 1, 2])
>>> edge_weight_data = {edge: (i + 1) * 0.5 for i, edge in enumerate(sorted(g.edge_list()))}
>>> for k, v in edge_weight_data.items():
g.update_edge(k[0], k[1], {"weight": v})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -833,7 +833,7 @@ def test_trainable_hermitian_warns(self):
expectation value of a Hermitian operator emits a warning if the
parameters to Hermitian are trainable."""

mx = qml.matrix(qml.PauliX(0) @ qml.PauliY(2))
mx = qml.numpy.array(qml.matrix(qml.PauliX(0) @ qml.PauliY(2)))
qs = qml.tape.QuantumScript([], [qml.expval(qml.Hermitian(mx, wires=[0, 2]))])

qs.trainable_params = {0}
Expand Down
13 changes: 12 additions & 1 deletion tests/devices/experimental/test_device_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,9 +71,20 @@ def test_shots(self):
shots_dev = self.MinimalDevice(shots=100)
assert shots_dev.shots == qml.measurements.Shots(100)

with pytest.raises(AttributeError):
with pytest.raises(
AttributeError, match="Shots can no longer be set on a device instance."
):
self.dev.shots = 100 # pylint: disable=attribute-defined-outside-init

def test_getattr_error(self):
"""Test that querying a property that doesn't exist informs about interface change."""

with pytest.raises(
AttributeError,
match=r"You may be looking for a property or method present in the legacy device",
):
_ = self.dev.expand_fn

def test_tracker_set_on_initialization(self):
"""Test that a new tracker instance is initialized with the class."""
assert isinstance(self.dev.tracker, qml.Tracker)
Expand Down
11 changes: 5 additions & 6 deletions tests/devices/test_default_mixed_jax.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@

jax = pytest.importorskip("jax")
jnp = pytest.importorskip("jax.numpy")
config = pytest.importorskip("jax.config")


decorators = [lambda x: x, jax.jit]
Expand Down Expand Up @@ -222,7 +221,7 @@ class TestDtypePreserved:
def test_real_dtype(self, enable_x64, r_dtype, measurement):
"""Test that the user-defined dtype of the device is preserved
for QNodes with real-valued outputs"""
config.config.update("jax_enable_x64", enable_x64)
jax.config.update("jax_enable_x64", enable_x64)
p = jnp.array(0.543)

dev = qml.device("default.mixed", wires=3, r_dtype=r_dtype)
Expand All @@ -243,7 +242,7 @@ def circuit(x):
def test_complex_dtype(self, enable_x64, c_dtype, measurement):
"""Test that the user-defined dtype of the device is preserved
for QNodes with complex-valued outputs"""
config.config.update("jax_enable_x64", enable_x64)
jax.config.update("jax_enable_x64", enable_x64)
p = jnp.array(0.543)

dev = qml.device("default.mixed", wires=3, c_dtype=c_dtype)
Expand Down Expand Up @@ -498,7 +497,7 @@ def circuit(x):
def test_state_differentiability(self, decorator, op, wire_ids, exp_fn, tol):
"""Test that the device state can be differentiated"""
# pylint: disable=too-many-arguments
config.config.update("jax_enable_x64", True)
jax.config.update("jax_enable_x64", True)

dev = qml.device("default.mixed", wires=1)

Expand Down Expand Up @@ -684,7 +683,7 @@ def test_jax_interface_gradient(self, operation, diff_method, tol):
"""Tests that the gradient of an arbitrary U3 gate is correct
using the JAX interface, using a variety of differentiation methods."""
if diff_method == "finite-diff":
config.config.update("jax_enable_x64", True)
jax.config.update("jax_enable_x64", True)

dev = qml.device("default.mixed", wires=1)
state = jnp.array(1j * np.array([1, -1]) / np.sqrt(2))
Expand Down Expand Up @@ -747,7 +746,7 @@ def test_ragged_differentiation(self, dev_name, diff_method, grad_on_execution,
with prob and expval outputs"""

if diff_method == "finite-diff":
config.config.update("jax_enable_x64", True)
jax.config.update("jax_enable_x64", True)

dev = qml.device(dev_name, wires=2)
x = jnp.array(0.543)
Expand Down
4 changes: 2 additions & 2 deletions tests/devices/test_default_qutrit_mixed.py
Original file line number Diff line number Diff line change
Expand Up @@ -1059,9 +1059,9 @@ class TestPRNGKeySeed:

def test_prng_key_as_seed(self):
"""Test that a jax PRNG can be passed as a seed."""
from jax.config import config
import jax

config.update("jax_enable_x64", True)
jax.config.update("jax_enable_x64", True)

from jax import random

Expand Down
11 changes: 10 additions & 1 deletion tests/devices/test_preprocess.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
"""Unit tests for preprocess in devices/qubit."""
import warnings

import pytest

Expand Down Expand Up @@ -137,10 +138,18 @@ def test_no_sampling():

def test_validate_adjoint_trainable_params_obs_warning():
"""Tests warning raised for validate_adjoint_trainable_params with trainable observables."""
tape = qml.tape.QuantumScript([], [qml.expval(2 * qml.PauliX(0))])

params = qml.numpy.array(0.123)
tape = qml.tape.QuantumScript([], [qml.expval(2 * qml.RX(params, wires=0))])
with pytest.warns(UserWarning, match="Differentiating with respect to the input "):
validate_adjoint_trainable_params(tape)

params_non_trainable = qml.numpy.array(0.123, requires_grad=False)
tape = qml.tape.QuantumScript([], [qml.expval(2 * qml.RX(params_non_trainable, wires=0))])
with warnings.catch_warnings():
warnings.simplefilter("error") # assert no warning raised
validate_adjoint_trainable_params(tape)


def test_validate_adjoint_trainable_params_state_prep_error():
"""Tests error raised for validate_adjoint_trainable_params with trainable state-preps."""
Expand Down
4 changes: 2 additions & 2 deletions tests/gradients/core/test_metric_tensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -1146,10 +1146,10 @@ def circuit(*params):
@pytest.mark.parametrize("ansatz, params", zip(fubini_ansatze, fubini_params))
@pytest.mark.parametrize("interface", ["auto", "jax"])
def test_correct_output_jax(self, ansatz, params, interface):
from jax import config
import jax
from jax import numpy as jnp

config.update("jax_enable_x64", True)
jax.config.update("jax_enable_x64", True)

expected = autodiff_metric_tensor(ansatz, self.num_wires)(*params)
dev = qml.device("default.qubit.jax", wires=self.num_wires + 1)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,7 @@

jax = pytest.importorskip("jax")
jnp = pytest.importorskip("jax.numpy")
config = pytest.importorskip("jax.config")
config.config.update("jax_enable_x64", True)
jax.config.update("jax_enable_x64", True)

pytestmark = pytest.mark.jax

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,7 @@
pytestmark = pytest.mark.jax

jax = pytest.importorskip("jax")
config = pytest.importorskip("jax.config")
config.config.update("jax_enable_x64", True)
jax.config.update("jax_enable_x64", True)

TOL_FOR_SPSA = 1.0
SEED_FOR_SPSA = 32651
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,7 @@
pytestmark = pytest.mark.jax

jax = pytest.importorskip("jax")
config = pytest.importorskip("jax.config")
config.config.update("jax_enable_x64", True)
jax.config.update("jax_enable_x64", True)

TOL_FOR_SPSA = 1.0
SEED_FOR_SPSA = 32651
Expand Down
Loading

0 comments on commit 465d337

Please sign in to comment.