Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Deprecate tape and qtape properties on the QNode #6583

Open
wants to merge 26 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
ca420b5
initial deprecations
andrijapau Nov 13, 2024
0de5a26
update changelog
andrijapau Nov 13, 2024
7eec801
remove tape/qtape from source code and put warnings on tests that use…
andrijapau Nov 13, 2024
7201df2
Merge branch 'master' into deprecate-tape-properties
andrijapau Nov 13, 2024
342e5f0
change construct_tape level
andrijapau Nov 13, 2024
c851e7e
change construct_tape level and typo
andrijapau Nov 13, 2024
7392ad9
fix qnspsa
andrijapau Nov 13, 2024
2e36047
fix code cov, this branch is never hit
andrijapau Nov 14, 2024
d082e89
Update pennylane/debugging/snapshot.py
andrijapau Nov 14, 2024
1557c29
Update pennylane/gradients/classical_jacobian.py
andrijapau Nov 14, 2024
4b5f391
Update pennylane/gradients/parameter_shift_hessian.py
andrijapau Nov 14, 2024
2903c29
Update pennylane/math/multi_dispatch.py
andrijapau Nov 14, 2024
2dd682d
Update pennylane/optimize/qnspsa.py
andrijapau Nov 14, 2024
e68955c
Update pennylane/optimize/qnspsa.py
andrijapau Nov 14, 2024
5704fe8
Update pennylane/optimize/qnspsa.py
andrijapau Nov 14, 2024
f76211d
Update pennylane/optimize/qnspsa.py
andrijapau Nov 14, 2024
878412b
Update pennylane/optimize/qnspsa.py
andrijapau Nov 14, 2024
1013125
Update pennylane/optimize/shot_adaptive.py
andrijapau Nov 14, 2024
86bb030
Update pennylane/transforms/core/transform_program.py
andrijapau Nov 14, 2024
7898e4b
Update pennylane/transforms/core/transform_program.py
andrijapau Nov 14, 2024
56d1c0a
Merge branch 'master' into deprecate-tape-properties
andrijapau Nov 14, 2024
1c0fa80
Merge branch 'master' into deprecate-tape-properties
andrijapau Nov 14, 2024
82bf23d
Update tests/test_qnode.py
andrijapau Nov 14, 2024
85bfe4c
address Christina's comments
andrijapau Nov 14, 2024
28b4c30
Update doc/releases/changelog-dev.md
andrijapau Nov 15, 2024
5dd7e4b
Update pennylane/workflow/qnode.py
andrijapau Nov 15, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion doc/development/deprecations.rst
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,14 @@ deprecations are listed below.
Pending deprecations
--------------------

* The ``tape`` and ``qtape`` properties of ``QNode`` have been deprecated.
Instead, use the ``qml.workflow.construct_tape`` function.

- Deprecated in v0.40
- Will be removed in v0.41

* The ``QNode.get_best_method`` and ``QNode.best_method_str`` methods have been deprecated.
Instead, use the ``qml.workflow.get_best_diff_method``.
Instead, use the ``qml.workflow.get_best_diff_method`` function.

- Deprecated in v0.40
- Will be removed in v0.41
Expand Down
3 changes: 2 additions & 1 deletion doc/introduction/inspecting_circuits.rst
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,7 @@ or to check whether two gates causally influence each other.

import pennylane as qml
from pennylane import CircuitGraph
from pennylane.workflow import construct_tape

dev = qml.device('lightning.qubit', wires=(0,1,2,3))

Expand All @@ -292,7 +293,7 @@ or to check whether two gates causally influence each other.


circuit()
tape = circuit.qtape
tape = construct_tape(circuit)()
ops = tape.operations
obs = tape.observables
g = CircuitGraph(ops, obs, tape.wires)
Expand Down
4 changes: 4 additions & 0 deletions doc/releases/changelog-dev.md
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,10 @@

<h3>Deprecations 👋</h3>

