qml.QNode¶
- class QNode(func, device, interface=Interface.AUTO, diff_method='best', *, shots='unset', grad_on_execution='best', cache='auto', cachesize=10000, max_diff=1, device_vjp=False, postselect_mode=None, mcm_method=None, gradient_kwargs=None, static_argnums=(), executor_backend=None)[source]¶
- Bases: - object- Represents a quantum node in the hybrid computational graph. - A quantum node contains a quantum function (corresponding to a variational circuit) and the computational device it is executed on. - The QNode calls the quantum function to construct a - QuantumTapeinstance representing the quantum circuit.- Parameters:
- func (Callable) – a quantum function 
- device (Device) – a PennyLane-compatible device 
- interface (str) – - The interface that will be used for classical backpropagation. This affects the types of objects that can be passed to/returned from the QNode. See - qml.math.SUPPORTED_INTERFACE_USER_INPUTfor a list of all accepted strings.- "autograd": Allows autograd to backpropagate through the QNode. The QNode accepts default Python types (floats, ints, lists, tuples, dicts) as well as NumPy array arguments, and returns NumPy arrays.
- "torch": Allows PyTorch to backpropagate through the QNode. The QNode accepts and returns Torch tensors.
- "tf": Allows TensorFlow in eager mode to backpropagate through the QNode. The QNode accepts and returns TensorFlow- tf.Variableand- tf.tensorobjects.
- "jax": Allows JAX to backpropagate through the QNode. The QNode accepts and returns JAX- Arrayobjects.
- None: The QNode accepts default Python types (floats, ints, lists, tuples, dicts) as well as NumPy array arguments, and returns NumPy arrays. It does not connect to any machine learning library automatically for backpropagation.
- "auto": The QNode automatically detects the interface from the input values of the quantum function.
 
- diff_method (str or .TransformDispatcher) – - The method of differentiation to use in the created QNode. Can either be a - TransformDispatcher, which includes all quantum gradient transforms in the- qml.gradientsmodule, or a string. The following strings are allowed:- "best": Best available method. Uses classical backpropagation or the device directly to compute the gradient if supported, otherwise will use the analytic parameter-shift rule where possible with finite-difference as a fallback.
- "device": Queries the device directly for the gradient. Only allowed on devices that provide their own gradient computation.
- "backprop": Use classical backpropagation. Only allowed on simulator devices that are classically end-to-end differentiable, for example- default.qubit. Note that the returned QNode can only be used with the machine-learning framework supported by the device.
- "adjoint": Uses an adjoint method that reverses through the circuit after a forward pass by iteratively applying the inverse (adjoint) gate. Only allowed on supported simulator devices such as- default.qubit.
- "parameter-shift": Use the analytic parameter-shift rule for all supported quantum operation arguments, with finite-difference as a fallback.
- "hadamard": Use the standard analytic hadamard gradient test rule for all supported quantum operation arguments. More info is in the documentation for- qml.gradients.hadamard_grad. Reversed, direct, and reversed-direct modes can be selected via a- "mode"in- gradient_kwargs.
- "finite-diff": Uses numerical finite-differences for all quantum operation arguments.
- "spsa": Uses a simultaneous perturbation of all operation arguments to approximate the derivative.
- None: QNode cannot be differentiated. Works the same as- interface=None.
 
