Skip to content

Commit

Permalink
Merge branch 'conditional-ops-alpha-sanitized' of github.com:PennyLan…
Browse files Browse the repository at this point in the history
…eAI/pennylane into conditional-ops-alpha-sanitized
  • Loading branch information
antalszava committed Mar 4, 2022
2 parents 481830f + 2b3c09a commit 53ff922
Show file tree
Hide file tree
Showing 9 changed files with 572 additions and 22 deletions.
21 changes: 15 additions & 6 deletions doc/releases/changelog-dev.md
Original file line number Diff line number Diff line change
Expand Up @@ -256,13 +256,19 @@
- A suite of integration tests has been added.
[(#2231)](https://github.com/PennyLaneAI/pennylane/pull/2231)
[(#2234)](https://github.com/PennyLaneAI/pennylane/pull/2234)
[(#2244)](https://github.com/PennyLaneAI/pennylane/pull/2244)
[(#2251)](https://github.com/PennyLaneAI/pennylane/pull/2251)
[(#2265)](https://github.com/PennyLaneAI/pennylane/pull/2265)

Circuit fragments that are disconnected from the terminal measurements are now removed.
[(#2254)](https://github.com/PennyLaneAI/pennylane/pull/2254)
- Circuit fragments that are disconnected from the terminal measurements are now removed.
[(#2254)](https://github.com/PennyLaneAI/pennylane/pull/2254)

`WireCut` operations that do not lead to a disconnection are now being removed.
[(#2260)](https://github.com/PennyLaneAI/pennylane/pull/2260)
- `WireCut` operations that do not lead to a disconnection are now being removed.
[(#2260)](https://github.com/PennyLaneAI/pennylane/pull/2260)

- Circuit cutting now remaps the wires of fragment circuits to match the available wires on the
device.
[(#2257)](https://github.com/PennyLaneAI/pennylane/pull/2257)

<h3>Improvements</h3>

Expand Down Expand Up @@ -312,7 +318,10 @@

<h3>Bug fixes</h3>

* The `qml.QubitUnitary` operation now supports jitting.
* The `qml.RandomLayers` template now decomposes when the weights are a list of lists.
[(#2266)](https://github.com/PennyLaneAI/pennylane/pull/2266/)

* The `qml.QubitUnitary` operation now supports jitting.
[(#2249)](https://github.com/PennyLaneAI/pennylane/pull/2249)

* Fixes a bug in the JAX interface where ``DeviceArray`` objects
Expand Down Expand Up @@ -482,7 +491,7 @@ The Operator class has undergone a major refactor with the following changes:

This release contains contributions from (in alphabetical order):

Sam Banning, Thomas Bromley, Anthony Hayes, Josh Izaac, Christina Lee,
Sam Banning, Thomas Bromley, Anthony Hayes, Josh Izaac, Christina Lee, Angus Lowe,
Maria Fernanda Morris, Romain Moyard, Zeyue Niu, Maria Schuld, Jay Soni,
Antal Száva, David Wierichs

4 changes: 3 additions & 1 deletion pennylane/operation.py
Original file line number Diff line number Diff line change
Expand Up @@ -2390,7 +2390,9 @@ def operation_derivative(operation) -> np.ndarray:
ValueError: if the operation does not have a generator or is not composed of a single
trainable parameter
"""
generator = qml.matrix(qml.generator(operation, format="observable"))
generator = qml.matrix(
qml.generator(operation, format="observable"), wire_order=operation.wires
)
return 1j * generator @ operation.get_matrix()


Expand Down
2 changes: 1 addition & 1 deletion pennylane/templates/layers/random.py
Original file line number Diff line number Diff line change
Expand Up @@ -247,7 +247,7 @@ def compute_decomposition(
# apply a random rotation gate to a random wire
gate = np.random.choice(rotations)
rnd_wire = wires.select_random(1)
op_list.append(gate(weights[l, i], wires=rnd_wire))
op_list.append(gate(weights[l][i], wires=rnd_wire))
i += 1

else:
Expand Down
2 changes: 2 additions & 0 deletions pennylane/transforms/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@
~transforms.expand_fragment_tapes
~transforms.contract_tensors
~transforms.qcut_processing_fn
~transforms.remap_tape_wires
~transforms.CutStrategy
Transforms that act on tapes
Expand Down Expand Up @@ -212,5 +213,6 @@
contract_tensors,
qcut_processing_fn,
cut_circuit,
remap_tape_wires,
CutStrategy,
)
4 changes: 2 additions & 2 deletions pennylane/transforms/defer_measurements.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,12 +69,12 @@ def qfunc(par):
>>> qnode = qml.QNode(transformed_qfunc, dev)
>>> par = np.array(np.pi/2, requires_grad=True)
>>> qnode(par)
tensor(0.43487747, requires_grad=True)
tensor(-0.12269009, requires_grad=True)
We can also differentiate parameters passed to conditional operations:
>>> qml.grad(qnode)(par)
-0.4962225160675968
-0.9924450321351936
.. note::
Expand Down
92 changes: 87 additions & 5 deletions pennylane/transforms/qcut.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
from dataclasses import InitVar, dataclass
from functools import partial
from itertools import product
from typing import Any, Callable, ClassVar, Dict, List, Sequence, Tuple, Union
from typing import Any, Callable, ClassVar, Dict, List, Optional, Sequence, Tuple, Union

from networkx import MultiDiGraph, weakly_connected_components, has_path

Expand Down Expand Up @@ -848,7 +848,7 @@ def _to_tensors(

ctr += dim

if len(results) != ctr:
if results.shape[0] != ctr:
raise ValueError(f"The results argument should be a flat list of length {ctr}")

return tensors
Expand Down Expand Up @@ -902,7 +902,7 @@ def qcut_processing_fn(

@batch_transform
def cut_circuit(
tape: QuantumTape, use_opt_einsum: bool = False
tape: QuantumTape, use_opt_einsum: bool = False, device_wires: Optional[Wires] = None
) -> Tuple[Tuple[QuantumTape], Callable]:
"""
Batch transform for circuit cutting.
Expand All @@ -919,10 +919,12 @@ def cut_circuit(
for faster tensor contractions of large networks but must be installed separately using,
e.g., ``pip install opt_einsum``. Both settings for ``use_opt_einsum`` result in a
differentiable contraction.
device_wires (.wires.Wires): Wires of the device that the cut circuits are to be run on
Returns:
Tuple[Tuple[QuantumTape], Callable]: the tapes corresponding to the circuit fragments as a result of cutting
and a post-processing function which combines the results via tensor contractions.
Tuple[Tuple[QuantumTape], Callable]: the tapes corresponding to the circuit fragments as a
result of cutting and a post-processing function which combines the results via tensor
contractions.
**Example**
Expand Down Expand Up @@ -992,6 +994,7 @@ def circuit(x):
replace_wire_cut_nodes(g)
fragments, communication_graph = fragment_graph(g)
fragment_tapes = [graph_to_tape(f) for f in fragments]
fragment_tapes = [remap_tape_wires(t, device_wires) for t in fragment_tapes]
expanded = [expand_fragment_tapes(t) for t in fragment_tapes]

configurations = []
Expand All @@ -1013,6 +1016,85 @@ def circuit(x):
)


@cut_circuit.custom_qnode_wrapper
def qnode_execution_wrapper(self, qnode, targs, tkwargs):
"""Here, we overwrite the QNode execution wrapper in order
to access the device wires."""

tkwargs.setdefault("device_wires", qnode.device.wires)
return self.default_qnode_wrapper(qnode, targs, tkwargs)


def remap_tape_wires(tape: QuantumTape, wires: Sequence) -> QuantumTape:
"""Map the wires of a tape to a new set of wires.
Given an :math:`n`-wire ``tape``, this function returns a new :class:`~.QuantumTape` with
operations and measurements acting on the first :math:`n` wires provided in the ``wires``
argument. The input ``tape`` is left unmodified.
.. note::
This function is designed for use as part of the circuit cutting workflow. Check out the
:doc:`transforms </code/qml_transforms>` page for more details.
Args:
tape (QuantumTape): the quantum tape whose wires should be remapped
wires (Sequence): the new set of wires to map to
Returns:
QuantumTape: A remapped copy of the input tape
Raises:
ValueError: if the number of wires in ``tape`` exceeds ``len(wires)``
**Example**
.. code-block:: python
with qml.tape.QuantumTape() as tape:
qml.RX(0.5, wires=2)
qml.RY(0.6, wires=3)
qml.CNOT(wires=[2, 3])
qml.expval(qml.PauliZ(2) @ qml.PauliZ(3))
new_wires = [0, 1]
new_tape = qml.transforms.remap_tape_wires(tape, new_wires)
>>> print(new_tape.draw())
0: ──RX(0.5)──╭C──╭┤ ⟨Z ⊗ Z⟩
1: ──RY(0.6)──╰X──╰┤ ⟨Z ⊗ Z⟩
"""
if len(tape.wires) > len(wires):
raise ValueError(
f"Attempting to run a {len(tape.wires)}-wire circuit on a "
f"{len(wires)}-wire device. Consider increasing the number of wires in "
f"your device."
)

wire_map = dict(zip(tape.wires, wires))
copy_ops = [copy.copy(op) for op in tape.operations]
copy_meas = [copy.copy(op) for op in tape.measurements]

with QuantumTape() as new_tape:
for op in copy_ops:
new_wires = Wires([wire_map[w] for w in op.wires])
op._wires = new_wires
apply(op)
for meas in copy_meas:
obs = meas.obs

if isinstance(obs, Tensor):
for obs in obs.obs:
new_wires = Wires([wire_map[w] for w in obs.wires])
obs._wires = new_wires
else:
new_wires = Wires([wire_map[w] for w in obs.wires])
obs._wires = new_wires
apply(meas)

return new_tape


@dataclass()
class CutStrategy:
"""
Expand Down
21 changes: 20 additions & 1 deletion tests/templates/test_layers/test_random.py
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ def circuit_template(weights):
def circuit_decomposed(weights):
# this structure is only true for a seed of 42 and 3 wires
qml.RX(weights[0, 0], wires=1)
qml.RX(weights[0, 1], wires=0)
qml.RX(weights[0][1], wires=0)
qml.CNOT(wires=[1, 0])
qml.RZ(weights[0, 2], wires=2)
return qml.expval(qml.PauliZ(0))
Expand All @@ -161,6 +161,25 @@ class TestInterfaces:
"""Tests that the template is compatible with all interfaces, including the computation
of gradients."""

def test_list_lists(self):
"""Tests the weights as a list of lists."""
weights = [[0.1, -2.1, 1.4]]

op = qml.RandomLayers(weights, wires=range(3), seed=42)

decomp = op.decomposition()
expected = [
qml.RX(weights[0][0], wires=1),
qml.RX(weights[0][1], wires=0),
qml.CNOT(wires=[1, 0]),
qml.RZ(weights[0][2], wires=2),
]

for op1, op2 in zip(decomp, expected):
assert op1.name == op2.name
assert op1.data == op2.data
assert op1.wires == op2.wires

def test_autograd(self, tol):
"""Tests the autograd interface."""

Expand Down
19 changes: 19 additions & 0 deletions tests/test_operation.py
Original file line number Diff line number Diff line change
Expand Up @@ -1390,6 +1390,25 @@ def test_cry(self):
)
assert np.allclose(derivative, expected_derivative)

def test_cry_non_consecutive(self):
"""Test if the function correctly returns the derivative of CRY
if the wires are not consecutive. This is expected behaviour, since
without any other context, the operation derivative should make no
assumption about the wire ordering."""
p = 0.3
op = qml.CRY(p, wires=[1, 0])

derivative = operation_derivative(op)
expected_derivative = 0.5 * np.array(
[
[0, 0, 0, 0],
[0, 0, 0, 0],
[0, 0, -np.sin(p / 2), -np.cos(p / 2)],
[0, 0, np.cos(p / 2), -np.sin(p / 2)],
]
)
assert np.allclose(derivative, expected_derivative)


class TestCVOperation:
"""Test the CVOperation class"""
Expand Down
Loading

0 comments on commit 53ff922

Please sign in to comment.