* The `tape` and `qtape` properties of `QNode` have been deprecated.
Instead, use the `qml.workflow.construct_tape` function.
[(#6583)](https://github.com/PennyLaneAI/pennylane/pull/6583)

* The `QNode.get_best_method` and `QNode.best_method_str` methods have been deprecated.
Instead, use the `qml.workflow.get_best_diff_method` function.
[(#6418)](https://github.com/PennyLaneAI/pennylane/pull/6418)
Expand Down
8 changes: 4 additions & 4 deletions pennylane/circuit_graph.py
Original file line number Diff line number Diff line change
Expand Up @@ -516,14 +516,14 @@ def max_simultaneous_measurements(self):
>>> def circuit_measure_max_once():
... return qml.expval(qml.X(0))
>>> qnode = qml.QNode(circuit_measure_max_once, dev)
>>> qnode()
>>> qnode.qtape.graph.max_simultaneous_measurements
>>> tape = qml.workflow.construct_tape(qnode)()
>>> tape.graph.max_simultaneous_measurements
1
>>> def circuit_measure_max_twice():
... return qml.expval(qml.X(0)), qml.probs(wires=0)
>>> qnode = qml.QNode(circuit_measure_max_twice, dev)
>>> qnode()
>>> qnode.qtape.graph.max_simultaneous_measurements
>>> tape = qml.workflow.construct_tape(qnode)()
>>> tape.graph.max_simultaneous_measurements
2

Returns:
Expand Down
4 changes: 2 additions & 2 deletions pennylane/debugging/snapshot.py
Original file line number Diff line number Diff line change
Expand Up @@ -219,8 +219,8 @@ def snapshots_qnode(self, qnode, targs, tkwargs):

def get_snapshots(*args, **kwargs):
# Need to construct to generate the tape and be able to validate
qnode.construct(args, kwargs)
qml.devices.preprocess.validate_measurements(qnode.tape)
tape = qml.workflow.construct_tape(qnode)(*args, **kwargs)
qml.devices.preprocess.validate_measurements(tape)

old_interface = qnode.interface
if old_interface == "auto":
Expand Down
59 changes: 24 additions & 35 deletions pennylane/drawer/draw.py
Original file line number Diff line number Diff line change
Expand Up @@ -314,41 +314,30 @@ def wrapper(*args, **kwargs):
except TypeError:
_wire_order = tapes[0].wires

if tapes is not None:
cache = {"tape_offset": 0, "matrices": []}
res = [
tape_text(
t,
wire_order=_wire_order,
show_all_wires=show_all_wires,
decimals=decimals,
show_matrices=False,
show_wire_labels=show_wire_labels,
max_length=max_length,
cache=cache,
)
for t in tapes
]
if show_matrices and cache["matrices"]:
mat_str = ""
for i, mat in enumerate(cache["matrices"]):
if qml.math.requires_grad(mat) and hasattr(mat, "detach"):
mat = mat.detach()
mat_str += f"\nM{i} = \n{mat}"
if mat_str:
mat_str = "\n" + mat_str
return "\n\n".join(res) + mat_str
return "\n\n".join(res)

return tape_text(
qnode.qtape,
wire_order=_wire_order,
show_all_wires=show_all_wires,
decimals=decimals,
show_matrices=show_matrices,
show_wire_labels=show_wire_labels,
max_length=max_length,
)
cache = {"tape_offset": 0, "matrices": []}
res = [
tape_text(
t,
wire_order=_wire_order,
show_all_wires=show_all_wires,
decimals=decimals,
show_matrices=False,
show_wire_labels=show_wire_labels,
max_length=max_length,
cache=cache,
)
for t in tapes
]
if show_matrices and cache["matrices"]:
mat_str = ""
for i, mat in enumerate(cache["matrices"]):
if qml.math.requires_grad(mat) and hasattr(mat, "detach"):
mat = mat.detach()
mat_str += f"\nM{i} = \n{mat}"
if mat_str:
mat_str = "\n" + mat_str
return "\n\n".join(res) + mat_str
return "\n\n".join(res)

return wrapper

Expand Down
5 changes: 3 additions & 2 deletions pennylane/fourier/qnode_spectrum.py
Original file line number Diff line number Diff line change
Expand Up @@ -400,15 +400,16 @@ def wrapper(*args, **kwargs):
)
# After construction, check whether invalid operations (for a spectrum)
# are present in the QNode
for m in qnode.qtape.measurements:
tape = qml.workflow.construct_tape(qnode)(*args, **kwargs)
andrijapau marked this conversation as resolved.
Show resolved Hide resolved
for m in tape.measurements:
if not isinstance(m, (qml.measurements.ExpectationMP, qml.measurements.ProbabilityMP)):
raise ValueError(
f"The measurement {m.__class__.__name__} is not supported as it likely does "
"not admit a Fourier spectrum."
)
cjacs = jac_fn(*args, **kwargs)
spectra = {}
tape = qml.transforms.expand_multipar(qnode.qtape)
tape = qml.transforms.expand_multipar(tape)
par_info = tape.par_info

# Iterate over jacobians per argument
Expand Down
3 changes: 1 addition & 2 deletions pennylane/gradients/classical_jacobian.py
Original file line number Diff line number Diff line change
Expand Up @@ -141,8 +141,7 @@ def classical_preprocessing(*args, **kwargs):
"""Returns the trainable gate parameters for a given QNode input."""
kwargs.pop("shots", None)
kwargs.pop("argnums", None)
qnode.construct(args, kwargs)
tape = qnode.qtape
tape = qml.workflow.construct_tape(qnode)(*args, **kwargs)

if expand_fn is not None:
tape = expand_fn(tape)
Expand Down
3 changes: 1 addition & 2 deletions pennylane/gradients/parameter_shift_hessian.py
Original file line number Diff line number Diff line change
Expand Up @@ -521,8 +521,7 @@ def param_shift_hessian(
the parameter-shifted tapes and a post-processing function to combine the execution
results of these tapes into the Hessian:

>>> circuit(x) # generate the QuantumTape inside the QNode
>>> tape = circuit.qtape
>>> tape = qml.workflow.construct_tape(circuit)(x)
>>> hessian_tapes, postproc_fn = qml.gradients.param_shift_hessian(tape)
>>> len(hessian_tapes)
13
Expand Down
6 changes: 3 additions & 3 deletions pennylane/gradients/pulse_gradient_odegen.py
Original file line number Diff line number Diff line change
Expand Up @@ -504,9 +504,9 @@ def circuit(params):
Alternatively, we may apply the transform to the tape of the pulse program, obtaining
the tapes with inserted ``PauliRot`` gates together with the post-processing function:

>>> circuit.construct((params,), {}) # Build the tape of the circuit.
>>> circuit.tape.trainable_params = [0, 1, 2]
>>> tapes, fun = qml.gradients.pulse_odegen(circuit.tape, argnum=[0, 1, 2])
>>> tape = qml.workflow.construct_tape(circuit)(params) # Build the tape of the circuit.
>>> tape.trainable_params = [0, 1, 2]
>>> tapes, fun = qml.gradients.pulse_odegen(tape, argnum=[0, 1, 2])
>>> len(tapes)
12

Expand Down
5 changes: 3 additions & 2 deletions pennylane/math/multi_dispatch.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@
from autoray import numpy as np
from numpy import ndarray

import pennylane as qml

from . import single_dispatch # pylint:disable=unused-import
from .utils import cast, cast_like, get_interface, requires_grad

Expand Down Expand Up @@ -1032,8 +1034,7 @@ def jax_argnums_to_tape_trainable(qnode, argnums, program, args, kwargs):
for i, arg in enumerate(args)
]

qnode.construct(args_jvp, kwargs)
tape = qnode.qtape
tape = qml.workflow.construct_tape(qnode, level=0)(*args_jvp, **kwargs)
tapes, _ = program((tape,))
del trace
return tuple(tape.get_parameters(trainable_only=False) for tape in tapes)
Expand Down
3 changes: 2 additions & 1 deletion pennylane/ops/functions/map_wires.py
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,8 @@ def map_wires(
>>> mapped_circuit = qml.map_wires(circuit, wire_map)
>>> mapped_circuit()
tensor([0.92885434, 0.07114566], requires_grad=True)
>>> list(mapped_circuit.tape)
>>> tape = qml.workflow.construct_tape(mapped_circuit)()
>>> list(tape)
[((RX(0.54, wires=[3]) @ X(2)) @ Z(1)) @ RY(1.23, wires=[0]), probs(wires=[3])]
"""
if isinstance(input, (Operator, MeasurementProcess)):
Expand Down
3 changes: 2 additions & 1 deletion pennylane/ops/functions/simplify.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,8 @@ def simplify(input: Union[Operator, MeasurementProcess, QuantumScript, QNode, Ca
... return qml.probs(wires=0)
>>> circuit()
tensor([0.64596329, 0.35403671], requires_grad=True)
>>> list(circuit.tape)
>>> tape = qml.workflow.construct_tape(circuit)()
>>> list(tape)
[RZ(11.566370614359172, wires=[0]) @ RY(11.566370614359172, wires=[0]) @ RX(11.566370614359172, wires=[0]),
probs(wires=[0])]
"""
Expand Down
3 changes: 2 additions & 1 deletion pennylane/optimize/adaptive.py
Original file line number Diff line number Diff line change
Expand Up @@ -199,14 +199,15 @@ def step_and_cost(self, circuit, operator_pool, drain_pool=False, params_zero=Tr
"""
cost = circuit()
qnode = copy.copy(circuit)
tape = qml.workflow.construct_tape(qnode)()

if drain_pool:
operator_pool = [
gate
for gate in operator_pool
if all(
gate.name != operation.name or gate.wires != operation.wires
for operation in circuit.tape.operations
for operation in tape.operations
)
]

Expand Down
23 changes: 12 additions & 11 deletions pennylane/optimize/qnspsa.py
Original file line number Diff line number Diff line change
Expand Up @@ -369,10 +369,10 @@ def _get_spsa_grad_tapes(self, cost, args, kwargs):
args_plus[index] = arg + self.finite_diff_step * direction
args_minus[index] = arg - self.finite_diff_step * direction

cost.construct(args_plus, kwargs)
tape_plus = cost.tape.copy(copy_operations=True)
cost.construct(args_minus, kwargs)
tape_minus = cost.tape.copy(copy_operations=True)
tape = qml.workflow.construct_tape(cost)(*args_plus, **kwargs)
tape_plus = tape.copy(copy_operations=True)
tape = qml.workflow.construct_tape(cost)(*args_minus, **kwargs)
tape_minus = tape.copy(copy_operations=True)
return [tape_plus, tape_minus], dirs

def _update_tensor(self, tensor_raw):
Expand Down Expand Up @@ -425,22 +425,23 @@ def _get_overlap_tape(self, cost, args1, args2, kwargs):
op_inv = self._get_operations(cost, args2, kwargs)

new_ops = op_forward + [qml.adjoint(op) for op in reversed(op_inv)]
return qml.tape.QuantumScript(new_ops, [qml.probs(wires=cost.tape.wires.labels)])
tape = qml.workflow.construct_tape(cost)(*args1, **kwargs)
return qml.tape.QuantumScript(new_ops, [qml.probs(wires=tape.wires.labels)])

@staticmethod
def _get_operations(cost, args, kwargs):
cost.construct(args, kwargs)
return cost.tape.operations
tape = qml.workflow.construct_tape(cost)(*args, **kwargs)
return tape.operations

def _apply_blocking(self, cost, args, kwargs, params_next):
cost.construct(args, kwargs)
tape_loss_curr = cost.tape.copy(copy_operations=True)
tape = qml.workflow.construct_tape(cost)(*args, **kwargs)
tape_loss_curr = tape.copy(copy_operations=True)

if not isinstance(params_next, list):
params_next = [params_next]

cost.construct(params_next, kwargs)
tape_loss_next = cost.tape.copy(copy_operations=True)
tape = qml.workflow.construct_tape(cost)(*params_next, **kwargs)
tape_loss_next = tape.copy(copy_operations=True)

program, _ = cost.device.preprocess()

Expand Down
3 changes: 2 additions & 1 deletion pennylane/optimize/riemannian_gradient.py
Original file line number Diff line number Diff line change
Expand Up @@ -383,8 +383,9 @@ def get_omegas(self):

obs_groupings, _ = qml.pauli.group_observables(self.observables, self.coeffs)
# get all circuits we need to calculate the coefficients
tape = qml.workflow.construct_tape(self.circuit)()
circuits = algebra_commutator(
self.circuit.qtape,
tape,
obs_groupings,
self.lie_algebra_basis_names,
self.nqubits,
Expand Down
3 changes: 1 addition & 2 deletions pennylane/optimize/shot_adaptive.py
Original file line number Diff line number Diff line change
Expand Up @@ -308,8 +308,7 @@ def _single_shot_qnode_gradients(self, qnode, args, kwargs):
"""Compute the single shot gradients of a QNode."""
self.check_device(qnode.device)

qnode.construct(args, kwargs)
tape = qnode.tape
tape = qml.workflow.construct_tape(qnode)(*args, **kwargs)
[expval] = tape.measurements
coeffs, observables = (
expval.obs.terms()
Expand Down
7 changes: 3 additions & 4 deletions pennylane/transforms/core/transform_program.py
Original file line number Diff line number Diff line change
Expand Up @@ -386,8 +386,7 @@ def classical_preprocessing(program, *args, **kwargs):
"""Returns the trainable gate parameters for a given QNode input."""
kwargs.pop("shots", None)
kwargs.pop("argnums", None)
qnode.construct(args, kwargs)
tape = qnode.qtape
tape = qml.workflow.construct_tape(qnode, level=0)(*args, **kwargs)
tapes, _ = program((tape,))
res = tuple(qml.math.stack(tape.get_parameters(trainable_only=True)) for tape in tapes)
if len(tapes) == 1:
Expand Down Expand Up @@ -456,8 +455,8 @@ def _jacobian(*args, **kwargs):
classical_jacobian = jacobian(
classical_preprocessing, sub_program, argnums, *args, **kwargs
)
qnode.construct(args, kwargs)
tapes, _ = sub_program((qnode.tape,))
tape = qml.workflow.construct_tape(qnode, level=0)(*args, **kwargs)
tapes, _ = sub_program((tape,))
multi_tapes = len(tapes) > 1
if not multi_tapes:
classical_jacobian = [classical_jacobian]
Expand Down
24 changes: 18 additions & 6 deletions pennylane/workflow/qnode.py
Original file line number Diff line number Diff line change
Expand Up @@ -835,7 +835,19 @@ def best_method_str(device: SupportedDeviceAPIs, interface: SupportedInterfaceUs

@property
def tape(self) -> QuantumTape:
"""The quantum tape"""
"""The quantum tape

.. warning::

This property is deprecated in v0.40 and will be removed in v0.41.
Instead, use the :func:`qml.workflow.construct_tape <.workflow.construct_tape>` function.
"""

warnings.warn(
"The tape/qtape property is deprecated and will be removed in v0.41. "
"Instead, use the qml.workflow.get_best_diff_method function.",
qml.PennyLaneDeprecationWarning,
)
return self._tape

qtape = tape # for backwards compatibility
Expand All @@ -859,10 +871,10 @@ def construct(self, args, kwargs): # pylint: disable=too-many-branches

self._tape = QuantumScript.from_queue(q, shots)

params = self.tape.get_parameters(trainable_only=False)
self.tape.trainable_params = qml.math.get_trainable_indices(params)
params = self._tape.get_parameters(trainable_only=False)
self._tape.trainable_params = qml.math.get_trainable_indices(params)

_validate_qfunc_output(self._qfunc_output, self.tape.measurements)
_validate_qfunc_output(self._qfunc_output, self._tape.measurements)

def _execution_component(self, args: tuple, kwargs: dict) -> qml.typing.Result:
"""Construct the transform program and execute the tapes. Helper function for ``__call__``
Expand All @@ -883,7 +895,7 @@ def _execution_component(self, args: tuple, kwargs: dict) -> qml.typing.Result:
gradient_fn = qml.gradients.param_shift
else:
gradient_fn = QNode.get_gradient_fn(
self.device, self.interface, self.diff_method, tape=self.tape
self.device, self.interface, self.diff_method, tape=self._tape
)[0]
execute_kwargs = copy.copy(self.execute_kwargs)

Expand Down Expand Up @@ -949,7 +961,7 @@ def _execution_component(self, args: tuple, kwargs: dict) -> qml.typing.Result:
# convert result to the interface in case the qfunc has no parameters

if (
len(self.tape.get_parameters(trainable_only=False)) == 0
len(self._tape.get_parameters(trainable_only=False)) == 0
and not self.transform_program.is_informative
):
res = _convert_to_interface(res, self.interface)
Expand Down
Loading