- grad_on_execution (bool, str) – Whether the gradients should be computed on the execution or not. Only applies if the device is queried for the gradient; gradient transform functions available in - qml.gradientsare only supported on the backward pass. The ‘best’ option chooses automatically between the two options and is default.
- cache="auto" (str or bool or dict or Cache) – Whether to cache evalulations. - "auto"indicates to cache only when- max_diff > 1. This can result in a reduction in quantum evaluations during higher order gradient computations. If- True, a cache with corresponding- cachesizeis created for each batch execution. If- False, no caching is used. You may also pass your own cache to be used; this can be any object that implements the special methods- __getitem__(),- __setitem__(), and- __delitem__(), such as a dictionary.
- cachesize (int) – The size of any auto-created caches. Only applies when - cache=True.
- max_diff (int) – If - diff_methodis a gradient transform, this option specifies the maximum number of derivatives to support. Increasing this value allows for higher order derivatives to be extracted, at the cost of additional (classical) computational overhead during the backwards pass.
- device_vjp (bool) – Whether or not to use the device-provided Vector Jacobian Product (VJP). A value of - Noneindicates to use it if the device provides it, but use the full jacobian otherwise.
- postselect_mode (str) – Configuration for handling shots with mid-circuit measurement postselection. If - "hw-like", invalid shots will be discarded and only results for valid shots will be returned. If- "fill-shots", results corresponding to the original number of shots will be returned. The default is- None, in which case the device will automatically choose the best configuration. For usage details, please refer to the dynamic quantum circuits page.
- mcm_method (str) – The strategy for applying mid-circuit measurements. Available methods include - "deferred"(to use the deferred measurement principle),- "one-shot"(to execute the circuit for each shot separately when using finite shots), and- "tree-traversal"(visits the tree of possible MCM sequences, only supported on- default.qubitand- lightning.qubit). If not provided, the device will select the method automatically. For usage details, refer to the dynamic quantum circuits page.
- gradient_kwargs (dict) – A dictionary of keyword arguments that are passed to the differentiation method. Please refer to the - qml.gradientsmodule for details on supported options for your chosen gradient transform.
- static_argnums (int | Sequence[int]) – Only applicable when the experimental capture mode is enabled. An - intor collection of- ints that specify which positional arguments to treat as static.
- executor_backend (ExecBackends | str) – The backend executor for concurrent function execution. This argument allows for selective control of how to run data-parallel/task-based parallel functions via a defined execution environment. All supported options can be queried using - MP_PoolExec().
 
 - Example - QNodes can be created by decorating a quantum function: - >>> dev = qml.device("default.qubit", wires=1) >>> @qml.qnode(dev) ... def circuit(x): ... qml.RX(x, wires=0) ... return qml.expval(qml.Z(0)) - or by instantiating the class directly: - >>> def circuit(x): ... qml.RX(x, wires=0) ... return qml.expval(qml.Z(0)) >>> dev = qml.device("default.qubit", wires=1) >>> qnode = qml.QNode(circuit, dev) - Parameter broadcasting- QNodes can be executed simultaneously for multiple parameter settings, which is called parameter broadcasting or parameter batching. We start with a simple example and briefly look at the scenarios in which broadcasting is possible and useful. Finally we give rules and conventions regarding the usage of broadcasting, together with some more complex examples. Also see the - Operatordocumentation for implementation details.- Example - Again consider the following - circuit:- >>> dev = qml.device("default.qubit", wires=1) >>> @qml.qnode(dev) ... def circuit(x): ... qml.RX(x, wires=0) ... return qml.expval(qml.Z(0)) - If we want to execute it at multiple values - x, we may pass those as a one-dimensional array to the QNode:- >>> x = np.array([np.pi / 6, np.pi * 3 / 4, np.pi * 7 / 6]) >>> circuit(x) tensor([ 0.8660254 , -0.70710678, -0.8660254 ], requires_grad=True) - The resulting array contains the QNode evaluations at the single values: - >>> [circuit(x_val) for x_val in x] [tensor(0.8660254, requires_grad=True), tensor(-0.70710678, requires_grad=True), tensor(-0.8660254, requires_grad=True)] - In addition to the results being stacked into one - tensoralready, the broadcasted execution actually is performed in one simulation of the quantum circuit, instead of three sequential simulations.- Benefits & Supported QNodes - Parameter broadcasting can be useful to simplify the execution syntax with QNodes. More importantly though, the simultaneous execution via broadcasting can be significantly faster than iterating over parameters manually. If we compare the execution time for the above QNode - circuitbetween broadcasting and manual iteration for an input size of- 100, we find a speedup factor of about \(30\). This speedup is a feature of classical simulators, but broadcasting may reduce the communication overhead for quantum hardware devices as well.- A QNode supports broadcasting if all operators that receive broadcasted parameters do so. (Operators that are used in the circuit but do not receive broadcasted inputs do not need to support it.) A list of supporting operators is available in - supports_broadcasting. Whether or not broadcasting delivers an increased performance will depend on whether the used device is a classical simulator and natively supports this.- If a device does not natively support broadcasting, it will execute broadcasted QNode calls by expanding the input arguments into separate executions. That is, every device can execute QNodes with broadcasting, but only supporting devices will benefit from it. - Usage - The first example above is rather simple. Broadcasting is possible in more complex scenarios as well, for which it is useful to understand the concept in more detail. The following rules and conventions apply: - There is at most one broadcasting axis - The broadcasted input has (exactly) one more axis than the operator(s) which receive(s) it would usually expect. For example, most operators expect a single scalar input and the broadcasted input correspondingly is a 1D array: - >>> x = np.array([1., 2., 3.]) >>> op = qml.RX(x, wires=0) # Additional axis of size 3. - An operator - opthat supports broadcasting indicates the expected number of axes–or dimensions–in its attribute- op.ndim_params. This attribute is a tuple with one integer per argument of- op. The batch size of a broadcasted operator is stored in- op.batch_size:- >>> op.ndim_params # RX takes one scalar input. (0,) >>> op.batch_size # The broadcasting axis has size 3. 3 - The broadcasting axis is always the leading axis of an argument passed to an operator: - >>> from scipy.stats import unitary_group >>> U = np.stack([unitary_group.rvs(4) for _ in range(3)]) >>> U.shape # U stores three two-qubit unitaries, each of shape 4x4 (3, 4, 4) >>> op = qml.QubitUnitary(U, wires=[0, 1]) >>> op.batch_size 3 - Stacking multiple broadcasting axes is not supported. - Multiple operators are broadcasted simultaneously - It is possible to broadcast multiple parameters simultaneously. In this case, the batch size of the broadcasting axes must match, and the parameters are combined like in Python’s - zipfunction. Non-broadcasted parameters do not need to be augmented manually but can simply be used as one would in individual QNode executions:- dev = qml.device("default.qubit", wires=4) @qml.qnode(dev) def circuit(x, y, U): qml.QubitUnitary(U, wires=[0, 1, 2, 3]) qml.RX(x, wires=0) qml.RY(y, wires=1) qml.RX(x, wires=2) qml.RY(y, wires=3) return qml.expval(qml.Z(0) @ qml.X(1) @ qml.Z(2) @ qml.Z(3)) x = np.array([0.4, 2.1, -1.3]) y = 2.71 U = np.stack([unitary_group.rvs(16) for _ in range(3)]) - This circuit takes three arguments, and the first two are used twice each. - xand- Uwill lead to a batch size of- 3for the- RXrotations and the multi-qubit unitary, respectively. The input- yis a- floatvalue and will be used together with all three values in- xand- U. We obtain three output values:- >>> circuit(x, y, U) tensor([-0.06939911, 0.26051235, -0.20361048], requires_grad=True) - This is equivalent to iterating over all broadcasted arguments using - zip:- >>> [circuit(x_val, y, U_val) for x_val, U_val in zip(x, U)] [tensor(-0.06939911, requires_grad=True), tensor(0.26051235, requires_grad=True), tensor(-0.20361048, requires_grad=True)] - In the same way it is possible to broadcast multiple arguments of a single operator, for example: - >>> qml.Rot.ndim_params # Rot takes three scalar arguments (0, 0, 0) >>> x = np.array([0.4, 2.3, -0.1]) # Broadcast the first argument with size 3 >>> y = 1.6 # Do not broadcast the second argument >>> z = np.array([1.2, -0.5, 2.5]) # Broadcast the third argument with size 3 >>> op = qml.Rot(x, y, z, wires=0) >>> op.batch_size 3 - Broadcasting does not modify classical processing - Note that classical processing in QNodes will happen before broadcasting is taken into account. This means, that while operators always interpret the first axis as the broadcasting axis, QNodes do not necessarily do so: - @qml.qnode(dev) def circuit_unpacking(x): qml.RX(x[0], wires=0) qml.RY(x[1], wires=1) qml.RZ(x[2], wires=1) return qml.expval(qml.Z(0) @ qml.X(1)) x = np.array([[1, 2], [3, 4], [5, 6]]) - The prepared parameter - xhas shape- (3, 2), corresponding to the three operations and a batch size of- 2:- >>> circuit_unpacking(x) tensor([0.02162852, 0.30239696], requires_grad=True) - If we were to iterate manually over the parameter settings, we probably would put the batching axis in - xfirst. This is not the behaviour with parameter broadcasting because it does not modify the unpacking step within the QNode, so that- xis unpacked first and the unpacked elements are expected to contain the broadcasted parameters for each operator individually; if we attempted to put the broadcasting axis of size- 2first, the indexing of- xwould fail in the- RZrotation within the QNode.- Attributes- The interface used by the QNode - Default shots for execution workflows. - The transform program used by the QNode. - interface¶
