diff --git a/doc/development/adding_operators.rst b/doc/development/adding_operators.rst
index 3d7b39aa08d..7a84666da66 100644
--- a/doc/development/adding_operators.rst
+++ b/doc/development/adding_operators.rst
@@ -70,25 +70,35 @@ The basic components of operators are the following:
>>> op.terms()
((1.0, 2.0), [PauliX(wires=[0]), PauliZ(wires=[0])])
- * Representation via the **eigenvalue decomposition** specified by eigenvalues (for the diagonal matrix, :meth:`.Operator.eigvals`)
+ * Representation via the **eigenvalue decomposition** specified by eigenvalues (for the diagonal matrix, :meth:`.Operator.get_eigvals`)
and diagonalizing gates (for the unitaries :meth:`.Operator.diagonalizing_gates`):
>>> op = qml.PauliX(0)
>>> op.diagonalizing_gates()
[Hadamard(wires=[0])]
- >>> op.eigvals()
+ >>> op.get_eigvals()
[ 1 -1]
- * Representation as a **matrix** (:meth:`.Operator.matrix`), as specified by a global wire order that tells us where the
+ .. note::
+
+ The :meth:`.Operator.get_eigvals` method is temporary and will be renamed to :meth:`.Operator.eigvals` in an
+ upcoming release. It is recommended to use the higher-level :func:`~.eigvals` function where possible.
+
+ * Representation as a **matrix** (:meth:`.Operator.get_matrix`), as specified by a global wire order that tells us where the
wires are found on a register:
>>> op = qml.PauliRot(0.2, "X", wires=["b"])
- >>> op.matrix(wire_order=["a", "b"])
+ >>> op.get_matrix(wire_order=["a", "b"])
[[9.95e-01-2.26e-18j 2.72e-17-9.98e-02j, 0+0j, 0+0j]
[2.72e-17-9.98e-02j 9.95e-01-2.26e-18j, 0+0j, 0+0j]
[0+0j, 0+0j, 9.95e-01-2.26e-18j 2.72e-17-9.98e-02j]
[0+0j, 0+0j, 2.72e-17-9.98e-02j 9.95e-01-2.26e-18j]]
+ .. note::
+
+ The :meth:`.Operator.get_matrix` method is temporary and will be renamed to :meth:`.Operator.matrix` in an
+ upcoming release. It is recommended to use the higher-level :func:`~.matrix` function where possible.
+
* Representation as a **sparse matrix** (:meth:`.Operator.sparse_matrix`):
>>> from scipy.sparse.coo import coo_matrix
@@ -214,7 +224,7 @@ whether it supports operations using the operation name.
- If the device registers support for an operation with the same name,
PennyLane leaves the gate implementation up to the device. The device
might have a hardcoded implementation, *or* it may refer to one of the
- numerical representations of the operator (such as :meth:`.Operator.matrix`).
+ numerical representations of the operator (such as :meth:`.Operator.get_matrix`).
- If the device does not register support for an operation with the same
name, PennyLane will automatically decompose the gate using :meth:`.Operator.decomposition`.
diff --git a/doc/development/guide/architecture.rst b/doc/development/guide/architecture.rst
index f2ec97da413..a4f45f234b0 100644
--- a/doc/development/guide/architecture.rst
+++ b/doc/development/guide/architecture.rst
@@ -88,10 +88,16 @@ details in the documentation on :doc:`adding operations >> op = qml.PauliRot(0.2, "X", wires=["b"])
- >>> op.matrix()
+ >>> op.get_matrix()
[[9.95004177e-01-2.25761781e-18j 2.72169462e-17-9.98334214e-02j]
[2.72169462e-17-9.98334214e-02j 9.95004177e-01-2.25761781e-18j]]
+.. note::
+
+ The ``op.get_matrix()`` method is temporary and will be renamed to ``op.matrix()`` in an
+ upcoming release. Where possible it is recommended to use higher-level functions such as
+ :func:`~.matrix`.
+
Devices query operators for their properties and representations to
gain information on how to implement the operator.
diff --git a/doc/introduction/measurements.rst b/doc/introduction/measurements.rst
index 4ee40ae031e..9c07dda65ca 100644
--- a/doc/introduction/measurements.rst
+++ b/doc/introduction/measurements.rst
@@ -158,8 +158,8 @@ Mid-circuit measurements and conditional operations
---------------------------------------------------
PennyLane allows specifying measurements in the middle of the circuit.
-Operations can then be conditioned on the measurement outcome of such
-mid-circuit measurements:
+Quantum functions such as operations can then be conditioned on the measurement
+outcome of such mid-circuit measurements:
.. code-block:: python
@@ -180,7 +180,7 @@ measurement on qubit 1 yielded ``1`` as an outcome, otherwise doing nothing
for the ``0`` measurement outcome.
PennyLane implements the deferred measurement principle to transform
-conditional operations with the :func:`defer_measurements` quantum
+conditional operations with the :func:`~.defer_measurements` quantum
function transform.
.. code-block:: python
@@ -226,6 +226,10 @@ differentiable and device-independent way. Performing true mid-circuit
measurements and conditional operations is dependent on the
quantum hardware and PennyLane device capabilities.
+For more examples on applying quantum functions conditionally, refer to the
+:func:`~.pennylane.cond` transform.
+
+
Changing the number of shots
----------------------------
diff --git a/doc/releases/changelog-0.21.0.md b/doc/releases/changelog-0.21.0.md
index c26d1d58836..9defcc03e61 100644
--- a/doc/releases/changelog-0.21.0.md
+++ b/doc/releases/changelog-0.21.0.md
@@ -660,7 +660,7 @@
This release contains contributions from (in alphabetical order):
Juan Miguel Arrazola, Ali Asadi, Utkarsh Azad, Sam Banning, Thomas Bromley,
-Esther Cruz, Christian Gogolin, Nathan Killoran, Christina Lee, Olivia Di
-Matteo, Diego Guala, Anthony Hayes, David Ittah, Josh Izaac, Soran Jahangiri,
-Edward Jiang, Ankit Khandelwal, Korbinian Kottmann, Romain Moyard, Lee James
+Esther Cruz, Olivia Di Matteo, Christian Gogolin, Diego Guala, Anthony Hayes,
+David Ittah, Josh Izaac, Soran Jahangiri, Edward Jiang, Ankit Khandelwal,
+Nathan Killoran, Korbinian Kottmann, Christina Lee, Romain Moyard, Lee James
O'Riordan, Maria Schuld, Jay Soni, Antal SzΓ‘va, David Wierichs, Shaoming Zhang.
diff --git a/doc/releases/changelog-0.22.0.md b/doc/releases/changelog-0.22.0.md
index 7d1f9e1b1fd..1d6df9aa68d 100644
--- a/doc/releases/changelog-0.22.0.md
+++ b/doc/releases/changelog-0.22.0.md
@@ -4,16 +4,208 @@
New features since last release
-* A new operation `qml.Snapshot` has been added to assist users in debugging quantum progams.
- The instruction is used to save the internal state of simulator devices at arbitrary points of
- execution, such as the quantum state vector and density matrix in the qubit case, or the
- covariance matrix and vector of means in the continuous variable case. The saved states can be
- retrieved in the form of a dictionary via the top-level `qml.snapshots` function.
+Quantum circuit cutting βοΈ
+
+* You can now run `N`-wire circuits on devices with fewer than `N` wires, by
+ strategically placing `WireCut` operations that allow their circuit to be
+ partitioned into smaller fragments, at a cost of needing to perform a greater
+ number of device executions. Circuit cutting is enabled by decorating a QNode
+ with the `@qml.cut_circuit` transform.
+ [(#2107)](https://github.com/PennyLaneAI/pennylane/pull/2107)
+ [(#2124)](https://github.com/PennyLaneAI/pennylane/pull/2124)
+ [(#2153)](https://github.com/PennyLaneAI/pennylane/pull/2153)
+ [(#2165)](https://github.com/PennyLaneAI/pennylane/pull/2165)
+ [(#2158)](https://github.com/PennyLaneAI/pennylane/pull/2158)
+ [(#2169)](https://github.com/PennyLaneAI/pennylane/pull/2169)
+ [(#2192)](https://github.com/PennyLaneAI/pennylane/pull/2192)
+ [(#2216)](https://github.com/PennyLaneAI/pennylane/pull/2216)
+ [(#2168)](https://github.com/PennyLaneAI/pennylane/pull/2168)
+ [(#2223)](https://github.com/PennyLaneAI/pennylane/pull/2223)
+ [(#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)
+ [(#2254)](https://github.com/PennyLaneAI/pennylane/pull/2254)
+ [(#2260)](https://github.com/PennyLaneAI/pennylane/pull/2260)
+ [(#2257)](https://github.com/PennyLaneAI/pennylane/pull/2257)
+ [(#2279)](https://github.com/PennyLaneAI/pennylane/pull/2279)
+
+ The example below shows how a three-wire circuit can be run on a two-wire device:
+
+ ```python
+ dev = qml.device("default.qubit", wires=2)
+
+ @qml.cut_circuit
+ @qml.qnode(dev)
+ def circuit(x):
+ qml.RX(x, wires=0)
+ qml.RY(0.9, wires=1)
+ qml.RX(0.3, wires=2)
+
+ qml.CZ(wires=[0, 1])
+ qml.RY(-0.4, wires=0)
+
+ qml.WireCut(wires=1)
+
+ qml.CZ(wires=[1, 2])
+
+ return qml.expval(qml.grouping.string_to_pauli_word("ZZZ"))
+ ```
+
+ Instead of executing the circuit directly, it will be partitioned into smaller fragments
+ according to the `WireCut` locations, and each fragment executed multiple times. Combining the
+ results of the fragment executions will recover the expected output of the original uncut circuit.
+
+ ```pycon
+ >>> x = np.array(0.531, requires_grad=True)
+ >>> circuit(0.531)
+ 0.47165198882111165
+ ```
+
+ Circuit cutting support is also differentiable:
+
+ ```pycon
+ >>> qml.grad(circuit)(x)
+ -0.276982865449393
+ ```
+
+ For more details on circuit cutting, check out the
+ [qml.cut_circuit](https://pennylane.readthedocs.io/en/latest/code/api/pennylane.cut_circuit.html)
+ documentation page or [Peng et. al](https://arxiv.org/abs/1904.00102).
+
+Conditional operations: quantum teleportation unlocked ππ
+
+* Support for mid-circuit measurements and conditional operations
+ has been added, to enable use cases like quantum teleportation, quantum error
+ correction and quantum error mitigation.
+ [(#2211)](https://github.com/PennyLaneAI/pennylane/pull/2211)
+ [(#2236)](https://github.com/PennyLaneAI/pennylane/pull/2236)
+ [(#2275)](https://github.com/PennyLaneAI/pennylane/pull/2275)
+
+ Two new functions have been added to support this capability:
+
+ - `qml.measure()` places mid-circuit measurements in the middle of a quantum function.
+
+ - `qml.cond()` allows operations and quantum functions to be conditioned on the result of a
+ previous measurement.
+
+ For example, the code below shows how to teleport a qubit from wire 0 to wire 2:
+
+ ```python
+ dev = qml.device("default.qubit", wires=3)
+ input_state = np.array([1, -1], requires_grad=False) / np.sqrt(2)
+
+ @qml.qnode(dev)
+ def teleport(state):
+ # Prepare input state
+ qml.QubitStateVector(state, wires=0)
+
+ # Prepare Bell state
+ qml.Hadamard(wires=1)
+ qml.CNOT(wires=[1, 2])
+
+ # Apply gates
+ qml.CNOT(wires=[0, 1])
+ qml.Hadamard(wires=0)
+
+ # Measure first two wires
+ m1 = qml.measure(0)
+ m2 = qml.measure(1)
+
+ # Condition final wire on results
+ qml.cond(m2 == 1, qml.PauliX)(wires=2)
+ qml.cond(m1 == 1, qml.PauliZ)(wires=2)
+
+ # Return state on final wire
+ return qml.density_matrix(wires=2)
+ ```
+
+ We can double-check that the qubit has been teleported by computing the
+ overlap between the input state and the resulting state on wire 2:
+
+ ```pycon
+ >>> output_state = teleport(input_state)
+ >>> output_state
+ tensor([[ 0.5+0.j, -0.5+0.j],
+ [-0.5+0.j, 0.5+0.j]], requires_grad=True)
+ >>> input_state.conj() @ output_state @ input_state
+ tensor(1.+0.j, requires_grad=True)
+ ```
+
+ For a full description of new capabilities, refer to the [Mid-circuit
+ measurements and conditional
+ operations](https://pennylane.readthedocs.io/en/latest/introduction/measurements.html#mid-circuit-measurements-and-conditional-operations)
+ section in the documentation.
+
+* Train mid-circuit measurements by deferring them, via the new
+ `@qml.defer_measurements` transform.
+ [(#2211)](https://github.com/PennyLaneAI/pennylane/pull/2211)
+ [(#2236)](https://github.com/PennyLaneAI/pennylane/pull/2236)
+ [(#2275)](https://github.com/PennyLaneAI/pennylane/pull/2275)
+
+ If a device doesn't natively support mid-circuit measurements, the `@qml.defer_measurements`
+ transform can be applied to the QNode to transform the QNode into one with _terminal_ measurements
+ and _controlled_ operations:
+
+ ```python
+ dev = qml.device("default.qubit", wires=2)
+
+ @qml.qnode(dev)
+ @qml.defer_measurements
+ def circuit(x):
+ qml.Hadamard(wires=0)
+
+ m = qml.measure(0)
+
+ def op_if_true():
+ return qml.RX(x**2, wires=1)
+
+ def op_if_false():
+ return qml.RY(x, wires=1)
+
+ qml.cond(m==1, op_if_true, op_if_false)()
+
+ return qml.expval(qml.PauliZ(1))
+ ```
+
+ ```pycon
+ >>> x = np.array(0.7, requires_grad=True)
+ >>> print(qml.draw(circuit, expansion_strategy="device")(x))
+ 0: ββHββCβββββββββXββCβββββββββXββ€
+ 1: βββββ°RX(0.49)βββββ°RY(0.70)βββββ€
+ >>> circuit(x)
+ tensor(0.82358752, requires_grad=True)
+ ```
+
+ Deferring mid-circuit measurements also enables differentiation:
+
+ ```pycon
+ >>> qml.grad(circuit)(x)
+ -0.651546965338656
+ ```
+
+Debug with mid-circuit quantum snapshots π·
+
+* A new operation `qml.Snapshot` has been added to assist in debugging quantum functions.
[(#2233)](https://github.com/PennyLaneAI/pennylane/pull/2233)
+ [(#2289)](https://github.com/PennyLaneAI/pennylane/pull/2289)
+ [(#2291)](https://github.com/PennyLaneAI/pennylane/pull/2291)
+ [(#2315)](https://github.com/PennyLaneAI/pennylane/pull/2315)
+
+ `qml.Snapshot` saves the internal state of devices at arbitrary points of execution.
- ```py
+ Currently supported devices include:
+
+ - `default.qubit`: each snapshot saves the quantum state vector
+ - `default.mixed`: each snapshot saves the density matrix
+ - `default.gaussian`: each snapshot saves the covariance matrix and vector of means
+
+ During normal execution, the snapshots are ignored:
+
+ ```python
dev = qml.device("default.qubit", wires=2)
-
+
@qml.qnode(dev, interface=None)
def circuit():
qml.Snapshot()
@@ -24,6 +216,10 @@
return qml.expval(qml.PauliX(0))
```
+ However, when using the `qml.snapshots`
+ transform, intermediate device states will be stored and returned alongside the
+ results.
+
```pycon
>>> qml.snapshots(circuit)()
{0: array([1.+0.j, 0.+0.j, 0.+0.j, 0.+0.j]),
@@ -32,7 +228,41 @@
'execution_results': array(0.)}
```
-* New functions and transforms of operators have been added. These include:
+Batch embedding and state preparation data π¦
+
+* Added the `@qml.batch_input` transform to enable batching non-trainable gate parameters.
+ In addition, the `qml.qnn.KerasLayer` class has been updated to natively support
+ batched training data.
+ [(#2069)](https://github.com/PennyLaneAI/pennylane/pull/2069)
+
+ As with other transforms, `@qml.batch_input` can be used to decorate QNodes:
+ ```python
+ dev = qml.device("default.qubit", wires=2, shots=None)
+
+ @qml.batch_input(argnum=0)
+ @qml.qnode(dev, diff_method="parameter-shift", interface="tf")
+ def circuit(inputs, weights):
+ # add a batch dimension to the embedding data
+ qml.AngleEmbedding(inputs, wires=range(2), rotation="Y")
+ qml.RY(weights[0], wires=0)
+ qml.RY(weights[1], wires=1)
+ return qml.expval(qml.PauliZ(1))
+ ```
+
+ Batched input parameters can then be passed during QNode evaluation:
+
+ ```pycon
+ >>> x = tf.random.uniform((10, 2), 0, 1)
+ >>> w = tf.random.uniform((2,), 0, 1)
+ >>> circuit(x, w)
+
+ ```
+
+Even more mighty quantum transforms πβ‘π¦
+
+* New functions and transforms of operators have been added:
- `qml.matrix()` for computing the matrix representation of one or more unitary operators.
[(#2241)](https://github.com/PennyLaneAI/pennylane/pull/2241)
@@ -57,7 +287,7 @@
```pycon
>>> x = torch.tensor(0.6, requires_grad=True)
>>> matrix_fn = qml.matrix(qml.RX)
- >>> matrix_fn(x)
+ >>> matrix_fn(x, wires=[0])
tensor([[0.9553+0.0000j, 0.0000-0.2955j],
[0.0000-0.2955j, 0.9553+0.0000j]], grad_fn=)
```
@@ -68,7 +298,7 @@
>>> loss = torch.real(torch.trace(matrix_fn(x, wires=0)))
>>> loss.backward()
>>> x.grad
- tensor(-0.5910)
+ tensor(-0.2955)
```
Some operator transform can also act on multiple operations, by passing
@@ -85,62 +315,6 @@
[ 0.+0.j, 0.+0.38268343j, 0.+0.j, -0.92387953+0.j]])
```
-* The user-interface for mid-circuit measurements and conditional operations
- has been added to support use cases like quantum teleportation.
- [(#2211)](https://github.com/PennyLaneAI/pennylane/pull/2211)
- [(#2236)](https://github.com/PennyLaneAI/pennylane/pull/2236)
- [(#2275)](https://github.com/PennyLaneAI/pennylane/pull/2275)
-
- The addition includes the `defer_measurements` device-independent transform
- that can be applied on devices that have no native mid-circuit measurements
- capabilities. This transform is applied by default when evaluating a QNode on a
- device that doesn't support mid-circuit measurements.
-
- For example, the code below shows how to teleport a qubit:
-
- ```python
- from scipy.stats import unitary_group
-
- random_state = unitary_group.rvs(2, random_state=1967)[0]
-
- dev = qml.device("default.mixed", wires=3)
-
- @qml.qnode(dev)
- def teleport(state):
- # Prepare input state
- qml.QubitStateVector(state, wires=0)
-
- # Prepare Bell state
- qml.Hadamard(wires=1)
- qml.CNOT(wires=[1, 2])
-
- # Apply gates
- qml.CNOT(wires=[0, 1])
- qml.Hadamard(wires=0)
-
- # Measure first two wires
- m1 = qml.measure(0)
- m2 = qml.measure(1)
-
- # Condition final wire on results
- qml.cond(m2 == 1, qml.PauliX)(wires=2)
- qml.cond(m1 == 1, qml.PauliZ)(wires=2)
-
- # Return state on final wire
- return qml.density_matrix(wires=2)
-
- output_projector = teleport(random_state)
- overlap = random_state.conj() @ output_projector @ random_state
- ```
- ```pycon
- >>> overlap
- tensor(1.+0.j, requires_grad=True)
- ```
-
- For further examples, refer to the [Mid-circuit measurements and conditional
- operations](https://pennylane.readthedocs.io/en/latest/introduction/measurements.html#mid-circuit-measurements-and-conditional-operations)
- section in the documentation.
-
* A new transform has been added to construct the pairwise-commutation directed acyclic graph (DAG)
representation of a quantum circuit.
[(#1712)](https://github.com/PennyLaneAI/pennylane/pull/1712)
@@ -161,7 +335,7 @@
... qml.CRZ(z, wires=[2, 0])
... qml.RY(-y, wires=1)
... return qml.expval(qml.PauliZ(0))
- >>> dag_fn = commutation_dag(circuit)
+ >>> dag_fn = qml.commutation_dag(circuit)
>>> dag = dag_fn(np.pi / 4, np.pi / 3, np.pi / 2)
```
@@ -169,8 +343,9 @@
the form `(ID, CommutationDAGNode)`:
```pycon
- nodes = dag.get_nodes()
- [(0, ), ...]
+ >>> nodes = dag.get_nodes()
+ >>> nodes
+ NodeDataView({0: , ...}, data='node')
```
Specific nodes in the commutation DAG can be accessed via the `get_node()` method:
@@ -187,7 +362,9 @@
[]
```
-* The text based drawer accessed via `qml.draw` has been overhauled.
+Improvements
+
+* The text-based drawer accessed via `qml.draw()` has been optimized and improved.
[(#2128)](https://github.com/PennyLaneAI/pennylane/pull/2128)
[(#2198)](https://github.com/PennyLaneAI/pennylane/pull/2198)
@@ -215,101 +392,41 @@
1: βββββ°RX(2.30)ββRot(1.20,3.20,0.70)ββ°RX(-2.30)ββ€ β°
```
-* Parametric operations now have the `parameter_frequencies`
- method that returns the frequencies with which a parameter
- enters a circuit. In addition, the general parameter-shift
- rule is now automatically used by `qml.gradients.param_shift`.
+* The frequencies of gate parameters are now accessible as an operation
+ property and can be used for circuit analysis, optimization via the
+ `RotosolveOptimizer` and differentiation with the parameter-shift rule
+ (including the general shift rule).
[(#2180)](https://github.com/PennyLaneAI/pennylane/pull/2180)
[(#2182)](https://github.com/PennyLaneAI/pennylane/pull/2182)
[(#2227)](https://github.com/PennyLaneAI/pennylane/pull/2227)
- The frequencies can be used for circuit analysis, optimization
- via the `RotosolveOptimizer` and differentiation with the
- parameter-shift rule. They assume that the circuit returns
- expectation values or probabilities, for a variance
- measurement the frequencies will differ.
-
- By default, the frequencies will be obtained from the
- `generator` property (if it is defined).
-
- When using `qml.gradients.param_shift`, either a custom `grad_recipe`
- or the parameter frequencies are used to obtain the shift rule
- for the operation, in that order of preference.
-
- See [Vidal and Theis (2018)](https://arxiv.org/abs/1812.06323)
- and [Wierichs et al. (2021)](https://arxiv.org/abs/2107.12390)
- for theoretical background information on the general
- parameter-shift rule.
-
-* Continued development of the circuit-cutting compiler:
-
- - A method for converting a quantum tape to a directed multigraph that is amenable
- to graph partitioning algorithms for circuit cutting has been added.
- [(#2107)](https://github.com/PennyLaneAI/pennylane/pull/2107)
-
- - A method to replace `WireCut` nodes in a directed multigraph with `MeasureNode`
- and `PrepareNode` placeholders has been added.
- [(#2124)](https://github.com/PennyLaneAI/pennylane/pull/2124)
-
- - A method has been added that takes a directed multigraph with `MeasureNode` and
- `PrepareNode` placeholders and fragments into subgraphs and a communication graph.
- [(#2153)](https://github.com/PennyLaneAI/pennylane/pull/2153)
-
- - A method has been added that takes a directed multigraph with `MeasureNode`
- and `PrepareNode` placeholder nodes and converts it into a tape.
- [(#2165)](https://github.com/PennyLaneAI/pennylane/pull/2165)
-
- - A differentiable tensor contraction function `contract_tensors` has been
- added.
- [(#2158)](https://github.com/PennyLaneAI/pennylane/pull/2158)
-
- - A method has been added that expands a quantum tape over `MeasureNode` and `PrepareNode`
- configurations.
- [(#2169)](https://github.com/PennyLaneAI/pennylane/pull/2169)
-
- - The postprocessing function for the `cut_circuit` transform has been added.
- [(#2192)](https://github.com/PennyLaneAI/pennylane/pull/2192)
-
- - The `cut_circuit` transform has been added.
- [(#2216)](https://github.com/PennyLaneAI/pennylane/pull/2216)
-
- - A class `CutStrategy` which acts as an interface and coordinates device/user
- constraints with circuit execution requirements to come up with the best sets
- of graph partitioning parameters.
- [(#2168)](https://github.com/PennyLaneAI/pennylane/pull/2168)
-
- - 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)
-
- - `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)
+ ```pycon
+ >>> op = qml.CRot(0.4, 0.1, 0.3, wires=[0, 1])
+ >>> op.parameter_frequencies
+ [(0.5, 1.0), (0.5, 1.0), (0.5, 1.0)]
+ ```
-Improvements
+ When using `qml.gradients.param_shift`, either a custom `grad_recipe` or the
+ parameter frequencies are used to obtain the shift rule for the operation, in
+ that order of preference.
-* Most compilation transforms, and relevant subroutines, have been updated to
- support just-in-time compilation with `jax.jit`.
- [(#1894)](https://github.com/PennyLaneAI/pennylane/pull/1894/)
+ See [Vidal and Theis (2018)](https://arxiv.org/abs/1812.06323) and [Wierichs
+ et al. (2021)](https://arxiv.org/abs/2107.12390) for theoretical background
+ information on the general parameter-shift rule.
* No two-term parameter-shift rule is assumed anymore by default.
[(#2227)](https://github.com/PennyLaneAI/pennylane/pull/2227)
Previously, operations marked for analytic differentiation that
- do not provide a `generator`, `parameter_frequencies` or a
+ did not provide a `generator`, `parameter_frequencies` or a
custom `grad_recipe` were assumed to satisfy the two-term shift
rule. This now has to be made explicit for custom operations
by adding any of the above attributes.
+* Most compilation transforms, and relevant subroutines, have been updated to
+ support just-in-time compilation with `jax.jit`.
+ [(#1894)](https://github.com/PennyLaneAI/pennylane/pull/1894/)
+
* The `qml.draw_mpl` transform supports a `expansion_strategy` keyword argument.
[(#2271)](https://github.com/PennyLaneAI/pennylane/pull/2271/)
@@ -322,61 +439,180 @@
qubit-wise commuting terms.
[(#2185)](https://github.com/PennyLaneAI/pennylane/pull/2185)
- ```pycon
- >>> qml.grouping.partition_pauli_group(2)
- [['II', 'IZ', 'ZI', 'ZZ'],
- ['IX', 'ZX'],
- ['IY', 'ZY'],
- ['XI', 'XZ'],
- ['XX'],
- ['XY'],
- ['YI', 'YZ'],
- ['YX'],
- ['YY']]
- ```
+* The Operator class has undergone a major refactor with the following changes:
+
+ * **Matrices**: the static method `Operator.compute_matrices()` defines the matrix representation
+ of the operator, and the function `qml.matrix(op)` computes this for a given
+ instance.
+ [(#1996)](https://github.com/PennyLaneAI/pennylane/pull/1996)
+
+ * **Eigvals**: the static method `Operator.compute_eigvals()` defines the matrix representation
+ of the operator, and the function `qml.eigvals(op)` computes this for a given
+ instance.
+ [(#2048)](https://github.com/PennyLaneAI/pennylane/pull/2048)
+
+ * **Decompositions**: the static method `Operator.compute_decomposition()` defines the matrix representation
+ of the operator, and the method `op.decomposition()` computes this for a given
+ instance.
+ [(#2024)](https://github.com/PennyLaneAI/pennylane/pull/2024)
+ [(#2053)](https://github.com/PennyLaneAI/pennylane/pull/2053)
+
+ * **Sparse matrices**: the static method `Operator.compute_sparse_matrix()` defines the sparse
+ matrix representation of the operator, and the method `op.sparse_matrix()` computes this for a
+ given instance.
+ [(#2050)](https://github.com/PennyLaneAI/pennylane/pull/2050)
+
+ * **Linear combinations of operators**: The static method `compute_terms()`, used for representing
+ the linear combination of coefficients and operators representing the operator, has been added.
+ The method `op.terms()` computes this for a given instance.
+ Currently, only the `Hamiltonian` class overwrites `compute_terms()` to store
+ coefficients and operators. The `Hamiltonian.terms` property hence becomes
+ a proper method called by `Hamiltonian.terms()`.
+ [(#2036)](https://github.com/PennyLaneAI/pennylane/pull/2036)
+
+ * **Diagonalization**: The `diagonalizing_gates()` representation has been moved to the
+ highest-level `Operator` class and is therefore available to all subclasses. A condition
+ `qml.operation.defines_diagonalizing_gates` has been added, which can be used in tape contexts
+ without queueing. In addition, a static `compute_diagonalizing_gates` method has been added,
+ which is called by default in `diagonalizing_gates()`.
+ [(#1985)](https://github.com/PennyLaneAI/pennylane/pull/1985)
+ [(#1993)](https://github.com/PennyLaneAI/pennylane/pull/1993)
+
+ * Error handling has been improved for Operator representations. Custom errors subclassing
+ `OperatorPropertyUndefined` are raised if a representation has not been defined. This replaces
+ the `NotImplementedError` and allows finer control for developers.
+ [(#2064)](https://github.com/PennyLaneAI/pennylane/pull/2064)
+ [(#2287)](https://github.com/PennyLaneAI/pennylane/pull/2287/)
+
+ * A `Operator.hyperparameters` attribute, used for storing operation parameters that are *never*
+ trainable, has been added to the operator class.
+ [(#2017)](https://github.com/PennyLaneAI/pennylane/pull/2017)
+
+ * The `string_for_inverse` attribute is removed.
+ [(#2021)](https://github.com/PennyLaneAI/pennylane/pull/2021)
+
+ * The `expand()` method was moved from the `Operation` class to the main `Operator` class.
+ [(#2053)](https://github.com/PennyLaneAI/pennylane/pull/2053)
+ [(#2239)](https://github.com/PennyLaneAI/pennylane/pull/2239)
-Breaking changes
+Deprecations
-* The `MultiControlledX` operation now accepts a single `wires` keyword argument for both `control_wires` and `wires`.
- The single `wires` keyword should be all the control wires followed by a single target wire.
- [(#2121)](https://github.com/PennyLaneAI/pennylane/pull/2121)
- [(#2278)](https://github.com/PennyLaneAI/pennylane/pull/2278)
+* There are several important changes when creating custom operations:
+ [(#2214)](https://github.com/PennyLaneAI/pennylane/pull/2214)
+ [(#2227)](https://github.com/PennyLaneAI/pennylane/pull/2227)
+ [(#2030)](https://github.com/PennyLaneAI/pennylane/pull/2030)
+ [(#2061)](https://github.com/PennyLaneAI/pennylane/pull/2061)
-Deprecations
+ - The `Operator.matrix` method has been deprecated and `Operator.compute_matrix`
+ should be defined instead. Operator matrices should be accessed using `qml.matrix(op)`.
+ If you were previously defining the class method `Operator._matrix()`, this is a a **breaking
+ change** --- please update your operation to instead overwrite `Operator.compute_matrix`.
-* The `qml.operation.Operation.get_parameter_shift` method has been deprecated
- and will be removed in a future release.
- [#2227](https://github.com/PennyLaneAI/pennylane/pull/2227)
+ - The `Operator.decomposition` method has been deprecated and `Operator.compute_decomposition`
+ should be defined instead. Operator decompositions should be accessed using `Operator.decomposition()`.
- Instead, the functionalities for general parameter-shift rules in the
- `qml.gradients` module should be used, together with the operation attributes
- `parameter_frequencies` or `grad_recipe`.
+ - The `Operator.eigvals` method has been deprecated and `Operator.compute_eigvals`
+ should be defined instead. Operator eigenvalues should be accessed using `qml.eigvals(op)`.
-* The `qml.finite_diff()` function has been deprecated and will be removed
- in an upcoming release. Instead,
- `qml.gradients.finite_diff()` can be used to compute purely quantum gradients
- (that is, gradients of tapes or QNode).
- [#2212](https://github.com/PennyLaneAI/pennylane/pull/2212)
+ - The `Operator.generator` property is now a method, and should return an *operator instance*
+ representing the generator. Note that unlike the other representations above, this is a
+ **breaking change**. Operator generators should be accessed using `qml.generator(op)`.
+
+ - The `Operation.get_parameter_shift` method has been deprecated
+ and will be removed in a future release.
+
+ Instead, the functionalities for general parameter-shift rules in the
+ `qml.gradients` module should be used, together with the operation attributes
+ `parameter_frequencies` or `grad_recipe`.
+
+* Executing tapes using `tape.execute(dev)` is deprecated.
+ Please use the `qml.execute([tape], dev)` function instead.
+ [(#2306)](https://github.com/PennyLaneAI/pennylane/pull/2306)
+
+* The subclasses of the quantum tape, including `JacobianTape`, `QubitParamShiftTape`,
+ `CVParamShiftTape`, and `ReversibleTape` are deprecated. Instead of calling
+ `JacobianTape.jacobian()` and `JacobianTape.hessian()`,
+ please use a standard `QuantumTape`, and apply gradient transforms using
+ the `qml.gradients` module.
+ [(#2306)](https://github.com/PennyLaneAI/pennylane/pull/2306)
* `qml.transforms.get_unitary_matrix()` has been deprecated and will be removed
in a future release. For extracting matrices of operations and quantum functions,
please use `qml.matrix()`.
[(#2248)](https://github.com/PennyLaneAI/pennylane/pull/2248)
+* The `qml.finite_diff()` function has been deprecated and will be removed
+ in an upcoming release. Instead,
+ `qml.gradients.finite_diff()` can be used to compute purely quantum gradients
+ (that is, gradients of tapes or QNode).
+ [(#2212)](https://github.com/PennyLaneAI/pennylane/pull/2212)
+
+* The `MultiControlledX` operation now accepts a single `wires` keyword argument for both `control_wires` and `wires`.
+ The single `wires` keyword should be all the control wires followed by a single target wire.
+ [(#2121)](https://github.com/PennyLaneAI/pennylane/pull/2121)
+ [(#2278)](https://github.com/PennyLaneAI/pennylane/pull/2278)
+
+Breaking changes
+
+* The representation of an operator as a matrix has been overhauled.
+ [(#1996)](https://github.com/PennyLaneAI/pennylane/pull/1996)
+
+ The "canonical matrix", which is independent of wires,
+ is now defined in the static method `compute_matrix()` instead of `_matrix`.
+ By default, this method is assumed to take all parameters and non-trainable
+ hyperparameters that define the operation.
+
+ ```pycon
+ >>> qml.RX.compute_matrix(0.5)
+ [[0.96891242+0.j 0. -0.24740396j]
+ [0. -0.24740396j 0.96891242+0.j ]]
+ ```
+
+ If no canonical matrix is specified for a gate, `compute_matrix()`
+ raises a `MatrixUndefinedError`.
+
+* The generator property has been updated to an instance method,
+ `Operator.generator()`. It now returns an instantiated operation,
+ representing the generator of the instantiated operator.
+ [(#2030)](https://github.com/PennyLaneAI/pennylane/pull/2030)
+ [(#2061)](https://github.com/PennyLaneAI/pennylane/pull/2061)
+
+ Various operators have been updated to specify the generator as either
+ an `Observable`, `Tensor`, `Hamiltonian`, `SparseHamiltonian`, or `Hermitian`
+ operator.
+
+ In addition, `qml.generator(operation)` has been added to aid in retrieving
+ generator representations of operators.
+
+* The argument `wires` in `heisenberg_obs`, `heisenberg_expand` and `heisenberg_tr`
+ was renamed to `wire_order` to be consistent with other matrix representations.
+ [(#2051)](https://github.com/PennyLaneAI/pennylane/pull/2051)
+
+* The property `kraus_matrices` has been changed to a method, and `_kraus_matrices` renamed to
+ `compute_kraus_matrices`, which is now a static method.
+ [(#2055)](https://github.com/PennyLaneAI/pennylane/pull/2055)
+
+* The `pennylane.measure` module has been renamed to `pennylane.measurements`.
+ [(#2236)](https://github.com/PennyLaneAI/pennylane/pull/2236)
+
Bug fixes
+* The `basis` property of `qml.SWAP` was set to `"X"`, which is incorrect; it is
+ now set to `None`.
+ [(#2287)](https://github.com/PennyLaneAI/pennylane/pull/2287/)
+
* 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.
+* The `qml.QubitUnitary` operation now supports just-in-time compilation using JAX.
[(#2249)](https://github.com/PennyLaneAI/pennylane/pull/2249)
-* Fixes a bug in the JAX interface where ``DeviceArray`` objects
+* Fixes a bug in the JAX interface where `DeviceArray` objects
were not being converted to NumPy arrays before executing an
external device.
[(#2255)](https://github.com/PennyLaneAI/pennylane/pull/2255)
-* The ``qml.ctrl`` transform now works correctly with gradient transforms
+* The `qml.ctrl` transform now works correctly with gradient transforms
such as the parameter-shift rule.
[(#2238)](https://github.com/PennyLaneAI/pennylane/pull/2238)
@@ -390,9 +626,18 @@
using the parameter-shift rule.
[(#2180)](https://github.com/PennyLaneAI/pennylane/pull/2180)
+* Fixes a bug where `qml.gradients.param_shift_hessian` would produce an
+ error whenever all elements of the Hessian are known in advance to be 0.
+ [(#2299)](https://github.com/PennyLaneAI/pennylane/pull/2299)
+
Documentation
-* Link to the strawberry fields docs for information on the CV model.
+* The developer guide on adding templates and the architecture overview were rewritten
+ to reflect the past and planned changes of the operator refactor.
+ [(#2066)](https://github.com/PennyLaneAI/pennylane/pull/2066)
+
+* Links to the Strawberry Fields documentation for information on the CV
+ model.
[(#2259)](https://github.com/PennyLaneAI/pennylane/pull/2259)
* Fixes the documentation example for `qml.QFT`.
@@ -413,135 +658,19 @@
display correctly in the docs.
[(#2286)](https://github.com/PennyLaneAI/pennylane/pull/2286)
-Operator class refactor
-
-The Operator class has undergone a major refactor with the following changes:
-
-* The static `compute_decomposition` method defines the decomposition
- of an operator into a product of simpler operators, and the instance method
- `decomposition()` computes this for a given instance. When a custom
- decomposition does not exist, the code now raises a custom `NoDecompositionError`
- instead of `NotImplementedError`.
- [(#2024)](https://github.com/PennyLaneAI/pennylane/pull/2024)
-
-* The `diagonalizing_gates()` representation has been moved to the highest-level
- `Operator` class and is therefore available to all subclasses. A condition
- `qml.operation.defines_diagonalizing_gates` has been added, which can be used
- in tape contexts without queueing.
- [(#1985)](https://github.com/PennyLaneAI/pennylane/pull/1985)
-
-* A static `compute_diagonalizing_gates` method has been added, which is called
- by default in `diagonalizing_gates()`.
- [(#1993)](https://github.com/PennyLaneAI/pennylane/pull/1993)
-
-* A `hyperparameters` attribute was added to the operator class.
- [(#2017)](https://github.com/PennyLaneAI/pennylane/pull/2017)
-
-* The representation of an operator as a matrix has been overhauled.
-
- The `matrix()` method now accepts a
- `wire_order` argument and calculates the correct numerical representation
- with respect to that ordering.
-
- ```pycon
- >>> op = qml.RX(0.5, wires="b")
- >>> op.matrix()
- [[0.96891242+0.j 0. -0.24740396j]
- [0. -0.24740396j 0.96891242+0.j ]]
- >>> op.matrix(wire_order=["a", "b"])
- [[0.9689+0.j 0.-0.2474j 0.+0.j 0.+0.j]
- [0.-0.2474j 0.9689+0.j 0.+0.j 0.+0.j]
- [0.+0.j 0.+0.j 0.9689+0.j 0.-0.2474j]
- [0.+0.j 0.+0.j 0.-0.2474j 0.9689+0.j]]
- ```
-
- The "canonical matrix", which is independent of wires,
- is now defined in the static method `compute_matrix()` instead of `_matrix`.
- By default, this method is assumed to take all parameters and non-trainable
- hyperparameters that define the operation.
-
- ```pycon
- >>> qml.RX.compute_matrix(0.5)
- [[0.96891242+0.j 0. -0.24740396j]
- [0. -0.24740396j 0.96891242+0.j ]]
- ```
-
- If no canonical matrix is specified for a gate, `compute_matrix()`
- raises a `NotImplementedError`.
-
- The new `matrix()` method is now used in the
- `pennylane.transforms.get_qubit_unitary()` transform.
- [(#1996)](https://github.com/PennyLaneAI/pennylane/pull/1996)
-
-* The `string_for_inverse` attribute is removed.
- [(#2021)](https://github.com/PennyLaneAI/pennylane/pull/2021)
-
-* A `terms()` method and a `compute_terms()` static method were added to `Operator`.
- Currently, only the `Hamiltonian` class overwrites `compute_terms` to store
- coefficients and operators. The `Hamiltonian.terms` property hence becomes
- a proper method called by `Hamiltonian.terms()`.
-
-* The generator property has been updated to an instance method,
- `Operator.generator()`. It now returns an instantiated operation,
- representing the generator of the instantiated operator.
- [(#2030)](https://github.com/PennyLaneAI/pennylane/pull/2030)
- [(#2061)](https://github.com/PennyLaneAI/pennylane/pull/2061)
-
- Various operators have been updated to specify the generator as either
- an `Observable`, `Tensor`, `Hamiltonian`, `SparseHamiltonian`, or `Hermitian`
- operator.
-
- In addition, a temporary utility function get_generator has been added
- to the utils module, to automate:
-
- - Extracting the matrix representation
- - Extracting the 'coefficient' if possible (only occurs if the generator is a single Pauli word)
- - Converting a Hamiltonian to a sparse matrix if there are more than 1 Pauli word present.
- - Negating the coefficient/taking the adjoint of the matrix if the operation was inverted
-
- This utility logic is currently needed because:
-
- - Extracting the matrix representation is not supported natively on
- Hamiltonians and SparseHamiltonians.
- - By default, calling `op.generator()` does not take into account `op.inverse()`.
- - If the generator is a single Pauli word, it is convenient to have access to
- both the coefficient and the observable separately.
-
-* Decompositions are now defined in `compute_decomposition`, instead of `expand`.
- [(#2053)](https://github.com/PennyLaneAI/pennylane/pull/2053)
-
-* The `expand` method was moved to the main `Operator` class.
- [(#2053)](https://github.com/PennyLaneAI/pennylane/pull/2053)
-
-* A `sparse_matrix` method and a `compute_sparse_matrix` static method were added
- to the `Operator` class. The sparse representation of `SparseHamiltonian`
- is moved to this method, so that its `matrix` method now returns a dense matrix.
- [(#2050)](https://github.com/PennyLaneAI/pennylane/pull/2050)
-
-* The argument `wires` in `heisenberg_obs`, `heisenberg_expand` and `heisenberg_tr`
- was renamed to `wire_order` to be consistent with other matrix representations.
- [(#2051)](https://github.com/PennyLaneAI/pennylane/pull/2051)
-
-* The property `kraus_matrices` has been changed to a method, and `_kraus_matrices` renamed to
- `compute_kraus_matrices`, which is now a static method.
- [(#2055)](https://github.com/PennyLaneAI/pennylane/pull/2055)
-
-* The developer guide on adding templates and the architecture overview were rewritten
- to reflect the past and planned changes of the operator refactor.
- [(#2066)](https://github.com/PennyLaneAI/pennylane/pull/2066)
-
-* Custom errors subclassing ``OperatorPropertyUndefined`` are raised if a representation
- has not been defined. This replaces the ``NotImplementedError`` and allows finer control
- for developers.
- [(#2064)](https://github.com/PennyLaneAI/pennylane/pull/2064)
+* Docstring examples now display using the updated text-based circuit drawer.
+ [(#2252)](https://github.com/PennyLaneAI/pennylane/pull/2252)
-* Moved ``expand()`` from ``Operation`` to ``Operator``.
- [(#2239)](https://github.com/PennyLaneAI/pennylane/pull/2239)
+* Add docstring to `OrbitalRotation.grad_recipe`.
+ [(#2193)](https://github.com/PennyLaneAI/pennylane/pull/2193)
Contributors
This release contains contributions from (in alphabetical order):
-Sam Banning, Thomas Bromley, Olivia Di Matteo, Anthony Hayes, David Ittah, Josh Izaac, Christina Lee, Angus Lowe,
-Maria Fernanda Morris, Romain Moyard, Zeyue Niu, Maria Schuld, Jay Soni,
-Antal SzΓ‘va, David Wierichs
+Catalina Albornoz, Jack Y. Araz, Juan Miguel Arrazola, Ali Asadi, Utkarsh Azad,
+Sam Banning, Thomas Bromley, Olivia Di Matteo, Christian Gogolin, Diego Guala,
+Anthony Hayes, David Ittah, Josh Izaac, Soran Jahangiri, Nathan Killoran,
+Christina Lee, Angus Lowe, Maria Fernanda Morris, Romain Moyard, Zeyue Niu, Lee
+James O'Riordan, Chae-Yeun Park, Maria Schuld, Jay Soni, Antal SzΓ‘va, David
+Wierichs.
diff --git a/pennylane/debugging.py b/pennylane/debugging.py
index 11cfa5b0522..060479c237e 100644
--- a/pennylane/debugging.py
+++ b/pennylane/debugging.py
@@ -28,7 +28,7 @@ class _Debugger:
"""
def __init__(self, dev):
- if dev.short_name == "lightning.qubit" or not hasattr(dev, "_debugger"):
+ if "Snapshot" not in dev.operations:
raise DeviceError("Device does not support snapshots.")
self.snapshots = {}
diff --git a/pennylane/drawer/tape_text.py b/pennylane/drawer/tape_text.py
index 9a01852b302..0354c2320bb 100644
--- a/pennylane/drawer/tape_text.py
+++ b/pennylane/drawer/tape_text.py
@@ -128,7 +128,7 @@ def tape_text(
qml.var(qml.PauliZ(0) @ qml.PauliZ(1))
qml.probs(wires=(0, 1, 2, "aux"))
- >>> print(tape_text(tape))
+ >>> print(qml.drawer.tape_text(tape))
0: ββQFTββRXββCββ€ βVar[Z@Z] βProbs
1: ββQFTββRYββCββ€ β°Var[Z@Z] βProbs
2: ββ°QFTββRZβββββ€ βProbs
@@ -139,7 +139,7 @@ def tape_text(
By default, parameters are omitted. By specifying the ``decimals`` keyword, parameters
are displayed to the specified precision. Matrix-valued parameters are never displayed.
- >>> print(tape_text(tape, decimals=2))
+ >>> print(qml.drawer.tape_text(tape, decimals=2))
0: ββQFTββRX(1.23)ββCββ€ βVar[Z@Z] βProbs
1: ββQFTββRY(1.23)ββCββ€ β°Var[Z@Z] βProbs
2: ββ°QFTββRZ(1.23)βββββ€ βProbs
@@ -154,7 +154,7 @@ def tape_text(
shape = qml.StronglyEntanglingLayers.shape(n_wires=5, n_layers=5)
params = rng.random(shape)
tape2 = qml.StronglyEntanglingLayers(params, wires=range(5)).expand()
- print(tape_text(tape2, max_length=60))
+ print(qml.drawer.tape_text(tape2, max_length=60))
.. code-block:: none
@@ -175,7 +175,7 @@ def tape_text(
The ``wire_order`` keyword specifies the order of the wires from
top to bottom:
- >>> print(tape_text(tape, wire_order=["aux", 2, 1, 0]))
+ >>> print(qml.drawer.tape_text(tape, wire_order=["aux", 2, 1, 0]))
aux: βββββββββββXββ€ βProbs
2: ββQFTββRZβββββ€ βProbs
1: ββQFTββRYββCββ€ βVar[Z@Z] βProbs
@@ -183,7 +183,7 @@ def tape_text(
If the wire order contains empty wires, they are only shown if the ``show_all_wires=True``.
- >>> print(tape_text(tape, wire_order=["a", "b", "aux", 0, 1, 2], show_all_wires=True))
+ >>> print(qml.drawer.tape_text(tape, wire_order=["a", "b", "aux", 0, 1, 2], show_all_wires=True))
a: ββββββββββββββ€
b: ββββββββββββββ€
aux: βββββββββββXββ€ βProbs
@@ -202,7 +202,7 @@ def tape_text(
qml.QubitUnitary(np.eye(2), wires=1)
qml.expval(qml.Hermitian(np.eye(4), wires=(0,1)))
- >>> print(tape_text(tape, show_matrices=True))
+ >>> print(qml.drawer.tape_text(tape, show_matrices=True))
0: ββU(M0)ββ€ β<π(M1)>
1: ββU(M0)ββ€ β°<π(M1)>
M0 =
@@ -219,7 +219,7 @@ def tape_text(
tape offset.
>>> cache = {'matrices': [-np.eye(3)]}
- >>> print(tape_text(tape, show_matrices=True, cache=cache))
+ >>> print(qml.drawer.tape_text(tape, show_matrices=True, cache=cache))
0: ββU(M1)ββ€ β<π(M2)>
1: ββU(M1)ββ€ β°<π(M2)>
M0 =
@@ -255,7 +255,7 @@ def tape_text(
qml.PauliX(0)
cache = {'tape_offset': 3}
- print(tape_text(tape, cache=cache))
+ print(qml.drawer.tape_text(tape, cache=cache))
print("New tape offset: ", cache['tape_offset'])
diff --git a/pennylane/gradients/__init__.py b/pennylane/gradients/__init__.py
index 38a0d458105..afecc469413 100644
--- a/pennylane/gradients/__init__.py
+++ b/pennylane/gradients/__init__.py
@@ -238,12 +238,12 @@ def circuit(weights):
>>> gradient_tapes, fn = qml.gradients.param_shift(tape)
>>> gradient_tapes
-[,
- ,
- ,
- ,
- ,
- ]
+[,
+ ,
+ ,
+ ,
+ ,
+ ]
This can be useful if the underlying circuits representing the gradient
computation need to be analyzed.
diff --git a/pennylane/gradients/finite_difference.py b/pennylane/gradients/finite_difference.py
index 79ce33385df..1e36c043b53 100644
--- a/pennylane/gradients/finite_difference.py
+++ b/pennylane/gradients/finite_difference.py
@@ -28,6 +28,7 @@
gradient_transform,
grad_method_validation,
choose_grad_methods,
+ gradient_analysis,
)
@@ -272,7 +273,7 @@ def finite_diff(
device evaluation. Instead, the processed tapes, and post-processing
function, which together define the gradient are directly returned:
- >>> with qml.tape.JacobianTape() as tape:
+ >>> with qml.tape.QuantumTape() as tape:
... qml.RX(params[0], wires=0)
... qml.RY(params[1], wires=0)
... qml.RX(params[2], wires=0)
@@ -280,10 +281,10 @@ def finite_diff(
... qml.var(qml.PauliZ(0))
>>> gradient_tapes, fn = qml.gradients.finite_diff(tape)
>>> gradient_tapes
- [,
- ,
- ,
- ]
+ [,
+ ,
+ ,
+ ]
This can be useful if the underlying circuits representing the gradient
computation need to be analyzed.
@@ -306,7 +307,7 @@ def finite_diff(
if validate_params:
if "grad_method" not in tape._par_info[0]:
- tape._update_gradient_info()
+ gradient_analysis(tape, grad_fn=finite_diff)
diff_methods = grad_method_validation("numeric", tape)
else:
diff_methods = ["F" for i in tape.trainable_params]
diff --git a/pennylane/gradients/gradient_transform.py b/pennylane/gradients/gradient_transform.py
index ff59483cb18..eec0501cf5d 100644
--- a/pennylane/gradients/gradient_transform.py
+++ b/pennylane/gradients/gradient_transform.py
@@ -20,6 +20,60 @@
from pennylane.transforms.tape_expand import expand_invalid_trainable
+def gradient_analysis(tape, use_graph=True, grad_fn=None):
+ """Update the parameter information dictionary of the tape with
+ gradient information of each parameter.
+
+ Parameter gradient methods include:
+
+ * ``None``: the parameter does not support differentiation.
+
+ * ``"0"``: the variational circuit output does not depend on this
+ parameter (the partial derivative is zero).
+
+ In addition, the operator might define its own grad method
+ via :attr:`.Operator.grad_method`.
+
+ Note that this function modifies the input tape in-place.
+
+ Args:
+ tape (.QuantumTape): the quantum tape to analyze
+ use_graph (bool): whether to use a directed-acyclic graph to determine
+ if the parameter has a gradient of 0
+ grad_fn (None or callable): The gradient transform performing the analysis.
+ This is an optional argument; if provided, and the tape has already
+ been analyzed for the gradient information by the same gradient transform,
+ the cached gradient analysis will be used.
+ """
+ # pylint:disable=protected-access
+ if grad_fn is not None and getattr(tape, "_gradient_fn", None) is grad_fn:
+ # gradient analysis has already been performed on this tape
+ return
+
+ if grad_fn is not None:
+ tape._gradient_fn = grad_fn
+
+ for idx, info in tape._par_info.items():
+
+ if idx not in tape.trainable_params:
+ # non-trainable parameters do not require a grad_method
+ info["grad_method"] = None
+ else:
+ op = tape._par_info[idx]["op"]
+
+ if not qml.operation.has_grad_method(op):
+ # no differentiation method is registered for this operation
+ info["grad_method"] = None
+
+ elif (tape._graph is not None) or use_graph:
+ if not any(tape.graph.has_path(op, ob) for ob in tape.observables):
+ # there is no influence of this operation on any of the observables
+ info["grad_method"] = "0"
+ continue
+
+ info["grad_method"] = op.grad_method
+
+
def grad_method_validation(method, tape):
"""Validates if the gradient method requested is supported by the trainable
parameters of a tape, and returns the allowed parameter gradient methods.
@@ -39,7 +93,7 @@ def grad_method_validation(method, tape):
Args:
method (str): the overall Jacobian differentiation method
- tape (.JacobianTape): the tape with associated parameter information
+ tape (.QuantumTape): the tape with associated parameter information
Returns:
tuple[str, None]: the allowed parameter gradient methods for each trainable parameter
diff --git a/pennylane/gradients/param_shift_hessian.py b/pennylane/gradients/param_shift_hessian.py
index 145485f2ab9..26bf6e257f0 100644
--- a/pennylane/gradients/param_shift_hessian.py
+++ b/pennylane/gradients/param_shift_hessian.py
@@ -20,8 +20,7 @@
import pennylane as qml
-from .parameter_shift import _gradient_analysis
-from .gradient_transform import grad_method_validation
+from .gradient_transform import gradient_analysis, grad_method_validation
from .hessian_transform import hessian_transform
@@ -265,13 +264,13 @@ def param_shift_hessian(tape, f0=None):
>>> tape = circuit.qtape
>>> hessian_tapes, postproc_fn = qml.gradients.param_shift_hessian(tape)
>>> hessian_tapes
- [,
- ,
- ,
- ,
- ,
- ,
- ]
+ [,
+ ,
+ ,
+ ,
+ ,
+ ,
+ ]
>>> postproc_fn(qml.execute(hessian_tapes, dev, None))
array([[-0.97517033, 0.01983384],
[ 0.01983384, -0.97517033]])
@@ -340,7 +339,7 @@ def param_shift_hessian(tape, f0=None):
f"Hessian. Only two-term parameter shift rules are currently supported."
)
- _gradient_analysis(tape)
+ gradient_analysis(tape, grad_fn=qml.gradients.param_shift)
diff_methods = grad_method_validation("analytic", tape)
if all(g == "0" for g in diff_methods):
diff --git a/pennylane/gradients/parameter_shift.py b/pennylane/gradients/parameter_shift.py
index 71846b438b9..b08988a20b1 100644
--- a/pennylane/gradients/parameter_shift.py
+++ b/pennylane/gradients/parameter_shift.py
@@ -25,6 +25,7 @@
gradient_transform,
grad_method_validation,
choose_grad_methods,
+ gradient_analysis,
)
from .finite_difference import finite_diff, generate_shifted_tapes
from .general_shift_rules import process_shifts
@@ -109,37 +110,6 @@ def _get_operation_recipe(tape, t_idx, shifts):
return coeffs, mults, shifts
-def _gradient_analysis(tape, use_graph=True):
- """Update the parameter information dictionary of the tape with
- gradient information of each parameter."""
-
- if getattr(tape, "_gradient_fn", None) is param_shift:
- # gradient analysis has already been performed on this tape
- return
-
- tape._gradient_fn = param_shift
-
- for idx, info in tape._par_info.items():
-
- if idx not in tape.trainable_params:
- # non-trainable parameters do not require a grad_method
- info["grad_method"] = None
- else:
- op = tape._par_info[idx]["op"]
-
- if not qml.operation.has_grad_method(op):
- # no differentiation method is registered for this operation
- info["grad_method"] = None
-
- elif (tape._graph is not None) or use_graph:
- if not any(tape.graph.has_path(op, ob) for ob in tape.observables):
- # there is no influence of this operation on any of the observables
- info["grad_method"] = "0"
- continue
-
- info["grad_method"] = op.grad_method
-
-
def expval_param_shift(tape, argnum=None, shifts=None, gradient_recipes=None, f0=None):
r"""Generate the parameter-shift tapes and postprocessing methods required
to compute the gradient of a gate parameter with respect to an
@@ -573,7 +543,7 @@ def param_shift(
device evaluation. Instead, the processed tapes, and post-processing
function, which together define the gradient are directly returned:
- >>> with qml.tape.JacobianTape() as tape:
+ >>> with qml.tape.QuantumTape() as tape:
... qml.RX(params[0], wires=0)
... qml.RY(params[1], wires=0)
... qml.RX(params[2], wires=0)
@@ -581,12 +551,12 @@ def param_shift(
... qml.var(qml.PauliZ(0))
>>> gradient_tapes, fn = qml.gradients.param_shift(tape)
>>> gradient_tapes
- [,
- ,
- ,
- ,
- ,
- ]
+ [,
+ ,
+ ,
+ ,
+ ,
+ ]
This can be useful if the underlying circuits representing the gradient
computation need to be analyzed.
@@ -613,7 +583,7 @@ def param_shift(
)
return [], lambda _: ()
- _gradient_analysis(tape)
+ gradient_analysis(tape, grad_fn=param_shift)
method = "analytic" if fallback_fn is None else "best"
diff_methods = grad_method_validation(method, tape)
diff --git a/pennylane/gradients/parameter_shift_cv.py b/pennylane/gradients/parameter_shift_cv.py
index bee9fb41401..b8808da771d 100644
--- a/pennylane/gradients/parameter_shift_cv.py
+++ b/pennylane/gradients/parameter_shift_cv.py
@@ -593,7 +593,7 @@ def circuit(weights):
to use during autodifferentiation:
>>> dev = qml.device("default.gaussian", wires=2)
- >>> @qml.qnode(dev, gradient_fn=qml.gradients.param_shift_cv)
+ >>> @qml.qnode(dev, diff_method="parameter-shift")
... def circuit(params):
... qml.Squeezing(params[0], params[1], wires=[0])
... qml.Squeezing(params[2], params[3], wires=[0])
@@ -621,16 +621,16 @@ def circuit(weights):
function, which together define the gradient are directly returned:
>>> r0, phi0, r1, phi1 = [0.4, -0.3, -0.7, 0.2]
- >>> with qml.tape.JacobianTape() as tape:
+ >>> with qml.tape.QuantumTape() as tape:
... qml.Squeezing(r0, phi0, wires=[0])
... qml.Squeezing(r1, phi1, wires=[0])
... qml.expval(qml.NumberOperator(0)) # second-order
>>> gradient_tapes, fn = qml.gradients.param_shift_cv(tape, dev)
>>> gradient_tapes
- [,
- ,
- ,
- ]
+ [,
+ ,
+ ,
+ ]
This can be useful if the underlying circuits representing the gradient
computation need to be analyzed.
diff --git a/pennylane/gradients/vjp.py b/pennylane/gradients/vjp.py
index 8164c13283f..9fee8fd5233 100644
--- a/pennylane/gradients/vjp.py
+++ b/pennylane/gradients/vjp.py
@@ -112,17 +112,16 @@ def vjp(tape, dy, gradient_fn, gradient_kwargs=None):
**Example**
- Consider the following Torch-compatible quantum tape:
+ Consider the following quantum tape with PyTorch parameters:
.. code-block:: python
import torch
- from pennylane.interfaces.torch import TorchInterface
x = torch.tensor([[0.1, 0.2, 0.3],
[0.4, 0.5, 0.6]], requires_grad=True, dtype=torch.float64)
- with TorchInterface.apply(qml.tape.JacobianTape()) as tape:
+ with qml.tape.QuantumTape() as tape:
qml.RX(x[0, 0], wires=0)
qml.RY(x[0, 1], wires=1)
qml.RZ(x[0, 2], wires=0)
@@ -145,10 +144,10 @@ def vjp(tape, dy, gradient_fn, gradient_kwargs=None):
Executing the VJP tapes, and applying the processing function:
>>> dev = qml.device("default.qubit", wires=2)
- >>> vjp = fn([t.execute(dev) for t in vjp_tapes])
+ >>> vjp = fn(qml.execute(vjp_tapes, dev, gradient_fn=qml.gradients.param_shift, interface="torch"))
>>> vjp
- tensor([-0.6069, -0.0451, 0.0451, -0.0139, -0.2809, 0.2809],
- dtype=torch.float64, grad_fn=)
+ tensor([-1.1562e-01, -1.3862e-02, -9.0841e-03, -1.3878e-16, -4.8217e-01,
+ 2.1329e-17], dtype=torch.float64, grad_fn=)
The output VJP is also differentiable with respect to the tape parameters:
@@ -156,7 +155,7 @@ def vjp(tape, dy, gradient_fn, gradient_kwargs=None):
>>> cost.backward()
>>> x.grad
tensor([[-1.1025e+00, -2.0554e-01, -1.4917e-01],
- [-1.9429e-09, -9.1580e-01, 1.3878e-09]], dtype=torch.float64)
+ [-1.2490e-16, -9.1580e-01, 0.0000e+00]], dtype=torch.float64)
"""
gradient_kwargs = gradient_kwargs or {}
num_params = len(tape.trainable_params)
@@ -259,12 +258,12 @@ def ansatz(x):
qml.RY(x[1, 1], wires=0)
qml.RZ(x[1, 2], wires=1)
- with TorchInterface.apply(qml.tape.JacobianTape()) as tape1:
+ with qml.tape.QuantumTape() as tape1:
ansatz(x)
qml.expval(qml.PauliZ(0))
qml.probs(wires=1)
- with TorchInterface.apply(qml.tape.JacobianTape()) as tape2:
+ with qml.tape.QuantumTape() as tape2:
ansatz(x)
qml.expval(qml.PauliZ(0) @ qml.PauliZ(1))
@@ -286,12 +285,12 @@ def ansatz(x):
Executing the VJP tapes, and applying the processing function:
>>> dev = qml.device("default.qubit", wires=2)
- >>> vjps = fn([t.execute(dev) for t in vjp_tapes])
+ >>> vjps = fn(qml.execute(vjp_tapes, dev, gradient_fn=qml.gradients.param_shift, interface="torch"))
>>> vjps
- [tensor([-0.6069, -0.0451, 0.0451, -0.0139, -0.2809, 0.2809],
- dtype=torch.float64, grad_fn=),
- tensor([ 0.1739, -0.1641, -0.0054, -0.2937, -0.4008, 0.0000],
- dtype=torch.float64, grad_fn=)]
+ [tensor([-1.1562e-01, -1.3862e-02, -9.0841e-03, -1.3878e-16, -4.8217e-01,
+ 2.1329e-17], dtype=torch.float64, grad_fn=),
+ tensor([ 1.7393e-01, -1.6412e-01, -5.3983e-03, -2.9366e-01, -4.0083e-01,
+ 2.1134e-17], dtype=torch.float64, grad_fn=)]
We have two VJPs; one per tape. Each one corresponds to the number of parameters
on the tapes (6).
@@ -301,8 +300,8 @@ def ansatz(x):
>>> cost = torch.sum(vjps[0] + vjps[1])
>>> cost.backward()
>>> x.grad
- tensor([[-4.7924e-01, -9.0857e-01, -2.4198e-01],
- [-9.2973e-02, -1.0772e+00, 4.7184e-09]], dtype=torch.float64)
+ tensor([[-0.4792, -0.9086, -0.2420],
+ [-0.0930, -1.0772, 0.0000]], dtype=torch.float64)
"""
gradient_kwargs = gradient_kwargs or {}
reshape_info = []
diff --git a/pennylane/interfaces/batch/__init__.py b/pennylane/interfaces/batch/__init__.py
index e9ee5bc1042..6e09b200f40 100644
--- a/pennylane/interfaces/batch/__init__.py
+++ b/pennylane/interfaces/batch/__init__.py
@@ -263,12 +263,12 @@ def execute(
dev = qml.device("lightning.qubit", wires=2)
def cost_fn(params, x):
- with qml.tape.JacobianTape() as tape1:
+ with qml.tape.QuantumTape() as tape1:
qml.RX(params[0], wires=0)
qml.RY(params[1], wires=0)
qml.expval(qml.PauliZ(0))
- with qml.tape.JacobianTape() as tape2:
+ with qml.tape.QuantumTape() as tape2:
qml.RX(params[2], wires=0)
qml.RY(x[0], wires=1)
qml.CNOT(wires=[0, 1])
@@ -277,7 +277,7 @@ def cost_fn(params, x):
tapes = [tape1, tape2]
# execute both tapes in a batch on the given device
- res = execute(tapes, dev, qml.gradients.param_shift)
+ res = qml.execute(tapes, dev, qml.gradients.param_shift, max_diff=2)
return res[0][0] + res[1][0, 0] - res[1][0, 1]
@@ -291,7 +291,7 @@ def cost_fn(params, x):
>>> params = np.array([0.1, 0.2, 0.3], requires_grad=True)
>>> x = np.array([0.5], requires_grad=True)
>>> cost_fn(params, x)
- 1.9305068163274222
+ tensor(1.93050682, requires_grad=True)
Since the ``execute`` function is differentiable, we can
also compute the gradient:
diff --git a/pennylane/measurements.py b/pennylane/measurements.py
index c4358b0a095..35d78addd37 100644
--- a/pennylane/measurements.py
+++ b/pennylane/measurements.py
@@ -193,7 +193,7 @@ def expand(self):
rotation and a measurement in the computational basis.
Returns:
- .JacobianTape: a quantum tape containing the operations
+ .QuantumTape: a quantum tape containing the operations
required to diagonalize the observable
**Example**
@@ -224,9 +224,7 @@ def expand(self):
if self.obs is None:
raise qml.operation.DecompositionUndefinedError
- from pennylane.tape import JacobianTape # pylint: disable=import-outside-toplevel
-
- with JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
self.obs.diagonalizing_gates()
MeasurementProcess(
self.return_type, wires=self.obs.wires, eigvals=self.obs.get_eigvals()
@@ -636,17 +634,25 @@ def branches(self):
return branch_dict
def __invert__(self):
- """Inverts the control value of the measurement."""
+ """Return a copy of the measurement value with an inverted control
+ value."""
+ inverted_self = copy.copy(self)
zero = self._zero_case
one = self._one_case
- self._control_value = one if self._control_value == zero else zero
+ inverted_self._control_value = one if self._control_value == zero else zero
- return self
+ return inverted_self
def __eq__(self, control_value):
"""Allow asserting measurement values."""
measurement_outcomes = {self._zero_case, self._one_case}
+
+ if not isinstance(control_value, tuple(type(val) for val in measurement_outcomes)):
+ raise MeasurementValueError(
+ f"The equality operator is used to assert measurement outcomes, but got a value with type {type(control_value)}."
+ )
+
if control_value not in measurement_outcomes:
raise MeasurementValueError(
f"Unknown measurement value asserted; the set of possible measurement outcomes is: {measurement_outcomes}."
@@ -698,7 +704,7 @@ def func(x, y):
tensor([0.90165331, 0.09834669], requires_grad=True)
Args:
- wires (Wires): The wires the measurement process applies to.
+ wires (Wires): The wire of the qubit the measurement process applies to.
Raises:
QuantumFunctionError: if multiple wires were specified
diff --git a/pennylane/operation.py b/pennylane/operation.py
index 05702a47bdf..c56c563db20 100644
--- a/pennylane/operation.py
+++ b/pennylane/operation.py
@@ -137,7 +137,7 @@ def expand_matrix(base_matrix, wires, wire_order):
... [5, 6, 7, 8],
... [9, 10, 11, 12],
... [13, 14, 15, 16]])
- >>> expand_matrix(base_matrix, wires=[0, 2], wire_order=[0, 2])
+ >>> print(expand_matrix(base_matrix, wires=[0, 2], wire_order=[0, 2]))
[[ 1 2 3 4]
[ 5 6 7 8]
[ 9 10 11 12]
@@ -145,7 +145,7 @@ def expand_matrix(base_matrix, wires, wire_order):
If the wire order is a permutation of ``wires``, the entries of the base matrix get permuted:
- >>> expand_matrix(base_matrix, wires=[0, 2], wire_order=[2, 0])
+ >>> print(expand_matrix(base_matrix, wires=[0, 2], wire_order=[2, 0]))
[[ 1 3 2 4]
[ 9 11 10 12]
[ 5 7 6 8]
@@ -153,7 +153,7 @@ def expand_matrix(base_matrix, wires, wire_order):
If the wire order contains wire labels not found in ``wires``, the matrix gets expanded:
- >>> expand_matrix(base_matrix, wires=[0, 2], wire_order=[0, 1, 2])
+ >>> print(expand_matrix(base_matrix, wires=[0, 2], wire_order=[0, 1, 2]))
[[ 1 2 0 0 3 4 0 0]
[ 5 6 0 0 7 8 0 0]
[ 0 0 1 2 0 0 3 4]
@@ -169,7 +169,7 @@ def expand_matrix(base_matrix, wires, wire_order):
... [3., 4.]], requires_grad=True)
>>> res = expand_matrix(base_matrix_torch, wires=["b"], wire_order=["a", "b"])
>>> type(res)
-
+ torch.Tensor
>>> res.requires_grad
True
"""
@@ -561,7 +561,7 @@ def compute_matrix(*params, **hyperparams): # pylint:disable=unused-argument
The canonical matrix is the textbook matrix representation that does not consider wires.
Implicitly, this assumes that the wires of the operator correspond to the global wire order.
- .. seealso:: :meth:`~.CNOT.matrix`
+ .. seealso:: :meth:`~.Operator.get_matrix` and :func:`~.matrix`
Args:
params (list): trainable parameters of the operator, as stored in the ``parameters`` attribute
@@ -633,7 +633,7 @@ def compute_sparse_matrix(*params, **hyperparams): # pylint:disable=unused-argu
The canonical matrix is the textbook matrix representation that does not consider wires.
Implicitly, this assumes that the wires of the operator correspond to the global wire order.
- .. seealso:: :meth:`~.SparseHamiltonian.sparse_matrix`
+ .. seealso:: :meth:`~.Operator.sparse_matrix`
Args:
params (list): trainable parameters of the operator, as stored in the ``parameters`` attribute
@@ -657,7 +657,7 @@ def sparse_matrix(self, wire_order=None):
A ``SparseMatrixUndefinedError`` is raised if the sparse matrix representation has not been defined.
- .. seealso:: :meth:`~.SparseHamiltonian.compute_sparse_matrix`
+ .. seealso:: :meth:`~.Operator.compute_sparse_matrix`
Args:
wire_order (Iterable): global wire order, must contain all wire labels from the operator's wires
@@ -686,7 +686,7 @@ def compute_eigvals(*params, **hyperparams):
Otherwise, no particular order for the eigenvalues is guaranteed.
- .. seealso:: :meth:`~.RZ.eigvals`
+ .. seealso:: :meth:`~.Operator.get_eigvals` and :func:`~.eigvals`
Args:
params (list): trainable parameters of the operator, as stored in the ``parameters`` attribute
@@ -765,7 +765,7 @@ def compute_terms(*params, **hyperparams): # pylint: disable=unused-argument
.. math:: O = \sum_i c_i O_i
- .. seealso:: :meth:`~.Hamiltonian.terms`
+ .. seealso:: :meth:`~.Operator.terms`
Args:
params (list): trainable parameters of the operator, as stored in the ``parameters`` attribute
@@ -784,7 +784,7 @@ def terms(self):
A ``TermsUndefinedError`` is raised if no representation by terms is defined.
- .. seealso:: :meth:`~.Hamiltonian.compute_terms`
+ .. seealso:: :meth:`~.Operator.compute_terms`
Returns:
tuple[list[tensor_like or float], list[.Operation]]: list of coefficients :math:`c_i`
@@ -993,7 +993,7 @@ def decomposition(self):
A ``DecompositionUndefinedError`` is raised if no representation by decomposition is defined.
- .. seealso:: :meth:`~.operation.Operator.compute_decomposition`.
+ .. seealso:: :meth:`~.Operator.compute_decomposition`.
Returns:
list[Operator]: decomposition of the operator
@@ -1008,7 +1008,7 @@ def compute_decomposition(*params, wires=None, **hyperparameters):
.. math:: O = O_1 O_2 \dots O_n.
- .. seealso:: :meth:`~.operation.Operator.decomposition`.
+ .. seealso:: :meth:`~.Operator.decomposition`.
Args:
params (list): trainable parameters of the operator, as stored in the ``parameters`` attribute
@@ -1033,7 +1033,7 @@ def compute_diagonalizing_gates(
The diagonalizing gates rotate the state into the eigenbasis
of the operator.
- .. seealso:: :meth:`~.PauliX.diagonalizing_gates`.
+ .. seealso:: :meth:`~.Operator.diagonalizing_gates`.
Args:
params (list): trainable parameters of the operator, as stored in the ``parameters`` attribute
@@ -1098,7 +1098,7 @@ def expand(self):
"""Returns a tape that has recorded the decomposition of the operator.
Returns:
- .JacobianTape: quantum tape
+ .QuantumTape: quantum tape
"""
tape = qml.tape.QuantumTape(do_queue=False)
@@ -1291,9 +1291,10 @@ def parameter_frequencies(self):
>>> op = qml.ControlledPhaseShift(0.1, wires=[0, 1])
>>> op.parameter_frequencies
[(1,)]
- >>> gen_eigvals = tuple(np.linalg.eigvals(op.generator[0] * op.generator[1]))
- >>> qml.gradients.eigvals_to_frequencies(gen_eigvals)
- (tensor(1., requires_grad=True),)
+ >>> gen = qml.generator(op, format="observable")
+ >>> gen_eigvals = qml.eigvals(gen)
+ >>> qml.gradients.eigvals_to_frequencies(tuple(gen_eigvals))
+ (1.0,)
For more details on this relationship, see :func:`.eigvals_to_frequencies`.
"""
diff --git a/pennylane/ops/functions/generator.py b/pennylane/ops/functions/generator.py
index 9be458db502..431c8a4f556 100644
--- a/pennylane/ops/functions/generator.py
+++ b/pennylane/ops/functions/generator.py
@@ -15,6 +15,11 @@
This module contains the qml.generator function.
"""
# pylint: disable=protected-access
+import inspect
+import warnings
+
+import numpy as np
+
import pennylane as qml
@@ -96,6 +101,28 @@ def _generator_prefactor(gen, op):
return obs, prefactor
+def _generator_backcompatibility(op):
+ r"""Preserve backwards compatibility behaviour for PennyLane
+ versions <=0.22, where generators returned List[type or ndarray, float].
+ This function raises a deprecation warning, and converts to the new
+ format where an instantiated Operator is returned."""
+ warnings.warn(
+ "The Operator.generator property is deprecated. Please update the operator so that "
+ "\n\t1. Operator.generator() is a method, and"
+ "\n\t2. Operator.generator() returns an Operator instance representing the operator.",
+ UserWarning,
+ )
+ gen = op.generator
+
+ if inspect.isclass(gen[0]):
+ return gen[1] * gen[0](wires=op.wires)
+
+ if isinstance(gen[0], np.ndarray) and len(gen[0].shape) == 2:
+ return gen[1] * qml.Hermitian(gen[0], wires=op.wires)
+
+ raise qml.operation.GeneratorUndefinedError
+
+
@qml.op_transform
def generator(op, format="prefactor"):
r"""Returns the generator of an operation.
@@ -107,19 +134,16 @@ def generator(op, format="prefactor"):
``'observable'``, or ``'hamiltonian'``. See below for more details.
Returns:
- .Observable or tuple[float, .Observable]: The returned generator, with format/type
+ .Observable or tuple[.Observable, float]: The returned generator, with format/type
dependent on the ``format`` argument.
- * ``"prefactor"``: Return the generator as ```(obs, prefactor)`` (representing
- :math:`G=p \hat{O}`), where
+ * ``"prefactor"``: Return the generator as ``(obs, prefactor)`` (representing
+ :math:`G=p \hat{O}`), where:
- - observable `\hat{O}` is one of :class:`~.Hermitian`,
+ - observable :math:`\hat{O}` is one of :class:`~.Hermitian`,
:class:`~.SparseHamiltonian`, or a tensor product
of Pauli words.
- - prefactor :math:`p` is a float
-
- The prefactor will in most cases be :math:`\pm 1.0`, unless the generator is a single Pauli
- word, in which case the prefactor is the coefficient of the Pauli word.
+ - prefactor :math:`p` is a float.
* ``"observable"``: Return the generator as a single observable as directly defined
by ``op``. Returned generators may be any type of observable, including
@@ -152,16 +176,22 @@ def generator(op, format="prefactor"):
>>> op = qml.RX(0.2, wires=0)
>>> qml.generator(op, format="prefactor") # output will always be (prefactor, obs)
- (Projector([1], wires=[0]), 1.0)
+ (PauliX(wires=[0]), -0.5)
>>> qml.generator(op, format="hamiltonian") # output will always be a Hamiltonian
- >>> qml.generator(op, format="observable") # ouput will be a simplified obs where possible
+ >>> qml.generator(qml.PhaseShift(0.1, wires=0), format="observable") # ouput will be a simplified obs where possible
Projector([1], wires=[0])
+
"""
if op.num_params != 1:
raise ValueError(f"Operation {op.name} is not written in terms of a single parameter")
- gen = op.generator()
+ try:
+ gen = op.generator()
+ except TypeError:
+ # For backwards compatibility with PennyLane
+ # versions <=0.22, assume op.generator is a property
+ gen = _generator_backcompatibility(op)
if not isinstance(gen, qml.operation.Observable):
raise qml.QuantumFunctionError(
diff --git a/pennylane/ops/functions/matrix.py b/pennylane/ops/functions/matrix.py
index 4a623032ecf..58ace17bab2 100644
--- a/pennylane/ops/functions/matrix.py
+++ b/pennylane/ops/functions/matrix.py
@@ -46,7 +46,7 @@ def matrix(op, *, wire_order=None):
>>> x = torch.tensor(0.6, requires_grad=True)
>>> matrix_fn = qml.matrix(qml.RX)
- >>> matrix_fn(x)
+ >>> matrix_fn(x, wires=0)
tensor([[0.9553+0.0000j, 0.0000-0.2955j],
[0.0000-0.2955j, 0.9553+0.0000j]], grad_fn=)
diff --git a/pennylane/ops/identity.py b/pennylane/ops/identity.py
index 9b5d8b4366a..a5483b70e2c 100644
--- a/pennylane/ops/identity.py
+++ b/pennylane/ops/identity.py
@@ -64,7 +64,7 @@ def compute_eigvals(): # pylint: disable=arguments-differ
**Example**
- >>> qml.Identity.compute_eigvals()
+ >>> print(qml.Identity.compute_eigvals())
[ 1 1]
"""
return np.array([1, 1])
@@ -83,7 +83,7 @@ def compute_matrix(): # pylint: disable=arguments-differ
**Example**
- >>> qml.Identity.compute_matrix()
+ >>> print(qml.Identity.compute_matrix())
[[1. 0.]
[0. 1.]]
"""
diff --git a/pennylane/ops/qubit/arithmetic_ops.py b/pennylane/ops/qubit/arithmetic_ops.py
index b8284241578..a24d5991bd0 100644
--- a/pennylane/ops/qubit/arithmetic_ops.py
+++ b/pennylane/ops/qubit/arithmetic_ops.py
@@ -107,7 +107,7 @@ def compute_matrix(): # pylint: disable=arguments-differ
**Example**
- >>> qml.QubitCarry.compute_matrix()
+ >>> print(qml.QubitCarry.compute_matrix())
[[1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
[0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
[0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0]
@@ -254,7 +254,7 @@ def compute_matrix(): # pylint: disable=arguments-differ
**Example**
- >>> qml.QubitSum.compute_matrix()
+ >>> print(qml.QubitSum.compute_matrix())
[[1 0 0 0 0 0 0 0]
[0 1 0 0 0 0 0 0]
[0 0 0 1 0 0 0 0]
diff --git a/pennylane/ops/qubit/matrix_ops.py b/pennylane/ops/qubit/matrix_ops.py
index 19f04cd99bc..53c0b6fc269 100644
--- a/pennylane/ops/qubit/matrix_ops.py
+++ b/pennylane/ops/qubit/matrix_ops.py
@@ -20,7 +20,7 @@
import numpy as np
import pennylane as qml
-from pennylane.operation import AnyWires, Operation
+from pennylane.operation import AnyWires, Operation, DecompositionUndefinedError
from pennylane.wires import Wires
@@ -241,6 +241,10 @@ def __init__(
total_wires = control_wires + wires
super().__init__(*params, wires=total_wires, do_queue=do_queue)
+ @staticmethod
+ def compute_decomposition(*params, wires=None, **hyperparameters):
+ raise DecompositionUndefinedError
+
@staticmethod
def compute_matrix(
U, control_wires, u_wires, control_values=None
diff --git a/pennylane/ops/qubit/non_parametric_ops.py b/pennylane/ops/qubit/non_parametric_ops.py
index 56c16f48612..6e25e9c7f74 100644
--- a/pennylane/ops/qubit/non_parametric_ops.py
+++ b/pennylane/ops/qubit/non_parametric_ops.py
@@ -66,7 +66,7 @@ def compute_matrix(): # pylint: disable=arguments-differ
**Example**
- >>> qml.Hadamard.compute_matrix()
+ >>> print(qml.Hadamard.compute_matrix())
[[ 0.70710678 0.70710678]
[ 0.70710678 -0.70710678]]
"""
@@ -92,7 +92,7 @@ def compute_eigvals(): # pylint: disable=arguments-differ
**Example**
- >>> qml.Hadamard.compute_eigvals()
+ >>> print(qml.Hadamard.compute_eigvals())
[ 1 -1]
"""
return pauli_eigs(1)
@@ -117,7 +117,7 @@ def compute_diagonalizing_gates(wires):
**Example**
- >>> qml.Hadamard.compute_diagonalizing_gates(wires=[0])
+ >>> print(qml.Hadamard.compute_diagonalizing_gates(wires=[0]))
[RY(-0.7853981633974483, wires=[0])]
"""
return [qml.RY(-np.pi / 4, wires=wires)]
@@ -138,7 +138,7 @@ def compute_decomposition(wires):
**Example:**
- >>> qml.Hadamard.compute_decomposition(0)
+ >>> print(qml.Hadamard.compute_decomposition(0))
[PhaseShift(1.5707963267948966, wires=[0]),
RX(1.5707963267948966, wires=[0]),
PhaseShift(1.5707963267948966, wires=[0])]
@@ -199,7 +199,7 @@ def compute_matrix(): # pylint: disable=arguments-differ
**Example**
- >>> qml.PauliX.compute_matrix()
+ >>> print(qml.PauliX.compute_matrix())
[[0 1]
[1 0]]
"""
@@ -225,7 +225,7 @@ def compute_eigvals(): # pylint: disable=arguments-differ
**Example**
- >>> qml.PauliX.compute_eigvals()
+ >>> print(qml.PauliX.compute_eigvals())
[ 1 -1]
"""
return pauli_eigs(1)
@@ -250,7 +250,7 @@ def compute_diagonalizing_gates(wires):
**Example**
- >>> qml.PauliX.compute_diagonalizing_gates(wires=[0])
+ >>> print(qml.PauliX.compute_diagonalizing_gates(wires=[0]))
[Hadamard(wires=[0])]
"""
return [Hadamard(wires=wires)]
@@ -272,7 +272,7 @@ def compute_decomposition(wires):
**Example:**
- >>> qml.PauliX.compute_decomposition(0)
+ >>> print(qml.PauliX.compute_decomposition(0))
[PhaseShift(1.5707963267948966, wires=[0]),
RX(3.141592653589793, wires=[0]),
PhaseShift(1.5707963267948966, wires=[0])]
@@ -335,7 +335,7 @@ def compute_matrix(): # pylint: disable=arguments-differ
**Example**
- >>> qml.PauliY.compute_matrix()
+ >>> print(qml.PauliY.compute_matrix())
[[ 0.+0.j -0.-1.j]
[ 0.+1.j 0.+0.j]]
"""
@@ -361,7 +361,7 @@ def compute_eigvals(): # pylint: disable=arguments-differ
**Example**
- >>> qml.PauliY.compute_eigvals()
+ >>> print(qml.PauliY.compute_eigvals())
[ 1 -1]
"""
return pauli_eigs(1)
@@ -386,7 +386,7 @@ def compute_diagonalizing_gates(wires):
**Example**
- >>> qml.PauliY.compute_diagonalizing_gates(wires=[0])
+ >>> print(qml.PauliY.compute_diagonalizing_gates(wires=[0]))
[PauliZ(wires=[0]), S(wires=[0]), Hadamard(wires=[0])]
"""
return [
@@ -411,7 +411,7 @@ def compute_decomposition(wires):
**Example:**
- >>> qml.PauliY.compute_decomposition(0)
+ >>> print(qml.PauliY.compute_decomposition(0))
[PhaseShift(1.5707963267948966, wires=[0]),
RY(3.141592653589793, wires=[0]),
PhaseShift(1.5707963267948966, wires=[0])]
@@ -472,7 +472,7 @@ def compute_matrix(): # pylint: disable=arguments-differ
**Example**
- >>> qml.PauliZ.compute_matrix()
+ >>> print(qml.PauliZ.compute_matrix())
[[ 1 0]
[ 0 -1]]
"""
@@ -498,7 +498,7 @@ def compute_eigvals(): # pylint: disable=arguments-differ
**Example**
- >>> qml.PauliZ.compute_eigvals()
+ >>> print(qml.PauliZ.compute_eigvals())
[ 1 -1]
"""
return pauli_eigs(1)
@@ -524,7 +524,7 @@ def compute_diagonalizing_gates(wires): # pylint: disable=unused-argument
**Example**
- >>> qml.PauliZ.compute_diagonalizing_gates(wires=[0])
+ >>> print(qml.PauliZ.compute_diagonalizing_gates(wires=[0]))
[]
"""
return []
@@ -545,7 +545,7 @@ def compute_decomposition(wires):
**Example:**
- >>> qml.PauliZ.compute_decomposition(0)
+ >>> print(qml.PauliZ.compute_decomposition(0))
[PhaseShift(3.141592653589793, wires=[0])]
"""
@@ -599,7 +599,7 @@ def compute_matrix(): # pylint: disable=arguments-differ
**Example**
- >>> qml.S.compute_matrix()
+ >>> print(qml.S.compute_matrix())
[[1.+0.j 0.+0.j]
[0.+0.j 0.+1.j]]
"""
@@ -625,7 +625,7 @@ def compute_eigvals(): # pylint: disable=arguments-differ
**Example**
- >>> qml.S.compute_eigvals()
+ >>> print(qml.S.compute_eigvals())
[1.+0.j 0.+1.j]
"""
return np.array([1, 1j])
@@ -647,7 +647,7 @@ def compute_decomposition(wires):
**Example:**
- >>> qml.S.compute_decomposition(0)
+ >>> print(qml.S.compute_decomposition(0))
[PhaseShift(1.5707963267948966, wires=[0])]
"""
@@ -700,7 +700,7 @@ def compute_matrix(): # pylint: disable=arguments-differ
**Example**
- >>> qml.T.compute_matrix()
+ >>> print(qml.T.compute_matrix())
[[1.+0.j 0. +0.j ]
[0.+0.j 0.70710678+0.70710678j]]
"""
@@ -726,7 +726,7 @@ def compute_eigvals(): # pylint: disable=arguments-differ
**Example**
- >>> qml.T.compute_eigvals()
+ >>> print(qml.T.compute_eigvals())
[1.+0.j 0.70710678+0.70710678j]
"""
return np.array([1, cmath.exp(1j * np.pi / 4)])
@@ -748,7 +748,7 @@ def compute_decomposition(wires):
**Example:**
- >>> qml.T.compute_decomposition(0)
+ >>> print(qml.T.compute_decomposition(0))
[PhaseShift(0.7853981633974483, wires=[0])]
"""
@@ -801,7 +801,7 @@ def compute_matrix(): # pylint: disable=arguments-differ
**Example**
- >>> qml.SX.compute_matrix()
+ >>> print(qml.SX.compute_matrix())
[[0.5+0.5j 0.5-0.5j]
[0.5-0.5j 0.5+0.5j]]
"""
@@ -828,7 +828,7 @@ def compute_eigvals(): # pylint: disable=arguments-differ
**Example**
- >>> qml.SX.compute_eigvals()
+ >>> print(qml.SX.compute_eigvals())
[1.+0.j 0.+1.j]
"""
return np.array([1, 1j])
@@ -850,7 +850,7 @@ def compute_decomposition(wires):
**Example:**
- >>> qml.SX.compute_decomposition(0)
+ >>> print(qml.SX.compute_decomposition(0))
[RZ(1.5707963267948966, wires=[0]),
RY(1.5707963267948966, wires=[0]),
RZ(-3.141592653589793, wires=[0]),
@@ -920,7 +920,7 @@ def compute_matrix(): # pylint: disable=arguments-differ
**Example**
- >>> qml.CNOT.compute_matrix()
+ >>> print(qml.CNOT.compute_matrix())
[[1 0 0 0]
[0 1 0 0]
[0 0 0 1]
@@ -983,7 +983,7 @@ def compute_matrix(): # pylint: disable=arguments-differ
**Example**
- >>> qml.CZ.compute_matrix()
+ >>> print(qml.CZ.compute_matrix())
[[ 1 0 0 0]
[ 0 1 0 0]
[ 0 0 1 0]
@@ -1012,7 +1012,7 @@ def compute_eigvals(): # pylint: disable=arguments-differ
**Example**
- >>> qml.CZ.compute_eigvals()
+ >>> print(qml.CZ.compute_eigvals())
[1, 1, 1, -1]
"""
return np.array([1, 1, 1, -1])
@@ -1070,7 +1070,7 @@ def compute_matrix(): # pylint: disable=arguments-differ
**Example**
- >>> qml.CY.compute_matrix()
+ >>> print(qml.CY.compute_matrix())
[[ 1.+0.j 0.+0.j 0.+0.j 0.+0.j]
[ 0.+0.j 1.+0.j 0.+0.j 0.+0.j]
[ 0.+0.j 0.+0.j 0.+0.j -0.-1.j]
@@ -1103,7 +1103,7 @@ def compute_decomposition(wires):
**Example:**
- >>> qml.CY.compute_decomposition(0)
+ >>> print(qml.CY.compute_decomposition(0))
[CRY(3.141592653589793, wires=[0, 1]), S(wires=[0])]
"""
@@ -1154,7 +1154,7 @@ def compute_matrix(): # pylint: disable=arguments-differ
**Example**
- >>> qml.SWAP.compute_matrix()
+ >>> print(qml.SWAP.compute_matrix())
[[1 0 0 0]
[0 0 1 0]
[0 1 0 0]
@@ -1179,7 +1179,7 @@ def compute_decomposition(wires):
**Example:**
- >>> qml.SWAP.compute_decomposition((0,1))
+ >>> print(qml.SWAP.compute_decomposition((0,1)))
[CNOT(wires=[0, 1]), CNOT(wires=[1, 0]), CNOT(wires=[0, 1])]
"""
@@ -1234,7 +1234,7 @@ def compute_matrix(): # pylint: disable=arguments-differ
**Example**
- >>> qml.ISWAP.compute_matrix()
+ >>> print(qml.ISWAP.compute_matrix())
[[1.+0.j 0.+0.j 0.+0.j 0.+0.j]
[0.+0.j 0.+0.j 0.+1.j 0.+0.j]
[0.+0.j 0.+1.j 0.+0.j 0.+0.j]
@@ -1263,7 +1263,7 @@ def compute_eigvals(): # pylint: disable=arguments-differ
**Example**
- >>> qml.ISWAP.compute_eigvals()
+ >>> print(qml.ISWAP.compute_eigvals())
[1j, -1j, 1, 1]
"""
return np.array([1j, -1j, 1, 1])
@@ -1285,7 +1285,7 @@ def compute_decomposition(wires):
**Example:**
- >>> qml.ISWAP.compute_decomposition((0,1))
+ >>> print(qml.ISWAP.compute_decomposition((0,1)))
[S(wires=[0]),
S(wires=[1]),
Hadamard(wires=[0]),
@@ -1348,7 +1348,7 @@ def compute_matrix(): # pylint: disable=arguments-differ
**Example**
- >>> qml.SISWAP.compute_matrix()
+ >>> print(qml.SISWAP.compute_matrix())
[[1.+0.j 0.+0.j 0.+0.j 0.+0.j]
[0.+0.j 0.70710678+0.j 0.+0.70710678j 0.+0.j]
[0.+0.j 0.+0.70710678j 0.70710678+0.j 0.+0.j]
@@ -1384,7 +1384,7 @@ def compute_eigvals(): # pylint: disable=arguments-differ
**Example**
- >>> qml.SISWAP.compute_eigvals()
+ >>> print(qml.SISWAP.compute_eigvals())
[0.70710678+0.70710678j 0.70710678-0.70710678j 1.+0.j 1.+0.j]
"""
return np.array([INV_SQRT2 * (1 + 1j), INV_SQRT2 * (1 - 1j), 1, 1])
@@ -1406,7 +1406,7 @@ def compute_decomposition(wires):
**Example:**
- >>> qml.SISWAP.compute_decomposition((0,1))
+ >>> print(qml.SISWAP.compute_decomposition((0,1)))
[SX(wires=[0]),
RZ(1.5707963267948966, wires=[0]),
CNOT(wires=[0, 1]),
@@ -1493,7 +1493,7 @@ def compute_matrix(): # pylint: disable=arguments-differ
**Example**
- >>> qml.CSWAP.compute_matrix()
+ >>> print(qml.CSWAP.compute_matrix())
[[1 0 0 0 0 0 0 0]
[0 1 0 0 0 0 0 0]
[0 0 1 0 0 0 0 0]
@@ -1533,7 +1533,7 @@ def compute_decomposition(wires):
**Example:**
- >>> qml.CSWAP.compute_decomposition((0,1,2))
+ >>> print(qml.CSWAP.compute_decomposition((0,1,2)))
[Toffoli(wires=[0, 2, 1]), Toffoli(wires=[0, 1, 2]), Toffoli(wires=[0, 2, 1])]
"""
@@ -1602,7 +1602,7 @@ def compute_matrix(): # pylint: disable=arguments-differ
**Example**
- >>> qml.Toffoli.compute_matrix()
+ >>> print(qml.Toffoli.compute_matrix())
[[1 0 0 0 0 0 0 0]
[0 1 0 0 0 0 0 0]
[0 0 1 0 0 0 0 0]
@@ -1642,7 +1642,7 @@ def compute_decomposition(wires):
**Example:**
- >>> qml.Toffoli.compute_decomposition((0,1,2))
+ >>> print(qml.Toffoli.compute_decomposition((0,1,2)))
[Hadamard(wires=[2]),
CNOT(wires=[1, 2]),
T.inv(wires=[2]),
@@ -1812,12 +1812,12 @@ def compute_matrix(
**Example**
- >>> qml.MultiControlledX.compute_matrix([0], '1')
+ >>> print(qml.MultiControlledX.compute_matrix([0], '1'))
[[1. 0. 0. 0.]
[0. 1. 0. 0.]
[0. 0. 0. 1.]
[0. 0. 1. 0.]]
- >>> qml.MultiControlledX.compute_matrix([1], '0')
+ >>> print(qml.MultiControlledX.compute_matrix([1], '0'))
[[0. 1. 0. 0.]
[1. 0. 0. 0.]
[0. 0. 1. 0.]
@@ -1875,7 +1875,7 @@ def compute_decomposition(wires=None, work_wires=None, control_values=None, **kw
**Example:**
- >>> qml.MultiControlledX.compute_decomposition(wires=[0,1,2,3],control_values="111", work_wires=qml.wires.Wires("aux"))
+ >>> print(qml.MultiControlledX.compute_decomposition(wires=[0,1,2,3],control_values="111", work_wires=qml.wires.Wires("aux")))
[Toffoli(wires=[2, 'aux', 3]),
Toffoli(wires=[0, 1, 'aux']),
Toffoli(wires=[2, 'aux', 3]),
@@ -2041,7 +2041,7 @@ def compute_decomposition(wires, only_visual=False): # pylint: disable=unused-a
**Example:**
- >>> qml.Barrier.compute_decomposition(0)
+ >>> print(qml.Barrier.compute_decomposition(0))
[]
"""
@@ -2050,7 +2050,10 @@ def compute_decomposition(wires, only_visual=False): # pylint: disable=unused-a
def label(self, decimals=None, base_label=None, cache=None):
return "||"
- def adjoint(self):
+ def _controlled(self, _):
+ return Barrier(wires=self.wires)
+
+ def adjoint(self, do_queue=False):
return Barrier(wires=self.wires)
@@ -2089,7 +2092,7 @@ def compute_decomposition(wires):
**Example:**
- >>> qml.WireCut.compute_decomposition(0)
+ >>> print(qml.WireCut.compute_decomposition(0))
[]
"""
diff --git a/pennylane/ops/snapshot.py b/pennylane/ops/snapshot.py
index c99840b3b99..bc838d058ef 100644
--- a/pennylane/ops/snapshot.py
+++ b/pennylane/ops/snapshot.py
@@ -15,8 +15,6 @@
This module contains the Snapshot (pseudo) operation that is common to both
cv and qubit computing paradigms in PennyLane.
"""
-import numpy as np
-
from pennylane.operation import AnyWires, Operation
@@ -35,6 +33,29 @@ class Snapshot(Operation):
Args:
tag (str or None): An optional custom tag for the snapshot, used to index it
in the snapshots dictionary.
+
+ **Example**
+
+ .. code-block:: python3
+
+ dev = qml.device("default.qubit", wires=2)
+
+ @qml.qnode(dev, interface=None)
+ def circuit():
+ qml.Snapshot()
+ qml.Hadamard(wires=0)
+ qml.Snapshot("very_important_state")
+ qml.CNOT(wires=[0, 1])
+ qml.Snapshot()
+ return qml.expval(qml.PauliX(0))
+
+ >>> qml.snapshots(circuit)()
+ {0: array([1.+0.j, 0.+0.j, 0.+0.j, 0.+0.j]),
+ 'very_important_state': array([0.70710678+0.j, 0.+0.j, 0.70710678+0.j, 0.+0.j]),
+ 2: array([0.70710678+0.j, 0.+0.j, 0.+0.j, 0.70710678+0.j]),
+ 'execution_results': array(0.)}
+
+ .. seealso:: :func:`~.snapshots`
"""
num_wires = AnyWires
num_params = 0
@@ -51,7 +72,8 @@ def label(self, decimals=None, base_label=None, cache=None):
def compute_decomposition(*params, wires=None, **hyperparameters):
return []
- # TODO: remove once pennylane-lightning#242 is resolved
- @staticmethod
- def compute_matrix(*params, **hyperparams):
- return np.eye(2)
+ def _controlled(self, _):
+ return Snapshot(tag=self.tag)
+
+ def adjoint(self, do_queue=False):
+ return Snapshot(tag=self.tag)
diff --git a/pennylane/optimize/lie_algebra.py b/pennylane/optimize/lie_algebra.py
index 9d2720dc572..8a21e5ee1eb 100644
--- a/pennylane/optimize/lie_algebra.py
+++ b/pennylane/optimize/lie_algebra.py
@@ -90,8 +90,8 @@ def algebra_commutator(tape, observables, lie_algebra_basis_names, nqubits):
for obs in observables:
for o in obs:
# create a list of tapes for the plus and minus shifted circuits
- tapes_plus = [qml.tape.JacobianTape(p + "_p") for p in lie_algebra_basis_names]
- tapes_min = [qml.tape.JacobianTape(p + "_m") for p in lie_algebra_basis_names]
+ tapes_plus = [qml.tape.QuantumTape(p + "_p") for p in lie_algebra_basis_names]
+ tapes_min = [qml.tape.QuantumTape(p + "_m") for p in lie_algebra_basis_names]
# loop through all operations on the input tape
for op in tape.operations:
diff --git a/pennylane/qnode.py b/pennylane/qnode.py
index 57b5cdf9bbd..692402899d7 100644
--- a/pennylane/qnode.py
+++ b/pennylane/qnode.py
@@ -482,7 +482,7 @@ def tape(self):
def construct(self, args, kwargs):
"""Call the quantum function with a tape context, ensuring the operations get queued."""
- self._tape = qml.tape.JacobianTape()
+ self._tape = qml.tape.QuantumTape()
with self.tape:
self._qfunc_output = self.func(*args, **kwargs)
diff --git a/pennylane/qnode_old.py b/pennylane/qnode_old.py
index 45196ac0a19..64a7755e597 100644
--- a/pennylane/qnode_old.py
+++ b/pennylane/qnode_old.py
@@ -40,6 +40,11 @@
class QNode:
"""Represents a quantum node in the hybrid computational graph.
+ .. warning::
+
+ This QNode is deprecated and due to be removed in an upcoming
+ release. Please use :class:`qml.QNode <.pennylane.QNode>` instead.
+
A *quantum node* contains a :ref:`quantum function `
(corresponding to a :ref:`variational circuit `)
and the computational device it is executed on.
@@ -163,6 +168,11 @@ def __init__(
argnum=None,
**kwargs,
):
+ warnings.warn(
+ "qml.qnode_old.QNode is deprecated, and will be removed in an "
+ "upcoming release. Please use qml.QNode instead.",
+ UserWarning,
+ )
if diff_method is None:
# TODO: update this behaviour once the new differentiable pipeline is the default
diff --git a/pennylane/tape/cv_param_shift.py b/pennylane/tape/cv_param_shift.py
index eb5b4092d58..0794dd72066 100644
--- a/pennylane/tape/cv_param_shift.py
+++ b/pennylane/tape/cv_param_shift.py
@@ -33,6 +33,12 @@
class CVParamShiftTape(QubitParamShiftTape):
r"""Quantum tape for CV parameter-shift analytic differentiation method.
+ .. warning::
+
+ The ``CVParamShiftTape`` is deprecated.
+ Please use a standard :class:`~.QuantumTape`, and apply gradient transforms using
+ the :mod:`.gradients` module to compute parameter-shift gradients.
+
This class extends the :class:`~.jacobian` method of the quantum tape
to support analytic gradients of Gaussian CV operations using the parameter-shift rule.
This gradient method returns *exact* gradients, and can be computed directly
diff --git a/pennylane/tape/jacobian_tape.py b/pennylane/tape/jacobian_tape.py
index 06b41b88664..3ce99ef2d01 100644
--- a/pennylane/tape/jacobian_tape.py
+++ b/pennylane/tape/jacobian_tape.py
@@ -51,6 +51,12 @@ class JacobianTape(QuantumTape):
See :mod:`pennylane.tape` for more details.
+ .. warning::
+
+ The ``JacobianTape`` as well as the ``JacobianTape.jacobian()`` method is deprecated.
+ Please use a standard :class:`~.QuantumTape`, and apply gradient transforms using
+ the :mod:`.gradients` module to compute Jacobians.
+
Args:
name (str): a name given to the quantum tape
do_queue (bool): Whether to queue this tape in a parent tape context.
@@ -439,6 +445,12 @@ def _choose_params_with_methods(diff_methods, argnum):
def jacobian(self, device, params=None, **options):
r"""Compute the Jacobian of the parametrized quantum circuit recorded by the quantum tape.
+ .. warning::
+
+ The ``JacobianTape`` as well as the ``JacobianTape.jacobian()`` method is deprecated.
+ Please use a standard :class:`~.QuantumTape`, and apply gradient transforms using
+ the :mod:`.gradients` module to compute Jacobians.
+
The quantum tape can be interpreted as a simple :math:`\mathbb{R}^m \to \mathbb{R}^n` function,
mapping :math:`m` (trainable) gate parameters to :math:`n` measurement statistics,
such as expectation values or probabilities.
@@ -543,6 +555,13 @@ def jacobian(self, device, params=None, **options):
array([], shape=(4, 0), dtype=float64)
"""
# pylint: disable=too-many-statements
+
+ warnings.warn(
+ "Differentiating tapes using JacobianTape.jacobian() is deprecated. "
+ "Please use ta standard QuantumTape with gradient transforms from "
+ "the qml.gradients module instead."
+ )
+
if any(m.return_type is State for m in self.measurements):
raise ValueError("The jacobian method does not support circuits that return the state")
@@ -665,6 +684,12 @@ def jacobian(self, device, params=None, **options):
def hessian(self, device, params=None, **options):
r"""Compute the Hessian of the parametrized quantum circuit recorded by the quantum tape.
+ .. warning::
+
+ The ``JacobianTape`` as well as the ``JacobianTape.hessian()`` method is deprecated.
+ Please use a standard :class:`~.QuantumTape`, and apply gradient transforms using
+ the :mod:`.gradients` module to compute Hessians.
+
The quantum tape can be interpreted as a simple :math:`\mathbb{R}^m \to \mathbb{R}^n` function,
mapping :math:`m` (trainable) gate parameters to :math:`n` measurement statistics,
such as expectation values or probabilities.
@@ -738,6 +763,12 @@ def hessian(self, device, params=None, **options):
>>> tape.hessian(dev)
array([], shape=(0, 0), dtype=float64)
"""
+ warnings.warn(
+ "Differentiating tapes using JacobianTape.hessian() is deprecated. "
+ "Please use ta standard QuantumTape with gradient transforms from "
+ "the qml.gradients module instead."
+ )
+
if any(m.return_type is State for m in self.measurements):
raise ValueError("The Hessian method does not support circuits that return the state")
diff --git a/pennylane/tape/qubit_param_shift.py b/pennylane/tape/qubit_param_shift.py
index 8a4898904d3..9457d83accb 100644
--- a/pennylane/tape/qubit_param_shift.py
+++ b/pennylane/tape/qubit_param_shift.py
@@ -82,6 +82,12 @@ def _get_operation_recipe(op, p_idx, shifts):
class QubitParamShiftTape(JacobianTape):
r"""Quantum tape for qubit parameter-shift analytic differentiation method.
+ .. warning::
+
+ The ``QubitParamShiftTape`` is deprecated.
+ Please use a standard :class:`~.QuantumTape`, and apply gradient transforms using
+ the :mod:`.gradients` module to compute parameter-shift gradients.
+
This class extends the :class:`~.jacobian` method of the quantum tape
to support analytic gradients of qubit operations using the parameter-shift rule.
This gradient method returns *exact* gradients, and can be computed directly
diff --git a/pennylane/tape/reversible.py b/pennylane/tape/reversible.py
index f2a9a3e9ee5..df7937b2041 100644
--- a/pennylane/tape/reversible.py
+++ b/pennylane/tape/reversible.py
@@ -34,6 +34,12 @@
class ReversibleTape(JacobianTape):
r"""Quantum tape for computing gradients via reversible analytic differentiation.
+ .. warning::
+
+ The ``ReversibleTape`` is deprecated.
+ Instead, create a standard ``QuantumTape``, and use devices that support
+ native adjoint differentiation methods, such as ``default.qubit`` and ``lightning.qubit``.
+
.. note::
The reversible analytic differentiation method has the following restrictions:
diff --git a/pennylane/tape/tape.py b/pennylane/tape/tape.py
index ee544552c56..1c4b3749dd1 100644
--- a/pennylane/tape/tape.py
+++ b/pennylane/tape/tape.py
@@ -19,6 +19,7 @@
import contextlib
import copy
from threading import RLock
+import warnings
import numpy as np
@@ -118,10 +119,10 @@ def expand_tape(tape, depth=1, stop_at=None, expand_measurements=False):
.. code-block:: python
- with JacobianTape() as tape:
+ with QuantumTape() as tape:
qml.BasisState(np.array([1, 1]), wires=[0, 'a'])
- with JacobianTape() as tape2:
+ with QuantumTape() as tape2:
qml.Rot(0.543, 0.1, 0.4, wires=0)
qml.CNOT(wires=[0, 'a'])
@@ -132,7 +133,7 @@ def expand_tape(tape, depth=1, stop_at=None, expand_measurements=False):
>>> tape.operations
[BasisState(array([1, 1]), wires=[0, 'a']),
- ,
+ ,
CNOT(wires=[0, 'a']),
RY(0.2, wires=['a'])]
@@ -254,19 +255,11 @@ class QuantumTape(AnnotatedQueue):
Once constructed, the quantum tape can be executed directly on a supported
- device:
+ device via the :func:`~.execute` function:
>>> dev = qml.device("default.qubit", wires=[0, 'a'])
-
- Execution can take place either using the in-place constructed parameters,
-
- >>> tape.execute(dev)
- [0.77750694]
-
- or by providing parameters at run time:
-
- >>> tape.execute(dev, params=[0.1, 0.1, 0.1])
- [0.99003329]
+ >>> qml.execute([tape], dev, gradient_fn=None)
+ [array([0.77750694])]
The trainable parameters of the tape can be explicitly set, and the values of
the parameters modified in-place:
@@ -539,10 +532,10 @@ def expand(self, depth=1, stop_at=None, expand_measurements=False):
.. code-block:: python
- with JacobianTape() as tape:
+ with QuantumTape() as tape:
qml.BasisState(np.array([1, 1]), wires=[0, 'a'])
- with JacobianTape() as tape2:
+ with QuantumTape() as tape2:
qml.Rot(0.543, 0.1, 0.4, wires=0)
qml.CNOT(wires=[0, 'a'])
@@ -553,7 +546,7 @@ def expand(self, depth=1, stop_at=None, expand_measurements=False):
>>> tape.operations
[BasisState(array([1, 1]), wires=[0, 'a']),
- ,
+ ,
CNOT(wires=[0, 'a']),
RY(0.2, wires=['a'])]
@@ -590,7 +583,7 @@ def inv(self):
.. code-block:: python
- with JacobianTape() as tape:
+ with QuantumTape() as tape:
qml.BasisState(np.array([1, 1]), wires=[0, 'a'])
qml.RX(0.432, wires=0)
qml.Rot(0.543, 0.1, 0.4, wires=0).inv()
@@ -709,21 +702,20 @@ def trainable_params(self):
automatically excluded from the Jacobian computation.
The number of trainable parameters determines the number of parameters passed to
- :meth:`~.set_parameters`, :meth:`~.execute`, and :meth:`~.JacobianTape.jacobian`,
- and changes the default output size of methods :meth:`~.JacobianTape.jacobian` and
- :meth:`~.get_parameters()`.
+ :meth:`~.set_parameters`, and changes the default output size of method :meth:`~.get_parameters()`.
.. note::
- Since the :meth:`~.JacobianTape.jacobian` method is not called for devices that support
- native backpropagation (such as ``default.qubit.tf`` and ``default.qubit.autograd``),
- this property contains no relevant information when using backpropagation to compute gradients.
+ For devices that support native backpropagation (such as
+ ``default.qubit.tf`` and ``default.qubit.autograd``), this
+ property contains no relevant information when using
+ backpropagation to compute gradients.
**Example**
.. code-block:: python
- with JacobianTape() as tape:
+ with QuantumTape() as tape:
qml.RX(0.432, wires=0)
qml.RY(0.543, wires=0)
qml.CNOT(wires=[0, 'a'])
@@ -792,7 +784,7 @@ def get_parameters(self, trainable_only=True, **kwargs): # pylint:disable=unuse
.. code-block:: python
- with JacobianTape() as tape:
+ with QuantumTape() as tape:
qml.RX(0.432, wires=0)
qml.RY(0.543, wires=0)
qml.CNOT(wires=[0, 'a'])
@@ -839,7 +831,7 @@ def set_parameters(self, params, trainable_only=True):
.. code-block:: python
- with JacobianTape() as tape:
+ with QuantumTape() as tape:
qml.RX(0.432, wires=0)
qml.RY(0.543, wires=0)
qml.CNOT(wires=[0, 'a'])
@@ -926,7 +918,7 @@ def operations(self):
.. code-block:: python
- with JacobianTape() as tape:
+ with QuantumTape() as tape:
qml.RX(0.432, wires=0)
qml.RY(0.543, wires=0)
qml.CNOT(wires=[0, 'a'])
@@ -949,7 +941,7 @@ def observables(self):
.. code-block:: python
- with JacobianTape() as tape:
+ with QuantumTape() as tape:
qml.RX(0.432, wires=0)
qml.RY(0.543, wires=0)
qml.CNOT(wires=[0, 'a'])
@@ -984,7 +976,7 @@ def measurements(self):
.. code-block:: python
- with JacobianTape() as tape:
+ with QuantumTape() as tape:
qml.RX(0.432, wires=0)
qml.RY(0.543, wires=0)
qml.CNOT(wires=[0, 'a'])
@@ -1305,6 +1297,11 @@ def hash(self):
def execute(self, device, params=None):
"""Execute the tape on a quantum device.
+ .. warning::
+
+ Executing tapes using ``tape.execute(dev)`` is deprecated.
+ Please use the :func:`~.execute` function instead.
+
Args:
device (.Device): a PennyLane device
that can execute quantum operations and return measurement statistics
@@ -1315,7 +1312,7 @@ def execute(self, device, params=None):
.. code-block:: python
- with JacobianTape() as tape:
+ with QuantumTape() as tape:
qml.RX(0.432, wires=0)
qml.RY(0.543, wires=0)
qml.CNOT(wires=[0, 'a'])
@@ -1339,6 +1336,11 @@ def execute(self, device, params=None):
>>> tape.get_parameters()
[0.432, 0.543, 0.133]
"""
+ warnings.warn(
+ "Executing tapes using tape.execute(dev) is deprecated. "
+ "Please use the qml.execute([tape], dev) function instead."
+ )
+
if params is None:
params = self.get_parameters()
@@ -1352,6 +1354,11 @@ def execute_device(self, params, device):
For more details on differentiable tape execution, see :meth:`~.execute`.
+ .. warning::
+
+ Executing tapes using ``tape.execute(dev)`` is deprecated.
+ Please use the :func:`~.execute` function instead.
+
Args:
device (~.Device): a PennyLane device
that can execute quantum operations and return measurement statistics
diff --git a/pennylane/transforms/batch_input.py b/pennylane/transforms/batch_input.py
index da48b8824c3..b2fd48ad854 100644
--- a/pennylane/transforms/batch_input.py
+++ b/pennylane/transforms/batch_input.py
@@ -66,13 +66,12 @@ def circuit(inputs, weights):
qml.RY(weights[1], wires=1)
return qml.expval(qml.PauliZ(1))
- >>> x = np.random.uniform(0,1,(10,2))
- >>> x.requires_grad = False
- >>> w = np.random.uniform(0,1,2)
+ >>> x = tf.random.uniform((10, 2), 0, 1)
+ >>> w = tf.random.uniform((2,), 0, 1)
>>> circuit(x, w)
+ array([0.46230079, 0.73971315, 0.95666004, 0.5355225 , 0.66180948,
+ 0.44519553, 0.93874261, 0.9483197 , 0.78737918, 0.90866411])>
"""
parameters = tape.get_parameters(trainable_only=False)
diff --git a/pennylane/transforms/batch_transform.py b/pennylane/transforms/batch_transform.py
index c92bd14c666..ba46a70faca 100644
--- a/pennylane/transforms/batch_transform.py
+++ b/pennylane/transforms/batch_transform.py
@@ -76,8 +76,8 @@ def my_transform(tape, a, b):
'''Generates two tapes, one with all RX replaced with RY,
and the other with all RX replaced with RZ.'''
- tape1 = qml.tape.JacobianTape()
- tape2 = qml.tape.JacobianTape()
+ tape1 = qml.tape.QuantumTape()
+ tape2 = qml.tape.QuantumTape()
# loop through all operations on the input tape
for op in tape.operations + tape.measurements:
@@ -102,7 +102,7 @@ def processing_fn(results):
We can apply this transform to a quantum tape:
- >>> with qml.tape.JacobianTape() as tape:
+ >>> with qml.tape.QuantumTape() as tape:
... qml.Hadamard(wires=0)
... qml.RX(-0.5, wires=0)
... qml.expval(qml.PauliX(0))
diff --git a/pennylane/transforms/commutation_dag.py b/pennylane/transforms/commutation_dag.py
index e911a74128b..570bc3558ab 100644
--- a/pennylane/transforms/commutation_dag.py
+++ b/pennylane/transforms/commutation_dag.py
@@ -65,7 +65,8 @@ def circuit(x, y, z):
the form ``(ID, CommutationDAGNode)``:
>>> nodes = dag.get_nodes()
- [(0, ), ...]
+ >>> nodes
+ NodeDataView({0: , ...}, data='node')
You can also access specific nodes (of type :class:`~.CommutationDAGNode`) by using the :meth:`~.get_node`
method. See :class:`~.CommutationDAGNode` for a list of available
@@ -205,7 +206,8 @@ def simplify_rotation(rot):
def simplify_controlled_rotation(crot):
- r"""Simplify a general one qubit controlled rotation into CRX, CRY, CRZ and CH.
+ r"""Simplify a general one qubit controlled rotation into CRX, CRY, CRZ and
+ controlled-Hadamard.
Args:
crot (pennylane.CRot): One qubit controlled rotation.
@@ -286,8 +288,8 @@ def simplify_u3(u3):
def simplify(operation):
r"""Simplify the (controlled) rotation operations :class:`~.Rot`,
:class:`~.U2`, :class:`~.U3`, and :class:`~.CRot` into one of
- :class:`~.RX`, :class:`~.CRX`, :class:`~.RY`, :class:`~.CRY`, :class:`~.`RZ`,
- :class:`~.CZ`, :class:`~.H` and :class:`~.CH` where possible.
+ :class:`~.RX`, :class:`~.CRX`, :class:`~.RY`, :class:`~.CRY`, :class:`~.RZ`,
+ :class:`~.CZ`, :class:`~.Hadamard` and controlled-Hadamard where possible.
Args:
operation (.Operation): Rotation or controlled rotation.
@@ -755,10 +757,10 @@ def _merge_no_duplicates(*iterables):
"""Merge K list without duplicate using python heapq ordered merging.
Args:
- *iterables: A list of k sorted lists
+ *iterables: A list of k sorted lists.
Yields:
- Iterator: List from the merging of the k ones (without duplicates)
+ Iterator: List from the merging of the k ones (without duplicates).
"""
last = object()
for val in heapq.merge(*iterables):
@@ -897,7 +899,7 @@ def get_node(self, node_id):
return self._multi_graph.nodes(data="node")[node_id]
def get_nodes(self):
- """Return iterable to loop through all the nodes in the DAG
+ """Return iterable to loop through all the nodes in the DAG.
Returns:
networkx.classes.reportviews.NodeDataView: Iterable nodes.
diff --git a/pennylane/transforms/condition.py b/pennylane/transforms/condition.py
index 468177d8ada..d0da00a4323 100644
--- a/pennylane/transforms/condition.py
+++ b/pennylane/transforms/condition.py
@@ -14,7 +14,6 @@
"""
Contains the condition transform.
"""
-from copy import copy
from functools import wraps
from typing import Type
@@ -88,21 +87,148 @@ def cond(condition, true_fn, false_fn=None):
dev = qml.device("default.qubit", wires=3)
- first_par = 0.1
- sec_par = 0.3
-
@qml.qnode(dev)
- def qnode():
+ def qnode(x, y):
+ qml.Hadamard(0)
m_0 = qml.measure(0)
- qml.cond(m_0, qml.RY)(first_par, wires=1)
+ qml.cond(m_0, qml.RY)(x, wires=1)
+ qml.Hadamard(2)
+ qml.RY(-np.pi/2, wires=[2])
m_1 = qml.measure(2)
- qml.cond(m_0, qml.RZ)(sec_par, wires=1)
+ qml.cond(m_1 == 0, qml.RX)(y, wires=1)
return qml.expval(qml.PauliZ(1))
+
+ .. code-block :: pycon
+
+ >>> first_par = np.array(0.3, requires_grad=True)
+ >>> sec_par = np.array(1.23, requires_grad=True)
+ >>> qnode(first_par, sec_par)
+ tensor(0.32677361, requires_grad=True)
+
+ .. note::
+
+ If the first argument of ``cond`` is a measurement value (e.g., ``m_0``
+ in ``qml.cond(m_0, qml.RY)``), then ``m_0 == 1`` is considered
+ internally.
+
+ .. UsageDetails::
+
+ **Conditional quantum functions**
+
+ The ``cond`` transform allows conditioning quantum functions too:
+
+ .. code-block:: python3
+
+ dev = qml.device("default.qubit", wires=2)
+
+ def qfunc(par, wires):
+ qml.Hadamard(wires[0])
+ qml.RY(par, wires[0])
+
+ @qml.qnode(dev)
+ def qnode(x):
+ qml.Hadamard(0)
+ m_0 = qml.measure(0)
+ qml.cond(m_0, qfunc)(x, wires=[1])
+ return qml.expval(qml.PauliZ(1))
+
+ .. code-block :: pycon
+
+ >>> par = np.array(0.3, requires_grad=True)
+ >>> qnode(par)
+ tensor(0.3522399, requires_grad=True)
+
+ **Passing two quantum functions**
+
+ In the qubit model, single-qubit measurements may result in one of two
+ outcomes. Such measurement outcomes may then be used to create
+ conditional expressions.
+
+ According to the truth value of the conditional expression passed to
+ ``cond``, the transform can apply a quantum function in both the
+ ``True`` and ``False`` case:
+
+ .. code-block:: python3
+
+ dev = qml.device("default.qubit", wires=2)
+
+ def qfunc1(x, wires):
+ qml.Hadamard(wires[0])
+ qml.RY(x, wires[0])
+
+ def qfunc2(x, wires):
+ qml.Hadamard(wires[0])
+ qml.RZ(x, wires[0])
+
+ @qml.qnode(dev)
+ def qnode1(x):
+ qml.Hadamard(0)
+ m_0 = qml.measure(0)
+ qml.cond(m_0, qfunc1, qfunc2)(x, wires=[1])
+ return qml.expval(qml.PauliZ(1))
+
+ .. code-block :: pycon
+
+ >>> par = np.array(0.3, requires_grad=True)
+ >>> qnode1(par)
+ tensor(-0.1477601, requires_grad=True)
+
+ The previous QNode is equivalent to using ``cond`` twice, inverting the
+ conditional expression in the second case using the ``~`` unary
+ operator:
+
+ .. code-block:: python3
+
+ @qml.qnode(dev)
+ def qnode2(x):
+ qml.Hadamard(0)
+ m_0 = qml.measure(0)
+ qml.cond(m_0, qfunc1)(x, wires=[1])
+ qml.cond(~m_0, qfunc2)(x, wires=[1])
+ return qml.expval(qml.PauliZ(1))
+
+ .. code-block :: pycon
+
+ >>> qnode2(par)
+ tensor(-0.1477601, requires_grad=True)
+
+ **Quantum functions with different signatures**
+
+ It may be that the two quantum functions passed to ``qml.cond`` have
+ different signatures. In such a case, ``lambda`` functions taking no
+ arguments can be used with Python closure:
+
+ .. code-block:: python3
+
+ dev = qml.device("default.qubit", wires=2)
+
+ def qfunc1(x, wire):
+ qml.Hadamard(wire)
+ qml.RY(x, wire)
+
+ def qfunc2(x, y, z, wire):
+ qml.Hadamard(wire)
+ qml.Rot(x, y, z, wire)
+
+ @qml.qnode(dev)
+ def qnode(a, x, y, z):
+ qml.Hadamard(0)
+ m_0 = qml.measure(0)
+ qml.cond(m_0, lambda: qfunc1(a, wire=1), lambda: qfunc2(x, y, z, wire=1))()
+ return qml.expval(qml.PauliZ(1))
+
+ .. code-block :: pycon
+
+ >>> par = np.array(0.3, requires_grad=True)
+ >>> x = np.array(1.2, requires_grad=True)
+ >>> y = np.array(1.1, requires_grad=True)
+ >>> z = np.array(0.3, requires_grad=True)
+ >>> qnode(par, x, y, z)
+ tensor(-0.30922805, requires_grad=True)
"""
if callable(true_fn):
# We assume that the callable is an operation or a quantum function
-
with_meas_err = (
"Only quantum functions that contain no measurements can be applied conditionally."
)
@@ -127,11 +253,10 @@ def wrapper(*args, **kwargs):
if else_tape.measurements:
raise ConditionalTransformError(with_meas_err)
- inverted_m = copy(condition)
- inverted_m = ~inverted_m
+ inverted_condition = ~condition
for op in else_tape.operations:
- Conditional(inverted_m, op)
+ Conditional(inverted_condition, op)
else:
raise ConditionalTransformError(
diff --git a/pennylane/transforms/draw.py b/pennylane/transforms/draw.py
index 6795c387416..7960a0ccf69 100644
--- a/pennylane/transforms/draw.py
+++ b/pennylane/transforms/draw.py
@@ -230,14 +230,12 @@ def circuit(a, w):
When requested with ``show_matrices=True``, matrix valued parameters are printed below the
circuit:
- .. code-block:: python
-
>>> @qml.qnode(qml.device('default.qubit', wires=2))
- ... def circuit():
+ ... def circuit3():
... qml.QubitUnitary(np.eye(2), wires=0)
... qml.QubitUnitary(-np.eye(4), wires=(0,1))
... return qml.expval(qml.Hermitian(np.eye(2), wires=1))
- >>> print(qml.draw(circuit, show_matrices=True)())
+ >>> print(qml.draw(circuit3, show_matrices=True)())
0: ββU(M0)ββU(M1)ββ€
1: βββββββββ°U(M1)ββ€ <π(M0)>
M0 =
@@ -295,13 +293,13 @@ def longer_circuit(params):
.. code-block:: python
- @qml.gradients.param_shift(shift=0.1)
+ @qml.gradients.param_shift(shifts=[(0.1,)])
@qml.qnode(qml.device('lightning.qubit', wires=1))
def transformed_circuit(x):
qml.RX(x, wires=0)
return qml.expval(qml.PauliZ(0))
- print(qml.draw(transformed_circuit)(np.array(1.0)))
+ print(qml.draw(transformed_circuit)(np.array(1.0, requires_grad=True)))
.. code-block:: none
diff --git a/pennylane/transforms/qcut.py b/pennylane/transforms/qcut.py
index 4335a0710c6..1e79a2c9353 100644
--- a/pennylane/transforms/qcut.py
+++ b/pennylane/transforms/qcut.py
@@ -722,7 +722,7 @@ def contract_tensors(
The network can then be contracted using:
- >>> qml.transforms.contract_tensors(tensors, graph, prep, meas)
+ >>> qml.transforms.qcut.contract_tensors(tensors, graph, prep, meas)
38
"""
# pylint: disable=import-outside-toplevel
diff --git a/pennylane/transforms/qfunc_transforms.py b/pennylane/transforms/qfunc_transforms.py
index 40e056b968c..ac0aab757ac 100644
--- a/pennylane/transforms/qfunc_transforms.py
+++ b/pennylane/transforms/qfunc_transforms.py
@@ -139,7 +139,7 @@ def my_transform(tape, x, y):
We can apply this transform to a quantum tape:
- >>> with qml.tape.JacobianTape() as tape:
+ >>> with qml.tape.QuantumTape() as tape:
... qml.Hadamard(wires=0)
... qml.CRX(-0.5, wires=[0, 1])
>>> new_tape = my_transform(tape, 1., 2.)
diff --git a/pennylane/transforms/tape_expand.py b/pennylane/transforms/tape_expand.py
index aa974a04b6a..e3444d3c6f6 100644
--- a/pennylane/transforms/tape_expand.py
+++ b/pennylane/transforms/tape_expand.py
@@ -74,7 +74,7 @@ def create_expand_fn(depth, stop_at=None, device=None, docstring=None):
.. code-block:: python
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.RX(0.2, wires=0)
qml.RX(qml.numpy.array(-2.4, requires_grad=True), wires=1)
qml.Rot(1.7, 0.92, -1.1, wires=0)
diff --git a/setup.py b/setup.py
index a45389ed7ba..ffd4bc7a6e3 100644
--- a/setup.py
+++ b/setup.py
@@ -28,7 +28,7 @@
"semantic_version==2.6",
"autoray",
"cachetools",
- "pennylane-lightning>=0.21",
+ "pennylane-lightning>=0.22",
]
info = {
diff --git a/tests/gradients/test_finite_difference.py b/tests/gradients/test_finite_difference.py
index aedaa5a3542..63be69ebce5 100644
--- a/tests/gradients/test_finite_difference.py
+++ b/tests/gradients/test_finite_difference.py
@@ -111,7 +111,7 @@ def test_behaviour(self):
def test_multipliers(self):
"""Test that the function behaves as expected when multipliers are used"""
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.PauliZ(0)
qml.RX(1.0, wires=0)
qml.CNOT(wires=[0, 2])
@@ -136,7 +136,7 @@ def test_non_differentiable_error(self):
respect to a non-differentiable argument"""
psi = np.array([1, 0, 1, 0], requires_grad=False) / np.sqrt(2)
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.QubitStateVector(psi, wires=[0, 1])
qml.RX(0.543, wires=[0])
qml.RY(-0.654, wires=[1])
@@ -165,7 +165,7 @@ def test_independent_parameter(self, mocker):
during the Jacobian computation."""
spy = mocker.spy(qml.gradients.finite_difference, "generate_shifted_tapes")
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.RX(0.543, wires=[0])
qml.RY(-0.654, wires=[1])
qml.expval(qml.PauliZ(0))
@@ -245,7 +245,7 @@ def test_y0(self, mocker):
values."""
dev = qml.device("default.qubit", wires=2)
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.RX(0.543, wires=[0])
qml.RY(-0.654, wires=[0])
qml.expval(qml.PauliZ(0))
@@ -262,7 +262,7 @@ def test_y0_provided(self):
values."""
dev = qml.device("default.qubit", wires=2)
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.RX(0.543, wires=[0])
qml.RY(-0.654, wires=[0])
qml.expval(qml.PauliZ(0))
@@ -278,12 +278,12 @@ def test_independent_parameters(self):
parameters, the gradient should be evaluated to zero without executing the device."""
dev = qml.device("default.qubit", wires=2)
- with qml.tape.JacobianTape() as tape1:
+ with qml.tape.QuantumTape() as tape1:
qml.RX(1, wires=[0])
qml.RX(1, wires=[1])
qml.expval(qml.PauliZ(0))
- with qml.tape.JacobianTape() as tape2:
+ with qml.tape.QuantumTape() as tape2:
qml.RX(1, wires=[0])
qml.RX(1, wires=[1])
qml.expval(qml.PauliZ(1))
@@ -314,7 +314,7 @@ def test_ragged_output(self, approx_order, strategy):
dev = qml.device("default.qubit", wires=3)
params = [1.0, 1.0, 1.0]
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.RX(params[0], wires=[0])
qml.RY(params[1], wires=[1])
qml.RZ(params[2], wires=[2])
@@ -333,7 +333,7 @@ def test_single_expectation_value(self, approx_order, strategy, tol):
x = 0.543
y = -0.654
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.RX(x, wires=[0])
qml.RY(y, wires=[1])
qml.CNOT(wires=[0, 1])
@@ -354,7 +354,7 @@ def test_single_expectation_value_with_argnum_all(self, approx_order, strategy,
x = 0.543
y = -0.654
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.RX(x, wires=[0])
qml.RY(y, wires=[1])
qml.CNOT(wires=[0, 1])
@@ -380,7 +380,7 @@ def test_single_expectation_value_with_argnum_one(self, approx_order, strategy,
x = 0.543
y = -0.654
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.RX(x, wires=[0])
qml.RY(y, wires=[1])
qml.CNOT(wires=[0, 1])
@@ -404,7 +404,7 @@ def test_multiple_expectation_values(self, approx_order, strategy, tol):
x = 0.543
y = -0.654
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.RX(x, wires=[0])
qml.RY(y, wires=[1])
qml.CNOT(wires=[0, 1])
@@ -425,7 +425,7 @@ def test_var_expectation_values(self, approx_order, strategy, tol):
x = 0.543
y = -0.654
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.RX(x, wires=[0])
qml.RY(y, wires=[1])
qml.CNOT(wires=[0, 1])
@@ -446,7 +446,7 @@ def test_prob_expectation_values(self, approx_order, strategy, tol):
x = 0.543
y = -0.654
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.RX(x, wires=[0])
qml.RY(y, wires=[1])
qml.CNOT(wires=[0, 1])
@@ -497,7 +497,7 @@ def test_autograd(self, approx_order, strategy, tol):
params = np.array([0.543, -0.654], requires_grad=True)
def cost_fn(x):
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.RX(x[0], wires=[0])
qml.RY(x[1], wires=[1])
qml.CNOT(wires=[0, 1])
@@ -525,7 +525,7 @@ def test_autograd_ragged(self, approx_order, strategy, tol):
params = np.array([0.543, -0.654], requires_grad=True)
def cost_fn(x):
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.RX(x[0], wires=[0])
qml.RY(x[1], wires=[1])
qml.CNOT(wires=[0, 1])
@@ -552,7 +552,7 @@ def test_tf(self, approx_order, strategy, tol):
params = tf.Variable([0.543, -0.654], dtype=tf.float64)
with tf.GradientTape() as t:
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.RX(params[0], wires=[0])
qml.RY(params[1], wires=[1])
qml.CNOT(wires=[0, 1])
@@ -584,7 +584,7 @@ def test_tf_ragged(self, approx_order, strategy, tol):
params = tf.Variable([0.543, -0.654], dtype=tf.float64)
with tf.GradientTape() as t:
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.RX(params[0], wires=[0])
qml.RY(params[1], wires=[1])
qml.CNOT(wires=[0, 1])
@@ -608,7 +608,7 @@ def test_torch(self, approx_order, strategy, tol):
dev = qml.device("default.qubit.torch", wires=2)
params = torch.tensor([0.543, -0.654], dtype=torch.float64, requires_grad=True)
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.RX(params[0], wires=[0])
qml.RY(params[1], wires=[1])
qml.CNOT(wires=[0, 1])
@@ -646,7 +646,7 @@ def test_jax(self, approx_order, strategy, tol):
params = jnp.array([0.543, -0.654])
def cost_fn(x):
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.RX(x[0], wires=[0])
qml.RY(x[1], wires=[1])
qml.CNOT(wires=[0, 1])
diff --git a/tests/gradients/test_gradient_transform.py b/tests/gradients/test_gradient_transform.py
index 922b695abf9..e382694c66c 100644
--- a/tests/gradients/test_gradient_transform.py
+++ b/tests/gradients/test_gradient_transform.py
@@ -24,14 +24,13 @@
class TestGradMethodValidation:
- """Test the helper function grad_method_validation, which is a
- reduced copy of the eponymous method of ``JacobianTape``."""
+ """Test the helper function grad_method_validation."""
@pytest.mark.parametrize("method", ["analytic", "best"])
def test_with_nondiff_parameters(self, method):
"""Test that trainable parameters without grad_method
are detected correctly, raising an exception."""
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.RX(np.array(0.1, requires_grad=True), wires=0)
qml.RX(np.array(0.1, requires_grad=True), wires=0)
qml.expval(qml.PauliZ(0))
@@ -43,7 +42,7 @@ def test_with_nondiff_parameters(self, method):
def test_with_numdiff_parameters_and_analytic(self):
"""Test that trainable parameters with numerical grad_method ``"F"``
together with ``method="analytic"`` raises an exception."""
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.RX(np.array(0.1, requires_grad=True), wires=0)
qml.RX(np.array(0.1, requires_grad=True), wires=0)
qml.expval(qml.PauliZ(0))
@@ -54,8 +53,7 @@ def test_with_numdiff_parameters_and_analytic(self):
class TestChooseGradMethods:
- """Test the helper function choose_grad_methods, which is a
- reduced copy of the eponymous method of ``JacobianTape``."""
+ """Test the helper function choose_grad_methods"""
all_diff_methods = [
["A"] * 2,
diff --git a/tests/gradients/test_hamiltonian_gradient.py b/tests/gradients/test_hamiltonian_gradient.py
index f76948693ad..3c4df7ae36e 100644
--- a/tests/gradients/test_hamiltonian_gradient.py
+++ b/tests/gradients/test_hamiltonian_gradient.py
@@ -21,7 +21,7 @@ def test_behaviour():
dev = qml.device("default.qubit", wires=2)
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.RY(0.3, wires=0)
qml.RX(0.5, wires=1)
qml.CNOT(wires=[0, 1])
@@ -34,13 +34,13 @@ def test_behaviour():
tapes, processing_fn = hamiltonian_grad(tape, idx=1)
res2 = processing_fn(dev.batch_execute(tapes))
- with qml.tape.JacobianTape() as tape1:
+ with qml.tape.QuantumTape() as tape1:
qml.RY(0.3, wires=0)
qml.RX(0.5, wires=1)
qml.CNOT(wires=[0, 1])
qml.expval(qml.PauliZ(0))
- with qml.tape.JacobianTape() as tape2:
+ with qml.tape.QuantumTape() as tape2:
qml.RY(0.3, wires=0)
qml.RX(0.5, wires=1)
qml.CNOT(wires=[0, 1])
diff --git a/tests/gradients/test_parameter_shift.py b/tests/gradients/test_parameter_shift.py
index 6b387867e1a..aed659b6c4d 100644
--- a/tests/gradients/test_parameter_shift.py
+++ b/tests/gradients/test_parameter_shift.py
@@ -17,7 +17,7 @@
import pennylane as qml
from pennylane import numpy as np
from pennylane.gradients import param_shift
-from pennylane.gradients.parameter_shift import _gradient_analysis
+from pennylane.gradients.gradient_transform import gradient_analysis
class TestGradAnalysis:
@@ -28,24 +28,25 @@ def test_non_differentiable(self):
correctly marked"""
psi = np.array([1, 0, 1, 0]) / np.sqrt(2)
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.QubitStateVector(psi, wires=[0, 1])
qml.RX(0.543, wires=[0])
qml.RY(-0.654, wires=[1])
qml.CNOT(wires=[0, 1])
qml.probs(wires=[0, 1])
- _gradient_analysis(tape)
+ gradient_analysis(tape)
assert tape._par_info[0]["grad_method"] is None
assert tape._par_info[1]["grad_method"] == "A"
assert tape._par_info[2]["grad_method"] == "A"
def test_analysis_caching(self, mocker):
- """Test that the gradient analysis is only executed once per tape"""
+ """Test that the gradient analysis is only executed once per tape
+ if grad_fn is set an unchanged."""
psi = np.array([1, 0, 1, 0]) / np.sqrt(2)
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.QubitStateVector(psi, wires=[0, 1])
qml.RX(0.543, wires=[0])
qml.RY(-0.654, wires=[1])
@@ -53,7 +54,7 @@ def test_analysis_caching(self, mocker):
qml.probs(wires=[0, 1])
spy = mocker.spy(qml.operation, "has_grad_method")
- _gradient_analysis(tape)
+ gradient_analysis(tape, grad_fn=5)
spy.assert_called()
assert tape._par_info[0]["grad_method"] is None
@@ -61,19 +62,19 @@ def test_analysis_caching(self, mocker):
assert tape._par_info[2]["grad_method"] == "A"
spy = mocker.spy(qml.operation, "has_grad_method")
- _gradient_analysis(tape)
+ gradient_analysis(tape, grad_fn=5)
spy.assert_not_called()
def test_independent(self):
"""Test that an independent variable is properly marked
as having a zero gradient"""
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.RX(0.543, wires=[0])
qml.RY(-0.654, wires=[1])
qml.expval(qml.PauliY(0))
- _gradient_analysis(tape)
+ gradient_analysis(tape)
assert tape._par_info[0]["grad_method"] == "A"
assert tape._par_info[1]["grad_method"] == "0"
@@ -82,12 +83,12 @@ def test_independent_no_graph_mode(self):
"""In non-graph mode, it is impossible to determine
if a parameter is independent or not"""
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.RX(0.543, wires=[0])
qml.RY(-0.654, wires=[1])
qml.expval(qml.PauliY(0))
- _gradient_analysis(tape, use_graph=False)
+ gradient_analysis(tape, use_graph=False)
assert tape._par_info[0]["grad_method"] == "A"
assert tape._par_info[1]["grad_method"] == "A"
@@ -98,14 +99,14 @@ def test_finite_diff(self, monkeypatch):
psi = np.array([1, 0, 1, 0]) / np.sqrt(2)
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.QubitStateVector(psi, wires=[0, 1])
qml.RX(0.543, wires=[0])
qml.RY(-0.654, wires=[1])
qml.CNOT(wires=[0, 1])
qml.probs(wires=[0, 1])
- _gradient_analysis(tape)
+ gradient_analysis(tape)
assert tape._par_info[0]["grad_method"] is None
assert tape._par_info[1]["grad_method"] == "F"
@@ -124,7 +125,7 @@ class TestShiftedTapes:
def test_behaviour(self):
"""Test that the function behaves as expected"""
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.PauliZ(0)
qml.RX(1.0, wires=0)
qml.CNOT(wires=[0, 2])
@@ -148,7 +149,7 @@ class TestParamShift:
def test_empty_circuit(self):
"""Test that an empty circuit works correctly"""
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.expval(qml.PauliZ(0))
with pytest.warns(UserWarning, match="gradient of a tape with no trainable parameters"):
@@ -157,7 +158,7 @@ def test_empty_circuit(self):
def test_all_parameters_independent(self):
"""Test that a circuit where all parameters do not affect the output"""
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.RX(0.4, wires=0)
qml.expval(qml.PauliZ(1))
@@ -169,7 +170,7 @@ def test_state_non_differentiable_error(self):
respect to a state"""
psi = np.array([1, 0, 1, 0]) / np.sqrt(2)
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.RX(0.543, wires=[0])
qml.RY(-0.654, wires=[1])
qml.state()
@@ -182,7 +183,7 @@ def test_independent_parameter(self, mocker):
during the Jacobian computation."""
spy = mocker.spy(qml.gradients.parameter_shift, "expval_param_shift")
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.RX(0.543, wires=[0])
qml.RY(-0.654, wires=[1])
qml.expval(qml.PauliZ(0))
@@ -262,7 +263,7 @@ def test_y0(self):
values."""
dev = qml.device("default.qubit", wires=2)
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.RX(0.543, wires=[0])
qml.RY(-0.654, wires=[0])
qml.expval(qml.PauliZ(0))
@@ -279,7 +280,7 @@ def test_y0_provided(self):
values."""
dev = qml.device("default.qubit", wires=2)
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.RX(0.543, wires=[0])
qml.RY(-0.654, wires=[0])
qml.expval(qml.PauliZ(0))
@@ -298,12 +299,12 @@ def test_independent_parameters_analytic(self):
parameters, the gradient should be evaluated to zero without executing the device."""
dev = qml.device("default.qubit", wires=2)
- with qml.tape.JacobianTape() as tape1:
+ with qml.tape.QuantumTape() as tape1:
qml.RX(1, wires=[0])
qml.RX(1, wires=[1])
qml.expval(qml.PauliZ(0))
- with qml.tape.JacobianTape() as tape2:
+ with qml.tape.QuantumTape() as tape2:
qml.RX(1, wires=[0])
qml.RX(1, wires=[1])
qml.expval(qml.PauliZ(1))
@@ -345,7 +346,7 @@ def grad_recipe(self):
x = np.array(0.654, requires_grad=True)
dev = qml.device("default.qubit", wires=2)
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
RX(x, wires=0)
qml.expval(qml.PauliZ(0))
@@ -382,7 +383,7 @@ class NewOp(qml.operation.Operation):
dev = qml.device("default.qubit", wires=2)
for op in [RX, NewOp]:
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
op(x, wires=0)
qml.expval(qml.PauliZ(0))
@@ -403,7 +404,7 @@ def test_pauli_rotation_gradient(self, mocker, G, theta, shift, tol):
spy = mocker.spy(qml.gradients.parameter_shift, "_get_operation_recipe")
dev = qml.device("default.qubit", wires=1)
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.QubitStateVector(np.array([1.0, -1.0], requires_grad=False) / np.sqrt(2), wires=0)
G(theta, wires=[0])
qml.expval(qml.PauliZ(0))
@@ -414,10 +415,12 @@ def test_pauli_rotation_gradient(self, mocker, G, theta, shift, tol):
assert len(tapes) == 2
autograd_val = fn(dev.batch_execute(tapes))
- manualgrad_val = (
- tape.execute(dev, params=[theta + np.pi / 2])
- - tape.execute(dev, params=[theta - np.pi / 2])
- ) / 2
+
+ tape_fwd, tape_bwd = tape.copy(copy_operations=True), tape.copy(copy_operations=True)
+ tape_fwd.set_parameters([theta + np.pi / 2])
+ tape_bwd.set_parameters([theta - np.pi / 2])
+
+ manualgrad_val = np.subtract(*dev.batch_execute([tape_fwd, tape_bwd])) / 2
assert np.allclose(autograd_val, manualgrad_val, atol=tol, rtol=0)
assert spy.call_args[1]["shifts"] == (shift,)
@@ -435,7 +438,7 @@ def test_Rot_gradient(self, mocker, theta, shift, tol):
dev = qml.device("default.qubit", wires=1)
params = np.array([theta, theta**3, np.sqrt(2) * theta])
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.QubitStateVector(np.array([1.0, -1.0], requires_grad=False) / np.sqrt(2), wires=0)
qml.Rot(*params, wires=[0])
qml.expval(qml.PauliZ(0))
@@ -452,8 +455,11 @@ def test_Rot_gradient(self, mocker, theta, shift, tol):
s = np.zeros_like(params)
s[idx] += np.pi / 2
- forward = tape.execute(dev, params=params + s)
- backward = tape.execute(dev, params=params - s)
+ tape.set_parameters(params + s)
+ forward = dev.execute(tape)
+
+ tape.set_parameters(params - s)
+ backward = dev.execute(tape)
manualgrad_val[0, idx] = (forward - backward) / 2
@@ -471,14 +477,14 @@ def test_controlled_rotation_gradient(self, G, tol):
dev = qml.device("default.qubit", wires=2)
b = 0.123
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.QubitStateVector(np.array([1.0, -1.0], requires_grad=False) / np.sqrt(2), wires=0)
G(b, wires=[0, 1])
qml.expval(qml.PauliX(0))
tape.trainable_params = {1}
- res = tape.execute(dev)
+ res = dev.execute(tape)
assert np.allclose(res, -np.cos(b / 2), atol=tol, rtol=0)
tapes, fn = qml.gradients.param_shift(tape)
@@ -498,14 +504,14 @@ def test_CRot_gradient(self, theta, tol):
dev = qml.device("default.qubit", wires=2)
a, b, c = np.array([theta, theta**3, np.sqrt(2) * theta])
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.QubitStateVector(np.array([1.0, -1.0], requires_grad=False) / np.sqrt(2), wires=0)
qml.CRot(a, b, c, wires=[0, 1])
qml.expval(qml.PauliX(0))
tape.trainable_params = {1, 2, 3}
- res = tape.execute(dev)
+ res = dev.execute(tape)
expected = -np.cos(b / 2) * np.cos(0.5 * (a + c))
assert np.allclose(res, expected, atol=tol, rtol=0)
@@ -534,7 +540,7 @@ def test_gradients_agree_finite_differences(self, tol):
order finite differences"""
params = np.array([0.1, -1.6, np.pi / 5])
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.RX(params[0], wires=[0])
qml.CNOT(wires=[0, 1])
qml.RY(-1.6, wires=[0])
@@ -562,7 +568,7 @@ def test_variance_gradients_agree_finite_differences(self, tol):
order finite differences"""
params = np.array([0.1, -1.6, np.pi / 5])
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.RX(params[0], wires=[0])
qml.CNOT(wires=[0, 1])
qml.RY(-1.6, wires=[0])
@@ -598,7 +604,7 @@ class RY(qml.RY):
grad_method = "F"
def cost_fn(params):
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.RX(params[0], wires=[0])
RY(params[1], wires=[1])
qml.CNOT(wires=[0, 1])
@@ -643,7 +649,7 @@ class RY(qml.RY):
class RX(qml.RX):
grad_method = "F"
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
RX(x, wires=[0])
RY(y, wires=[1])
qml.CNOT(wires=[0, 1])
@@ -669,7 +675,7 @@ def test_single_expectation_value(self, tol):
x = 0.543
y = -0.654
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.RX(x, wires=[0])
qml.RY(y, wires=[1])
qml.CNOT(wires=[0, 1])
@@ -691,7 +697,7 @@ def test_multiple_expectation_values(self, tol):
x = 0.543
y = -0.654
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.RX(x, wires=[0])
qml.RY(y, wires=[1])
qml.CNOT(wires=[0, 1])
@@ -714,7 +720,7 @@ def test_var_expectation_values(self, tol):
x = 0.543
y = -0.654
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.RX(x, wires=[0])
qml.RY(y, wires=[1])
qml.CNOT(wires=[0, 1])
@@ -737,7 +743,7 @@ def test_prob_expectation_values(self, tol):
x = 0.543
y = -0.654
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.RX(x, wires=[0])
qml.RY(y, wires=[1])
qml.CNOT(wires=[0, 1])
@@ -782,11 +788,11 @@ def test_involutory_variance(self, tol):
dev = qml.device("default.qubit", wires=1)
a = 0.54
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.RX(a, wires=0)
qml.var(qml.PauliZ(0))
- res = tape.execute(dev)
+ res = dev.execute(tape)
expected = 1 - np.cos(a) ** 2
assert np.allclose(res, expected, atol=tol, rtol=0)
@@ -810,13 +816,13 @@ def test_non_involutory_variance(self, tol):
A = np.array([[4, -1 + 6j], [-1 - 6j, 2]])
a = 0.54
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.RX(a, wires=0)
qml.var(qml.Hermitian(A, 0))
tape.trainable_params = {0}
- res = tape.execute(dev)
+ res = dev.execute(tape)
expected = (39 / 2) - 6 * np.sin(2 * a) + (35 / 2) * np.cos(2 * a)
assert np.allclose(res, expected, atol=tol, rtol=0)
@@ -840,7 +846,7 @@ def test_involutory_and_noninvolutory_variance(self, tol):
A = np.array([[4, -1 + 6j], [-1 - 6j, 2]])
a = 0.54
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.RX(a, wires=0)
qml.RX(a, wires=1)
qml.var(qml.PauliZ(0))
@@ -848,7 +854,7 @@ def test_involutory_and_noninvolutory_variance(self, tol):
tape.trainable_params = {0, 1}
- res = tape.execute(dev)
+ res = dev.execute(tape)
expected = [1 - np.cos(a) ** 2, (39 / 2) - 6 * np.sin(2 * a) + (35 / 2) * np.cos(2 * a)]
assert np.allclose(res, expected, atol=tol, rtol=0)
@@ -874,7 +880,7 @@ def test_expval_and_variance(self, tol):
b = -0.423
c = 0.123
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.RX(a, wires=0)
qml.RY(b, wires=1)
qml.CNOT(wires=[1, 2])
@@ -884,7 +890,7 @@ def test_expval_and_variance(self, tol):
qml.expval(qml.PauliZ(1))
qml.var(qml.PauliZ(2))
- res = tape.execute(dev)
+ res = dev.execute(tape)
expected = np.array(
[
np.sin(a) ** 2,
@@ -921,7 +927,7 @@ def test_projector_variance(self, tol):
P = np.array([1])
x, y = 0.765, -0.654
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.RX(x, wires=0)
qml.RY(y, wires=1)
qml.CNOT(wires=[0, 1])
@@ -929,7 +935,7 @@ def test_projector_variance(self, tol):
tape.trainable_params = {0, 1}
- res = tape.execute(dev)
+ res = dev.execute(tape)
expected = 0.25 * np.sin(x / 2) ** 2 * (3 + np.cos(2 * y) + 2 * np.cos(x) * np.sin(y) ** 2)
assert np.allclose(res, expected, atol=tol, rtol=0)
@@ -962,7 +968,7 @@ def test_autograd(self, tol):
params = np.array([0.543, -0.654], requires_grad=True)
def cost_fn(x):
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.RX(x[0], wires=[0])
qml.RY(x[1], wires=[1])
qml.CNOT(wires=[0, 1])
@@ -993,7 +999,7 @@ def test_tf(self, tol):
params = tf.Variable([0.543, -0.654], dtype=tf.float64)
with tf.GradientTape() as t:
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.RX(params[0], wires=[0])
qml.RY(params[1], wires=[1])
qml.CNOT(wires=[0, 1])
@@ -1025,7 +1031,7 @@ def test_torch(self, tol):
dev = qml.device("default.qubit.torch", wires=2)
params = torch.tensor([0.543, -0.654], dtype=torch.float64, requires_grad=True)
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.RX(params[0], wires=[0])
qml.RY(params[1], wires=[1])
qml.CNOT(wires=[0, 1])
@@ -1058,7 +1064,7 @@ def test_jax(self, tol):
params = jnp.array([0.543, -0.654])
def cost_fn(x):
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.RX(x[0], wires=[0])
qml.RY(x[1], wires=[1])
qml.CNOT(wires=[0, 1])
@@ -1095,7 +1101,7 @@ def test_not_expval_error(self):
weights = np.array([0.4, 0.5])
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.RX(weights[0], wires=0)
qml.RY(weights[1], wires=1)
qml.CNOT(wires=[0, 1])
@@ -1117,7 +1123,7 @@ def test_no_trainable_coeffs(self, mocker, tol):
weights = np.array([0.4, 0.5])
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.RX(weights[0], wires=0)
qml.RY(weights[1], wires=1)
qml.CNOT(wires=[0, 1])
@@ -1156,7 +1162,7 @@ def test_trainable_coeffs(self, mocker, tol):
weights = np.array([0.4, 0.5])
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.RX(weights[0], wires=0)
qml.RY(weights[1], wires=1)
qml.CNOT(wires=[0, 1])
@@ -1204,7 +1210,7 @@ def test_multiple_hamiltonians(self, mocker, tol):
weights = np.array([0.4, 0.5])
x, y = weights
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.RX(weights[0], wires=0)
qml.RY(weights[1], wires=1)
qml.CNOT(wires=[0, 1])
@@ -1247,7 +1253,7 @@ def cost_fn(weights, coeffs1, coeffs2, dev=None):
obs2 = [qml.PauliZ(0)]
H2 = qml.Hamiltonian(coeffs2, obs2)
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.RX(weights[0], wires=0)
qml.RY(weights[1], wires=1)
qml.CNOT(wires=[0, 1])
diff --git a/tests/gradients/test_parameter_shift_cv.py b/tests/gradients/test_parameter_shift_cv.py
index bf86136e547..647d2eca228 100644
--- a/tests/gradients/test_parameter_shift_cv.py
+++ b/tests/gradients/test_parameter_shift_cv.py
@@ -34,7 +34,7 @@ def test_non_differentiable(self):
"""Test that a non-differentiable parameter is
correctly marked"""
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.FockState(1, wires=0)
qml.Displacement(0.543, 0, wires=[1])
qml.Beamsplitter(0, 0, wires=[0, 1])
@@ -60,7 +60,7 @@ def test_independent(self):
"""Test that an independent variable is properly marked
as having a zero gradient"""
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.Rotation(0.543, wires=[0])
qml.Rotation(-0.654, wires=[1])
qml.expval(qml.P(0))
@@ -75,10 +75,10 @@ def test_independent(self):
def test_finite_diff(self, monkeypatch):
"""If an op has grad_method=F, this should be respected
- by the qml.tape.JacobianTape"""
+ by the qml.tape.QuantumTape"""
monkeypatch.setattr(qml.Rotation, "grad_method", "F")
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.Rotation(0.543, wires=[0])
qml.Squeezing(0.543, 0, wires=[0])
qml.expval(qml.P(0))
@@ -92,7 +92,7 @@ def test_non_gaussian_operation(self):
a differentiable Gaussian operation results in
numeric differentiation."""
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.Rotation(1.0, wires=[0])
qml.Rotation(1.0, wires=[1])
# Non-Gaussian
@@ -107,7 +107,7 @@ def test_non_gaussian_operation(self):
# Kerr gate does not support the parameter-shift rule
assert _grad_method(tape, 2) == "F"
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.Rotation(1.0, wires=[0])
qml.Rotation(1.0, wires=[1])
# entangle the modes
@@ -127,7 +127,7 @@ def test_probability(self):
"""Probability is the expectation value of a
higher order observable, and thus only supports numerical
differentiation"""
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.Rotation(0.543, wires=[0])
qml.Squeezing(0.543, 0, wires=[0])
qml.probs(wires=0)
@@ -141,19 +141,19 @@ def test_variance(self):
parameter-shift is supported. If the observable is second order,
however, only finite-differences is supported."""
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.Rotation(1.0, wires=[0])
qml.var(qml.P(0)) # first order
assert _grad_method(tape, 0) == "A"
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.Rotation(1.0, wires=[0])
qml.var(qml.NumberOperator(0)) # second order
assert _grad_method(tape, 0) == "F"
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.Rotation(1.0, wires=[0])
qml.Rotation(1.0, wires=[1])
qml.Beamsplitter(0.5, 0.0, wires=[0, 1])
@@ -169,7 +169,7 @@ def test_second_order_expectation(self):
"""Test that the expectation of a second-order observable forces
the gradient method to use the second-order parameter-shift rule"""
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.Rotation(1.0, wires=[0])
qml.expval(qml.NumberOperator(0)) # second order
@@ -181,7 +181,7 @@ def test_unknown_op_grad_method(self, monkeypatch):
doesn't recognize"""
monkeypatch.setattr(qml.Rotation, "grad_method", "B")
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.Rotation(1.0, wires=0)
qml.expval(qml.X(0))
@@ -297,7 +297,7 @@ def test_no_trainable_params_tape(self):
dev = qml.device("default.gaussian", wires=2)
weights = [0.1, 0.2]
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.Displacement(weights[0], 0.0, wires=[0])
qml.Rotation(weights[1], wires=[0])
qml.expval(qml.X(0))
@@ -333,7 +333,7 @@ def circuit(params):
def test_state_non_differentiable_error(self):
"""Test error raised if attempting to differentiate with
respect to a state"""
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.state()
with pytest.raises(ValueError, match=r"return the state is not supported"):
@@ -346,7 +346,7 @@ def test_force_order2(self, mocker):
dev = qml.device("default.gaussian", wires=1)
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.Displacement(1.0, 0.0, wires=[0])
qml.Rotation(2.0, wires=[0])
qml.expval(qml.X(0))
@@ -369,7 +369,7 @@ def test_no_poly_xp_support(self, mocker, monkeypatch, caplog):
monkeypatch.delitem(dev._observable_map, "PolyXP")
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.Rotation(1.0, wires=[0])
qml.expval(qml.NumberOperator(0))
@@ -389,7 +389,7 @@ def test_no_poly_xp_support_variance(self, mocker, monkeypatch, caplog):
monkeypatch.delitem(dev._observable_map, "PolyXP")
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.Rotation(1.0, wires=[0])
qml.var(qml.X(0))
@@ -405,7 +405,7 @@ def test_independent_parameters_analytic(self):
parameters, the gradient should be evaluated to zero without executing the device."""
dev = qml.device("default.gaussian", wires=2)
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.Displacement(1, 0, wires=[1])
qml.Displacement(1, 0, wires=[0])
qml.expval(qml.X(0))
@@ -434,7 +434,7 @@ def test_all_independent(self):
"""Test the case where expectation values are independent of all parameters."""
dev = qml.device("default.gaussian", wires=2)
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.Displacement(1, 0, wires=[1])
qml.Displacement(1, 0, wires=[1])
qml.expval(qml.X(0))
@@ -462,7 +462,7 @@ def test_rotation_gradient(self, gradient_recipes, mocker, tol):
alpha = 0.5643
theta = 0.23354
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.Displacement(alpha, 0.0, wires=[0])
qml.Rotation(theta, wires=[0])
qml.expval(qml.X(0))
@@ -490,7 +490,7 @@ def test_beamsplitter_gradient(self, mocker, tol):
alpha = 0.5643
theta = 0.23354
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.Displacement(alpha, 0.0, wires=[0])
qml.Beamsplitter(theta, 0.0, wires=[0, 1])
qml.expval(qml.X(0))
@@ -518,7 +518,7 @@ def test_displacement_gradient(self, mocker, tol):
r = 0.5643
phi = 0.23354
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.Displacement(r, phi, wires=[0])
qml.expval(qml.X(0))
@@ -555,7 +555,7 @@ class Rotation(qml.operation.CVOperation):
alpha = 0.5643
r = 0.23354
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.Displacement(alpha, 0.0, wires=[0])
qml.Squeezing(r, 0.0, wires=[0])
@@ -589,7 +589,7 @@ def test_squeezed_number_state_gradient(self, mocker, tol):
r = 0.23354
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.Squeezing(r, 0.0, wires=[0])
# the fock state projector is a 'non-Gaussian' observable
qml.expval(qml.FockStateProjector(np.array([2, 0]), wires=[0, 1]))
@@ -615,7 +615,7 @@ def test_multiple_squeezing_gradient(self, mocker, tol):
r0, phi0, r1, phi1 = [0.4, -0.3, -0.7, 0.2]
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.Squeezing(r0, phi0, wires=[0])
qml.Squeezing(r1, phi1, wires=[0])
qml.expval(qml.NumberOperator(0)) # second order
@@ -646,7 +646,7 @@ def test_multiple_second_order_observables(self, mocker, tol):
r = [0.4, -0.7, 0.1, 0.2]
p = [0.1, 0.2, 0.3, 0.4]
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.Squeezing(r[0], p[0], wires=[0])
qml.Squeezing(r[1], p[1], wires=[0])
qml.Squeezing(r[2], p[2], wires=[1])
@@ -688,7 +688,7 @@ def test_gradients_gaussian_circuit(self, op, obs, tol):
finite difference and analytic methods."""
tol = 1e-2
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.Displacement(0.5, 0, wires=0)
qml.apply(op)
qml.Beamsplitter(1.3, -2.3, wires=[0, 1])
@@ -698,7 +698,7 @@ def test_gradients_gaussian_circuit(self, op, obs, tol):
qml.expval(obs(wires=0))
dev = qml.device("default.gaussian", wires=2)
- res = tape.execute(dev)
+ res = qml.execute([tape], dev, None)
tape.trainable_params = set(range(2, 2 + op.num_params))
@@ -768,7 +768,7 @@ def test_interferometer_unitary(self, t, tol):
requires_grad=False,
)
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.Displacement(0.543, 0, wires=0)
qml.InterferometerUnitary(U, wires=[0, 1])
qml.expval(qml.X(0))
@@ -805,14 +805,14 @@ def test_first_order_observable(self, tol):
r = 0.543
phi = -0.654
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.Squeezing(r, 0, wires=0)
qml.Rotation(phi, wires=0)
qml.var(qml.X(0))
tape.trainable_params = {0, 2}
- res = tape.execute(dev)
+ res = qml.execute([tape], dev, None)
expected = np.exp(2 * r) * np.sin(phi) ** 2 + np.exp(-2 * r) * np.cos(phi) ** 2
assert np.allclose(res, expected, atol=tol, rtol=0)
@@ -841,14 +841,14 @@ def test_second_order_cv(self, tol):
n = 0.12
a = 0.765
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.ThermalState(n, wires=0)
qml.Displacement(a, 0, wires=0)
qml.var(qml.NumberOperator(0))
tape.trainable_params = {0, 1}
- res = tape.execute(dev)
+ res = qml.execute([tape], dev, None)
expected = n**2 + n + np.abs(a) ** 2 * (1 + 2 * n)
assert np.allclose(res, expected, atol=tol, rtol=0)
@@ -866,7 +866,7 @@ def test_expval_and_variance(self, tol):
a, b = [0.54, -0.423]
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.Displacement(0.5, 0, wires=0)
qml.Squeezing(a, 0, wires=0)
qml.Squeezing(b, 0, wires=1)
@@ -893,7 +893,7 @@ def test_error_analytic_second_order(self):
order observable to compute the variance derivative analytically"""
dev = qml.device("default.gaussian", wires=1)
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.Displacement(1.0, 0, wires=0)
qml.var(qml.NumberOperator(0))
@@ -917,7 +917,7 @@ class DummyOp(qml.operation.CVOperation):
dev.operations.add(DummyOp)
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
DummyOp(1, wires=[0])
qml.expval(qml.X(0))
@@ -939,7 +939,7 @@ def test_gradients_gaussian_circuit(self, op, obs, tol):
finite difference and analytic methods."""
tol = 1e-2
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.Displacement(0.5, 0, wires=0)
qml.apply(op)
qml.Beamsplitter(1.3, -2.3, wires=[0, 1])
@@ -949,7 +949,7 @@ def test_gradients_gaussian_circuit(self, op, obs, tol):
qml.var(obs(wires=0))
dev = qml.device("default.gaussian", wires=2)
- res = tape.execute(dev)
+ res = qml.execute([tape], dev, None)
tape.trainable_params = set(range(2, 2 + op.num_params))
@@ -978,7 +978,7 @@ def test_squeezed_mean_photon_variance(self, tol):
r = 0.12
phi = 0.105
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.Squeezing(r, 0, wires=0)
qml.Rotation(phi, wires=0)
qml.var(qml.X(wires=[0]))
@@ -1001,7 +1001,7 @@ def test_displaced_thermal_mean_photon_variance(self, tol):
n = 0.12
a = 0.105
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.ThermalState(n, wires=0)
qml.Displacement(a, 0, wires=0)
qml.var(qml.TensorN(wires=[0]))
@@ -1025,7 +1025,7 @@ def test_autograd_gradient(self, tol):
phi = 0.105
def cost_fn(x):
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.Squeezing(x[0], 0, wires=0)
qml.Rotation(x[1], wires=0)
qml.var(qml.X(wires=[0]))
@@ -1050,7 +1050,7 @@ def test_tf(self, tol):
params = tf.Variable([0.543, -0.654], dtype=tf.float64)
with tf.GradientTape() as t:
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.Squeezing(params[0], 0, wires=0)
qml.Rotation(params[1], wires=0)
qml.var(qml.X(wires=[0]))
@@ -1088,7 +1088,7 @@ def test_torch(self, tol):
dev = qml.device("default.gaussian", wires=1)
params = torch.tensor([0.543, -0.654], dtype=torch.float64, requires_grad=True)
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.Squeezing(params[0], 0, wires=0)
qml.Rotation(params[1], wires=0)
qml.var(qml.X(wires=[0]))
@@ -1130,7 +1130,7 @@ def test_jax(self, tol):
params = jnp.array([0.543, -0.654])
def cost_fn(x):
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.Squeezing(params[0], 0, wires=0)
qml.Rotation(params[1], wires=0)
qml.var(qml.X(wires=[0]))
diff --git a/tests/gradients/test_vjp.py b/tests/gradients/test_vjp.py
index 70bd9671c5f..354ebe3dfb3 100644
--- a/tests/gradients/test_vjp.py
+++ b/tests/gradients/test_vjp.py
@@ -140,7 +140,7 @@ def test_single_expectation_value(self, tol):
x = 0.543
y = -0.654
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.RX(x, wires=[0])
qml.RY(y, wires=[1])
qml.CNOT(wires=[0, 1])
@@ -165,7 +165,7 @@ def test_multiple_expectation_values(self, tol):
x = 0.543
y = -0.654
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.RX(x, wires=[0])
qml.RY(y, wires=[1])
qml.CNOT(wires=[0, 1])
@@ -191,7 +191,7 @@ def test_prob_expectation_values(self, tol):
x = 0.543
y = -0.654
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.RX(x, wires=[0])
qml.RY(y, wires=[1])
qml.CNOT(wires=[0, 1])
@@ -240,7 +240,7 @@ def test_dtype_matches_dy(self, dtype):
zero-like."""
x = np.array([0.1], dtype=np.float64)
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.RX(x[0], wires=0)
qml.expval(qml.PauliZ(0))
@@ -280,7 +280,7 @@ def test_autograd(self, tol):
params = np.array([0.543, -0.654], requires_grad=True)
def cost_fn(x, dy):
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
ansatz(x[0], x[1])
tape.trainable_params = {0, 1}
@@ -306,7 +306,7 @@ def test_torch(self, tol):
params = torch.tensor(params_np, requires_grad=True, dtype=torch.float64)
dy = torch.tensor([-1.0, 0.0, 0.0, 1.0], dtype=torch.float64)
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
ansatz(params[0], params[1])
tape.trainable_params = {0, 1}
@@ -334,7 +334,7 @@ def test_tf(self, tol):
dy = tf.constant([-1.0, 0.0, 0.0, 1.0], dtype=tf.float64)
with tf.GradientTape() as t:
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
ansatz(params[0], params[1])
tape.trainable_params = {0, 1}
@@ -393,7 +393,7 @@ def test_jax(self, tol):
@partial(jax.jit, static_argnums=1)
def cost_fn(x, dy):
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
ansatz(x[0], x[1])
tape.trainable_params = {0, 1}
@@ -422,7 +422,7 @@ def test_one_tape_no_trainable_parameters(self):
qml.CNOT(wires=[0, 1])
qml.expval(qml.PauliZ(0))
- with qml.tape.JacobianTape() as tape2:
+ with qml.tape.QuantumTape() as tape2:
qml.RX(0.4, wires=0)
qml.RX(0.6, wires=0)
qml.CNOT(wires=[0, 1])
@@ -478,7 +478,7 @@ def test_zero_dy(self):
qml.CNOT(wires=[0, 1])
qml.expval(qml.PauliZ(0))
- with qml.tape.JacobianTape() as tape2:
+ with qml.tape.QuantumTape() as tape2:
qml.RX(0.4, wires=0)
qml.RX(0.6, wires=0)
qml.CNOT(wires=[0, 1])
@@ -502,12 +502,12 @@ def test_reduction_append(self):
"""Test the 'append' reduction strategy"""
dev = qml.device("default.qubit", wires=2)
- with qml.tape.JacobianTape() as tape1:
+ with qml.tape.QuantumTape() as tape1:
qml.RX(0.4, wires=0)
qml.CNOT(wires=[0, 1])
qml.expval(qml.PauliZ(0))
- with qml.tape.JacobianTape() as tape2:
+ with qml.tape.QuantumTape() as tape2:
qml.RX(0.4, wires=0)
qml.RX(0.6, wires=0)
qml.CNOT(wires=[0, 1])
@@ -531,12 +531,12 @@ def test_reduction_extend(self):
"""Test the 'extend' reduction strategy"""
dev = qml.device("default.qubit", wires=2)
- with qml.tape.JacobianTape() as tape1:
+ with qml.tape.QuantumTape() as tape1:
qml.RX(0.4, wires=0)
qml.CNOT(wires=[0, 1])
qml.expval(qml.PauliZ(0))
- with qml.tape.JacobianTape() as tape2:
+ with qml.tape.QuantumTape() as tape2:
qml.RX(0.4, wires=0)
qml.RX(0.6, wires=0)
qml.CNOT(wires=[0, 1])
diff --git a/tests/interfaces/test_batch_autograd.py b/tests/interfaces/test_batch_autograd.py
index f98eba3ab42..847549226ba 100644
--- a/tests/interfaces/test_batch_autograd.py
+++ b/tests/interfaces/test_batch_autograd.py
@@ -41,7 +41,7 @@ def test_import_error(self, mocker):
dev = qml.device("default.qubit", wires=2, shots=None)
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.expval(qml.PauliY(1))
with pytest.raises(
@@ -60,7 +60,7 @@ def test_jacobian_options(self, mocker, tol):
dev = qml.device("default.qubit", wires=1)
def cost(a, device):
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.RY(a[0], wires=0)
qml.RX(a[1], wires=0)
qml.expval(qml.PauliZ(0))
@@ -85,7 +85,7 @@ def test_incorrect_mode(self):
dev = qml.device("default.qubit", wires=1)
def cost(a, device):
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.RY(a[0], wires=0)
qml.RX(a[1], wires=0)
qml.expval(qml.PauliZ(0))
@@ -104,7 +104,7 @@ def test_unknown_interface(self):
dev = qml.device("default.qubit", wires=1)
def cost(a, device):
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.RY(a[0], wires=0)
qml.RX(a[1], wires=0)
qml.expval(qml.PauliZ(0))
@@ -120,7 +120,7 @@ def test_forward_mode(self, mocker):
spy = mocker.spy(dev, "execute_and_gradients")
def cost(a):
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.RY(a[0], wires=0)
qml.RX(a[1], wires=0)
qml.expval(qml.PauliZ(0))
@@ -146,7 +146,7 @@ def test_backward_mode(self, mocker):
spy_gradients = mocker.spy(qml.devices.DefaultQubit, "gradients")
def cost(a):
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.RY(a[0], wires=0)
qml.RX(a[1], wires=0)
qml.expval(qml.PauliZ(0))
@@ -182,7 +182,7 @@ def test_no_batch_transform(self, mocker):
x = 0.6
y = 0.2
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.RX(x, wires=0)
qml.RY(y, wires=1)
qml.CNOT(wires=[0, 1])
@@ -209,7 +209,7 @@ def test_cache_maxsize(self, mocker):
spy = mocker.spy(qml.interfaces.batch, "cache_execute")
def cost(a, cachesize):
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.RY(a[0], wires=0)
qml.RX(a[1], wires=0)
qml.probs(wires=0)
@@ -230,7 +230,7 @@ def test_custom_cache(self, mocker):
spy = mocker.spy(qml.interfaces.batch, "cache_execute")
def cost(a, cache):
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.RY(a[0], wires=0)
qml.RX(a[1], wires=0)
qml.probs(wires=0)
@@ -250,7 +250,7 @@ def test_caching_param_shift(self, tol):
dev = qml.device("default.qubit", wires=1)
def cost(a, cache):
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.RY(a[0], wires=0)
qml.RX(a[1], wires=0)
qml.probs(wires=0)
@@ -294,7 +294,7 @@ def test_caching_param_shift_hessian(self, num_params, tol):
N = len(params)
def cost(x, cache):
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.RX(x[0], wires=[0])
qml.RY(x[1], wires=[1])
@@ -345,7 +345,7 @@ def test_caching_adjoint_backward(self):
params = np.array([0.1, 0.2, 0.3])
def cost(a, cache):
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.RY(a[0], wires=0)
qml.RX(a[1], wires=0)
qml.RY(a[2], wires=0)
@@ -400,12 +400,12 @@ def test_execution(self, execute_kwargs):
dev = qml.device("default.qubit", wires=1)
def cost(a, b):
- with qml.tape.JacobianTape() as tape1:
+ with qml.tape.QuantumTape() as tape1:
qml.RY(a, wires=0)
qml.RX(b, wires=0)
qml.expval(qml.PauliZ(0))
- with qml.tape.JacobianTape() as tape2:
+ with qml.tape.QuantumTape() as tape2:
qml.RY(a, wires=0)
qml.RX(b, wires=0)
qml.expval(qml.PauliZ(0))
@@ -426,7 +426,7 @@ def test_scalar_jacobian(self, execute_kwargs, tol):
dev = qml.device("default.qubit", wires=2)
def cost(a):
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.RY(a, wires=0)
qml.expval(qml.PauliZ(0))
return execute([tape], dev, **execute_kwargs)[0]
@@ -435,7 +435,7 @@ def cost(a):
assert res.shape == (1,)
# compare to standard tape jacobian
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.RY(a, wires=0)
qml.expval(qml.PauliZ(0))
@@ -452,7 +452,7 @@ def test_jacobian(self, execute_kwargs, tol):
b = np.array(0.2, requires_grad=True)
def cost(a, b, device):
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.RY(a, wires=0)
qml.RX(b, wires=1)
qml.CNOT(wires=[0, 1])
@@ -480,15 +480,15 @@ def test_tape_no_parameters(self, execute_kwargs, tol):
dev = qml.device("default.qubit", wires=1)
def cost(params):
- with qml.tape.JacobianTape() as tape1:
+ with qml.tape.QuantumTape() as tape1:
qml.Hadamard(0)
qml.expval(qml.PauliX(0))
- with qml.tape.JacobianTape() as tape2:
+ with qml.tape.QuantumTape() as tape2:
qml.RY(np.array(0.5, requires_grad=False), wires=0)
qml.expval(qml.PauliZ(0))
- with qml.tape.JacobianTape() as tape3:
+ with qml.tape.QuantumTape() as tape3:
qml.RY(params[0], wires=0)
qml.RX(params[1], wires=0)
qml.expval(qml.PauliZ(0))
@@ -513,7 +513,7 @@ def test_reusing_quantum_tape(self, execute_kwargs, tol):
dev = qml.device("default.qubit", wires=2)
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.RY(a, wires=0)
qml.RX(b, wires=1)
qml.CNOT(wires=[0, 1])
@@ -554,7 +554,7 @@ def test_classical_processing(self, execute_kwargs, tol):
c = np.array(0.3, requires_grad=True)
def cost(a, b, c, device):
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.RY(a * c, wires=0)
qml.RZ(b, wires=0)
qml.RX(c + c**2 + np.sin(a), wires=0)
@@ -575,7 +575,7 @@ def test_no_trainable_parameters(self, execute_kwargs, tol):
b = np.array(0.2, requires_grad=False)
def cost(a, b, device):
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.RY(a, wires=0)
qml.RX(b, wires=0)
qml.CNOT(wires=[0, 1])
@@ -606,7 +606,7 @@ def test_matrix_parameter(self, execute_kwargs, tol):
a = np.array(0.1, requires_grad=True)
def cost(a, U, device):
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.QubitUnitary(U, wires=0)
qml.RY(a, wires=0)
qml.expval(qml.PauliZ(0))
@@ -626,7 +626,7 @@ def test_differentiable_expand(self, execute_kwargs, tol):
class U3(qml.U3):
def expand(self):
- tape = qml.tape.JacobianTape()
+ tape = qml.tape.QuantumTape()
theta, phi, lam = self.data
wires = self.wires
tape._ops += [
@@ -636,7 +636,7 @@ def expand(self):
return tape
def cost_fn(a, p, device):
- tape = qml.tape.JacobianTape()
+ tape = qml.tape.QuantumTape()
with tape:
qml.RX(a, wires=0)
@@ -678,7 +678,7 @@ def test_probability_differentiation(self, execute_kwargs, tol):
pytest.skip("Adjoint differentiation does not yet support probabilities")
def cost(x, y, device):
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.RX(x, wires=[0])
qml.RY(y, wires=[1])
qml.CNOT(wires=[0, 1])
@@ -731,7 +731,7 @@ def test_ragged_differentiation(self, execute_kwargs, tol):
pytest.skip("Adjoint differentiation does not yet support probabilities")
def cost(x, y, device):
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.RX(x, wires=[0])
qml.RY(y, wires=[1])
qml.CNOT(wires=[0, 1])
@@ -769,7 +769,7 @@ def test_sampling(self, execute_kwargs):
pytest.skip("Adjoint differentiation does not support samples")
def cost(x, device):
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.Hadamard(wires=[0])
qml.CNOT(wires=[0, 1])
qml.sample(qml.PauliZ(0))
@@ -801,13 +801,13 @@ def test_parameter_shift_hessian(self, params, tol, recwarn):
params = np.array([0.543, -0.654], requires_grad=True)
def cost_fn(x):
- with qml.tape.JacobianTape() as tape1:
+ with qml.tape.QuantumTape() as tape1:
qml.RX(x[0], wires=[0])
qml.RY(x[1], wires=[1])
qml.CNOT(wires=[0, 1])
qml.var(qml.PauliZ(0) @ qml.PauliX(1))
- with qml.tape.JacobianTape() as tape2:
+ with qml.tape.QuantumTape() as tape2:
qml.RX(x[0], wires=0)
qml.RY(x[0], wires=1)
qml.CNOT(wires=[0, 1])
@@ -847,7 +847,7 @@ def test_adjoint_hessian(self, tol, recwarn):
params = np.array([0.543, -0.654], requires_grad=True)
def cost_fn(x):
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.RX(x[0], wires=[0])
qml.RY(x[1], wires=[1])
qml.CNOT(wires=[0, 1])
@@ -876,13 +876,13 @@ def test_max_diff(self, tol):
params = np.array([0.543, -0.654], requires_grad=True)
def cost_fn(x):
- with qml.tape.JacobianTape() as tape1:
+ with qml.tape.QuantumTape() as tape1:
qml.RX(x[0], wires=[0])
qml.RY(x[1], wires=[1])
qml.CNOT(wires=[0, 1])
qml.var(qml.PauliZ(0) @ qml.PauliX(1))
- with qml.tape.JacobianTape() as tape2:
+ with qml.tape.QuantumTape() as tape2:
qml.RX(x[0], wires=0)
qml.RY(x[0], wires=1)
qml.CNOT(wires=[0, 1])
@@ -917,7 +917,7 @@ def test_changing_shots(self, mocker, tol):
dev = qml.device("default.qubit", wires=2, shots=None)
a, b = np.array([0.543, -0.654], requires_grad=True)
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.RY(a, wires=0)
qml.RX(b, wires=1)
qml.CNOT(wires=[0, 1])
@@ -947,7 +947,7 @@ def test_overriding_shots_with_same_value(self, mocker):
dev = qml.device("default.qubit", wires=2, shots=123)
a, b = np.array([0.543, -0.654], requires_grad=True)
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.RY(a, wires=0)
qml.RX(b, wires=1)
qml.CNOT(wires=[0, 1])
@@ -980,7 +980,7 @@ def test_overriding_device_with_shot_vector(self):
a, b = np.array([0.543, -0.654], requires_grad=True)
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.RY(a, wires=0)
qml.RX(b, wires=1)
qml.CNOT(wires=[0, 1])
@@ -1003,7 +1003,7 @@ def test_gradient_integration(self, tol):
a, b = np.array([0.543, -0.654], requires_grad=True)
def cost_fn(a, b, shots):
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.RY(a, wires=0)
qml.RX(b, wires=1)
qml.CNOT(wires=[0, 1])
@@ -1045,7 +1045,7 @@ def _cost_fn(weights, coeffs1, coeffs2, dev=None):
obs2 = [qml.PauliZ(0)]
H2 = qml.Hamiltonian(coeffs2, obs2)
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.RX(weights[0], wires=0)
qml.RY(weights[1], wires=1)
qml.CNOT(wires=[0, 1])
diff --git a/tests/interfaces/test_batch_autograd_qnode.py b/tests/interfaces/test_batch_autograd_qnode.py
index be00e29c869..57c5f14ed06 100644
--- a/tests/interfaces/test_batch_autograd_qnode.py
+++ b/tests/interfaces/test_batch_autograd_qnode.py
@@ -17,7 +17,7 @@
import pennylane as qml
from pennylane import qnode, QNode
-from pennylane.tape import JacobianTape
+from pennylane.tape import QuantumTape
qubit_device_and_diff_method = [
["default.qubit", "finite-diff", "backward"],
@@ -394,7 +394,7 @@ def expand(self):
theta, phi, lam = self.data
wires = self.wires
- with JacobianTape() as tape:
+ with QuantumTape() as tape:
qml.Rot(lam, theta, -lam, wires=wires)
qml.PhaseShift(phi + lam, wires=wires)
diff --git a/tests/interfaces/test_batch_jax.py b/tests/interfaces/test_batch_jax.py
index aef2bbed3a8..0b0580b276e 100644
--- a/tests/interfaces/test_batch_jax.py
+++ b/tests/interfaces/test_batch_jax.py
@@ -39,7 +39,7 @@ def test_jacobian_options(self, mocker, interface, tol):
dev = qml.device("default.qubit", wires=1)
def cost(a, device):
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.RY(a[0], wires=0)
qml.RX(a[1], wires=0)
qml.expval(qml.PauliZ(0))
@@ -65,7 +65,7 @@ def test_incorrect_mode(self, interface):
dev = qml.device("default.qubit", wires=1)
def cost(a, device):
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.RY(a[0], wires=0)
qml.RX(a[1], wires=0)
qml.expval(qml.PauliZ(0))
@@ -90,7 +90,7 @@ def test_unknown_interface(self, interface):
dev = qml.device("default.qubit", wires=1)
def cost(a, device):
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.RY(a[0], wires=0)
qml.RX(a[1], wires=0)
qml.expval(qml.PauliZ(0))
@@ -111,7 +111,7 @@ def test_forward_mode(self, interface, mocker):
spy = mocker.spy(dev, "execute_and_gradients")
def cost(a):
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.RY(a[0], wires=0)
qml.RX(a[1], wires=0)
qml.expval(qml.PauliZ(0))
@@ -141,7 +141,7 @@ def test_backward_mode(self, interface, mocker):
spy_gradients = mocker.spy(qml.devices.DefaultQubit, "gradients")
def cost(a):
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.RY(a[0], wires=0)
qml.RX(a[1], wires=0)
qml.expval(qml.PauliZ(0))
@@ -176,7 +176,7 @@ def test_max_diff_error(self, interface):
InterfaceUnsupportedError,
match="The JAX interface only supports first order derivatives.",
):
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.RY(a[0], wires=0)
qml.RX(a[1], wires=0)
qml.expval(qml.PauliZ(0))
@@ -201,7 +201,7 @@ def test_cache_maxsize(self, interface, mocker):
spy = mocker.spy(qml.interfaces.batch, "cache_execute")
def cost(a, cachesize):
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.RY(a[0], wires=0)
qml.RX(a[1], wires=0)
qml.expval(qml.PauliZ(0))
@@ -228,7 +228,7 @@ def test_custom_cache(self, interface, mocker):
spy = mocker.spy(qml.interfaces.batch, "cache_execute")
def cost(a, cache):
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.RY(a[0], wires=0)
qml.RX(a[1], wires=0)
qml.expval(qml.PauliZ(0))
@@ -253,12 +253,12 @@ def test_custom_cache_multiple(self, interface, mocker):
b = jnp.array(0.2)
def cost(a, b, cache):
- with qml.tape.JacobianTape() as tape1:
+ with qml.tape.QuantumTape() as tape1:
qml.RY(a, wires=0)
qml.RX(b, wires=0)
qml.expval(qml.PauliZ(0))
- with qml.tape.JacobianTape() as tape2:
+ with qml.tape.QuantumTape() as tape2:
qml.RY(a, wires=0)
qml.RX(b, wires=0)
qml.expval(qml.PauliZ(0))
@@ -284,7 +284,7 @@ def test_caching_param_shift(self, interface, tol):
dev = qml.device("default.qubit", wires=1)
def cost(a, cache):
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.RY(a[0], wires=0)
qml.RX(a[1], wires=0)
qml.expval(qml.PauliZ(0))
@@ -326,7 +326,7 @@ def test_caching_adjoint_backward(self, interface):
params = jnp.array([0.1, 0.2, 0.3])
def cost(a, cache):
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.RY(a[0], wires=0)
qml.RX(a[1], wires=0)
qml.RY(a[2], wires=0)
@@ -382,12 +382,12 @@ def test_execution(self, execute_kwargs, interface):
dev = qml.device("default.qubit", wires=1)
def cost(a, b):
- with qml.tape.JacobianTape() as tape1:
+ with qml.tape.QuantumTape() as tape1:
qml.RY(a, wires=0)
qml.RX(b, wires=0)
qml.expval(qml.PauliZ(0))
- with qml.tape.JacobianTape() as tape2:
+ with qml.tape.QuantumTape() as tape2:
qml.RY(a, wires=0)
qml.RX(b, wires=0)
qml.expval(qml.PauliZ(0))
@@ -408,7 +408,7 @@ def test_scalar_jacobian(self, execute_kwargs, interface, tol):
dev = qml.device("default.qubit", wires=2)
def cost(a):
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.RY(a, wires=0)
qml.expval(qml.PauliZ(0))
return execute([tape], dev, interface=interface, **execute_kwargs)[0][0]
@@ -417,7 +417,7 @@ def cost(a):
assert res.shape == ()
# compare to standard tape jacobian
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.RY(a, wires=0)
qml.expval(qml.PauliZ(0))
@@ -435,7 +435,7 @@ def test_reusing_quantum_tape(self, execute_kwargs, interface, tol):
dev = qml.device("default.qubit", wires=2)
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.RY(a, wires=0)
qml.RX(b, wires=1)
qml.CNOT(wires=[0, 1])
@@ -479,7 +479,7 @@ def test_classical_processing_single_tape(self, execute_kwargs, interface, tol):
c = jnp.array(0.3)
def cost(a, b, c, device):
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.RY(a * c, wires=0)
qml.RZ(b, wires=0)
qml.RX(c + c**2 + jnp.sin(a), wires=0)
@@ -498,13 +498,13 @@ def test_classical_processing_multiple_tapes(self, execute_kwargs, interface, to
params = jax.numpy.array([0.3, 0.2])
def cost_fn(x):
- with qml.tape.JacobianTape() as tape1:
+ with qml.tape.QuantumTape() as tape1:
qml.Hadamard(0)
qml.RY(x[0], wires=[0])
qml.CNOT(wires=[0, 1])
qml.expval(qml.PauliZ(0))
- with qml.tape.JacobianTape() as tape2:
+ with qml.tape.QuantumTape() as tape2:
qml.Hadamard(0)
qml.CRX(2 * x[0] * x[1], wires=[0, 1])
qml.RX(2 * x[1], wires=[1])
@@ -524,13 +524,13 @@ def test_multiple_tapes_output(self, execute_kwargs, interface, tol):
params = jax.numpy.array([0.3, 0.2])
def cost_fn(x):
- with qml.tape.JacobianTape() as tape1:
+ with qml.tape.QuantumTape() as tape1:
qml.Hadamard(0)
qml.RY(x[0], wires=[0])
qml.CNOT(wires=[0, 1])
qml.expval(qml.PauliZ(0))
- with qml.tape.JacobianTape() as tape2:
+ with qml.tape.QuantumTape() as tape2:
qml.Hadamard(0)
qml.CRX(2 * x[0] * x[1], wires=[0, 1])
qml.RX(2 * x[1], wires=[1])
@@ -551,7 +551,7 @@ def test_matrix_parameter(self, execute_kwargs, interface, tol):
U = qml.RY(a, wires=0).get_matrix()
def cost(U, device):
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.PauliX(0)
qml.QubitUnitary(U, wires=0)
qml.expval(qml.PauliZ(0))
@@ -573,7 +573,7 @@ def test_differentiable_expand(self, execute_kwargs, interface, tol):
class U3(qml.U3):
def expand(self):
- tape = qml.tape.JacobianTape()
+ tape = qml.tape.QuantumTape()
theta, phi, lam = self.data
wires = self.wires
tape._ops += [
@@ -583,7 +583,7 @@ def expand(self):
return tape
def cost_fn(a, p, device):
- tape = qml.tape.JacobianTape()
+ tape = qml.tape.QuantumTape()
with tape:
qml.RX(a, wires=0)
@@ -624,7 +624,7 @@ def test_independent_expval(self, execute_kwargs, interface):
params = jnp.array([0.1, 0.2, 0.3])
def cost(a, cache):
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.RY(a[0], wires=0)
qml.RX(a[1], wires=0)
qml.RY(a[2], wires=0)
@@ -652,7 +652,7 @@ def test_raises_for_jax_jit(self, execute_kwargs, interface, ret, mes):
params = jnp.array([0.1, 0.2, 0.3])
def cost(a, cache):
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.RY(a[0], wires=0)
qml.RX(a[1], wires=0)
qml.RY(a[2], wires=0)
@@ -686,7 +686,7 @@ def test_multiple_expvals(self, execute_kwargs):
params = jnp.array([0.1, 0.2, 0.3])
def cost(a, cache):
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.RY(a[0], wires=0)
qml.RX(a[1], wires=0)
qml.RY(a[2], wires=0)
@@ -712,7 +712,7 @@ def test_multiple_expvals_single_par(self, execute_kwargs):
params = jnp.array([0.1])
def cost(a, cache):
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.RY(a[0], wires=0)
qml.expval(qml.PauliZ(0))
qml.expval(qml.PauliZ(1))
@@ -732,14 +732,14 @@ def test_multi_tape_jacobian(self, execute_kwargs):
pytest.skip("The forward mode is tested separately as it should raise an error.")
def cost(x, y, device, interface, ek):
- with qml.tape.JacobianTape() as tape1:
+ with qml.tape.QuantumTape() as tape1:
qml.RX(x, wires=[0])
qml.RY(y, wires=[1])
qml.CNOT(wires=[0, 1])
qml.expval(qml.PauliZ(0))
qml.expval(qml.PauliZ(1))
- with qml.tape.JacobianTape() as tape2:
+ with qml.tape.QuantumTape() as tape2:
qml.RX(x, wires=[0])
qml.RY(y, wires=[1])
qml.CNOT(wires=[0, 1])
@@ -777,14 +777,14 @@ def test_multi_tape_jacobian_probs_expvals(self, execute_kwargs):
pytest.skip("The adjoint diff method doesn't support probabilities.")
def cost(x, y, device, interface, ek):
- with qml.tape.JacobianTape() as tape1:
+ with qml.tape.QuantumTape() as tape1:
qml.RX(x, wires=[0])
qml.RY(y, wires=[1])
qml.CNOT(wires=[0, 1])
qml.expval(qml.PauliZ(0))
qml.expval(qml.PauliZ(1))
- with qml.tape.JacobianTape() as tape2:
+ with qml.tape.QuantumTape() as tape2:
qml.RX(x, wires=[0])
qml.RY(y, wires=[1])
qml.CNOT(wires=[0, 1])
@@ -820,7 +820,7 @@ def test_multiple_expvals_raises_fwd_device_grad(self, execute_kwargs):
params = jnp.array([0.1, 0.2, 0.3])
def cost(a, cache):
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.RY(a[0], wires=0)
qml.RX(a[1], wires=0)
qml.RY(a[2], wires=0)
diff --git a/tests/interfaces/test_batch_jax_qnode.py b/tests/interfaces/test_batch_jax_qnode.py
index b22a7dfa11c..159fc742580 100644
--- a/tests/interfaces/test_batch_jax_qnode.py
+++ b/tests/interfaces/test_batch_jax_qnode.py
@@ -18,7 +18,7 @@
import pennylane as qml
from pennylane import qnode, QNode
-from pennylane.tape import JacobianTape
+from pennylane.tape import QuantumTape
from pennylane.interfaces.batch import InterfaceUnsupportedError
qubit_device_and_diff_method = [
@@ -176,7 +176,7 @@ def expand(self):
theta, phi, lam = self.data
wires = self.wires
- with JacobianTape() as tape:
+ with QuantumTape() as tape:
qml.Rot(lam, theta, -lam, wires=wires)
qml.PhaseShift(phi + lam, wires=wires)
diff --git a/tests/interfaces/test_batch_tensorflow.py b/tests/interfaces/test_batch_tensorflow.py
index 837e2063a42..7d0c25157f7 100644
--- a/tests/interfaces/test_batch_tensorflow.py
+++ b/tests/interfaces/test_batch_tensorflow.py
@@ -37,7 +37,7 @@ def test_jacobian_options(self, mocker, tol):
dev = qml.device("default.qubit", wires=1)
with tf.GradientTape() as t:
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.RY(a[0], wires=0)
qml.RX(a[1], wires=0)
qml.expval(qml.PauliZ(0))
@@ -63,7 +63,7 @@ def test_incorrect_mode(self):
dev = qml.device("default.qubit", wires=1)
with tf.GradientTape() as t:
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.RY(a[0], wires=0)
qml.RX(a[1], wires=0)
qml.expval(qml.PauliZ(0))
@@ -80,7 +80,7 @@ def test_forward_mode(self, mocker):
spy = mocker.spy(dev, "execute_and_gradients")
with tf.GradientTape() as t:
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.RY(a[0], wires=0)
qml.RX(a[1], wires=0)
qml.expval(qml.PauliZ(0))
@@ -105,7 +105,7 @@ def test_backward_mode(self, mocker):
a = tf.Variable([0.1, 0.2])
with tf.GradientTape() as t:
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.RY(a[0], wires=0)
qml.RX(a[1], wires=0)
qml.expval(qml.PauliZ(0))
@@ -137,7 +137,7 @@ def test_cache_maxsize(self, mocker):
a = tf.Variable([0.1, 0.2])
with tf.GradientTape() as t:
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.RY(a[0], wires=0)
qml.RX(a[1], wires=0)
qml.probs(wires=0)
@@ -159,7 +159,7 @@ def test_custom_cache(self, mocker):
custom_cache = {}
with tf.GradientTape() as t:
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.RY(a[0], wires=0)
qml.RX(a[1], wires=0)
qml.probs(wires=0)
@@ -180,7 +180,7 @@ def test_caching_param_shift(self, tol):
a = tf.Variable([0.1, 0.2], dtype=tf.float64)
def cost(a, cache):
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.RY(a[0], wires=0)
qml.RX(a[1], wires=0)
qml.probs(wires=0)
@@ -221,7 +221,7 @@ def test_caching_param_shift_hessian(self, num_params, tol):
N = params.shape[0]
def cost(x, cache):
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.RX(x[0], wires=[0])
qml.RY(x[1], wires=[1])
@@ -302,12 +302,12 @@ def test_execution(self, execute_kwargs):
b = tf.Variable(0.2)
with tf.GradientTape() as t:
- with qml.tape.JacobianTape() as tape1:
+ with qml.tape.QuantumTape() as tape1:
qml.RY(a, wires=0)
qml.RX(b, wires=0)
qml.expval(qml.PauliZ(0))
- with qml.tape.JacobianTape() as tape2:
+ with qml.tape.QuantumTape() as tape2:
qml.RY(a, wires=0)
qml.RX(b, wires=0)
qml.expval(qml.PauliZ(0))
@@ -324,7 +324,7 @@ def test_scalar_jacobian(self, execute_kwargs, tol):
dev = qml.device("default.qubit", wires=2)
with tf.GradientTape() as t:
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.RY(a, wires=0)
qml.expval(qml.PauliZ(0))
res = execute([tape], dev, **execute_kwargs)[0]
@@ -333,7 +333,7 @@ def test_scalar_jacobian(self, execute_kwargs, tol):
assert res.shape == (1,)
# compare to standard tape jacobian
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.RY(a, wires=0)
qml.expval(qml.PauliZ(0))
@@ -351,7 +351,7 @@ def test_jacobian(self, execute_kwargs, tol):
dev = qml.device("default.qubit", wires=2)
with tf.GradientTape() as t:
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.RY(a, wires=0)
qml.RX(b, wires=1)
qml.CNOT(wires=[0, 1])
@@ -377,15 +377,15 @@ def test_tape_no_parameters(self, execute_kwargs, tol):
x, y = 1.0 * params
with tf.GradientTape() as t:
- with qml.tape.JacobianTape() as tape1:
+ with qml.tape.QuantumTape() as tape1:
qml.Hadamard(0)
qml.expval(qml.PauliX(0))
- with qml.tape.JacobianTape() as tape2:
+ with qml.tape.QuantumTape() as tape2:
qml.RY(0.5, wires=0)
qml.expval(qml.PauliZ(0))
- with qml.tape.JacobianTape() as tape3:
+ with qml.tape.QuantumTape() as tape3:
qml.RY(params[0], wires=0)
qml.RX(params[1], wires=0)
qml.expval(qml.PauliZ(0))
@@ -407,7 +407,7 @@ def test_reusing_quantum_tape(self, execute_kwargs, tol):
dev = qml.device("default.qubit", wires=2)
with tf.GradientTape() as t:
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.RY(a, wires=0)
qml.RX(b, wires=1)
qml.CNOT(wires=[0, 1])
@@ -446,7 +446,7 @@ def test_reusing_pre_constructed_quantum_tape(self, execute_kwargs, tol):
dev = qml.device("default.qubit", wires=2)
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.RY(a, wires=0)
qml.RX(b, wires=1)
qml.CNOT(wires=[0, 1])
@@ -486,7 +486,7 @@ def test_classical_processing(self, execute_kwargs, tol):
dev = qml.device("default.qubit", wires=1)
with tf.GradientTape() as t:
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.RY(a * c, wires=0)
qml.RZ(b, wires=0)
qml.RX(c + c**2 + tf.sin(a), wires=0)
@@ -507,7 +507,7 @@ def test_no_trainable_parameters(self, execute_kwargs, tol):
dev = qml.device("default.qubit", wires=2)
with tf.GradientTape() as t:
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.RY(0.2, wires=0)
qml.RX(b, wires=0)
qml.CNOT(wires=[0, 1])
@@ -533,7 +533,7 @@ def test_matrix_parameter(self, execute_kwargs, U, tol):
with tf.GradientTape() as t:
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.QubitUnitary(U, wires=0)
qml.RY(a, wires=0)
qml.expval(qml.PauliZ(0))
@@ -552,7 +552,7 @@ def test_differentiable_expand(self, execute_kwargs, tol):
class U3(qml.U3):
def expand(self):
- tape = qml.tape.JacobianTape()
+ tape = qml.tape.QuantumTape()
theta, phi, lam = self.data
wires = self.wires
tape._ops += [
@@ -561,7 +561,7 @@ def expand(self):
]
return tape
- qtape = qml.tape.JacobianTape()
+ qtape = qml.tape.QuantumTape()
dev = qml.device("default.qubit", wires=1)
a = np.array(0.1)
@@ -611,7 +611,7 @@ def test_probability_differentiation(self, execute_kwargs, tol):
y = tf.Variable(-0.654, dtype=tf.float64)
with tf.GradientTape() as t:
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.RX(x, wires=[0])
qml.RY(y, wires=[1])
qml.CNOT(wires=[0, 1])
@@ -654,7 +654,7 @@ def test_ragged_differentiation(self, execute_kwargs, tol):
y = tf.Variable(-0.654, dtype=tf.float64)
with tf.GradientTape() as t:
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.RX(x, wires=[0])
qml.RY(y, wires=[1])
qml.CNOT(wires=[0, 1])
@@ -685,7 +685,7 @@ def test_sampling(self, execute_kwargs):
dev = qml.device("default.qubit", wires=2, shots=10)
with tf.GradientTape() as t:
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.Hadamard(wires=[0])
qml.CNOT(wires=[0, 1])
qml.sample(qml.PauliZ(0))
@@ -718,13 +718,13 @@ def test_parameter_shift_hessian(self, params, tol):
with tf.GradientTape() as t2:
with tf.GradientTape() as t1:
- with qml.tape.JacobianTape() as tape1:
+ with qml.tape.QuantumTape() as tape1:
qml.RX(params[0], wires=[0])
qml.RY(params[1], wires=[1])
qml.CNOT(wires=[0, 1])
qml.var(qml.PauliZ(0) @ qml.PauliX(1))
- with qml.tape.JacobianTape() as tape2:
+ with qml.tape.QuantumTape() as tape2:
qml.RX(params[0], wires=0)
qml.RY(params[0], wires=1)
qml.CNOT(wires=[0, 1])
@@ -760,7 +760,7 @@ def test_hessian_vector_valued(self, tol):
with tf.GradientTape() as t2:
with tf.GradientTape(persistent=True) as t1:
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.RY(params[0], wires=0)
qml.RX(params[1], wires=0)
qml.probs(wires=0)
@@ -807,7 +807,7 @@ def test_adjoint_hessian(self, tol):
with tf.GradientTape() as t2:
with tf.GradientTape() as t1:
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.RX(params[0], wires=[0])
qml.RY(params[1], wires=[1])
qml.CNOT(wires=[0, 1])
@@ -836,13 +836,13 @@ def test_max_diff(self, tol):
with tf.GradientTape() as t2:
with tf.GradientTape() as t1:
- with qml.tape.JacobianTape() as tape1:
+ with qml.tape.QuantumTape() as tape1:
qml.RX(params[0], wires=[0])
qml.RY(params[1], wires=[1])
qml.CNOT(wires=[0, 1])
qml.var(qml.PauliZ(0) @ qml.PauliX(1))
- with qml.tape.JacobianTape() as tape2:
+ with qml.tape.QuantumTape() as tape2:
qml.RX(params[0], wires=0)
qml.RY(params[0], wires=1)
qml.CNOT(wires=[0, 1])
@@ -889,7 +889,7 @@ def _cost_fn(weights, coeffs1, coeffs2, dev=None):
obs2 = [qml.PauliZ(0)]
H2 = qml.Hamiltonian(coeffs2, obs2)
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.RX(weights[0], wires=0)
qml.RY(weights[1], wires=1)
qml.CNOT(wires=[0, 1])
diff --git a/tests/interfaces/test_batch_tensorflow_qnode.py b/tests/interfaces/test_batch_tensorflow_qnode.py
index a21f6fd2186..f4ddb8a365d 100644
--- a/tests/interfaces/test_batch_tensorflow_qnode.py
+++ b/tests/interfaces/test_batch_tensorflow_qnode.py
@@ -19,7 +19,7 @@
import pennylane as qml
from pennylane import qnode, QNode
-from pennylane.tape import JacobianTape
+from pennylane.tape import QuantumTape
qubit_device_and_diff_method = [
@@ -369,7 +369,7 @@ def expand(self):
theta, phi, lam = self.data
wires = self.wires
- with JacobianTape() as tape:
+ with QuantumTape() as tape:
qml.Rot(lam, theta, -lam, wires=wires)
qml.PhaseShift(phi + lam, wires=wires)
diff --git a/tests/interfaces/test_batch_torch.py b/tests/interfaces/test_batch_torch.py
index b926d6083b1..e677d8fc810 100644
--- a/tests/interfaces/test_batch_torch.py
+++ b/tests/interfaces/test_batch_torch.py
@@ -35,7 +35,7 @@ def test_jacobian_options(self, mocker, tol):
dev = qml.device("default.qubit", wires=1)
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.RY(a[0], wires=0)
qml.RX(a[1], wires=0)
qml.expval(qml.PauliZ(0))
@@ -60,7 +60,7 @@ def test_incorrect_mode(self):
dev = qml.device("default.qubit", wires=1)
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.RY(a[0], wires=0)
qml.RX(a[1], wires=0)
qml.expval(qml.PauliZ(0))
@@ -78,7 +78,7 @@ def test_forward_mode_reuse_state(self, mocker):
a = torch.tensor([0.1, 0.2], requires_grad=True)
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.RY(a[0], wires=0)
qml.RX(a[1], wires=0)
qml.expval(qml.PauliZ(0))
@@ -102,7 +102,7 @@ def test_forward_mode(self, mocker):
a = torch.tensor([0.1, 0.2], requires_grad=True)
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.RY(a[0], wires=0)
qml.RX(a[1], wires=0)
qml.expval(qml.PauliZ(0))
@@ -127,7 +127,7 @@ def test_backward_mode(self, mocker):
a = torch.tensor([0.1, 0.2], requires_grad=True)
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.RY(a[0], wires=0)
qml.RX(a[1], wires=0)
qml.expval(qml.PauliZ(0))
@@ -158,7 +158,7 @@ def test_cache_maxsize(self, mocker):
spy = mocker.spy(qml.interfaces.batch, "cache_execute")
def cost(a, cachesize):
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.RY(a[0], wires=0)
qml.RX(a[1], wires=0)
qml.probs(wires=0)
@@ -182,7 +182,7 @@ def test_custom_cache(self, mocker):
spy = mocker.spy(qml.interfaces.batch, "cache_execute")
def cost(a, cache):
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.RY(a[0], wires=0)
qml.RX(a[1], wires=0)
qml.probs(wires=0)
@@ -205,7 +205,7 @@ def test_caching_param_shift(self, tol):
dev = qml.device("default.qubit", wires=1)
def cost(a, cache):
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.RY(a[0], wires=0)
qml.RX(a[1], wires=0)
qml.probs(wires=0)
@@ -237,7 +237,7 @@ def test_caching_param_shift_hessian(self, num_params, tol):
N = len(params)
def cost(x, cache):
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.RX(x[0], wires=[0])
qml.RY(x[1], wires=[1])
@@ -290,7 +290,7 @@ def test_caching_adjoint_backward(self):
params = torch.tensor([0.1, 0.2, 0.3])
def cost(a, cache):
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.RY(a[0], wires=0)
qml.RX(a[1], wires=0)
qml.RY(a[2], wires=0)
@@ -362,12 +362,12 @@ def test_execution(self, torch_device, execute_kwargs):
a = torch.tensor(0.1, requires_grad=True, device=torch_device)
b = torch.tensor(0.2, requires_grad=False, device=torch_device)
- with qml.tape.JacobianTape() as tape1:
+ with qml.tape.QuantumTape() as tape1:
qml.RY(a, wires=0)
qml.RX(b, wires=0)
qml.expval(qml.PauliZ(0))
- with qml.tape.JacobianTape() as tape2:
+ with qml.tape.QuantumTape() as tape2:
qml.RY(a, wires=0)
qml.RX(b, wires=0)
qml.expval(qml.PauliZ(0))
@@ -383,7 +383,7 @@ def test_scalar_jacobian(self, torch_device, execute_kwargs, tol):
a = torch.tensor(0.1, requires_grad=True, dtype=torch.float64, device=torch_device)
dev = qml.device("default.qubit", wires=2)
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.RY(a, wires=0)
qml.expval(qml.PauliZ(0))
@@ -412,7 +412,7 @@ def test_jacobian(self, torch_device, execute_kwargs, tol):
dev = qml.device("default.qubit", wires=2)
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.RZ(torch.tensor(0.543, device=torch_device), wires=0)
qml.RY(a, wires=0)
qml.RX(b, wires=1)
@@ -449,15 +449,15 @@ def test_tape_no_parameters(self, torch_device, execute_kwargs, tol):
params = torch.tensor([0.1, 0.2], requires_grad=True, device=torch_device)
x, y = params.detach()
- with qml.tape.JacobianTape() as tape1:
+ with qml.tape.QuantumTape() as tape1:
qml.Hadamard(0)
qml.expval(qml.PauliX(0))
- with qml.tape.JacobianTape() as tape2:
+ with qml.tape.QuantumTape() as tape2:
qml.RY(0.5, wires=0)
qml.expval(qml.PauliZ(0))
- with qml.tape.JacobianTape() as tape3:
+ with qml.tape.QuantumTape() as tape3:
qml.RY(params[0], wires=0)
qml.RX(params[1], wires=0)
qml.expval(qml.PauliZ(0))
@@ -484,7 +484,7 @@ def test_reusing_quantum_tape(self, torch_device, execute_kwargs, tol):
dev = qml.device("default.qubit", wires=2)
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.RY(a, wires=0)
qml.RX(b, wires=1)
qml.CNOT(wires=[0, 1])
@@ -534,7 +534,7 @@ def test_classical_processing(self, torch_device, execute_kwargs, tol):
dev = qml.device("default.qubit", wires=1)
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.RY(params[0] * params[1], wires=0)
qml.RZ(0.2, wires=0)
qml.RX(params[1] + params[1] ** 2 + torch.sin(params[0]), wires=0)
@@ -567,7 +567,7 @@ def test_no_trainable_parameters(self, torch_device, execute_kwargs, tol):
"""Test evaluation and Jacobian if there are no trainable parameters"""
dev = qml.device("default.qubit", wires=2)
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.RY(0.2, wires=0)
qml.RX(torch.tensor(0.1, device=torch_device), wires=0)
qml.CNOT(wires=[0, 1])
@@ -600,7 +600,7 @@ def test_matrix_parameter(self, torch_device, U, execute_kwargs, tol):
dev = qml.device("default.qubit", wires=2)
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.QubitUnitary(U, wires=0)
qml.RY(a, wires=0)
qml.expval(qml.PauliZ(0))
@@ -621,7 +621,7 @@ def test_differentiable_expand(self, torch_device, execute_kwargs, tol):
class U3(qml.U3):
def expand(self):
- tape = qml.tape.JacobianTape()
+ tape = qml.tape.QuantumTape()
theta, phi, lam = self.data
wires = self.wires
tape._ops += [
@@ -630,7 +630,7 @@ def expand(self):
]
return tape
- tape = qml.tape.JacobianTape()
+ tape = qml.tape.QuantumTape()
dev = qml.device("default.qubit", wires=1)
a = np.array(0.1)
@@ -698,7 +698,7 @@ def test_probability_differentiation(self, torch_device, execute_kwargs, tol):
x = torch.tensor(x_val, requires_grad=True, device=torch_device)
y = torch.tensor(y_val, requires_grad=True, device=torch_device)
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.RX(x, wires=[0])
qml.RY(y, wires=[1])
qml.CNOT(wires=[0, 1])
@@ -745,7 +745,7 @@ def test_ragged_differentiation(self, torch_device, execute_kwargs, tol):
x = torch.tensor(x_val, requires_grad=True, device=torch_device)
y = torch.tensor(y_val, requires_grad=True, device=torch_device)
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.RX(x, wires=[0])
qml.RY(y, wires=[1])
qml.CNOT(wires=[0, 1])
@@ -787,7 +787,7 @@ def test_sampling(self, torch_device, execute_kwargs):
dev = qml.device("default.qubit", wires=2, shots=10)
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.Hadamard(wires=[0])
qml.CNOT(wires=[0, 1])
qml.sample(qml.PauliZ(0))
@@ -805,7 +805,7 @@ def test_sampling_expval(self, torch_device, execute_kwargs):
dev = qml.device("default.qubit", wires=2, shots=10)
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.Hadamard(wires=[0])
qml.CNOT(wires=[0, 1])
qml.sample(qml.PauliZ(0))
@@ -828,7 +828,7 @@ def test_sampling_gradient_error(self, torch_device, execute_kwargs):
x = torch.tensor(0.65, requires_grad=True)
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.RX(x, wires=[0])
qml.sample(qml.PauliZ(0))
@@ -848,7 +848,7 @@ def test_repeated_application_after_expand(self, torch_device, execute_kwargs, t
weights = torch.ones((3,))
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.U3(*weights, wires=0)
qml.expval(qml.PauliZ(wires=0))
@@ -875,13 +875,13 @@ def test_parameter_shift_hessian(self, torch_device, params, tol):
params = torch.tensor([0.543, -0.654], requires_grad=True, dtype=torch.float64)
def cost_fn(x):
- with qml.tape.JacobianTape() as tape1:
+ with qml.tape.QuantumTape() as tape1:
qml.RX(x[0], wires=[0])
qml.RY(x[1], wires=[1])
qml.CNOT(wires=[0, 1])
qml.var(qml.PauliZ(0) @ qml.PauliX(1))
- with qml.tape.JacobianTape() as tape2:
+ with qml.tape.QuantumTape() as tape2:
qml.RX(x[0], wires=0)
qml.RY(x[0], wires=1)
qml.CNOT(wires=[0, 1])
@@ -917,7 +917,7 @@ def test_hessian_vector_valued(self, torch_device, tol):
dev = qml.device("default.qubit", wires=1)
def circuit(x):
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.RY(x[0], wires=0)
qml.RX(x[1], wires=0)
qml.probs(wires=0)
@@ -985,7 +985,7 @@ def test_adjoint_hessian(self, torch_device, tol):
)
def cost_fn(x):
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.RX(x[0], wires=[0])
qml.RY(x[1], wires=[1])
qml.CNOT(wires=[0, 1])
@@ -1010,13 +1010,13 @@ def test_max_diff(self, torch_device, tol):
params = torch.tensor([0.543, -0.654], requires_grad=True, dtype=torch.float64)
def cost_fn(x):
- with qml.tape.JacobianTape() as tape1:
+ with qml.tape.QuantumTape() as tape1:
qml.RX(x[0], wires=[0])
qml.RY(x[1], wires=[1])
qml.CNOT(wires=[0, 1])
qml.var(qml.PauliZ(0) @ qml.PauliX(1))
- with qml.tape.JacobianTape() as tape2:
+ with qml.tape.QuantumTape() as tape2:
qml.RX(x[0], wires=0)
qml.RY(x[0], wires=1)
qml.CNOT(wires=[0, 1])
@@ -1067,7 +1067,7 @@ def _cost_fn(weights, coeffs1, coeffs2, dev=None):
obs2 = [qml.PauliZ(0)]
H2 = qml.Hamiltonian(coeffs2, obs2)
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.RX(weights[0], wires=0)
qml.RY(weights[1], wires=1)
qml.CNOT(wires=[0, 1])
diff --git a/tests/interfaces/test_batch_torch_qnode.py b/tests/interfaces/test_batch_torch_qnode.py
index fe18a1aa039..8dc910be533 100644
--- a/tests/interfaces/test_batch_torch_qnode.py
+++ b/tests/interfaces/test_batch_torch_qnode.py
@@ -20,7 +20,7 @@
import pennylane as qml
from pennylane import qnode, QNode
-from pennylane.tape import JacobianTape
+from pennylane.tape import QuantumTape
qubit_device_and_diff_method = [
@@ -385,7 +385,7 @@ def expand(self):
theta, phi, lam = self.data
wires = self.wires
- with JacobianTape() as tape:
+ with QuantumTape() as tape:
qml.Rot(lam, theta, -lam, wires=wires)
qml.PhaseShift(phi + lam, wires=wires)
diff --git a/tests/ops/functions/test_generator.py b/tests/ops/functions/test_generator.py
index ab3fc72f892..a21c7c1b557 100644
--- a/tests/ops/functions/test_generator.py
+++ b/tests/ops/functions/test_generator.py
@@ -136,6 +136,47 @@ def test_unknown_format(self):
qml.generator(qml.RX, format=None)(0.5, wires=0)
+class TestBackwardsCompatibility:
+ """Test that operators that provide the old style generator property
+ continue to work with a deprecation warning."""
+
+ def test_return_class(self):
+ """Test that an old-style Operator that has a generator property,
+ and that returns a list containing (a) operator class and (b) prefactor,
+ continues to work but also raises a deprecation warning."""
+
+ class DeprecatedClassOp(CustomOp):
+ generator = [qml.PauliX, -0.6]
+
+ op = DeprecatedClassOp(0.5, wires="a")
+
+ with pytest.warns(UserWarning, match=r"The Operator\.generator property is deprecated"):
+ gen, prefactor = qml.generator(op)
+
+ assert isinstance(gen, qml.operation.Operator)
+ assert prefactor == -0.6
+ assert gen.name == "PauliX"
+ assert gen.wires.tolist() == ["a"]
+
+ def test_return_array(self):
+ """Test that an old-style Operator that has a generator property,
+ and that returns a list containing (a) array and (b) prefactor,
+ continues to work but also raises a deprecation warning."""
+
+ class DeprecatedClassOp(CustomOp):
+ generator = [np.diag([0, 1]), -0.6]
+
+ op = DeprecatedClassOp(0.5, wires="a")
+
+ with pytest.warns(UserWarning, match=r"The Operator\.generator property is deprecated"):
+ gen, prefactor = qml.generator(op)
+
+ assert isinstance(gen, qml.operation.Operator)
+ assert prefactor == -0.6
+ assert gen.name == "Hermitian"
+ assert gen.wires.tolist() == ["a"]
+
+
class TestPrefactorReturn:
"""Tests for format="prefactor". This format attempts to isolate a prefactor
(if possible) from the generator, which is useful if the generator is a Pauli word."""
diff --git a/tests/ops/qubit/test_matrix_ops.py b/tests/ops/qubit/test_matrix_ops.py
index 2f96b0f12b8..e600156c662 100644
--- a/tests/ops/qubit/test_matrix_ops.py
+++ b/tests/ops/qubit/test_matrix_ops.py
@@ -453,6 +453,13 @@ def test_matrix_representation(self, tol):
assert np.allclose(res_static, expected, atol=tol)
assert np.allclose(res_dynamic, expected, atol=tol)
+ def test_no_decomp(self):
+ """Test that ControlledQubitUnitary raises a decomposition undefined
+ error."""
+ mat = qml.PauliX(0).get_matrix()
+ with pytest.raises(qml.operation.DecompositionUndefinedError):
+ qml.ControlledQubitUnitary(mat, wires=0, control_wires=1).decomposition()
+
label_data = [
(X, qml.QubitUnitary(X, wires=0)),
diff --git a/tests/ops/qubit/test_non_parametric_ops.py b/tests/ops/qubit/test_non_parametric_ops.py
index 8924018b88f..41e8c77bacd 100644
--- a/tests/ops/qubit/test_non_parametric_ops.py
+++ b/tests/ops/qubit/test_non_parametric_ops.py
@@ -554,6 +554,27 @@ def barrier():
assert queue[1].name == "Barrier"
assert queue[4].name == "Barrier"
+ def test_barrier_control(self):
+ """Test if Barrier is correctly included in queue when controlling"""
+ dev = qml.device("default.qubit", wires=3)
+
+ def barrier():
+ qml.PauliX(wires=0)
+ qml.Barrier(wires=[0, 1])
+ qml.CNOT(wires=[0, 1])
+
+ @qml.qnode(dev)
+ def circuit():
+ barrier()
+ qml.ctrl(barrier, 2)()
+ return qml.state()
+
+ circuit()
+ tape = circuit.tape.expand(stop_at=lambda op: op.name in ["Barrier", "PauliX", "CNOT"])
+
+ assert tape.operations[1].name == "Barrier"
+ assert tape.operations[4].name == "Barrier"
+
class TestWireCut:
"""Tests for the WireCut operator"""
diff --git a/tests/ops/test_snapshot.py b/tests/ops/test_snapshot.py
index b3b8bc50923..8d68631a166 100644
--- a/tests/ops/test_snapshot.py
+++ b/tests/ops/test_snapshot.py
@@ -26,3 +26,15 @@ def test_label_method():
"""Test the label method for the Snapshot operation."""
assert Snapshot().label() == "|S|"
assert Snapshot("my_label").label() == "|S|"
+
+
+def test_control():
+ """Test the _controlled method for the Snapshot operation."""
+ assert isinstance(Snapshot()._controlled(0), Snapshot)
+ assert Snapshot("my_label")._controlled(0).tag == Snapshot("my_label").tag
+
+
+def test_adjoint():
+ """Test the adjoint method for the Snapshot operation."""
+ assert isinstance(Snapshot().adjoint(), Snapshot)
+ assert Snapshot("my_label").adjoint().tag == Snapshot("my_label").tag
diff --git a/tests/qnn/test_keras.py b/tests/qnn/test_keras.py
index 4c123be90f5..4bdb39acf4d 100644
--- a/tests/qnn/test_keras.py
+++ b/tests/qnn/test_keras.py
@@ -479,7 +479,7 @@ def f(inputs, weights):
with tf.GradientTape() as tape:
out = tf.reduce_sum(qlayer(inputs))
- spy = mocker.spy(qml.tape.QubitParamShiftTape, "jacobian")
+ spy = mocker.spy(qml.gradients, "param_shift")
grad = tape.gradient(out, qlayer.trainable_weights)
assert grad is not None
diff --git a/tests/tape/test_qnode_old.py b/tests/tape/test_qnode_old.py
index 6f6e27e4d6e..668ae05fc08 100644
--- a/tests/tape/test_qnode_old.py
+++ b/tests/tape/test_qnode_old.py
@@ -606,6 +606,11 @@ def test_unrecognized_keyword_arguments_validation(self):
)
expected_warnings = {
+ (
+ UserWarning,
+ f"qml.qnode_old.QNode is deprecated, and will be removed in an "
+ "upcoming release. Please use qml.QNode instead.",
+ ),
(UserWarning, f"'{unrecognized_one}'{warning_text}"),
(UserWarning, f"'{unrecognized_two}'{warning_text}"),
}
@@ -632,6 +637,11 @@ def test_unrecognized_keyword_arguments_validation_decorator(self):
)
expected_warnings = {
+ (
+ UserWarning,
+ f"qml.qnode_old.QNode is deprecated, and will be removed in an "
+ "upcoming release. Please use qml.QNode instead.",
+ ),
(UserWarning, f"'{unrecognized_one}'{warning_text}"),
(UserWarning, f"'{unrecognized_two}'{warning_text}"),
}
diff --git a/tests/templates/test_subroutines/test_approx_time_evolution.py b/tests/templates/test_subroutines/test_approx_time_evolution.py
index 12a91d53beb..7d4a73fc28e 100644
--- a/tests/templates/test_subroutines/test_approx_time_evolution.py
+++ b/tests/templates/test_subroutines/test_approx_time_evolution.py
@@ -374,7 +374,7 @@ def test_trainable_hamiltonian(dev_name, diff_method):
def create_tape(coeffs, t):
H = qml.Hamiltonian(coeffs, obs)
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.templates.ApproxTimeEvolution(H, t, 2)
qml.expval(qml.PauliZ(0))
diff --git a/tests/templates/test_subroutines/test_qpe.py b/tests/templates/test_subroutines/test_qpe.py
index 04659af861c..11e6c21449c 100644
--- a/tests/templates/test_subroutines/test_qpe.py
+++ b/tests/templates/test_subroutines/test_qpe.py
@@ -64,7 +64,7 @@ def test_phase_estimated(self, phase):
tape = tape.expand(depth=2, stop_at=lambda obj: obj.name in dev.operations)
- res = tape.execute(dev).flatten()
+ res = dev.execute(tape).flatten()
initial_estimate = np.argmax(res) / 2 ** (wires - 1)
# We need to rescale because RX is exp(- i theta X / 2) and we expect a unitary of the
@@ -112,7 +112,7 @@ def test_phase_estimated_two_qubit(self):
qml.probs(estimation_wires)
tape = tape.expand(depth=2, stop_at=lambda obj: obj.name in dev.operations)
- res = tape.execute(dev).flatten()
+ res = dev.execute(tape).flatten()
if phase < 0:
estimate = np.argmax(res) / 2 ** (wires - 2) - 1
diff --git a/tests/test_debugging.py b/tests/test_debugging.py
index 1a933f3f85d..ad09e7484d5 100644
--- a/tests/test_debugging.py
+++ b/tests/test_debugging.py
@@ -22,11 +22,12 @@
class TestSnapshot:
"""Test the Snapshot instruction for simulators."""
- def test_default_qubit(self):
+ @pytest.mark.parametrize("method", [None, "backprop", "parameter-shift", "adjoint"])
+ def test_default_qubit(self, method):
"""Test that multiple snapshots are returned correctly on the state-vector simulator."""
dev = qml.device("default.qubit", wires=2)
- @qml.qnode(dev)
+ @qml.qnode(dev, diff_method=method)
def circuit():
qml.Snapshot()
qml.Hadamard(wires=0)
@@ -49,11 +50,12 @@ def circuit():
assert all(k1 == k2 for k1, k2 in zip(result.keys(), expected.keys()))
assert all(np.allclose(v1, v2) for v1, v2 in zip(result.values(), expected.values()))
- def test_default_mixed(self):
+ @pytest.mark.parametrize("method", [None, "parameter-shift"])
+ def test_default_mixed(self, method):
"""Test that multiple snapshots are returned correctly on the density-matrix simulator."""
dev = qml.device("default.mixed", wires=2)
- @qml.qnode(dev)
+ @qml.qnode(dev, diff_method=method)
def circuit():
qml.Snapshot()
qml.Hadamard(wires=0)
@@ -78,11 +80,12 @@ def circuit():
assert all(k1 == k2 for k1, k2 in zip(result.keys(), expected.keys()))
assert all(np.allclose(v1, v2) for v1, v2 in zip(result.values(), expected.values()))
- def test_default_gaussian(self):
+ @pytest.mark.parametrize("method", [None, "parameter-shift"])
+ def test_default_gaussian(self, method):
"""Test that multiple snapshots are returned correctly on the CV simulator."""
dev = qml.device("default.gaussian", wires=2)
- @qml.qnode(dev)
+ @qml.qnode(dev, diff_method=method)
def circuit():
qml.Snapshot()
qml.Displacement(0.5, 0, wires=0)
@@ -124,11 +127,12 @@ def circuit():
for v1, v2 in zip(result.values(), expected.values())
)
- def test_lightning_qubit(self):
+ @pytest.mark.parametrize("method", [None, "parameter-shift", "adjoint"])
+ def test_lightning_qubit(self, method):
"""Test that an error is (currently) raised on the lightning simulator."""
dev = qml.device("lightning.qubit", wires=2)
- @qml.qnode(dev)
+ @qml.qnode(dev, diff_method=method)
def circuit():
qml.Snapshot()
qml.Hadamard(wires=0)
@@ -253,26 +257,26 @@ def circuit():
if m == "state":
assert np.allclose(result[2], result["execution_results"])
- @pytest.mark.parametrize("method", [None, "backprop", "parameter-shift", "adjoint"])
- def test_different_diff_methods(self, method):
- """Test that snapshots work with different differentiation methods."""
+ def test_controlled_circuit(self):
+ """Test that snapshots are returned correctly even with a controlled circuit."""
dev = qml.device("default.qubit", wires=2)
- @qml.qnode(dev, diff_method=method)
- def circuit():
+ def circuit(params, wire):
+ qml.Hadamard(wire)
qml.Snapshot()
- qml.Hadamard(wires=0)
- qml.Snapshot("very_important_state")
- qml.CNOT(wires=[0, 1])
- qml.Snapshot()
- return qml.expval(qml.PauliZ(0))
+ qml.Rot(*params, wire)
- result = qml.snapshots(circuit)()
+ @qml.qnode(dev)
+ def qnode(params):
+ qml.Hadamard(0)
+ qml.ctrl(circuit, 0)(params, wire=1)
+ return qml.expval(qml.PauliZ(1))
+
+ params = np.array([1.3, 1.4, 0.2])
+ result = qml.snapshots(qnode)(params)
expected = {
- 0: np.array([1, 0, 0, 0]),
- "very_important_state": np.array([1 / np.sqrt(2), 0, 1 / np.sqrt(2), 0]),
- 2: np.array([1 / np.sqrt(2), 0, 0, 1 / np.sqrt(2)]),
- "execution_results": np.array(0),
+ 0: np.array([1 / np.sqrt(2), 0, 0.5, 0.5]),
+ "execution_results": np.array(0.36819668),
}
assert all(k1 == k2 for k1, k2 in zip(result.keys(), expected.keys()))
diff --git a/tests/test_device.py b/tests/test_device.py
index af577763506..9e296c19113 100644
--- a/tests/test_device.py
+++ b/tests/test_device.py
@@ -918,7 +918,7 @@ class TestBatchExecution:
qml.PauliX(wires=0)
qml.expval(qml.PauliZ(wires=0)), qml.expval(qml.PauliZ(wires=1))
- with qml.tape.JacobianTape() as tape2:
+ with qml.tape.QuantumTape() as tape2:
qml.PauliX(wires=0)
qml.expval(qml.PauliZ(wires=0))
diff --git a/tests/test_measurements.py b/tests/test_measurements.py
index 90cb7081e6e..c59f07f23b1 100644
--- a/tests/test_measurements.py
+++ b/tests/test_measurements.py
@@ -347,10 +347,27 @@ def test_measurement_value_inversion(self, val_pair, num_inv, expected_idx):
one_case = val_pair[1]
mv = MeasurementValue(measurement_id="1234", zero_case=zero_case, one_case=one_case)
for _ in range(num_inv):
- mv = mv.__invert__()
+ mv_new = mv.__invert__()
+
+ # Check that inversion involves creating a copy
+ assert not mv_new is mv
+
+ mv = mv_new
assert mv._control_value == val_pair[expected_idx]
+ def test_measurement_value_assertion_error_wrong_type(self):
+ """Test that the return_type related info is updated for a
+ measurement."""
+ mv1 = MeasurementValue(measurement_id="1111")
+ mv2 = MeasurementValue(measurement_id="2222")
+
+ with pytest.raises(
+ MeasurementValueError,
+ match="The equality operator is used to assert measurement outcomes, but got a value with type",
+ ):
+ mv1 == mv2
+
def test_measurement_value_assertion_error(self):
"""Test that the return_type related info is updated for a
measurement."""
diff --git a/tests/test_qnode.py b/tests/test_qnode.py
index c39c36407ba..c4a40b04399 100644
--- a/tests/test_qnode.py
+++ b/tests/test_qnode.py
@@ -21,7 +21,7 @@
from pennylane import numpy as pnp
from pennylane import qnode, QNode
from pennylane.transforms import draw
-from pennylane.tape import JacobianTape
+from pennylane.tape import QuantumTape
def dummyfunc():
@@ -408,12 +408,12 @@ def func(x, y):
res = qn(x, y)
- assert isinstance(qn.qtape, JacobianTape)
+ assert isinstance(qn.qtape, QuantumTape)
assert len(qn.qtape.operations) == 3
assert len(qn.qtape.observables) == 1
assert qn.qtape.num_params == 2
- expected = qn.qtape.execute(dev)
+ expected = qml.execute([qn.tape], dev, None)
assert np.allclose(res, expected, atol=tol, rtol=0)
# when called, a new quantum tape is constructed
@@ -614,12 +614,12 @@ def func(x, y):
res = func(x, y)
- assert isinstance(func.qtape, JacobianTape)
+ assert isinstance(func.qtape, QuantumTape)
assert len(func.qtape.operations) == 3
assert len(func.qtape.observables) == 1
assert func.qtape.num_params == 2
- expected = func.qtape.execute(dev)
+ expected = qml.execute([func.tape], dev, None)
assert np.allclose(res, expected, atol=tol, rtol=0)
# when called, a new quantum tape is constructed
diff --git a/tests/test_qubit_device.py b/tests/test_qubit_device.py
index 97b6f48f56b..639cdecc1af 100644
--- a/tests/test_qubit_device.py
+++ b/tests/test_qubit_device.py
@@ -832,7 +832,7 @@ class TestBatchExecution:
qml.PauliX(wires=0)
qml.expval(qml.PauliZ(wires=0)), qml.expval(qml.PauliZ(wires=1))
- with qml.tape.JacobianTape() as tape2:
+ with qml.tape.QuantumTape() as tape2:
qml.PauliX(wires=0)
qml.expval(qml.PauliZ(wires=0))
diff --git a/tests/test_qubit_device_adjoint_jacobian.py b/tests/test_qubit_device_adjoint_jacobian.py
index 10a2bfcfc6b..4984c737592 100644
--- a/tests/test_qubit_device_adjoint_jacobian.py
+++ b/tests/test_qubit_device_adjoint_jacobian.py
@@ -32,7 +32,7 @@ def test_not_expval(self, dev):
"""Test if a QuantumFunctionError is raised for a tape with measurements that are not
expectation values"""
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.RX(0.1, wires=0)
qml.var(qml.PauliZ(0))
@@ -44,7 +44,7 @@ def test_finite_shots_warns(self):
dev = qml.device("default.qubit", wires=1, shots=1)
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.expval(qml.PauliZ(0))
with pytest.warns(
@@ -56,7 +56,7 @@ def test_unsupported_op(self, dev):
"""Test if a QuantumFunctionError is raised for an unsupported operation, i.e.,
multi-parameter operations that are not qml.Rot"""
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.CRot(0.1, 0.2, 0.3, wires=[0, 1])
qml.expval(qml.PauliZ(0))
@@ -68,7 +68,7 @@ def test_unsupported_op(self, dev):
def test_pauli_rotation_gradient(self, G, theta, tol, dev):
"""Tests that the automatic gradients of Pauli rotations are correct."""
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.QubitStateVector(np.array([1.0, -1.0], requires_grad=False) / np.sqrt(2), wires=0)
G(theta, wires=[0])
qml.expval(qml.PauliZ(0))
@@ -88,7 +88,7 @@ def test_Rot_gradient(self, theta, tol, dev):
correct."""
params = np.array([theta, theta**3, np.sqrt(2) * theta])
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.QubitStateVector(np.array([1.0, -1.0], requires_grad=False) / np.sqrt(2), wires=0)
qml.Rot(*params, wires=[0])
qml.expval(qml.PauliZ(0))
@@ -106,7 +106,7 @@ def test_Rot_gradient(self, theta, tol, dev):
def test_ry_gradient(self, par, tol, dev):
"""Test that the gradient of the RY gate matches the exact analytic formula."""
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.RY(par, wires=[0])
qml.expval(qml.PauliX(0))
@@ -125,7 +125,7 @@ def test_rx_gradient(self, tol, dev):
"""Test that the gradient of the RX gate matches the known formula."""
a = 0.7418
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.RX(a, wires=0)
qml.expval(qml.PauliZ(0))
@@ -139,7 +139,7 @@ def test_multiple_rx_gradient(self, tol):
dev = qml.device("default.qubit", wires=3)
params = np.array([np.pi, np.pi / 2, np.pi / 3])
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.RX(params[0], wires=0)
qml.RX(params[1], wires=1)
qml.RX(params[2], wires=2)
@@ -162,7 +162,7 @@ def test_gradients(self, op, obs, tol, dev):
"""Tests that the gradients of circuits match between the finite difference and device
methods."""
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.Hadamard(wires=0)
qml.RX(0.543, wires=0)
qml.CNOT(wires=[0, 1])
@@ -188,7 +188,7 @@ def test_gradient_gate_with_multiple_parameters(self, tol, dev):
"""Tests that gates with multiple free parameters yield correct gradients."""
x, y, z = [0.5, 0.3, -0.7]
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.RX(0.4, wires=[0])
qml.Rot(x, y, z, wires=[0])
qml.RY(-0.2, wires=[0])
@@ -210,7 +210,7 @@ def test_use_device_state(self, tol, dev):
x, y, z = [0.5, 0.3, -0.7]
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.RX(0.4, wires=[0])
qml.Rot(x, y, z, wires=[0])
qml.RY(-0.2, wires=[0])
@@ -220,7 +220,7 @@ def test_use_device_state(self, tol, dev):
dM1 = dev.adjoint_jacobian(tape)
- tape.execute(dev)
+ qml.execute([tape], dev, None)
dM2 = dev.adjoint_jacobian(tape, use_device_state=True)
assert np.allclose(dM1, dM2, atol=tol, rtol=0)
@@ -229,7 +229,7 @@ def test_provide_starting_state(self, tol, dev):
"""Tests provides correct answer when provided starting state."""
x, y, z = [0.5, 0.3, -0.7]
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.RX(0.4, wires=[0])
qml.Rot(x, y, z, wires=[0])
qml.RY(-0.2, wires=[0])
@@ -239,7 +239,7 @@ def test_provide_starting_state(self, tol, dev):
dM1 = dev.adjoint_jacobian(tape)
- tape.execute(dev)
+ qml.execute([tape], dev, None)
dM2 = dev.adjoint_jacobian(tape, starting_state=dev._pre_rotated_state)
assert np.allclose(dM1, dM2, atol=tol, rtol=0)
diff --git a/tests/transforms/test_adjoint.py b/tests/transforms/test_adjoint.py
index 4ca0845f98e..b87349705a9 100644
--- a/tests/transforms/test_adjoint.py
+++ b/tests/transforms/test_adjoint.py
@@ -134,7 +134,7 @@ def my_circuit():
)
-with qml.tape.JacobianTape() as tape:
+with qml.tape.QuantumTape() as tape:
qml.PauliX(0)
qml.Hadamard(1)
diff --git a/tests/transforms/test_adjoint_metric_tensor.py b/tests/transforms/test_adjoint_metric_tensor.py
index 0684c9efab2..44752bf7e06 100644
--- a/tests/transforms/test_adjoint_metric_tensor.py
+++ b/tests/transforms/test_adjoint_metric_tensor.py
@@ -819,7 +819,7 @@ def ansatz(x, y):
def test_error_finite_shots(self):
"""Test that an error is raised if the device has a finite number of shots set."""
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.RX(0.2, wires=0)
qml.RY(1.9, wires=1)
dev = qml.device("default.qubit", wires=2, shots=1)
diff --git a/tests/transforms/test_batch_input.py b/tests/transforms/test_batch_input.py
new file mode 100644
index 00000000000..c03fd3198fc
--- /dev/null
+++ b/tests/transforms/test_batch_input.py
@@ -0,0 +1,128 @@
+# Copyright 2018-2021 Xanadu Quantum Technologies Inc.
+
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+
+# http://www.apache.org/licenses/LICENSE-2.0
+
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""
+Unit tests for the batch inputs transform.
+"""
+import pytest
+
+import pennylane as qml
+from pennylane import numpy as np
+
+
+def test_simple_circuit():
+ """Test that batching works for a simple circuit"""
+ dev = qml.device("default.qubit", wires=2)
+
+ @qml.batch_input(argnum=0)
+ @qml.qnode(dev, diff_method="parameter-shift")
+ def circuit(inputs, weights):
+ qml.AngleEmbedding(inputs, wires=range(2), rotation="Y")
+ qml.RY(weights[0], wires=0)
+ qml.RY(weights[1], wires=1)
+ return qml.expval(qml.PauliZ(1))
+
+ batch_size = 5
+ inputs = np.random.uniform(0, np.pi, (batch_size, 2))
+ inputs.requires_grad = False
+ weights = np.random.uniform(-np.pi, np.pi, (2,))
+
+ res = circuit(inputs, weights)
+ assert res.shape == (batch_size,)
+
+
+def test_value_error():
+ """Test if the batch_input raises relevant errors correctly"""
+
+ dev = qml.device("default.qubit", wires=2)
+
+ @qml.batch_input(argnum=[0, 1])
+ @qml.qnode(dev, diff_method="parameter-shift")
+ def circuit(input1, input2, weights):
+ qml.AngleEmbedding(input1, wires=range(2), rotation="Y")
+ qml.RY(input2[0], wires=0)
+ qml.RY(weights[0], wires=0)
+ qml.RY(weights[1], wires=1)
+ return qml.expval(qml.PauliZ(1))
+
+ batch_size = 5
+ input1 = np.random.uniform(0, np.pi, (batch_size, 2))
+ input1.requires_grad = False
+ input2 = np.random.uniform(0, np.pi, (4, 1))
+ input2.requires_grad = False
+ weights = np.random.uniform(-np.pi, np.pi, (2,))
+
+ with pytest.raises(ValueError, match="Batch dimension for all gate arguments"):
+ res = circuit(input1, input2, weights)
+
+
+def test_mottonenstate_preparation(mocker):
+ """Test that batching works for MottonenStatePreparation"""
+ dev = qml.device("default.qubit", wires=3)
+
+ @qml.batch_input(argnum=0)
+ @qml.qnode(dev)
+ def circuit(data, weights):
+ qml.templates.MottonenStatePreparation(data, wires=[0, 1, 2])
+ qml.templates.StronglyEntanglingLayers(weights, wires=[0, 1, 2])
+ return qml.probs(wires=[0, 1, 2])
+
+ batch_size = 3
+
+ # create a batched input statevector
+ data = np.random.random((batch_size, 2**3), requires_grad=False)
+ data /= np.linalg.norm(data, axis=1).reshape(-1, 1) # normalize
+
+ # weights is not batched
+ weights = np.random.random((10, 3, 3), requires_grad=True)
+
+ spy = mocker.spy(circuit.device, "batch_execute")
+ res = circuit(data, weights)
+ assert res.shape == (batch_size, 2**3)
+ assert len(spy.call_args[0][0]) == batch_size
+
+ # check the results against individually executed circuits (no batching)
+ @qml.qnode(dev)
+ def circuit2(data, weights):
+ qml.templates.MottonenStatePreparation(data, wires=[0, 1, 2])
+ qml.templates.StronglyEntanglingLayers(weights, wires=[0, 1, 2])
+ return qml.probs(wires=[0, 1, 2])
+
+ indiv_res = []
+ for state in data:
+ indiv_res.append(circuit2(state, weights))
+ assert np.allclose(res, indiv_res)
+
+
+@pytest.mark.parametrize("diff_method", ["backprop", "adjoint", "parameter-shift"])
+def test_autograd(diff_method, tol):
+ """Test derivatives when using autograd"""
+ dev = qml.device("default.qubit", wires=2)
+
+ @qml.batch_input
+ @qml.qnode(dev, diff_method=diff_method)
+ def circuit(x):
+ qml.RX(x, wires=0)
+ qml.RY(0.1, wires=1)
+ qml.CNOT(wires=[0, 1])
+ return qml.expval(qml.PauliZ(0) @ qml.PauliX(1))
+
+ def cost(x):
+ return np.sum(circuit(x))
+
+ batch_size = 3
+ x = np.linspace(0.1, 0.5, batch_size, requires_grad=True)
+
+ res = qml.grad(cost)(x)
+ expected = -np.sin(0.1) * np.sin(x)
+ assert np.allclose(res, expected, atol=tol, rtol=0)
diff --git a/tests/transforms/test_batch_transform.py b/tests/transforms/test_batch_transform.py
index 586d426d895..f2789878259 100644
--- a/tests/transforms/test_batch_transform.py
+++ b/tests/transforms/test_batch_transform.py
@@ -493,8 +493,8 @@ def my_transform(tape, weights):
"""Generates two tapes, one with all RX replaced with RY,
and the other with all RX replaced with RZ."""
- tape1 = qml.tape.JacobianTape()
- tape2 = qml.tape.JacobianTape()
+ tape1 = qml.tape.QuantumTape()
+ tape2 = qml.tape.QuantumTape()
# loop through all operations on the input tape
for op in tape.operations + tape.measurements:
@@ -650,13 +650,13 @@ def test_result(self, mocker):
x = 0.6
y = 0.7
- with qml.tape.JacobianTape() as tape1:
+ with qml.tape.QuantumTape() as tape1:
qml.RX(x, wires=0)
qml.RY(y, wires=1)
qml.CNOT(wires=[0, 1])
qml.expval(H)
- with qml.tape.JacobianTape() as tape2:
+ with qml.tape.QuantumTape() as tape2:
qml.Hadamard(wires=0)
qml.CRX(x, wires=[0, 1])
qml.CNOT(wires=[0, 1])
@@ -683,13 +683,13 @@ def test_differentiation(self):
weights = np.array([0.6, 0.8], requires_grad=True)
def cost(weights):
- with qml.tape.JacobianTape() as tape1:
+ with qml.tape.QuantumTape() as tape1:
qml.RX(weights[0], wires=0)
qml.RY(weights[1], wires=1)
qml.CNOT(wires=[0, 1])
qml.expval(H)
- with qml.tape.JacobianTape() as tape2:
+ with qml.tape.QuantumTape() as tape2:
qml.Hadamard(wires=0)
qml.CRX(weights[0], wires=[0, 1])
qml.CNOT(wires=[0, 1])
diff --git a/tests/transforms/test_condition.py b/tests/transforms/test_condition.py
index efe98f9e1d5..a131d44edf7 100644
--- a/tests/transforms/test_condition.py
+++ b/tests/transforms/test_condition.py
@@ -69,9 +69,32 @@ def f(x):
assert ops[4].return_type == qml.operation.Probability
- def test_cond_queues_with_else(self):
- """Test that qml.cond queues Conditional operations as expected when an
- else qfunc is also provided."""
+ def tape_with_else(f, g, r):
+ """Tape that uses cond by passing both a true and false func."""
+ with qml.tape.QuantumTape() as tape:
+ m_0 = qml.measure(0)
+ qml.cond(m_0, f, g)(r)
+ qml.probs(wires=1)
+
+ return tape
+
+ def tape_uses_cond_twice(f, g, r):
+ """Tape that uses cond twice such that it's equivalent to using cond
+ with two functions being passed (tape_with_else)."""
+ with qml.tape.QuantumTape() as tape:
+ m_0 = qml.measure(0)
+ qml.cond(m_0, f)(r)
+ qml.cond(~m_0, g)(r)
+ qml.probs(wires=1)
+
+ return tape
+
+ @pytest.mark.parametrize("tape", [tape_with_else, tape_uses_cond_twice])
+ def test_cond_queues_with_else(self, tape):
+ """Test that qml.cond queues Conditional operations as expected in two cases:
+ 1. When an else qfunc is provided;
+ 2. When qml.cond is used twice equivalent to using an else qfunc.
+ """
r = 1.234
def f(x):
@@ -82,11 +105,7 @@ def f(x):
def g(x):
qml.PauliY(1)
- with qml.tape.QuantumTape() as tape:
- m_0 = qml.measure(0)
- qml.cond(m_0, f, g)(r)
- qml.probs(wires=1)
-
+ tape = tape(f, g, r)
ops = tape.queue
target_wire = qml.wires.Wires(1)
@@ -111,6 +130,13 @@ def g(x):
assert isinstance(ops[4].then_op, qml.PauliY)
assert ops[4].then_op.wires == target_wire
+ # Check that: the measurement value is the same for true_fn conditional
+ # ops
+ assert ops[1].meas_val is ops[2].meas_val is ops[3].meas_val
+
+ # However, it is not the same for the false_fn
+ assert ops[3].meas_val is not ops[4].meas_val
+
assert ops[5].return_type == qml.operation.Probability
def test_cond_error(self):
diff --git a/tests/transforms/test_hamiltonian_expand.py b/tests/transforms/test_hamiltonian_expand.py
index a849b833ed7..441a04e7fc0 100644
--- a/tests/transforms/test_hamiltonian_expand.py
+++ b/tests/transforms/test_hamiltonian_expand.py
@@ -160,7 +160,7 @@ def test_hamiltonian_dif_autograd(self, tol):
0.64123,
]
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
for i in range(2):
qml.RX(np.array(0), wires=0)
qml.RX(np.array(0), wires=1)
@@ -204,7 +204,7 @@ def test_hamiltonian_dif_tensorflow(self):
]
with tf.GradientTape() as gtape:
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
for i in range(2):
qml.RX(var[i, 0], wires=0)
qml.RX(var[i, 1], wires=1)
diff --git a/tests/transforms/test_metric_tensor.py b/tests/transforms/test_metric_tensor.py
index dd6c0e9eddd..434ab5e0591 100644
--- a/tests/transforms/test_metric_tensor.py
+++ b/tests/transforms/test_metric_tensor.py
@@ -580,7 +580,7 @@ def test_multi_qubit_gates(self):
"""Test that a tape with Ising gates has the correct metric tensor tapes."""
dev = qml.device("default.qubit", wires=3)
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.Hadamard(0)
qml.Hadamard(2)
qml.IsingXX(0.2, wires=[0, 1])
@@ -1353,7 +1353,7 @@ def aux_wire_ansatz_1(x, y):
def test_get_aux_wire(aux_wire, ansatz):
"""Test ``_get_aux_wire`` without device_wires."""
x, y = np.array([0.2, 0.1], requires_grad=True)
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
ansatz(x, y)
out = _get_aux_wire(aux_wire, tape, None)
@@ -1366,7 +1366,7 @@ def test_get_aux_wire(aux_wire, ansatz):
def test_get_aux_wire_with_device_wires():
"""Test ``_get_aux_wire`` with device_wires."""
x, y = np.array([0.2, 0.1], requires_grad=True)
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.RX(x, wires=0)
qml.RX(x, wires="one")
diff --git a/tests/transforms/test_optimization/test_undo_swaps.py b/tests/transforms/test_optimization/test_undo_swaps.py
index 64606dc0497..c968464970d 100644
--- a/tests/transforms/test_optimization/test_undo_swaps.py
+++ b/tests/transforms/test_optimization/test_undo_swaps.py
@@ -36,7 +36,7 @@ def qfunc():
transformed_qfunc = undo_swaps(qfunc)
tape = qml.transforms.make_tape(transformed_qfunc)()
- res = tape.execute(qml.device("default.qubit", wires=2))
+ res = qml.device("default.qubit", wires=2).execute(tape)
assert len(tape.operations) == 2
assert np.allclose(res[0][0], 0.5)
@@ -52,7 +52,7 @@ def qfunc():
transformed_qfunc = undo_swaps(qfunc)
tape = qml.transforms.make_tape(transformed_qfunc)()
- res = tape.execute(qml.device("default.qubit", wires=2))
+ res = qml.device("default.qubit", wires=2).execute(tape)
assert len(tape.operations) == 2
assert np.allclose(res[0][2], 1.0)
@@ -75,10 +75,10 @@ def qfunc2():
transformed_qfunc = undo_swaps(qfunc1)
tape1 = qml.transforms.make_tape(transformed_qfunc)()
- res1 = tape1.execute(qml.device("default.qubit", wires=3))
+ res1 = qml.device("default.qubit", wires=3).execute(tape1)
tape2 = qml.transforms.make_tape(qfunc2)()
- res2 = tape2.execute(qml.device("default.qubit", wires=3))
+ res2 = qml.device("default.qubit", wires=3).execute(tape2)
assert np.allclose(res1, res2)
@@ -102,10 +102,10 @@ def qfunc2():
transformed_qfunc = undo_swaps(qfunc1)
tape1 = qml.transforms.make_tape(transformed_qfunc)()
- res1 = tape1.execute(qml.device("default.qubit", wires=3))
+ res1 = qml.device("default.qubit", wires=3).execute(tape1)
tape2 = qml.transforms.make_tape(qfunc2)()
- res2 = tape2.execute(qml.device("default.qubit", wires=3))
+ res2 = qml.device("default.qubit", wires=3).execute(tape2)
assert np.allclose(res1, res2)
diff --git a/tests/transforms/test_tape_expand.py b/tests/transforms/test_tape_expand.py
index 0319b566029..1393929502f 100644
--- a/tests/transforms/test_tape_expand.py
+++ b/tests/transforms/test_tape_expand.py
@@ -25,7 +25,7 @@ class TestCreateExpandFn:
crit_0 = (~qml.operation.is_trainable) | (qml.operation.has_gen & qml.operation.is_trainable)
doc_0 = "Test docstring."
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.RX(0.2, wires=0)
qml.RY(qml.numpy.array(2.1, requires_grad=True), wires=1)
qml.Rot(*qml.numpy.array([0.5, 0.2, -0.1], requires_grad=True), wires=0)
@@ -63,7 +63,7 @@ def test_device_and_stopping_expansion(self, mocker):
dev = qml.device("default.qubit", wires=1)
expand_fn = qml.transforms.create_expand_fn(device=dev, depth=10, stop_at=self.crit_0)
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.U1(0.2, wires=0)
qml.Rot(*qml.numpy.array([0.5, 0.2, -0.1], requires_grad=True), wires=0)
@@ -80,7 +80,7 @@ def test_device_only_expansion(self, mocker):
dev = qml.device("default.qubit", wires=1)
expand_fn = qml.transforms.create_expand_fn(device=dev, depth=10)
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.U1(0.2, wires=0)
qml.Rot(*qml.numpy.array([0.5, 0.2, -0.1], requires_grad=True), wires=0)
@@ -96,7 +96,7 @@ def test_depth_only_expansion(self):
"""Test that passing a depth simply expands to that depth"""
dev = qml.device("default.qubit", wires=0)
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.RX(0.2, wires=0)
qml.RY(qml.numpy.array(2.1, requires_grad=True), wires=1)
qml.Rot(*qml.numpy.array([0.5, 0.2, -0.1], requires_grad=True), wires=0)
@@ -169,7 +169,7 @@ class TestExpandNonunitaryGen:
def test_do_not_expand(self):
"""Test that a tape with single-parameter operations with
unitary generators and non-parametric operations is not touched."""
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.RX(0.2, wires=0)
qml.Hadamard(0)
qml.PauliRot(0.9, "XY", wires=[0, 1])
@@ -182,7 +182,7 @@ def test_do_not_expand(self):
def test_expand_multi_par(self):
"""Test that a tape with single-parameter operations with
unitary generators and non-parametric operations is not touched."""
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.RX(0.2, wires=0)
qml.Hadamard(0)
qml.Rot(0.9, 1.2, -0.6, wires=0)
@@ -209,7 +209,7 @@ class _PhaseShift(qml.PhaseShift):
def generator(self):
return None
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.RX(0.2, wires=0)
qml.Hadamard(0)
_PhaseShift(2.1, wires=1)
@@ -225,7 +225,7 @@ def test_expand_nonunitary_generator(self):
"""Test that a tape with single-parameter operations with
unitary generators and non-parametric operations is not touched."""
- with qml.tape.JacobianTape() as tape:
+ with qml.tape.QuantumTape() as tape:
qml.RX(0.2, wires=0)
qml.Hadamard(0)
qml.PhaseShift(2.1, wires=1)