- The interface used by the QNode 
 - shots¶
- Default shots for execution workflows. - Note that this property is not able to be set directly; only set_shots can modify it. 
 - transform_program¶
- The transform program used by the QNode. 
 - Methods- __call__(*args, **kwargs)- Call self as a function. - add_transform(transform_container)- Add a transform (container) to the transform program. - construct(args, kwargs)- Call the quantum function with a tape context, ensuring the operations get queued. - update(**kwargs)- Returns a new QNode instance but with updated settings (e.g., a different diff_method). - update_shots(shots)- Update the number of shots used by the QNode. - add_transform(transform_container)[source]¶
- Add a transform (container) to the transform program. - Warning - This method is deprecated and will be removed in v0.44. Instead, please use - push_back()on the- QNode.transform_programproperty to add transforms to the transform program.- Warning - This is a developer facing feature and is called when a transform is applied on a QNode. 
 - construct(args, kwargs)[source]¶
- Call the quantum function with a tape context, ensuring the operations get queued. 
 - update(**kwargs)[source]¶
- Returns a new QNode instance but with updated settings (e.g., a different diff_method). Any settings not specified will retain their original value. - Note - The QNode`s transform program cannot be updated using this method. - Keyword Arguments:
- **kwargs – The provided keyword arguments must match that of - QNode.__init__(). The list of supported gradient keyword arguments can be found at- qml.gradients.SUPPORTED_GRADIENT_KWARGS.
- Returns:
- new QNode with updated settings 
- Return type:
- qnode (QNode) 
- Raises:
- ValueError – if provided keyword arguments are invalid 
 - Example - Let’s begin by defining a - QNodeobject,- dev = qml.device("default.qubit") @qml.qnode(dev, diff_method="parameter-shift") def circuit(x): qml.RZ(x, wires=0) qml.CNOT(wires=[0, 1]) qml.RY(x, wires=1) return qml.expval(qml.PauliZ(1)) - If we wish to try out a new configuration without having to repeat the boilerplate above, we can use the - QNode.updatemethod. For example, we can update the differentiation method and execution arguments,- >>> new_circuit = circuit.update(diff_method="adjoint", device_vjp=True) >>> print(new_circuit.diff_method) adjoint >>> print(new_circuit.execute_kwargs["device_vjp"]) True - Similarly, if we wish to re-configure the interface used for execution, - >>> new_circuit= circuit.update(interface="torch") >>> new_circuit(1) tensor(0.5403, dtype=torch.float64)