From 28e04e719635aed80383ed2c1c1cbbeb8ae5e451 Mon Sep 17 00:00:00 2001 From: dwierichs Date: Tue, 18 Jun 2024 11:14:30 +0200 Subject: [PATCH 01/15] new quickstart --- doc/index.rst | 1 + doc/introduction/measurements.rst | 422 -------------- doc/introduction/mid_circuit_measurements.rst | 521 ++++++++++++++++++ 3 files changed, 522 insertions(+), 422 deletions(-) create mode 100644 doc/introduction/mid_circuit_measurements.rst diff --git a/doc/index.rst b/doc/index.rst index d9949173010..655497cdcde 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -159,6 +159,7 @@ PennyLane is **free** and **open source**, released under the Apache License, Ve introduction/interfaces introduction/operations introduction/measurements + introduction/mid_circuit_measurements introduction/templates introduction/inspecting_circuits introduction/compiling_circuits diff --git a/doc/introduction/measurements.rst b/doc/introduction/measurements.rst index eab6c62e60a..2f6e6c693fe 100644 --- a/doc/introduction/measurements.rst +++ b/doc/introduction/measurements.rst @@ -244,428 +244,6 @@ state :math:`|00\rangle`, and a :math:`0.25\%` probability of measuring state :math:`|01\rangle`. -.. _mid_circuit_measurements: - -Mid-circuit measurements and conditional operations ---------------------------------------------------- - -PennyLane allows specifying measurements in the middle of the circuit. -Quantum functions such as operations can then be conditioned on the measurement -outcome of such mid-circuit measurements: - -.. code-block:: python - - def my_quantum_function(x, y): - qml.RY(x, wires=0) - qml.CNOT(wires=[0, 1]) - m_0 = qml.measure(1) - - qml.cond(m_0, qml.RY)(y, wires=0) - return qml.probs(wires=[0]) - -.. _deferred_measurements: - -Deferred measurements -********************* - -A quantum function with mid-circuit measurements (defined using -:func:`~.pennylane.measure`) and conditional operations (defined using -:func:`~.pennylane.cond`) can be executed by applying the `deferred measurement -principle `__. In -the example above, we apply the :class:`~.RY` rotation if the mid-circuit -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:`~.pennylane.defer_measurements` quantum -function transform. The deferred measurement principle provides a natural method -to simulate the application of mid-circuit measurements and conditional operations -in a differentiable and device-independent way. Performing true mid-circuit -measurements and conditional operations is dependent on the quantum hardware and -PennyLane device capabilities. - -.. code-block:: python - - transformed_qfunc = qml.transforms.defer_measurements(my_quantum_function) - transformed_qnode = qml.QNode(transformed_qfunc, dev) - pars = np.array([0.643, 0.246], requires_grad=True) - ->>> transformed_qnode(*pars) -tensor([0.90165331, 0.09834669], requires_grad=True) - -``qml.defer_measurements`` can be applied as decorator equally well: - -.. code-block:: python - - @qml.qnode(dev) - @qml.defer_measurements - def qnode(x, y): - (...) - - @qml.defer_measurements - @qml.qnode(dev) - def qnode(x, y): - (...) - -.. note:: - - The deferred measurements principle requires an additional wire, or qubit, for each mid-circuit - measurement, limiting the number of measurements that can be used both on classical simulators - and quantum hardware. The one-shot transform below does not have this limitation, but has - computational cost that scales with the number of shots used. - -.. _one_shot_transform: - -The one-shot transform -********************** - -Devices supporting mid-circuit measurements (defined using -:func:`~.pennylane.measure`) and conditional operations (defined using -:func:`~.pennylane.cond`) natively can estimate dynamic circuits by executing -them one shot at a time. This is the default behaviour of a :class:`~.pennylane.QNode` that has a -device supporting mid-circuit measurements, as well as any :class:`~.pennylane.QNode` with the -:func:`~.pennylane.dynamic_one_shot` quantum function transform. -As the name suggests, this transform only works for a :class:`~.pennylane.QNode` executing with finite shots -and it will raise an error if the device does not support mid-circuit measurements -natively. -The :func:`~.pennylane.defer_measurements` transform therefore remains the default for -analytic calculations. - -The :func:`~.pennylane.dynamic_one_shot` transform is usually advantageous compared -with the :func:`~.pennylane.defer_measurements` transform in the -large-number-of-mid-circuit-measurements and small-number-of-shots limit. This is because, unlike the -deferred measurement principle, the method does not need an additional wire for every -mid-circuit measurement present in the circuit. Otherwise, one generally gets -equivalent results, so you may try both in an attempt to improve performance without -worrying further about accuracy. - -The transform can be applied to a QNode as follows: - -.. code-block:: python - - @qml.dynamic_one_shot - @qml.qnode(dev) - def my_quantum_function(x, y): - (...) - -.. warning:: - - Dynamic circuits executed with shots should be differentiated with the finite-difference method. - If the ``defer_measurements`` transform is used in analytic mode, ``backprop`` is also a viable - option. - -.. _tree_traversal: - -The tree-traversal algorithm -**************************** - -Dynamic circuit execution is akin to traversing a binary tree where each MCM -corresponds to a node and groups of gates between the MCMs correspond to edges. -The :func:`~.pennylane.dynamic_one_shot` approach picks a branch of the tree randomly -and simulates it from beginning to end. -This is wasteful in many cases; the same branch is simulated many times -when there are more shots than branches for example. -The tree-traversal algorithm does away with such redundancy while retaining the -exponential gains in memory of the one-shot approach compared with the deferred -measurement principle, among other advantages. - -Briefly, it proceeds cutting an :math:`n_{MCM}` circuit into :math:`n_{MCM}+1` -circuit segments. Each segment can be executed on either the 0- or 1-branch, -which gives rise to a binary tree with :math:`2^{n_{MCM}}` leaves. Terminal -measurements are obtained at the leaves, and propagated and combined back up at each -node up the tree. The tree is visited using a depth-first pattern. The tree-traversal -method improves on :func:`~.pennylane.dynamic_one_shot` by taking all samples at a -node or leaf at once. Neglecting overheads, simulating all branches requires the same -amount of computations as :func:`~. pennylane.defer_measurements`, but without the -:math:`O(2^{n_{MCM}})` memory requirement. To save time, a copy of the state vector -is made at every branching point, or MCM, requiring at most :math:`n_{MCM}+1` state -vectors at any instant, an exponential improvement compared with :func:`~. pennylane. -defer_measurements`. Since the counts of many nodes come out to be zero in practice, -it is often possible to ignore entire sub-trees, thereby reducing the computational -burden. - -To summarize, this algorithm gives us the best of both worlds. In the limit of few -shots and/or many mid-circuit measurements, it is as fast as the naive shot-by-shot implementation -because few sub-trees are explored. In the limit of many shots and/or few mid-circuit measurements, it is -equal to or faster than the deferred measurement algorithm (albeit with more -overheads in practice) because each tree edge is visited at most once, all while -reducing the memory requirements exponentially. - -The tree-traversal algorithm is not a transform. Its usage is therefore specified -by passing an ``mcm_method`` option to a QNode (see section -:ref:`"Configuring mid-circuit measurements" `). For example, - -.. code-block:: python - - @qml.qnode(dev, mcm_method="tree-traversal") - def my_quantum_function(x, y): - (...) - -.. warning:: - - The tree-traversal algorithm is only implemented in the :class:`~.pennylane.devices.DefaultQubit` device. - -Resetting wires -*************** - -Wires can be reused as normal after making mid-circuit measurements. Moreover, a measured wire can also be -reset to the :math:`|0 \rangle` state by setting the ``reset`` keyword argument of :func:`~.pennylane.measure` -to ``True``. - -.. code-block:: python3 - - dev = qml.device("default.qubit", wires=3) - - @qml.qnode(dev) - def func(): - qml.PauliX(1) - m_0 = qml.measure(1, reset=True) - qml.PauliX(1) - return qml.probs(wires=[1]) - -Executing this QNode: - ->>> func() -tensor([0., 1.], requires_grad=True) - -Conditional operators -********************* - -Users can create conditional operators controlled on mid-circuit measurements using -:func:`~.pennylane.cond`. The condition for a conditional operator may simply be -the measured value returned by a ``measure()`` call, or we may construct a boolean -condition based on such values and pass it to ``cond()``: - -.. code-block:: python - - @qml.qnode(dev) - @qml.defer_measurements - def qnode_conditional_op_on_zero(x, y): - qml.RY(x, wires=0) - qml.CNOT(wires=[0, 1]) - m_0 = qml.measure(1) - - qml.cond(m_0 == 0, qml.RY)(y, wires=0) - return qml.probs(wires=[0]) - - pars = np.array([0.643, 0.246], requires_grad=True) - ->>> qnode_conditional_op_on_zero(*pars) -tensor([0.88660045, 0.11339955], requires_grad=True) - -For more examples on applying quantum functions conditionally, refer to the -:func:`~.pennylane.cond` documentation. - -Postselecting mid-circuit measurements -************************************** - -PennyLane also supports postselecting on mid-circuit measurement outcomes by specifying the ``postselect`` -keyword argument of :func:`~.pennylane.measure`. Postselection discards outcomes that do not meet the -criteria provided by the ``postselect`` argument. For example, specifying ``postselect=1`` on wire 0 would -be equivalent to projecting the state vector onto the :math:`|1\rangle` state on wire 0, i.e., disregarding -all outcomes where :math:`|0\rangle` is measured on wire 0: - -.. code-block:: python3 - - dev = qml.device("default.qubit") - - @qml.qnode(dev) - def func(x): - qml.RX(x, wires=0) - m0 = qml.measure(0, postselect=1) - qml.cond(m0, qml.PauliX)(wires=1) - return qml.sample(wires=1) - -By postselecting on ``1``, we only consider the ``1`` measurement outcome on wire 0. So, the probability of -measuring ``1`` on wire 1 after postselection should also be 1. Executing this QNode with 10 shots: - ->>> func(np.pi / 2, shots=10) -array([1, 1, 1, 1, 1, 1, 1]) - -Note that only 7 samples are returned. This is because samples that do not meet the postselection criteria are -discarded. To learn more about postselecting mid-circuit measurements, refer to the :func:`~.pennylane.measure` -documentation. - -.. note:: - - Currently, postselection support is only available on :class:`~.pennylane.devices.DefaultQubit`. Using - postselection on other devices will raise an error. - -.. _mid_circuit_measurements_statistics: - -Mid-circuit measurement statistics -********************************** - -Statistics can be collected on mid-circuit measurements along with terminal measurement statistics. -Currently, ``qml.probs``, ``qml.sample``, ``qml.expval``, ``qml.var``, and ``qml.counts`` are supported, -and can be requested along with other measurements. The devices that currently support collecting such -statistics are :class:`~.pennylane.devices.DefaultQubit`, :class:`~.pennylane.devices.DefaultMixed`, and -:class:`~.pennylane.devices.DefaultQubitLegacy`. - -.. code-block:: python3 - - dev = qml.device("default.qubit", wires=2) - - @qml.qnode(dev) - def func(x, y): - qml.RX(x, wires=0) - m0 = qml.measure(0) - qml.cond(m0, qml.RY)(y, wires=1) - return qml.probs(wires=1), qml.probs(op=m0) - -Executing this ``QNode``: - ->>> func(np.pi / 2, np.pi / 4) -(tensor([0.9267767, 0.0732233], requires_grad=True), - tensor([0.5, 0.5], requires_grad=True)) - -Users can also collect statistics on mid-circuit measurements manipulated using arithmetic/boolean operators. -This works for both unary and binary operators. To see a full list of supported operators, refer to the -:func:`~.pennylane.measure` documentation. An example for collecting such statistics is shown below: - -.. code-block:: python3 - - import pennylane as qml - - dev = qml.device("default.qubit") - - @qml.qnode(dev) - def circuit(phi, theta): - qml.RX(phi, wires=0) - m0 = qml.measure(wires=0) - qml.RY(theta, wires=1) - m1 = qml.measure(wires=1) - return qml.sample(~m0 - 2 * m1) - -Executing this ``QNode``: - ->>> circuit(1.23, 4.56, shots=5) -array([-1, -2, 1, -1, 1]) - -Collecting statistics for mid-circuit measurements manipulated using arithmetic/boolean operators is supported -with ``qml.expval``, ``qml.var``, ``qml.sample``, and ``qml.counts``. - -Moreover, statistics for multiple mid-circuit measurements can be collected by passing lists of mid-circuit -measurement values to the measurement process: - -.. code-block:: python3 - - import pennylane as qml - - dev = qml.device("default.qubit") - - @qml.qnode(dev) - def circuit(phi, theta): - qml.RX(phi, wires=0) - m0 = qml.measure(wires=0) - qml.RY(theta, wires=1) - m1 = qml.measure(wires=1) - return qml.sample([m0, m1]) - -Executing this ``QNode``: - ->>> circuit(1.23, 4.56, shots=5) -array([[0, 1], - [1, 1], - [0, 1], - [0, 0], - [1, 1]]) - -Collecting statistics for sequences of mid-circuit measurements is supported with ``qml.sample``, -``qml.probs``, and ``qml.counts``. - -.. warning:: - - When collecting statistics for a list of mid-circuit measurements, values manipulated using - arithmetic operators should not be used as this behaviour is not supported. - -.. _mcm_config: - -Configuring mid-circuit measurements -************************************ - -As seen above, there are multiple ways in which circuits with mid-circuit measurements can be executed with -PennyLane. For ease of use, we provide the following configuration options to users when initializing a -:class:`~pennylane.QNode`: - -* ``mcm_method``: To set the method used for applying mid-circuit measurements. Use ``mcm_method="deferred"`` - to apply the :ref:`deferred measurements principle `, ``mcm_method="one-shot"`` to apply - the :ref:`one-shot transform ` or ``mcm_method="tree-traversal"`` to execute the - :ref:`tree-traversal algorithm `. - When executing with finite shots, ``mcm_method="one-shot"`` - will be the default, and ``mcm_method="deferred"`` otherwise. Additionally, if using :func:`~pennylane.qjit`, - ``mcm_method="single-branch-statistics"`` can also be used and will be the default. Using this method, a single - branch of the execution tree will be randomly explored. - - .. warning:: - - If the ``mcm_method`` argument is provided, the :func:`~pennylane.defer_measurements` or - :func:`~pennylane.dynamic_one_shot` transforms must not be applied directly to the :class:`~pennylane.QNode` - as it can lead to incorrect behaviour. - -* ``postselect_mode``: To configure how invalid shots are handled when postselecting mid-circuit measurements - with finite-shot circuits. Use ``postselect_mode="hw-like"`` to discard invalid samples. In this case, the number - of samples that are used for processing results can be less than the total number of shots. If - ``postselect_mode="fill-shots"`` is used, then the postselected value will be sampled unconditionally, and all - samples will be valid. This is equivalent to sampling until the number of valid samples matches the total number - of shots. The default behaviour is ``postselect_mode="hw-like"``. - - .. code-block:: python3 - - import pennylane as qml - import numpy as np - - dev = qml.device("default.qubit", wires=3, shots=10) - - def circuit(x): - qml.RX(x, 0) - m0 = qml.measure(0, postselect=1) - qml.CNOT([0, 1]) - return qml.sample(qml.PauliZ(0)) - - fill_shots_qnode = qml.QNode(circuit, dev, mcm_method="one-shot", postselect_mode="fill-shots") - hw_like_qnode = qml.QNode(circuit, dev, mcm_method="one-shot", postselect_mode="hw-like") - - >>> fill_shots_qnode(np.pi / 2) - array([-1., -1., -1., -1., -1., -1., -1., -1., -1., -1.]) - >>> hw_like_qnode(np.pi / 2) - array([-1., -1., -1., -1., -1., -1., -1.]) - - .. note:: - - When using the ``jax`` interface, ``postselect_mode="hw-like"`` will have different behaviour based on the - chosen ``mcm_method``. - - * If ``mcm_method="one-shot"``, invalid shots will not be discarded. Instead, invalid samples will be replaced - by ``np.iinfo(np.int32).min``. These invalid samples will not be used for processing final results (like - expectation values), but will appear in the ``QNode`` output if samples are requested directly. Consider - the circuit below: - - .. code-block:: python3 - - import pennylane as qml - import jax - import jax.numpy as jnp - - dev = qml.device("default.qubit", wires=3, shots=10, seed=jax.random.PRNGKey(123)) - - @qml.qnode(dev, postselect_mode="hw-like", mcm_method="one-shot") - def circuit(x): - qml.RX(x, 0) - qml.measure(0, postselect=1) - return qml.sample(qml.PauliZ(0)) - - >>> x = jnp.array(1.8) - >>> f(x) - Array([-2.1474836e+09, -1.0000000e+00, -2.1474836e+09, -2.1474836e+09, - -1.0000000e+00, -2.1474836e+09, -1.0000000e+00, -2.1474836e+09, - -1.0000000e+00, -1.0000000e+00], dtype=float32, weak_type=True) - - * When using ``jax.jit``, using ``mcm_method="deferred"`` is not supported with ``postselect_mode="hw-like"`` and - an error will be raised if this configuration is requested. This is due to limitations of the - :func:`~pennylane.defer_measurements` transform, and this behaviour will change in the future to be more - consistent with ``mcm_method="one-shot"``. - Changing the number of shots ---------------------------- diff --git a/doc/introduction/mid_circuit_measurements.rst b/doc/introduction/mid_circuit_measurements.rst new file mode 100644 index 00000000000..797779ec718 --- /dev/null +++ b/doc/introduction/mid_circuit_measurements.rst @@ -0,0 +1,521 @@ +.. role:: html(raw) + :format: html + +.. _mid_circuit_measurements: + +Mid-circuit measurements +======================== + +.. currentmodule:: pennylane.measure + +PennyLane allows using measurements in the middle of a quantum circuit. +Such measurements are called mid-circuit measurements and can be used to +shape the structure of the circuit dynamically, and to gather information +about the quantum state during the circuit execution. +The function to perform a mid-circuit measurement in PennyLane is +:func:`~.pennylane.measure`, and can be used as follows: + +.. code-block:: python + + dev = qml.device("default.qubit") + + @qml.qnode(dev) + def my_qnode(x, y): + qml.RY(x, wires=0) + qml.CNOT(wires=[0, 1]) + m_0 = qml.measure(1, reset=False, postselect=None) + + qml.cond(m_0, qml.RY)(y, wires=0) + return qml.probs(wires=[0]), qml.expval(m_0) + +See the following sections for details on +:func:`~.pennylane.measure`, :func:`~.pennylane.cond`, and statistics +of mid-circuit measurements, as well as information about simulation +strategies and how to configure them. +Additional information can be found in the documentation of the individual +methods. Also consider our +`Introduction to mid-circuit measurements +`__, +`How to collect statistics of mid-circuit measurements +`__, +and `How to create dynamci circuits with mid-circuit measurements +`__. + +Resetting wires +*************** + +Wires can be reused as normal after making mid-circuit measurements. Moreover, a measured wire +can also be reset to the :math:`|0 \rangle` state by setting the ``reset`` keyword argument +of :func:`~.pennylane.measure` to ``True``: + +.. code-block:: python3 + + dev = qml.device("default.qubit", wires=3) + + @qml.qnode(dev) + def func(): + qml.PauliX(1) + m_0 = qml.measure(1, reset=True) + qml.PauliX(1) + return qml.probs(wires=[1]) + +Executing this QNode: + +.. code-block:: pycon + + >>> func() + tensor([0., 1.], requires_grad=True) + +Postselecting mid-circuit measurements +************************************** + +PennyLane also supports postselecting on mid-circuit measurement outcomes by specifying the +``postselect`` keyword argument of :func:`~.pennylane.measure`. By default, postselection +discards outcomes that do not match the ``postselect`` argument. +For example, specifying ``postselect=1`` is equivalent to projecting the state vector onto +the :math:`|1\rangle` state, i.e., disregarding all outcomes where :math:`|0\rangle` is measured: + +.. code-block:: python3 + + dev = qml.device("default.qubit") + + @qml.qnode(dev) + def func(x): + qml.RX(x, wires=0) + m_0 = qml.measure(0, postselect=1) + return qml.sample(wires=0) + +By postselecting on ``1``, we only consider results that measured the outcome ``1``. +Executing this QNode with 10 shots yields + +.. code-block:: pycon + + >>> func(np.pi / 2, shots=10) + array([1, 1, 1, 1, 1, 1, 1]) + +Note that only 7 samples are returned. This is because samples that do not meet the postselection criteria are +discarded. This behaviour can be customized, see the section +:ref:`"Configuring mid-circuit measurements" `. + +.. note:: + + Currently, postselection support is only available on :class:`~.pennylane.devices.DefaultQubit`. Using + postselection on other devices will raise an error. + +Conditional operators / Dynamic circuits +---------------------------------------- + +Users can create conditional operators controlled on mid-circuit measurements using +:func:`~.pennylane.cond`. The condition for a conditional operator may simply be +the measured value returned by a ``measure()`` call, or we may construct a boolean +condition based on such values and pass it to ``cond()``: + +.. code-block:: python + + @qml.qnode(dev) + @qml.defer_measurements + def qnode_conditional_op_on_zero(x, y): + qml.RY(x, wires=0) + qml.CNOT(wires=[0, 1]) + m_0 = qml.measure(1) + + qml.cond(m_0 == 0, qml.RY)(y, wires=0) + return qml.probs(wires=[0]) + + pars = np.array([0.643, 0.246], requires_grad=True) + +.. code-block:: pycon + + >>> qnode_conditional_op_on_zero(*pars) + tensor([0.88660045, 0.11339955], requires_grad=True) + +For more examples, refer to the :func:`~.pennylane.cond` documentation +and `How to create dynamic circuits with mid-circuit measurements +`__. + +.. _mid_circuit_measurements_statistics: + +Mid-circuit measurement statistics +---------------------------------- + +Statistics of mid-circuit measurements can be collected along with terminal measurement statistics. +Currently, ``qml.probs``, ``qml.sample``, ``qml.expval``, ``qml.var``, and ``qml.counts`` +are supported, and devices that currently support collecting such +statistics are :class:`~.pennylane.devices.DefaultQubit`, +:class:`~.pennylane.devices.DefaultMixed`, and :class:`~.pennylane.devices.DefaultQubitLegacy`. + +.. code-block:: python3 + + dev = qml.device("default.qubit", wires=2) + + @qml.qnode(dev) + def func(x, y): + qml.RX(x, wires=0) + m_0 = qml.measure(0) + qml.cond(m_0, qml.RY)(y, wires=1) + return qml.probs(wires=1), qml.probs(op=m_0) + +Executing this ``QNode``: + +.. code-block:: pycon + + >>> func(np.pi / 2, np.pi / 4) + (tensor([0.9267767, 0.0732233], requires_grad=True), + tensor([0.5, 0.5], requires_grad=True)) + +Users can also collect statistics on mid-circuit measurements manipulated using arithmetic/boolean operators. +This works for both unary and binary operators. To see a full list of supported operators, refer to the +:func:`~.pennylane.measure` documentation. An example for collecting such statistics is shown below: + +.. code-block:: python3 + + import pennylane as qml + + dev = qml.device("default.qubit") + + @qml.qnode(dev) + def circuit(phi, theta): + qml.RX(phi, wires=0) + m_0 = qml.measure(wires=0) + qml.RY(theta, wires=1) + m_1 = qml.measure(wires=1) + return qml.sample(~m_0 - 2 * m_1) + +Executing this ``QNode``: + +.. code-block:: pycon + + >>> circuit(1.23, 4.56, shots=5) + array([-1, -2, 1, -1, 1]) + +Collecting statistics for mid-circuit measurements manipulated using arithmetic/boolean operators is supported +with ``qml.expval``, ``qml.var``, ``qml.sample``, and ``qml.counts``. + +Moreover, statistics for multiple mid-circuit measurements can be collected by passing lists of mid-circuit +measurement values to the measurement process: + +.. code-block:: python3 + + import pennylane as qml + + dev = qml.device("default.qubit") + + @qml.qnode(dev) + def circuit(phi, theta): + qml.RX(phi, wires=0) + m_0 = qml.measure(wires=0) + qml.RY(theta, wires=1) + m_1 = qml.measure(wires=1) + return qml.sample([m_0, m_1]) + +Executing this ``QNode``: + +>>> circuit(1.23, 4.56, shots=5) +array([[0, 1], + [1, 1], + [0, 1], + [0, 0], + [1, 1]]) + +Collecting statistics for sequences of mid-circuit measurements is supported with ``qml.sample``, +``qml.probs``, and ``qml.counts``. + +.. warning:: + + When collecting statistics for a list of mid-circuit measurements, arithmetic + expressions are not supported. + + +Simulation of mid-circuit measurements +-------------------------------------- + +PennyLane currently offers three methods to simulate mid-circuit measurements +on classical computers: the deferred measurements principle, dynamic one-shot +sampling, and a tree-traversal approach. These methods differ in their memory requirements +and computational cost, as well as their compatibility with other features such as +shots and differentiation methods. +While the requirements depend on details of the simulation, the expected +scalings are + +.. role:: gr +.. role:: or +.. role:: rd + +.. raw:: html + + + + + +.. rst-class:: tb + ++--------------------------+--------------------------------------------------+-------------------------------------------------------+-------------------------------------+-----------------------------+ +| **Simulation technique** | **Memory** | **Time** | **Differentiation support** | **Supports shots/analytic** | +| Deferred measurements | :rd: :math:`\mathcal{O}\left(2^{n_{MCM}}\right)` | :rd: :math:`\mathcal{O}\left(2^{n_{MCM}}\right)` | :gr: yes | :gr: yes / yes | +| Dynamic one-shot | :gr: :math:`\mathcal{O}\left(1\right)` | :rd: :math:`\mathcal{O}\left(n_{shots}\right)` | :or: finite differences\ :math:`^1` | :or: yes / no | +| Tree-traversal | :or: :math:`\mathcal{O}\left(n_{MCM}+1\right)` | :or: :math:`\mathcal{O}\left(\leq 2^{n_{MCM}}\right)` | :rd: TBD | :rd: TBD | ++--------------------------+--------------------------------------------------+-------------------------------------------------------+-------------------------------------+-----------------------------+ + +The strengths and weaknesses of the simulation techniques differ strongly and the best +technique will depend on details of the simulation workflow. As a rule of thumb: + +- dynamic one-shot sampling excels in the many-measurements-few-shots regime, + +- the tree-traversal technique can handle large-scale simulations with many shots + and measurements, and + +- deferred measurements are the generalist solution that enables mid-circuit measurement + support under (almost) all circumstances, but at large memory cost. + +By default, ``QNode``\ s use deferred measurements and dynamic one-shot sampling (if supported) +when executed without and with shots, respectively. + +Deferred measurements +********************* + +A quantum function with mid-circuit measurements (defined using +:func:`~.pennylane.measure`) and conditional operations (defined using +:func:`~.pennylane.cond`) can be executed by applying the `deferred measurement +principle `__. +Accordingly, statistics of mid-circuit measurements become conventional terminal measurement +statistics. +In PennyLane this technique is available as the transform :func:`~.pennylane.defer_measurements`, +which can be applied to :class:`~.pennylane.QNode`\ s, quantum functions and tapes +as usual. It is the default technique for ``QNode``\ s that do not execute with shots. + +The deferred measurement principle provides a powerful method to simulate +mid-circuit measurements, conditional operations and measurement statistics +in a differentiable and device-independent way. It adds an auxiliary qubit +to the circuit for each mid-circuit measurement, leading to overheads of both +memory and simulation time that scale exponentially with the number of measurements. + +.. code-block:: pycon + + deferred_qnode = qml.defer_measurements(my_qnode) + pars = np.array([0.643, 0.246]) + >>> deferred_qnode(*pars) + (tensor([0.90165331, 0.09834669], requires_grad=True), + tensor(0.09984972, requires_grad=True)) + +The effect of deferring measurements becomes clear if we draw the ``QNode`` +before and after applying the transform: + +.. code-block:: pycon + + >>> qml.draw(my_qnode)(*pars) + 0: ──RY(0.64)─╭●───────RY(0.25)─┤ Probs + 1: ───────────╰X──┤↗├──║────────┤ + ╚═══╩════════╡ + >>> qml.draw(deferred_qnode)(*pars) + 0: ──RY(0.64)─╭●────╭RY(0.25)─┤ Probs + 1: ───────────╰X─╭●─│─────────┤ + 2: ──────────────╰X─╰●────────┤ + +Mid-circuit measurements are deferred to the end of the circuit, and conditionally applied +operations become (quantumly) controlled operations. +``qml.defer_measurements`` can be applied as decorator equally well: + +.. code-block:: python + + @qml.qnode(dev) + @qml.defer_measurements + def qnode(x, y): + (...) + + @qml.defer_measurements + @qml.qnode(dev) + def qnode(x, y): + (...) + +.. note:: + + The deferred measurements principle requires an additional wire, or qubit, for each mid-circuit + measurement, limiting the number of measurements that can be used both on classical simulators + and quantum hardware. + +.. _one_shot_transform: + +Dynamic one-shot sampling +************************* + +Devices that natively support mid-circuit measurements (defined using +:func:`~.pennylane.measure`) and conditional operations (defined using +:func:`~.pennylane.cond`) can estimate dynamic circuits by executing +them one shot at a time, sampling a dynamic circuit execution path for each +shot. +This technique is the default for a shot-based +:class:`~.pennylane.QNode` that uses a device supporting mid-circuit measurements, +as well as any :class:`~.pennylane.QNode` with the +:func:`~.pennylane.dynamic_one_shot` quantum function transform. +As the name suggests, this transform only works for a :class:`~.pennylane.QNode` +executing with finite shots and it will raise an error if the device does not support +mid-circuit measurements natively. + +The :func:`~.pennylane.dynamic_one_shot` transform is usually advantageous compared +with the :func:`~.pennylane.defer_measurements` transform in the +many-mid-circuit-measurements and few-shots limit. This is because, unlike the +deferred measurement principle, the method does not need an additional wire for every +mid-circuit measurement in the circuit. +The transform can be applied to a QNode as follows: + +.. code-block:: python + + @qml.dynamic_one_shot + @qml.qnode(dev) + def my_quantum_function(x, y): + (...) + +.. warning:: + + Dynamic circuits executed with shots should be differentiated with the finite-difference method. + +.. _tree_traversal: + +Tree-traversal algorithm +************************ + +Dynamic circuit execution is akin to traversing a binary tree where each mid-circuit measurement +corresponds to a node and groups of gates between them correspond to edges. +The :func:`~.pennylane.dynamic_one_shot` approach above picks a branch of the tree at random +and simulates it from beginning to end. +This is wasteful in many cases; the same branch is simulated many times +when there are more shots than branches for example, and shared information between branches +is not reused. +The tree-traversal algorithm does away with such redundancy while retaining the +exponential gains in memory of the one-shot approach compared with the deferred +measurement principle, among other advantages. + +The algorithm cuts an :math:`n_{MCM}` circuit into :math:`n_{MCM}+1` +circuit segments. Each segment can be executed on either the 0- or 1-branch, +which gives rise to a binary tree with :math:`2^{n_{MCM}}` leaves. Terminal +measurements are obtained at the leaves, and propagated and combined back up at each +node up the tree. The tree is explored using a depth-first pattern. The tree-traversal +method improves on :func:`~.pennylane.dynamic_one_shot` by collecting all samples at a +node or leaf at once. Neglecting overheads, simulating all branches requires the same +amount of computations as :func:`~. pennylane.defer_measurements`, but without the +:math:`O(2^{n_{MCM}})` memory requirement. To save time, a copy of the state vector +is made at every node, or mid-circuit measurement, requiring :math:`n_{MCM}+1` state +vectors, an exponential improvement compared to :func:`~.pennylane.defer_measurements`. +Since the counts of many nodes come out to be zero for shot-based simulations, +it is often possible to ignore entire sub-trees, thereby reducing the computational +cost. + +To summarize, this algorithm gives us the best of both worlds. In the limit of few +shots and/or many mid-circuit measurements, it is as fast as the naive shot-by-shot +implementation of ``dynamic_one_shot`` because few sub-trees are explored. +In the limit of many shots and/or few mid-circuit measurements, it is +equal to or faster than the deferred measurement algorithm (albeit with more +overheads in practice) because each tree edge is visited at most once, all while +exponentially reducing the memory requirements. + +The tree-traversal algorithm is not a transform. Its usage is therefore specified +by passing an ``mcm_method`` option to a QNode (see section +:ref:`"Configuring mid-circuit measurements" `). For example, + +.. code-block:: python + + @qml.qnode(dev, mcm_method="tree-traversal") + def my_qnode(x, y): + (...) + +.. warning:: + + The tree-traversal algorithm is only implemented for the + :class:`~.pennylane.devices.DefaultQubit` device. + +.. _mcm_config: + +Configuring mid-circuit measurements +************************************ + +As seen above, there are multiple ways in which circuits with mid-circuit measurements can be executed with +PennyLane. For ease of use, we provide the following configuration options to users when initializing a +:class:`~pennylane.QNode`: + +* ``mcm_method``: To set the method used for applying mid-circuit measurements. Use ``mcm_method="deferred"`` + to apply the :ref:`deferred measurements principle `, ``mcm_method="one-shot"`` to apply + the :ref:`one-shot transform ` or ``mcm_method="tree-traversal"`` to execute the + :ref:`tree-traversal algorithm `. + When executing with finite shots, ``mcm_method="one-shot"`` + will be the default, and ``mcm_method="deferred"`` otherwise. Additionally, if using :func:`~pennylane.qjit`, + ``mcm_method="single-branch-statistics"`` can also be used and will be the default. Using this method, a single + branch of the execution tree will be chosen randomly. + + .. warning:: + + If the ``mcm_method`` argument is provided, the :func:`~pennylane.defer_measurements` or + :func:`~pennylane.dynamic_one_shot` transforms must not be applied directly to the :class:`~pennylane.QNode` + as this can lead to incorrect behaviour. + +* ``postselect_mode``: To configure how invalid shots are handled when postselecting mid-circuit measurements + with finite-shot circuits. Use ``postselect_mode="hw-like"`` to discard invalid samples. In this case, the number + of samples that are used to process results can be smaller than the total number of shots. If + ``postselect_mode="fill-shots"`` is used, the postselected value will be sampled unconditionally, and all + samples will be valid. This is equivalent to sampling until the number of valid samples matches the total number + of shots. The default behaviour is ``postselect_mode="hw-like"``. + + .. code-block:: python3 + + import pennylane as qml + import numpy as np + + dev = qml.device("default.qubit", wires=3, shots=10) + + def circuit(x): + qml.RX(x, 0) + m_0 = qml.measure(0, postselect=1) + qml.CNOT([0, 1]) + return qml.sample(qml.PauliZ(0)) + + fill_shots_qnode = qml.QNode(circuit, dev, mcm_method="one-shot", postselect_mode="fill-shots") + hw_like_qnode = qml.QNode(circuit, dev, mcm_method="one-shot", postselect_mode="hw-like") + + >>> fill_shots_qnode(np.pi / 2) + array([-1., -1., -1., -1., -1., -1., -1., -1., -1., -1.]) + >>> hw_like_qnode(np.pi / 2) + array([-1., -1., -1., -1., -1., -1., -1.]) + + .. note:: + + When using the ``jax`` interface, ``postselect_mode="hw-like"`` will have different behaviour based on the + chosen ``mcm_method``. + + * If ``mcm_method="one-shot"``, invalid shots will not be discarded. Instead, invalid samples will be replaced + by ``np.iinfo(np.int32).min``. These invalid samples will not be used for processing final results (like + expectation values), but will appear in the ``QNode`` output if samples are requested directly. For example: + + .. code-block:: python3 + + import jax + import jax.numpy as jnp + + dev = qml.device("default.qubit", wires=3, shots=10, seed=jax.random.PRNGKey(123)) + + @qml.qnode(dev, postselect_mode="hw-like", mcm_method="one-shot") + def circuit(x): + qml.RX(x, 0) + qml.measure(0, postselect=1) + return qml.sample(qml.PauliZ(0)) + + >>> x = jnp.array(1.8) + >>> f(x) + Array([-2.1474836e+09, -1.0000000e+00, -2.1474836e+09, -2.1474836e+09, + -1.0000000e+00, -2.1474836e+09, -1.0000000e+00, -2.1474836e+09, + -1.0000000e+00, -1.0000000e+00], dtype=float32, weak_type=True) + + * When using ``jax.jit``, using ``mcm_method="deferred"`` is not supported with ``postselect_mode="hw-like"`` and + an error will be raised if this configuration is requested. This is due to current limitations of the + :func:`~pennylane.defer_measurements` transform, and this behaviour will change in the future to be more + consistent with ``mcm_method="one-shot"``. From cd52b7311861c16ea6a387eb58b4fcdb08b00ae8 Mon Sep 17 00:00:00 2001 From: dwierichs Date: Tue, 18 Jun 2024 11:23:37 +0200 Subject: [PATCH 02/15] changelog --- doc/releases/changelog-dev.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/doc/releases/changelog-dev.md b/doc/releases/changelog-dev.md index 320196f0a74..1cff3c03e89 100644 --- a/doc/releases/changelog-dev.md +++ b/doc/releases/changelog-dev.md @@ -79,6 +79,10 @@

Improvements 🛠

+* Move information about mid-circuit measurements from the measurements quickstart page to its own + [mid-circuit measurements quickstart page](https://docs.pennylane.ai/en/stable/introduction/mid-circuit_measurements.html) + [(#5870)](https://github.com/PennyLaneAI/pennylane/pull/5870) + * Add operation and measurement specific routines in `default.tensor` to improve scalability. [(#5795)](https://github.com/PennyLaneAI/pennylane/pull/5795) From 52028ac4e4d25a5be78ae528196763d35d9883ec Mon Sep 17 00:00:00 2001 From: dwierichs Date: Tue, 18 Jun 2024 11:38:38 +0200 Subject: [PATCH 03/15] polish, extend, fix --- doc/introduction/mid_circuit_measurements.rst | 21 ++++++++++++------- doc/releases/changelog-dev.md | 2 +- pennylane/gradients/parameter_shift.py | 2 ++ 3 files changed, 17 insertions(+), 8 deletions(-) diff --git a/doc/introduction/mid_circuit_measurements.rst b/doc/introduction/mid_circuit_measurements.rst index 797779ec718..f64b33109a8 100644 --- a/doc/introduction/mid_circuit_measurements.rst +++ b/doc/introduction/mid_circuit_measurements.rst @@ -261,12 +261,17 @@ scalings are .. rst-class:: tb -+--------------------------+--------------------------------------------------+-------------------------------------------------------+-------------------------------------+-----------------------------+ -| **Simulation technique** | **Memory** | **Time** | **Differentiation support** | **Supports shots/analytic** | -| Deferred measurements | :rd: :math:`\mathcal{O}\left(2^{n_{MCM}}\right)` | :rd: :math:`\mathcal{O}\left(2^{n_{MCM}}\right)` | :gr: yes | :gr: yes / yes | -| Dynamic one-shot | :gr: :math:`\mathcal{O}\left(1\right)` | :rd: :math:`\mathcal{O}\left(n_{shots}\right)` | :or: finite differences\ :math:`^1` | :or: yes / no | -| Tree-traversal | :or: :math:`\mathcal{O}\left(n_{MCM}+1\right)` | :or: :math:`\mathcal{O}\left(\leq 2^{n_{MCM}}\right)` | :rd: TBD | :rd: TBD | -+--------------------------+--------------------------------------------------+-------------------------------------------------------+-------------------------------------+-----------------------------+ ++--------------------------+--------------------------------------------------+-------------------------------------------------------+---------------------------------------+-----------------------------+ +| **Simulation technique** | **Memory** | **Time** | **Differentiation support** | **Supports shots/analytic** | +| Deferred measurements | :rd: :math:`\mathcal{O}\left(2^{n_{MCM}}\right)` | :rd: :math:`\mathcal{O}\left(2^{n_{MCM}}\right)` | :gr: yes | :gr: yes / yes | +| Dynamic one-shot | :gr: :math:`\mathcal{O}\left(1\right)` | :rd: :math:`\mathcal{O}\left(n_{shots}\right)` | :or: finite differences\ :math:`{}^1` | :or: yes / no | +| Tree-traversal | :or: :math:`\mathcal{O}\left(n_{MCM}+1\right)` | :or: :math:`\mathcal{O}\left(\leq 2^{n_{MCM}}\right)` | :or: finite differences\ :math:`{}^1` | :or: yes / no | ++--------------------------+--------------------------------------------------+-------------------------------------------------------+---------------------------------------+-----------------------------+ + +:math:`{}^1` In principle, parameter-shift differentiation is supported as long as no +postselection is used. Parameters within ``qml.cond``\ itionally applied operations will +fall back to finite differences, so that a proper value for ``h`` should be provided, see +:func:`~.pennylane.finite_diff`. The strengths and weaknesses of the simulation techniques differ strongly and the best technique will depend on details of the simulation workflow. As a rule of thumb: @@ -282,6 +287,8 @@ technique will depend on details of the simulation workflow. As a rule of thumb: By default, ``QNode``\ s use deferred measurements and dynamic one-shot sampling (if supported) when executed without and with shots, respectively. +.. _deferred_measurements: + Deferred measurements ********************* @@ -379,7 +386,7 @@ The transform can be applied to a QNode as follows: .. warning:: - Dynamic circuits executed with shots should be differentiated with the finite-difference method. + Dynamic circuits executed with shots should be differentiated with the finite difference method. .. _tree_traversal: diff --git a/doc/releases/changelog-dev.md b/doc/releases/changelog-dev.md index 1cff3c03e89..6afb695f34d 100644 --- a/doc/releases/changelog-dev.md +++ b/doc/releases/changelog-dev.md @@ -80,7 +80,7 @@

Improvements 🛠

* Move information about mid-circuit measurements from the measurements quickstart page to its own - [mid-circuit measurements quickstart page](https://docs.pennylane.ai/en/stable/introduction/mid-circuit_measurements.html) + [mid-circuit measurements quickstart page](https://docs.pennylane.ai/en/stable/introduction/mid_circuit_measurements.html) [(#5870)](https://github.com/PennyLaneAI/pennylane/pull/5870) * Add operation and measurement specific routines in `default.tensor` to improve scalability. diff --git a/pennylane/gradients/parameter_shift.py b/pennylane/gradients/parameter_shift.py index acdc7946ea0..437e888a81f 100644 --- a/pennylane/gradients/parameter_shift.py +++ b/pennylane/gradients/parameter_shift.py @@ -1088,6 +1088,8 @@ def param_shift( Note that ``broadcast=True`` requires additional memory by a factor of the largest batch_size of the created tapes. """ + print(tape) + print(tape.operations) transform_name = "parameter-shift rule" assert_no_state_returns(tape.measurements, transform_name) From ec5836dfb3a5948dfc62cee65bacd079b9a331fe Mon Sep 17 00:00:00 2001 From: dwierichs Date: Tue, 18 Jun 2024 13:38:45 +0200 Subject: [PATCH 04/15] polish --- doc/introduction/mid_circuit_measurements.rst | 55 ++++++++++--------- 1 file changed, 29 insertions(+), 26 deletions(-) diff --git a/doc/introduction/mid_circuit_measurements.rst b/doc/introduction/mid_circuit_measurements.rst index f64b33109a8..7aa66f387e6 100644 --- a/doc/introduction/mid_circuit_measurements.rst +++ b/doc/introduction/mid_circuit_measurements.rst @@ -44,9 +44,8 @@ and `How to create dynamci circuits with mid-circuit measurements Resetting wires *************** -Wires can be reused as normal after making mid-circuit measurements. Moreover, a measured wire -can also be reset to the :math:`|0 \rangle` state by setting the ``reset`` keyword argument -of :func:`~.pennylane.measure` to ``True``: +Wires can be reused after making mid-circuit measurements. Moreover, a measured wire can be +reset to the :math:`|0 \rangle` state by setting ``reset=True`` in :func:`~.pennylane.measure`: .. code-block:: python3 @@ -99,8 +98,7 @@ discarded. This behaviour can be customized, see the section .. note:: - Currently, postselection support is only available on :class:`~.pennylane.devices.DefaultQubit`. Using - postselection on other devices will raise an error. + Currently, postselection is only supported on :class:`~.pennylane.devices.DefaultQubit`. Conditional operators / Dynamic circuits ---------------------------------------- @@ -129,8 +127,8 @@ condition based on such values and pass it to ``cond()``: >>> qnode_conditional_op_on_zero(*pars) tensor([0.88660045, 0.11339955], requires_grad=True) -For more examples, refer to the :func:`~.pennylane.cond` documentation -and `How to create dynamic circuits with mid-circuit measurements +For more examples, refer to the :func:`~.cond` documentation +and the `how-to on creating dynamic circuits with mid-circuit measurements `__. .. _mid_circuit_measurements_statistics: @@ -139,10 +137,10 @@ Mid-circuit measurement statistics ---------------------------------- Statistics of mid-circuit measurements can be collected along with terminal measurement statistics. -Currently, ``qml.probs``, ``qml.sample``, ``qml.expval``, ``qml.var``, and ``qml.counts`` +Currently, :func:`~.probs`, :func:`~.sample`, :func:`~.expval`, :func:`~.var`, and :func:`~.counts` are supported, and devices that currently support collecting such -statistics are :class:`~.pennylane.devices.DefaultQubit`, -:class:`~.pennylane.devices.DefaultMixed`, and :class:`~.pennylane.devices.DefaultQubitLegacy`. +statistics are :class:`~.DefaultQubit`, :class:`~.DefaultMixed`, +and :class:`~.DefaultQubitLegacy`. .. code-block:: python3 @@ -254,24 +252,27 @@ scalings are .. rst-class:: tb -+--------------------------+--------------------------------------------------+-------------------------------------------------------+---------------------------------------+-----------------------------+ -| **Simulation technique** | **Memory** | **Time** | **Differentiation support** | **Supports shots/analytic** | -| Deferred measurements | :rd: :math:`\mathcal{O}\left(2^{n_{MCM}}\right)` | :rd: :math:`\mathcal{O}\left(2^{n_{MCM}}\right)` | :gr: yes | :gr: yes / yes | -| Dynamic one-shot | :gr: :math:`\mathcal{O}\left(1\right)` | :rd: :math:`\mathcal{O}\left(n_{shots}\right)` | :or: finite differences\ :math:`{}^1` | :or: yes / no | -| Tree-traversal | :or: :math:`\mathcal{O}\left(n_{MCM}+1\right)` | :or: :math:`\mathcal{O}\left(\leq 2^{n_{MCM}}\right)` | :or: finite differences\ :math:`{}^1` | :or: yes / no | -+--------------------------+--------------------------------------------------+-------------------------------------------------------+---------------------------------------+-----------------------------+ ++--------------------------+-------------------------------------------+------------------------------------------------+-------------------------------------------+----------------------------------+ +| **Simulation technique** | **Memory** | **Time** | **Differentiation support** | **Supports shots/analytic mode** | ++==========================+===========================================+================================================+===========================================+==================================+ +| Deferred measurements | :rd:`\ ` :math:`\mathcal{O}(2^{n_{MCM}})` | :rd:`\ ` :math:`\mathcal{O}(2^{n_{MCM}})` | :gr:`\ ` yes | :gr:`\ ` yes / yes | ++--------------------------+-------------------------------------------+------------------------------------------------+-------------------------------------------+----------------------------------+ +| Dynamic one-shot | :gr:`\ ` :math:`\mathcal{O}(1)` | :rd:`\ ` :math:`\mathcal{O}(n_{shots})` | :or:`\ ` finite differences\ :math:`{}^1` | :or:`\ ` yes / no | ++--------------------------+-------------------------------------------+------------------------------------------------+-------------------------------------------+----------------------------------+ +| Tree-traversal | :or:`\ ` :math:`\mathcal{O}(n_{MCM}+1)` | :or:`\ ` :math:`\mathcal{O}(\leq 2^{n_{MCM}})` | :or:`\ ` finite differences\ :math:`{}^1` | :or:`\ ` yes / no | ++--------------------------+-------------------------------------------+------------------------------------------------+-------------------------------------------+----------------------------------+ :math:`{}^1` In principle, parameter-shift differentiation is supported as long as no postselection is used. Parameters within ``qml.cond``\ itionally applied operations will fall back to finite differences, so that a proper value for ``h`` should be provided, see -:func:`~.pennylane.finite_diff`. +:func:`~.pennylane.gradients.finite_diff`. The strengths and weaknesses of the simulation techniques differ strongly and the best technique will depend on details of the simulation workflow. As a rule of thumb: @@ -463,7 +464,7 @@ PennyLane. For ease of use, we provide the following configuration options to us .. warning:: If the ``mcm_method`` argument is provided, the :func:`~pennylane.defer_measurements` or - :func:`~pennylane.dynamic_one_shot` transforms must not be applied directly to the :class:`~pennylane.QNode` + :func:`~pennylane.dynamic_one_shot` transforms must not be applied manually to the :class:`~pennylane.QNode` as this can lead to incorrect behaviour. * ``postselect_mode``: To configure how invalid shots are handled when postselecting mid-circuit measurements @@ -480,18 +481,19 @@ PennyLane. For ease of use, we provide the following configuration options to us dev = qml.device("default.qubit", wires=3, shots=10) - def circuit(x): + def circ(x): qml.RX(x, 0) m_0 = qml.measure(0, postselect=1) qml.CNOT([0, 1]) return qml.sample(qml.PauliZ(0)) - fill_shots_qnode = qml.QNode(circuit, dev, mcm_method="one-shot", postselect_mode="fill-shots") - hw_like_qnode = qml.QNode(circuit, dev, mcm_method="one-shot", postselect_mode="hw-like") + method = "one-shot" + fill_shots_node = qml.QNode(circ, dev, mcm_method=method, postselect_mode="fill-shots") + hw_like_node = qml.QNode(circ, dev, mcm_method=method, postselect_mode="hw-like") - >>> fill_shots_qnode(np.pi / 2) + >>> fill_shots_node(np.pi / 2) array([-1., -1., -1., -1., -1., -1., -1., -1., -1., -1.]) - >>> hw_like_qnode(np.pi / 2) + >>> hw_like_node(np.pi / 2) array([-1., -1., -1., -1., -1., -1., -1.]) .. note:: @@ -508,7 +510,8 @@ PennyLane. For ease of use, we provide the following configuration options to us import jax import jax.numpy as jnp - dev = qml.device("default.qubit", wires=3, shots=10, seed=jax.random.PRNGKey(123)) + key = jax.random.PRNGKey(123) + dev = qml.device("default.qubit", wires=3, shots=10, seed=key) @qml.qnode(dev, postselect_mode="hw-like", mcm_method="one-shot") def circuit(x): From 5332d932e641b35c014da14c3bca6759bc7ceacb Mon Sep 17 00:00:00 2001 From: dwierichs Date: Tue, 18 Jun 2024 14:36:14 +0200 Subject: [PATCH 05/15] fix --- doc/introduction/mid_circuit_measurements.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/introduction/mid_circuit_measurements.rst b/doc/introduction/mid_circuit_measurements.rst index 7aa66f387e6..5f520f06351 100644 --- a/doc/introduction/mid_circuit_measurements.rst +++ b/doc/introduction/mid_circuit_measurements.rst @@ -139,7 +139,7 @@ Mid-circuit measurement statistics Statistics of mid-circuit measurements can be collected along with terminal measurement statistics. Currently, :func:`~.probs`, :func:`~.sample`, :func:`~.expval`, :func:`~.var`, and :func:`~.counts` are supported, and devices that currently support collecting such -statistics are :class:`~.DefaultQubit`, :class:`~.DefaultMixed`, +statistics are :class:`~.pennylane.devices.DefaultQubit`, :class:`~.DefaultMixed`, and :class:`~.DefaultQubitLegacy`. .. code-block:: python3 From fe2f5ed8bb777baaed295963ea702bd70442112f Mon Sep 17 00:00:00 2001 From: David Wierichs Date: Tue, 18 Jun 2024 16:13:56 +0200 Subject: [PATCH 06/15] Apply suggestions from code review Co-authored-by: Vincent Michaud-Rioux --- doc/introduction/mid_circuit_measurements.rst | 9 ++++----- pennylane/gradients/parameter_shift.py | 2 -- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/doc/introduction/mid_circuit_measurements.rst b/doc/introduction/mid_circuit_measurements.rst index 5f520f06351..687c5009964 100644 --- a/doc/introduction/mid_circuit_measurements.rst +++ b/doc/introduction/mid_circuit_measurements.rst @@ -137,7 +137,7 @@ Mid-circuit measurement statistics ---------------------------------- Statistics of mid-circuit measurements can be collected along with terminal measurement statistics. -Currently, :func:`~.probs`, :func:`~.sample`, :func:`~.expval`, :func:`~.var`, and :func:`~.counts` +Currently, :func:`~.counts`, :func:`~.expval`, :func:`~.probs`, :func:`~.sample`, and :func:`~.var` are supported, and devices that currently support collecting such statistics are :class:`~.pennylane.devices.DefaultQubit`, :class:`~.DefaultMixed`, and :class:`~.DefaultQubitLegacy`. @@ -187,7 +187,7 @@ Executing this ``QNode``: array([-1, -2, 1, -1, 1]) Collecting statistics for mid-circuit measurements manipulated using arithmetic/boolean operators is supported -with ``qml.expval``, ``qml.var``, ``qml.sample``, and ``qml.counts``. +with :func:`~.counts`, :func:`~.expval`, :func:`~.sample`, and :func:`~.var`. Moreover, statistics for multiple mid-circuit measurements can be collected by passing lists of mid-circuit measurement values to the measurement process: @@ -215,8 +215,7 @@ array([[0, 1], [0, 0], [1, 1]]) -Collecting statistics for sequences of mid-circuit measurements is supported with ``qml.sample``, -``qml.probs``, and ``qml.counts``. +Collecting statistics for sequences of mid-circuit measurements is supported with :func:`~.counts`, :func:`~.probs`, and :func:`~.sample`. .. warning:: @@ -270,7 +269,7 @@ scalings are +--------------------------+-------------------------------------------+------------------------------------------------+-------------------------------------------+----------------------------------+ :math:`{}^1` In principle, parameter-shift differentiation is supported as long as no -postselection is used. Parameters within ``qml.cond``\ itionally applied operations will +postselection is used. Parameters within conditionally applied operations will fall back to finite differences, so that a proper value for ``h`` should be provided, see :func:`~.pennylane.gradients.finite_diff`. diff --git a/pennylane/gradients/parameter_shift.py b/pennylane/gradients/parameter_shift.py index 437e888a81f..acdc7946ea0 100644 --- a/pennylane/gradients/parameter_shift.py +++ b/pennylane/gradients/parameter_shift.py @@ -1088,8 +1088,6 @@ def param_shift( Note that ``broadcast=True`` requires additional memory by a factor of the largest batch_size of the created tapes. """ - print(tape) - print(tape.operations) transform_name = "parameter-shift rule" assert_no_state_returns(tape.measurements, transform_name) From 80b2358f5013cc2edb6c6a5104c2828152fc9d4b Mon Sep 17 00:00:00 2001 From: dwierichs Date: Wed, 19 Jun 2024 10:23:30 +0200 Subject: [PATCH 07/15] review --- doc/introduction/mid_circuit_measurements.rst | 312 +++++++----------- doc/releases/changelog-dev.md | 8 +- 2 files changed, 122 insertions(+), 198 deletions(-) diff --git a/doc/introduction/mid_circuit_measurements.rst b/doc/introduction/mid_circuit_measurements.rst index 687c5009964..158d9193e91 100644 --- a/doc/introduction/mid_circuit_measurements.rst +++ b/doc/introduction/mid_circuit_measurements.rst @@ -34,15 +34,14 @@ of mid-circuit measurements, as well as information about simulation strategies and how to configure them. Additional information can be found in the documentation of the individual methods. Also consider our -`Introduction to mid-circuit measurements -`__, -`How to collect statistics of mid-circuit measurements -`__, -and `How to create dynamci circuits with mid-circuit measurements -`__. +:doc:`Introduction to mid-circuit measurements `, +:doc:`how-to on collecting statistics of mid-circuit measurements +`, +and :doc:`how-to on creating dynamic circuits with mid-circuit measurements +`. Resetting wires -*************** +--------------- Wires can be reused after making mid-circuit measurements. Moreover, a measured wire can be reset to the :math:`|0 \rangle` state by setting ``reset=True`` in :func:`~.pennylane.measure`: @@ -66,7 +65,7 @@ Executing this QNode: tensor([0., 1.], requires_grad=True) Postselecting mid-circuit measurements -************************************** +-------------------------------------- PennyLane also supports postselecting on mid-circuit measurement outcomes by specifying the ``postselect`` keyword argument of :func:`~.pennylane.measure`. By default, postselection @@ -96,10 +95,6 @@ Note that only 7 samples are returned. This is because samples that do not meet discarded. This behaviour can be customized, see the section :ref:`"Configuring mid-circuit measurements" `. -.. note:: - - Currently, postselection is only supported on :class:`~.pennylane.devices.DefaultQubit`. - Conditional operators / Dynamic circuits ---------------------------------------- @@ -111,7 +106,6 @@ condition based on such values and pass it to ``cond()``: .. code-block:: python @qml.qnode(dev) - @qml.defer_measurements def qnode_conditional_op_on_zero(x, y): qml.RY(x, wires=0) qml.CNOT(wires=[0, 1]) @@ -128,8 +122,8 @@ condition based on such values and pass it to ``cond()``: tensor([0.88660045, 0.11339955], requires_grad=True) For more examples, refer to the :func:`~.cond` documentation -and the `how-to on creating dynamic circuits with mid-circuit measurements -`__. +and the :doc:`how-to on creating dynamic circuits with mid-circuit measurements +`. .. _mid_circuit_measurements_statistics: @@ -208,23 +202,26 @@ measurement values to the measurement process: Executing this ``QNode``: ->>> circuit(1.23, 4.56, shots=5) -array([[0, 1], - [1, 1], - [0, 1], - [0, 0], - [1, 1]]) +.. code-block:: pycon -Collecting statistics for sequences of mid-circuit measurements is supported with :func:`~.counts`, :func:`~.probs`, and :func:`~.sample`. + >>> circuit(1.23, 4.56, shots=5) + array([[0, 1], + [1, 1], + [0, 1], + [0, 0], + [1, 1]]) + +Collecting statistics for sequences of mid-circuit measurements is supported with +:func:`~.counts`, :func:`~.probs`, and :func:`~.sample`. .. warning:: - When collecting statistics for a list of mid-circuit measurements, arithmetic - expressions are not supported. + When collecting statistics for a sequence of mid-circuit measurements, the + sequence may not contain arithmetic expressions. Simulation of mid-circuit measurements --------------------------------------- +====================================== PennyLane currently offers three methods to simulate mid-circuit measurements on classical computers: the deferred measurements principle, dynamic one-shot @@ -232,7 +229,7 @@ sampling, and a tree-traversal approach. These methods differ in their memory re and computational cost, as well as their compatibility with other features such as shots and differentiation methods. While the requirements depend on details of the simulation, the expected -scalings are +scalings with respect to the number of mid-circuit measurements (and shots) are .. role:: gr .. role:: or @@ -258,20 +255,20 @@ scalings are .. rst-class:: tb -+--------------------------+-------------------------------------------+------------------------------------------------+-------------------------------------------+----------------------------------+ -| **Simulation technique** | **Memory** | **Time** | **Differentiation support** | **Supports shots/analytic mode** | -+==========================+===========================================+================================================+===========================================+==================================+ -| Deferred measurements | :rd:`\ ` :math:`\mathcal{O}(2^{n_{MCM}})` | :rd:`\ ` :math:`\mathcal{O}(2^{n_{MCM}})` | :gr:`\ ` yes | :gr:`\ ` yes / yes | -+--------------------------+-------------------------------------------+------------------------------------------------+-------------------------------------------+----------------------------------+ -| Dynamic one-shot | :gr:`\ ` :math:`\mathcal{O}(1)` | :rd:`\ ` :math:`\mathcal{O}(n_{shots})` | :or:`\ ` finite differences\ :math:`{}^1` | :or:`\ ` yes / no | -+--------------------------+-------------------------------------------+------------------------------------------------+-------------------------------------------+----------------------------------+ -| Tree-traversal | :or:`\ ` :math:`\mathcal{O}(n_{MCM}+1)` | :or:`\ ` :math:`\mathcal{O}(\leq 2^{n_{MCM}})` | :or:`\ ` finite differences\ :math:`{}^1` | :or:`\ ` yes / no | -+--------------------------+-------------------------------------------+------------------------------------------------+-------------------------------------------+----------------------------------+ ++--------------------------+-------------------------------------------+-----------------------------------------------------------+-------------------------------------------+----------------------------------+ +| **Simulation technique** | **Memory** | **Time** | **Differentiation support** | **Supports shots/analytic mode** | ++==========================+===========================================+===========================================================+===========================================+==================================+ +| Deferred measurements | :rd:`\ ` :math:`\mathcal{O}(2^{n_{MCM}})` | :rd:`\ ` :math:`\mathcal{O}(2^{n_{MCM}})` | :gr:`\ ` yes | :gr:`\ ` yes / yes | ++--------------------------+-------------------------------------------+-----------------------------------------------------------+-------------------------------------------+----------------------------------+ +| Dynamic one-shot | :gr:`\ ` :math:`\mathcal{O}(1)` | :rd:`\ ` :math:`\mathcal{O}(n_{shots})` | :or:`\ ` finite differences\ :math:`{}^1` | :or:`\ ` yes / no | ++--------------------------+-------------------------------------------+-----------------------------------------------------------+-------------------------------------------+----------------------------------+ +| Tree-traversal | :or:`\ ` :math:`\mathcal{O}(n_{MCM}+1)` | :or:`\ ` :math:`\mathcal{O}(min(n_{shots}, 2^{n_{MCM}}))` | :or:`\ ` finite differences\ :math:`{}^1` | :or:`\ ` yes / no | ++--------------------------+-------------------------------------------+-----------------------------------------------------------+-------------------------------------------+----------------------------------+ :math:`{}^1` In principle, parameter-shift differentiation is supported as long as no postselection is used. Parameters within conditionally applied operations will -fall back to finite differences, so that a proper value for ``h`` should be provided, see -:func:`~.pennylane.gradients.finite_diff`. +fall back to finite differences, so a proper value for ``h`` should be provided (see +:func:`~.pennylane.gradients.finite_diff`). The strengths and weaknesses of the simulation techniques differ strongly and the best technique will depend on details of the simulation workflow. As a rule of thumb: @@ -282,25 +279,23 @@ technique will depend on details of the simulation workflow. As a rule of thumb: and measurements, and - deferred measurements are the generalist solution that enables mid-circuit measurement - support under (almost) all circumstances, but at large memory cost. + support under (almost) all circumstances, but at large memory cost. It is the only method + supporting analytic simulations. By default, ``QNode``\ s use deferred measurements and dynamic one-shot sampling (if supported) -when executed without and with shots, respectively. +when executed without and with shots, respectively. The method can be configured with +the keyword argument ``mcm_method`` at ``QNode`` creation +(see :ref:`"Configuring mid-circuit measurements" `). .. _deferred_measurements: Deferred measurements -********************* - -A quantum function with mid-circuit measurements (defined using -:func:`~.pennylane.measure`) and conditional operations (defined using -:func:`~.pennylane.cond`) can be executed by applying the `deferred measurement -principle `__. -Accordingly, statistics of mid-circuit measurements become conventional terminal measurement -statistics. -In PennyLane this technique is available as the transform :func:`~.pennylane.defer_measurements`, -which can be applied to :class:`~.pennylane.QNode`\ s, quantum functions and tapes -as usual. It is the default technique for ``QNode``\ s that do not execute with shots. +--------------------- + +A quantum function with mid-circuit measurements can be executed via the +`deferred measurement principle `__. +In PennyLane, this technique is available via ``mcm_method="deferred"`` or as the +transform :func:`~.pennylane.defer_measurements`. The deferred measurement principle provides a powerful method to simulate mid-circuit measurements, conditional operations and measurement statistics @@ -310,8 +305,8 @@ memory and simulation time that scale exponentially with the number of measureme .. code-block:: pycon - deferred_qnode = qml.defer_measurements(my_qnode) - pars = np.array([0.643, 0.246]) + >>> deferred_qnode = qml.defer_measurements(my_qnode) + >>> pars = np.array([0.643, 0.246]) >>> deferred_qnode(*pars) (tensor([0.90165331, 0.09834669], requires_grad=True), tensor(0.09984972, requires_grad=True)) @@ -332,57 +327,34 @@ before and after applying the transform: Mid-circuit measurements are deferred to the end of the circuit, and conditionally applied operations become (quantumly) controlled operations. -``qml.defer_measurements`` can be applied as decorator equally well: -.. code-block:: python - - @qml.qnode(dev) - @qml.defer_measurements - def qnode(x, y): - (...) +.. note:: - @qml.defer_measurements - @qml.qnode(dev) - def qnode(x, y): - (...) + This method requires an additional qubit for each mid-circuit measurement, which limits + the number of measurements that can be used both on classical simulators and quantum hardware. -.. note:: + Postselection with deferred measurements is only supported on + :class:`~.pennylane.devices.DefaultQubit`. - The deferred measurements principle requires an additional wire, or qubit, for each mid-circuit - measurement, limiting the number of measurements that can be used both on classical simulators - and quantum hardware. .. _one_shot_transform: Dynamic one-shot sampling -************************* - -Devices that natively support mid-circuit measurements (defined using -:func:`~.pennylane.measure`) and conditional operations (defined using -:func:`~.pennylane.cond`) can estimate dynamic circuits by executing -them one shot at a time, sampling a dynamic circuit execution path for each -shot. -This technique is the default for a shot-based -:class:`~.pennylane.QNode` that uses a device supporting mid-circuit measurements, -as well as any :class:`~.pennylane.QNode` with the -:func:`~.pennylane.dynamic_one_shot` quantum function transform. -As the name suggests, this transform only works for a :class:`~.pennylane.QNode` -executing with finite shots and it will raise an error if the device does not support -mid-circuit measurements natively. +------------------------- + +Devices that natively support mid-circuit measurements can evaluate dynamic circuits +by executing them one shot at a time, sampling a dynamic execution path for each shot. + +In PennyLane, this technique is available via the QNode argument ``mcm_method="one-shot"`` +or as the transform :func:`~.pennylane.dynamic_one_shot`. +As the name suggests, this transform only works for a :class:`~.pennylane.QNode` executing +with finite shots and it requires the device to support mid-circuit measurements natively. The :func:`~.pennylane.dynamic_one_shot` transform is usually advantageous compared with the :func:`~.pennylane.defer_measurements` transform in the many-mid-circuit-measurements and few-shots limit. This is because, unlike the deferred measurement principle, the method does not need an additional wire for every mid-circuit measurement in the circuit. -The transform can be applied to a QNode as follows: - -.. code-block:: python - - @qml.dynamic_one_shot - @qml.qnode(dev) - def my_quantum_function(x, y): - (...) .. warning:: @@ -391,140 +363,92 @@ The transform can be applied to a QNode as follows: .. _tree_traversal: Tree-traversal algorithm -************************ +------------------------ Dynamic circuit execution is akin to traversing a binary tree where each mid-circuit measurement -corresponds to a node and groups of gates between them correspond to edges. -The :func:`~.pennylane.dynamic_one_shot` approach above picks a branch of the tree at random -and simulates it from beginning to end. -This is wasteful in many cases; the same branch is simulated many times -when there are more shots than branches for example, and shared information between branches -is not reused. -The tree-traversal algorithm does away with such redundancy while retaining the -exponential gains in memory of the one-shot approach compared with the deferred -measurement principle, among other advantages. - -The algorithm cuts an :math:`n_{MCM}` circuit into :math:`n_{MCM}+1` -circuit segments. Each segment can be executed on either the 0- or 1-branch, -which gives rise to a binary tree with :math:`2^{n_{MCM}}` leaves. Terminal -measurements are obtained at the leaves, and propagated and combined back up at each -node up the tree. The tree is explored using a depth-first pattern. The tree-traversal -method improves on :func:`~.pennylane.dynamic_one_shot` by collecting all samples at a -node or leaf at once. Neglecting overheads, simulating all branches requires the same -amount of computations as :func:`~. pennylane.defer_measurements`, but without the -:math:`O(2^{n_{MCM}})` memory requirement. To save time, a copy of the state vector -is made at every node, or mid-circuit measurement, requiring :math:`n_{MCM}+1` state -vectors, an exponential improvement compared to :func:`~.pennylane.defer_measurements`. +corresponds to a node and gates between them correspond to edges. The tree-traversal algorithm +explores this tree depth-first. It improves upon the dynamic one-shot approach above, which +simulates a randomly chosen branch from beginning to end for each shot, by collecting all +samples at a node or leaf at once. + +In PennyLane, this technique is available via the QNode argument ``mcm_method="tree-traversal"``; +it is not a transform. + +The tree-traversal algorithm combines the exponential savings of memory of the one-shot +approach with sampling efficiency of deferred measurements. +Neglecting overheads, simulating all branches requires the same +amount of computations as :func:`~.pennylane.defer_measurements`, but without the +:math:`O(2^{n_{MCM}})` memory cost. To save time, a copy of the state vector +is made at every mid-circuit measurement, requiring :math:`n_{MCM}+1` state +vectors, an exponential improvement over :func:`~.pennylane.defer_measurements`. Since the counts of many nodes come out to be zero for shot-based simulations, it is often possible to ignore entire sub-trees, thereby reducing the computational cost. -To summarize, this algorithm gives us the best of both worlds. In the limit of few -shots and/or many mid-circuit measurements, it is as fast as the naive shot-by-shot -implementation of ``dynamic_one_shot`` because few sub-trees are explored. -In the limit of many shots and/or few mid-circuit measurements, it is -equal to or faster than the deferred measurement algorithm (albeit with more -overheads in practice) because each tree edge is visited at most once, all while -exponentially reducing the memory requirements. - -The tree-traversal algorithm is not a transform. Its usage is therefore specified -by passing an ``mcm_method`` option to a QNode (see section -:ref:`"Configuring mid-circuit measurements" `). For example, - -.. code-block:: python - - @qml.qnode(dev, mcm_method="tree-traversal") - def my_qnode(x, y): - (...) - .. warning:: - The tree-traversal algorithm is only implemented for the - :class:`~.pennylane.devices.DefaultQubit` device. + The tree-traversal algorithm is only supported by the + :class:`~.pennylane.devices.DefaultQubit` device, and currently does + not support just-in-time (JIT) compilation. .. _mcm_config: Configuring mid-circuit measurements -************************************ +------------------------------------ -As seen above, there are multiple ways in which circuits with mid-circuit measurements can be executed with -PennyLane. For ease of use, we provide the following configuration options to users when initializing a -:class:`~pennylane.QNode`: +As described above, there are multiple simulation techniques for circuits with +mid-circuit measurements in PennyLane. They can be configured when initializing a +:class:`~pennylane.QNode`, using the following keywords: -* ``mcm_method``: To set the method used for applying mid-circuit measurements. Use ``mcm_method="deferred"`` - to apply the :ref:`deferred measurements principle `, ``mcm_method="one-shot"`` to apply - the :ref:`one-shot transform ` or ``mcm_method="tree-traversal"`` to execute the - :ref:`tree-traversal algorithm `. - When executing with finite shots, ``mcm_method="one-shot"`` - will be the default, and ``mcm_method="deferred"`` otherwise. Additionally, if using :func:`~pennylane.qjit`, - ``mcm_method="single-branch-statistics"`` can also be used and will be the default. Using this method, a single - branch of the execution tree will be chosen randomly. +* ``mcm_method``: Sets the method used for applying mid-circuit measurements. The options are + ``"deferred"``, ``"one-shot"``, and ``"tree-traversal"`` for the three techniques described above. + The default is ``mcm_method="one-shot"`` when executing with shots, and ``"deferred"`` otherwise. + When using :func:`~pennylane.qjit`, there is the additional (default) option + ``mcm_method="single-branch-statistics"``, which explores a single branch of the execution + tree at random. .. warning:: - If the ``mcm_method`` argument is provided, the :func:`~pennylane.defer_measurements` or - :func:`~pennylane.dynamic_one_shot` transforms must not be applied manually to the :class:`~pennylane.QNode` - as this can lead to incorrect behaviour. + If the ``mcm_method`` argument is provided, the transforms for deferred measurements + or dynamic one-shot sampling must not be applied manually to the :class:`~pennylane.QNode`. -* ``postselect_mode``: To configure how invalid shots are handled when postselecting mid-circuit measurements - with finite-shot circuits. Use ``postselect_mode="hw-like"`` to discard invalid samples. In this case, the number - of samples that are used to process results can be smaller than the total number of shots. If - ``postselect_mode="fill-shots"`` is used, the postselected value will be sampled unconditionally, and all - samples will be valid. This is equivalent to sampling until the number of valid samples matches the total number - of shots. The default behaviour is ``postselect_mode="hw-like"``. +* ``postselect_mode``: Configures how invalid shots are handled when postselecting + mid-circuit measurements with finite-shot circuits. Use ``"hw-like"`` to discard invalid samples. + In this case, fewer than the total number of shots may be used to process results. Use + ``"fill-shots"`` to sample the postselected value unconditionally, creating valid samples + only. This is equivalent to sampling until the number of valid + samples matches the total number of shots. The default is ``"hw-like"``. .. code-block:: python3 - import pennylane as qml - import numpy as np - dev = qml.device("default.qubit", wires=3, shots=10) - def circ(x): - qml.RX(x, 0) + def circ(): + qml.Hadamard(0) m_0 = qml.measure(0, postselect=1) - qml.CNOT([0, 1]) return qml.sample(qml.PauliZ(0)) - method = "one-shot" - fill_shots_node = qml.QNode(circ, dev, mcm_method=method, postselect_mode="fill-shots") - hw_like_node = qml.QNode(circ, dev, mcm_method=method, postselect_mode="hw-like") - - >>> fill_shots_node(np.pi / 2) - array([-1., -1., -1., -1., -1., -1., -1., -1., -1., -1.]) - >>> hw_like_node(np.pi / 2) - array([-1., -1., -1., -1., -1., -1., -1.]) - - .. note:: - - When using the ``jax`` interface, ``postselect_mode="hw-like"`` will have different behaviour based on the - chosen ``mcm_method``. + fill_shots = qml.QNode(circ, dev, mcm_method="one-shot", postselect_mode="fill-shots") + hw_like= qml.QNode(circ, dev, mcm_method="one-shot", postselect_mode="hw-like") - * If ``mcm_method="one-shot"``, invalid shots will not be discarded. Instead, invalid samples will be replaced - by ``np.iinfo(np.int32).min``. These invalid samples will not be used for processing final results (like - expectation values), but will appear in the ``QNode`` output if samples are requested directly. For example: + .. code-block:: pycon - .. code-block:: python3 + >>> fill_shots() + array([-1., -1., -1., -1., -1., -1., -1., -1., -1., -1.]) + >>> hw_like() + array([-1., -1., -1., -1., -1., -1., -1.]) - import jax - import jax.numpy as jnp - - key = jax.random.PRNGKey(123) - dev = qml.device("default.qubit", wires=3, shots=10, seed=key) + .. note:: - @qml.qnode(dev, postselect_mode="hw-like", mcm_method="one-shot") - def circuit(x): - qml.RX(x, 0) - qml.measure(0, postselect=1) - return qml.sample(qml.PauliZ(0)) + When using the ``jax`` interface, the postselection mode ``"hw-like"`` will change + behaviour with the simulation technique. - >>> x = jnp.array(1.8) - >>> f(x) - Array([-2.1474836e+09, -1.0000000e+00, -2.1474836e+09, -2.1474836e+09, - -1.0000000e+00, -2.1474836e+09, -1.0000000e+00, -2.1474836e+09, - -1.0000000e+00, -1.0000000e+00], dtype=float32, weak_type=True) + * For dynamic one-shot, invalid shots will not be discarded, but will be replaced + by ``np.iinfo(np.int32).min``. They will not be used for processing final results (like + expectation values), but they will appear in the output of ``QNode``\ s that return + samples directly. + + * When using ``jax.jit``, the combination ``"deferred"`` and ``"hw-like"`` is not supported, + due to limitations of the :func:`~pennylane.defer_measurements` transform. This behaviour + will change in the future. - * When using ``jax.jit``, using ``mcm_method="deferred"`` is not supported with ``postselect_mode="hw-like"`` and - an error will be raised if this configuration is requested. This is due to current limitations of the - :func:`~pennylane.defer_measurements` transform, and this behaviour will change in the future to be more - consistent with ``mcm_method="one-shot"``. diff --git a/doc/releases/changelog-dev.md b/doc/releases/changelog-dev.md index 0b353423ee8..089d77d74e1 100644 --- a/doc/releases/changelog-dev.md +++ b/doc/releases/changelog-dev.md @@ -79,10 +79,6 @@

Improvements 🛠

-* Move information about mid-circuit measurements from the measurements quickstart page to its own - [mid-circuit measurements quickstart page](https://docs.pennylane.ai/en/stable/introduction/mid_circuit_measurements.html) - [(#5870)](https://github.com/PennyLaneAI/pennylane/pull/5870) - * Add operation and measurement specific routines in `default.tensor` to improve scalability. [(#5795)](https://github.com/PennyLaneAI/pennylane/pull/5795) @@ -367,6 +363,10 @@

Documentation 📝

+* Move information about mid-circuit measurements from the measurements quickstart page to its own + [mid-circuit measurements quickstart page](https://docs.pennylane.ai/en/stable/introduction/mid_circuit_measurements.html) + [(#5870)](https://github.com/PennyLaneAI/pennylane/pull/5870) + * The documentation for the `default.tensor` device has been added. [(#5719)](https://github.com/PennyLaneAI/pennylane/pull/5719) From 3d1a6a9c799b63179834c5ae5f3f4c4bf8df2295 Mon Sep 17 00:00:00 2001 From: dwierichs Date: Wed, 19 Jun 2024 10:34:23 +0200 Subject: [PATCH 08/15] complex cond example -> docstring; polish --- doc/introduction/mid_circuit_measurements.rst | 20 ++++++++--------- pennylane/ops/op_math/condition.py | 22 ++++++++++++++++++- 2 files changed, 30 insertions(+), 12 deletions(-) diff --git a/doc/introduction/mid_circuit_measurements.rst b/doc/introduction/mid_circuit_measurements.rst index 158d9193e91..b892f61652d 100644 --- a/doc/introduction/mid_circuit_measurements.rst +++ b/doc/introduction/mid_circuit_measurements.rst @@ -28,7 +28,7 @@ The function to perform a mid-circuit measurement in PennyLane is qml.cond(m_0, qml.RY)(y, wires=0) return qml.probs(wires=[0]), qml.expval(m_0) -See the following sections for details on +See the following sections for details on :func:`~.pennylane.measure`, :func:`~.pennylane.cond`, and statistics of mid-circuit measurements, as well as information about simulation strategies and how to configure them. @@ -121,7 +121,7 @@ condition based on such values and pass it to ``cond()``: >>> qnode_conditional_op_on_zero(*pars) tensor([0.88660045, 0.11339955], requires_grad=True) -For more examples, refer to the :func:`~.cond` documentation +For more examples, refer to the :func:`~.pennylane.cond` documentation and the :doc:`how-to on creating dynamic circuits with mid-circuit measurements `. @@ -132,9 +132,7 @@ Mid-circuit measurement statistics Statistics of mid-circuit measurements can be collected along with terminal measurement statistics. Currently, :func:`~.counts`, :func:`~.expval`, :func:`~.probs`, :func:`~.sample`, and :func:`~.var` -are supported, and devices that currently support collecting such -statistics are :class:`~.pennylane.devices.DefaultQubit`, :class:`~.DefaultMixed`, -and :class:`~.DefaultQubitLegacy`. +are supported. .. code-block:: python3 @@ -228,7 +226,7 @@ on classical computers: the deferred measurements principle, dynamic one-shot sampling, and a tree-traversal approach. These methods differ in their memory requirements and computational cost, as well as their compatibility with other features such as shots and differentiation methods. -While the requirements depend on details of the simulation, the expected +While the requirements depend on details of the simulation, the expected scalings with respect to the number of mid-circuit measurements (and shots) are .. role:: gr @@ -299,7 +297,7 @@ transform :func:`~.pennylane.defer_measurements`. The deferred measurement principle provides a powerful method to simulate mid-circuit measurements, conditional operations and measurement statistics -in a differentiable and device-independent way. It adds an auxiliary qubit +in a differentiable and device-independent way. It adds an auxiliary qubit to the circuit for each mid-circuit measurement, leading to overheads of both memory and simulation time that scale exponentially with the number of measurements. @@ -367,7 +365,7 @@ Tree-traversal algorithm Dynamic circuit execution is akin to traversing a binary tree where each mid-circuit measurement corresponds to a node and gates between them correspond to edges. The tree-traversal algorithm -explores this tree depth-first. It improves upon the dynamic one-shot approach above, which +explores this tree depth-first. It improves upon the dynamic one-shot approach above, which simulates a randomly chosen branch from beginning to end for each shot, by collecting all samples at a node or leaf at once. @@ -396,7 +394,7 @@ cost. Configuring mid-circuit measurements ------------------------------------ -As described above, there are multiple simulation techniques for circuits with +As described above, there are multiple simulation techniques for circuits with mid-circuit measurements in PennyLane. They can be configured when initializing a :class:`~pennylane.QNode`, using the following keywords: @@ -414,7 +412,7 @@ mid-circuit measurements in PennyLane. They can be configured when initializing * ``postselect_mode``: Configures how invalid shots are handled when postselecting mid-circuit measurements with finite-shot circuits. Use ``"hw-like"`` to discard invalid samples. - In this case, fewer than the total number of shots may be used to process results. Use + In this case, fewer than the total number of shots may be used to process results. Use ``"fill-shots"`` to sample the postselected value unconditionally, creating valid samples only. This is equivalent to sampling until the number of valid samples matches the total number of shots. The default is ``"hw-like"``. @@ -447,7 +445,7 @@ mid-circuit measurements in PennyLane. They can be configured when initializing by ``np.iinfo(np.int32).min``. They will not be used for processing final results (like expectation values), but they will appear in the output of ``QNode``\ s that return samples directly. - + * When using ``jax.jit``, the combination ``"deferred"`` and ``"hw-like"`` is not supported, due to limitations of the :func:`~pennylane.defer_measurements` transform. This behaviour will change in the future. diff --git a/pennylane/ops/op_math/condition.py b/pennylane/ops/op_math/condition.py index 1764b554ed3..d3da2814719 100644 --- a/pennylane/ops/op_math/condition.py +++ b/pennylane/ops/op_math/condition.py @@ -237,7 +237,7 @@ def false_fn(): .. code-block:: python3 - dev = qml.device("default.qubit", wires=2) + dev = qml.device("default.qubit") def qfunc(par, wires): qml.Hadamard(wires[0]) @@ -256,6 +256,26 @@ def qnode(x): >>> qnode(par) tensor(0.3522399, requires_grad=True) + **Postprocessing multiple measurements into a condition** + + The Boolean condition for ``cond`` may consist of arithmetic expressions + of one or multiple mid-circuit measurements: + + .. code-block:: python3 + + def cond_fn(mcms): + first_term = np.prod(mcms) + second_term = (2 ** np.arange(len(mcms))) @ mcms + return (1 - first_term) * (second_term > 3) + + @qml.qnode(dev) + def qnode(x): + ... + mcms = [qml.measure(w) for w in range(4)] + qml.cond(cond_fn(mcms), qml.RX)(x, wires=4) + ... + return qml.expval(qml.Z(1)) + **Passing two quantum functions** In the qubit model, single-qubit measurements may result in one of two From 76d151290d78147c85b2c4517f7aad2d25db7af2 Mon Sep 17 00:00:00 2001 From: dwierichs Date: Wed, 19 Jun 2024 16:29:46 +0200 Subject: [PATCH 09/15] headers --- doc/introduction/mid_circuit_measurements.rst | 25 +++++++++++-------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/doc/introduction/mid_circuit_measurements.rst b/doc/introduction/mid_circuit_measurements.rst index b892f61652d..59fc283bc7e 100644 --- a/doc/introduction/mid_circuit_measurements.rst +++ b/doc/introduction/mid_circuit_measurements.rst @@ -31,7 +31,7 @@ The function to perform a mid-circuit measurement in PennyLane is See the following sections for details on :func:`~.pennylane.measure`, :func:`~.pennylane.cond`, and statistics of mid-circuit measurements, as well as information about simulation -strategies and how to configure them. +strategies and how to configure them . Additional information can be found in the documentation of the individual methods. Also consider our :doc:`Introduction to mid-circuit measurements `, @@ -40,8 +40,11 @@ methods. Also consider our and :doc:`how-to on creating dynamic circuits with mid-circuit measurements `. +Available features +------------------ + Resetting wires ---------------- +*************** Wires can be reused after making mid-circuit measurements. Moreover, a measured wire can be reset to the :math:`|0 \rangle` state by setting ``reset=True`` in :func:`~.pennylane.measure`: @@ -65,7 +68,7 @@ Executing this QNode: tensor([0., 1.], requires_grad=True) Postselecting mid-circuit measurements --------------------------------------- +************************************** PennyLane also supports postselecting on mid-circuit measurement outcomes by specifying the ``postselect`` keyword argument of :func:`~.pennylane.measure`. By default, postselection @@ -96,7 +99,7 @@ discarded. This behaviour can be customized, see the section :ref:`"Configuring mid-circuit measurements" `. Conditional operators / Dynamic circuits ----------------------------------------- +**************************************** Users can create conditional operators controlled on mid-circuit measurements using :func:`~.pennylane.cond`. The condition for a conditional operator may simply be @@ -128,7 +131,7 @@ and the :doc:`how-to on creating dynamic circuits with mid-circuit measurements .. _mid_circuit_measurements_statistics: Mid-circuit measurement statistics ----------------------------------- +********************************** Statistics of mid-circuit measurements can be collected along with terminal measurement statistics. Currently, :func:`~.counts`, :func:`~.expval`, :func:`~.probs`, :func:`~.sample`, and :func:`~.var` @@ -218,8 +221,8 @@ Collecting statistics for sequences of mid-circuit measurements is supported wit sequence may not contain arithmetic expressions. -Simulation of mid-circuit measurements -====================================== +Simulation techniques +--------------------- PennyLane currently offers three methods to simulate mid-circuit measurements on classical computers: the deferred measurements principle, dynamic one-shot @@ -288,7 +291,7 @@ the keyword argument ``mcm_method`` at ``QNode`` creation .. _deferred_measurements: Deferred measurements ---------------------- +********************* A quantum function with mid-circuit measurements can be executed via the `deferred measurement principle `__. @@ -338,7 +341,7 @@ operations become (quantumly) controlled operations. .. _one_shot_transform: Dynamic one-shot sampling -------------------------- +************************* Devices that natively support mid-circuit measurements can evaluate dynamic circuits by executing them one shot at a time, sampling a dynamic execution path for each shot. @@ -361,7 +364,7 @@ mid-circuit measurement in the circuit. .. _tree_traversal: Tree-traversal algorithm ------------------------- +************************ Dynamic circuit execution is akin to traversing a binary tree where each mid-circuit measurement corresponds to a node and gates between them correspond to edges. The tree-traversal algorithm @@ -392,7 +395,7 @@ cost. .. _mcm_config: Configuring mid-circuit measurements ------------------------------------- +************************************ As described above, there are multiple simulation techniques for circuits with mid-circuit measurements in PennyLane. They can be configured when initializing a From 5cf1d880d906d794702002d65d9503686e986ff0 Mon Sep 17 00:00:00 2001 From: dwierichs Date: Wed, 19 Jun 2024 16:41:21 +0200 Subject: [PATCH 10/15] ref --- doc/introduction/mid_circuit_measurements.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/doc/introduction/mid_circuit_measurements.rst b/doc/introduction/mid_circuit_measurements.rst index 59fc283bc7e..51826dc7591 100644 --- a/doc/introduction/mid_circuit_measurements.rst +++ b/doc/introduction/mid_circuit_measurements.rst @@ -31,7 +31,7 @@ The function to perform a mid-circuit measurement in PennyLane is See the following sections for details on :func:`~.pennylane.measure`, :func:`~.pennylane.cond`, and statistics of mid-circuit measurements, as well as information about simulation -strategies and how to configure them . +strategies and how to configure them :ref:`further below `. Additional information can be found in the documentation of the individual methods. Also consider our :doc:`Introduction to mid-circuit measurements `, @@ -220,6 +220,7 @@ Collecting statistics for sequences of mid-circuit measurements is supported wit When collecting statistics for a sequence of mid-circuit measurements, the sequence may not contain arithmetic expressions. +.. _simulation_techniques: Simulation techniques --------------------- From 786e98640421727478b831a125823a325ccc07ac Mon Sep 17 00:00:00 2001 From: dwierichs Date: Thu, 20 Jun 2024 10:48:56 +0200 Subject: [PATCH 11/15] fix links, headers --- doc/introduction/mid_circuit_measurements.rst | 32 +++++++++++-------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/doc/introduction/mid_circuit_measurements.rst b/doc/introduction/mid_circuit_measurements.rst index 51826dc7591..5a041e381de 100644 --- a/doc/introduction/mid_circuit_measurements.rst +++ b/doc/introduction/mid_circuit_measurements.rst @@ -3,7 +3,7 @@ .. _mid_circuit_measurements: -Mid-circuit measurements +Dynamic quantum circuits ======================== .. currentmodule:: pennylane.measure @@ -12,6 +12,13 @@ PennyLane allows using measurements in the middle of a quantum circuit. Such measurements are called mid-circuit measurements and can be used to shape the structure of the circuit dynamically, and to gather information about the quantum state during the circuit execution. + +Available features +------------------ + +Mid-circuit measurements +************************ + The function to perform a mid-circuit measurement in PennyLane is :func:`~.pennylane.measure`, and can be used as follows: @@ -34,17 +41,14 @@ of mid-circuit measurements, as well as information about simulation strategies and how to configure them :ref:`further below `. Additional information can be found in the documentation of the individual methods. Also consider our -:doc:`Introduction to mid-circuit measurements `, -:doc:`how-to on collecting statistics of mid-circuit measurements -`, -and :doc:`how-to on creating dynamic circuits with mid-circuit measurements -`. - -Available features ------------------- +:doc:`Introduction to mid-circuit measurements `_, +:doc:`how-to on collecting statistics of mid-circuit measurements +`_, +and :doc:`how-to on creating dynamic circuits with mid-circuit measurements +`_. -Resetting wires -*************** +Resetting qubits +**************** Wires can be reused after making mid-circuit measurements. Moreover, a measured wire can be reset to the :math:`|0 \rangle` state by setting ``reset=True`` in :func:`~.pennylane.measure`: @@ -98,8 +102,8 @@ Note that only 7 samples are returned. This is because samples that do not meet discarded. This behaviour can be customized, see the section :ref:`"Configuring mid-circuit measurements" `. -Conditional operators / Dynamic circuits -**************************************** +Conditional operators +********************* Users can create conditional operators controlled on mid-circuit measurements using :func:`~.pennylane.cond`. The condition for a conditional operator may simply be @@ -126,7 +130,7 @@ condition based on such values and pass it to ``cond()``: For more examples, refer to the :func:`~.pennylane.cond` documentation and the :doc:`how-to on creating dynamic circuits with mid-circuit measurements -`. +`_. .. _mid_circuit_measurements_statistics: From d88d5672777191de43cf7c70a77d27f6bf4593bd Mon Sep 17 00:00:00 2001 From: dwierichs Date: Thu, 20 Jun 2024 13:14:47 +0200 Subject: [PATCH 12/15] polish --- doc/introduction/mid_circuit_measurements.rst | 38 +++++++++---------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/doc/introduction/mid_circuit_measurements.rst b/doc/introduction/mid_circuit_measurements.rst index 5a041e381de..26375f0beae 100644 --- a/doc/introduction/mid_circuit_measurements.rst +++ b/doc/introduction/mid_circuit_measurements.rst @@ -41,11 +41,9 @@ of mid-circuit measurements, as well as information about simulation strategies and how to configure them :ref:`further below `. Additional information can be found in the documentation of the individual methods. Also consider our -:doc:`Introduction to mid-circuit measurements `_, -:doc:`how-to on collecting statistics of mid-circuit measurements -`_, -and :doc:`how-to on creating dynamic circuits with mid-circuit measurements -`_. +:doc:`Introduction to mid-circuit measurements ` +:doc:`how-to on collecting statistics of mid-circuit measurements `, +and :doc:`how-to on creating dynamic circuits with mid-circuit measurements `. Resetting qubits **************** @@ -130,7 +128,7 @@ condition based on such values and pass it to ``cond()``: For more examples, refer to the :func:`~.pennylane.cond` documentation and the :doc:`how-to on creating dynamic circuits with mid-circuit measurements -`_. +`. .. _mid_circuit_measurements_statistics: @@ -222,7 +220,7 @@ Collecting statistics for sequences of mid-circuit measurements is supported wit .. warning:: When collecting statistics for a sequence of mid-circuit measurements, the - sequence may not contain arithmetic expressions. + sequence must not contain arithmetic expressions. .. _simulation_techniques: @@ -261,17 +259,19 @@ scalings with respect to the number of mid-circuit measurements (and shots) are .. rst-class:: tb -+--------------------------+-------------------------------------------+-----------------------------------------------------------+-------------------------------------------+----------------------------------+ -| **Simulation technique** | **Memory** | **Time** | **Differentiation support** | **Supports shots/analytic mode** | -+==========================+===========================================+===========================================================+===========================================+==================================+ -| Deferred measurements | :rd:`\ ` :math:`\mathcal{O}(2^{n_{MCM}})` | :rd:`\ ` :math:`\mathcal{O}(2^{n_{MCM}})` | :gr:`\ ` yes | :gr:`\ ` yes / yes | -+--------------------------+-------------------------------------------+-----------------------------------------------------------+-------------------------------------------+----------------------------------+ -| Dynamic one-shot | :gr:`\ ` :math:`\mathcal{O}(1)` | :rd:`\ ` :math:`\mathcal{O}(n_{shots})` | :or:`\ ` finite differences\ :math:`{}^1` | :or:`\ ` yes / no | -+--------------------------+-------------------------------------------+-----------------------------------------------------------+-------------------------------------------+----------------------------------+ -| Tree-traversal | :or:`\ ` :math:`\mathcal{O}(n_{MCM}+1)` | :or:`\ ` :math:`\mathcal{O}(min(n_{shots}, 2^{n_{MCM}}))` | :or:`\ ` finite differences\ :math:`{}^1` | :or:`\ ` yes / no | -+--------------------------+-------------------------------------------+-----------------------------------------------------------+-------------------------------------------+----------------------------------+ - -:math:`{}^1` In principle, parameter-shift differentiation is supported as long as no ++--------------------------+-------------------------------------------+-----------------------------------------------------------+-------------------------------------------+--------------------+ +| **Simulation technique** | **Memory** | **Time** | **Differentiation** | **shots/analytic** | ++==========================+===========================================+===========================================================+===========================================+====================+ +| Deferred measurements | :rd:`\ ` :math:`\mathcal{O}(2^{n_{MCM}})` | :rd:`\ ` :math:`\mathcal{O}(2^{n_{MCM}})` | :gr:`\ ` yes \ :math:`{}^1` | :gr:`\ ` yes / yes | ++--------------------------+-------------------------------------------+-----------------------------------------------------------+-------------------------------------------+--------------------+ +| Dynamic one-shot | :gr:`\ ` :math:`\mathcal{O}(1)` | :rd:`\ ` :math:`\mathcal{O}(n_{shots})` | :or:`\ ` finite differences\ :math:`{}^2` | :or:`\ ` yes / no | ++--------------------------+-------------------------------------------+-----------------------------------------------------------+-------------------------------------------+--------------------+ +| Tree-traversal | :or:`\ ` :math:`\mathcal{O}(n_{MCM}+1)` | :or:`\ ` :math:`\mathcal{O}(min(n_{shots}, 2^{n_{MCM}}))` | :or:`\ ` finite differences\ :math:`{}^2` | :or:`\ ` yes / no | ++--------------------------+-------------------------------------------+-----------------------------------------------------------+-------------------------------------------+--------------------+ + +:math:`{}^1` Backpropagation and finite differences are fully supported. The adjoint method +and the parameter-shift rule are supported if no postselection is used. +:math:`{}^2` In principle, parameter-shift differentiation is supported as long as no postselection is used. Parameters within conditionally applied operations will fall back to finite differences, so a proper value for ``h`` should be provided (see :func:`~.pennylane.gradients.finite_diff`). @@ -435,7 +435,7 @@ mid-circuit measurements in PennyLane. They can be configured when initializing return qml.sample(qml.PauliZ(0)) fill_shots = qml.QNode(circ, dev, mcm_method="one-shot", postselect_mode="fill-shots") - hw_like= qml.QNode(circ, dev, mcm_method="one-shot", postselect_mode="hw-like") + hw_like = qml.QNode(circ, dev, mcm_method="one-shot", postselect_mode="hw-like") .. code-block:: pycon From 98a94e81e7fdf64391157bd4c26d968fe287c5af Mon Sep 17 00:00:00 2001 From: dwierichs Date: Thu, 20 Jun 2024 21:33:19 +0200 Subject: [PATCH 13/15] glossary links --- doc/development/guide/architecture.rst | 2 +- doc/index.rst | 2 +- doc/introduction/interfaces.rst | 11 +- doc/introduction/mid_circuit_measurements.rst | 460 ------------------ 4 files changed, 8 insertions(+), 467 deletions(-) delete mode 100644 doc/introduction/mid_circuit_measurements.rst diff --git a/doc/development/guide/architecture.rst b/doc/development/guide/architecture.rst index adced2325cb..53fbe1beb07 100644 --- a/doc/development/guide/architecture.rst +++ b/doc/development/guide/architecture.rst @@ -8,7 +8,7 @@ PennyLane's core feature is the ability to compute gradients of variational quantum circuits in a way that is compatible with classical techniques such as backpropagation. PennyLane thus extends the automatic differentiation algorithms common in optimization and machine learning to include quantum and -:doc:`hybrid computations `. +`hybrid computations `_. A plugin system makes the framework compatible with many quantum simulators or hardware devices, remote or local. diff --git a/doc/index.rst b/doc/index.rst index e9cac0afbc7..04b52d62405 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -159,7 +159,7 @@ PennyLane is **free** and **open source**, released under the Apache License, Ve introduction/interfaces introduction/operations introduction/measurements - introduction/mid_circuit_measurements + introduction/dynamic_quantum_circuits introduction/templates introduction/inspecting_circuits introduction/compiling_circuits diff --git a/doc/introduction/interfaces.rst b/doc/introduction/interfaces.rst index 5a3bd1e6fc2..7eb3d391600 100644 --- a/doc/introduction/interfaces.rst +++ b/doc/introduction/interfaces.rst @@ -7,7 +7,8 @@ Gradients and training ====================== PennyLane offers seamless integration between classical and quantum computations. Code up quantum -circuits in PennyLane, compute :doc:`gradients of quantum circuits `, and +circuits in PennyLane, compute `gradients of quantum circuits +`_, and connect them easily to the top scientific computing and machine learning libraries. Training and interfaces @@ -182,8 +183,8 @@ The interface between PennyLane and automatic differentiation libraries relies o to compute or estimate gradients of quantum circuits. There are different strategies to do so, and they may depend on the device used. -When creating a QNode, you can specify the :doc:`differentiation method -` like this: +When creating a QNode, you can specify the `differentiation method +`_ like this: .. code-block:: python @@ -230,8 +231,8 @@ However, when using a simulator, you may notice that the number of circuit execu compute the gradients with these methods :doc:`scales linearly ` with the number of trainable circuit parameters. -* ``"parameter-shift"``: Use the analytic :doc:`parameter-shift rule - ` for all supported quantum operation arguments, with +* ``"parameter-shift"``: Use the analytic `parameter-shift rule + `_ for all supported quantum operation arguments, with finite-difference as a fallback. * ``"finite-diff"``: Use numerical finite-differences for all quantum operation arguments. diff --git a/doc/introduction/mid_circuit_measurements.rst b/doc/introduction/mid_circuit_measurements.rst deleted file mode 100644 index 26375f0beae..00000000000 --- a/doc/introduction/mid_circuit_measurements.rst +++ /dev/null @@ -1,460 +0,0 @@ -.. role:: html(raw) - :format: html - -.. _mid_circuit_measurements: - -Dynamic quantum circuits -======================== - -.. currentmodule:: pennylane.measure - -PennyLane allows using measurements in the middle of a quantum circuit. -Such measurements are called mid-circuit measurements and can be used to -shape the structure of the circuit dynamically, and to gather information -about the quantum state during the circuit execution. - -Available features ------------------- - -Mid-circuit measurements -************************ - -The function to perform a mid-circuit measurement in PennyLane is -:func:`~.pennylane.measure`, and can be used as follows: - -.. code-block:: python - - dev = qml.device("default.qubit") - - @qml.qnode(dev) - def my_qnode(x, y): - qml.RY(x, wires=0) - qml.CNOT(wires=[0, 1]) - m_0 = qml.measure(1, reset=False, postselect=None) - - qml.cond(m_0, qml.RY)(y, wires=0) - return qml.probs(wires=[0]), qml.expval(m_0) - -See the following sections for details on -:func:`~.pennylane.measure`, :func:`~.pennylane.cond`, and statistics -of mid-circuit measurements, as well as information about simulation -strategies and how to configure them :ref:`further below `. -Additional information can be found in the documentation of the individual -methods. Also consider our -:doc:`Introduction to mid-circuit measurements ` -:doc:`how-to on collecting statistics of mid-circuit measurements `, -and :doc:`how-to on creating dynamic circuits with mid-circuit measurements `. - -Resetting qubits -**************** - -Wires can be reused after making mid-circuit measurements. Moreover, a measured wire can be -reset to the :math:`|0 \rangle` state by setting ``reset=True`` in :func:`~.pennylane.measure`: - -.. code-block:: python3 - - dev = qml.device("default.qubit", wires=3) - - @qml.qnode(dev) - def func(): - qml.PauliX(1) - m_0 = qml.measure(1, reset=True) - qml.PauliX(1) - return qml.probs(wires=[1]) - -Executing this QNode: - -.. code-block:: pycon - - >>> func() - tensor([0., 1.], requires_grad=True) - -Postselecting mid-circuit measurements -************************************** - -PennyLane also supports postselecting on mid-circuit measurement outcomes by specifying the -``postselect`` keyword argument of :func:`~.pennylane.measure`. By default, postselection -discards outcomes that do not match the ``postselect`` argument. -For example, specifying ``postselect=1`` is equivalent to projecting the state vector onto -the :math:`|1\rangle` state, i.e., disregarding all outcomes where :math:`|0\rangle` is measured: - -.. code-block:: python3 - - dev = qml.device("default.qubit") - - @qml.qnode(dev) - def func(x): - qml.RX(x, wires=0) - m_0 = qml.measure(0, postselect=1) - return qml.sample(wires=0) - -By postselecting on ``1``, we only consider results that measured the outcome ``1``. -Executing this QNode with 10 shots yields - -.. code-block:: pycon - - >>> func(np.pi / 2, shots=10) - array([1, 1, 1, 1, 1, 1, 1]) - -Note that only 7 samples are returned. This is because samples that do not meet the postselection criteria are -discarded. This behaviour can be customized, see the section -:ref:`"Configuring mid-circuit measurements" `. - -Conditional operators -********************* - -Users can create conditional operators controlled on mid-circuit measurements using -:func:`~.pennylane.cond`. The condition for a conditional operator may simply be -the measured value returned by a ``measure()`` call, or we may construct a boolean -condition based on such values and pass it to ``cond()``: - -.. code-block:: python - - @qml.qnode(dev) - def qnode_conditional_op_on_zero(x, y): - qml.RY(x, wires=0) - qml.CNOT(wires=[0, 1]) - m_0 = qml.measure(1) - - qml.cond(m_0 == 0, qml.RY)(y, wires=0) - return qml.probs(wires=[0]) - - pars = np.array([0.643, 0.246], requires_grad=True) - -.. code-block:: pycon - - >>> qnode_conditional_op_on_zero(*pars) - tensor([0.88660045, 0.11339955], requires_grad=True) - -For more examples, refer to the :func:`~.pennylane.cond` documentation -and the :doc:`how-to on creating dynamic circuits with mid-circuit measurements -`. - -.. _mid_circuit_measurements_statistics: - -Mid-circuit measurement statistics -********************************** - -Statistics of mid-circuit measurements can be collected along with terminal measurement statistics. -Currently, :func:`~.counts`, :func:`~.expval`, :func:`~.probs`, :func:`~.sample`, and :func:`~.var` -are supported. - -.. code-block:: python3 - - dev = qml.device("default.qubit", wires=2) - - @qml.qnode(dev) - def func(x, y): - qml.RX(x, wires=0) - m_0 = qml.measure(0) - qml.cond(m_0, qml.RY)(y, wires=1) - return qml.probs(wires=1), qml.probs(op=m_0) - -Executing this ``QNode``: - -.. code-block:: pycon - - >>> func(np.pi / 2, np.pi / 4) - (tensor([0.9267767, 0.0732233], requires_grad=True), - tensor([0.5, 0.5], requires_grad=True)) - -Users can also collect statistics on mid-circuit measurements manipulated using arithmetic/boolean operators. -This works for both unary and binary operators. To see a full list of supported operators, refer to the -:func:`~.pennylane.measure` documentation. An example for collecting such statistics is shown below: - -.. code-block:: python3 - - import pennylane as qml - - dev = qml.device("default.qubit") - - @qml.qnode(dev) - def circuit(phi, theta): - qml.RX(phi, wires=0) - m_0 = qml.measure(wires=0) - qml.RY(theta, wires=1) - m_1 = qml.measure(wires=1) - return qml.sample(~m_0 - 2 * m_1) - -Executing this ``QNode``: - -.. code-block:: pycon - - >>> circuit(1.23, 4.56, shots=5) - array([-1, -2, 1, -1, 1]) - -Collecting statistics for mid-circuit measurements manipulated using arithmetic/boolean operators is supported -with :func:`~.counts`, :func:`~.expval`, :func:`~.sample`, and :func:`~.var`. - -Moreover, statistics for multiple mid-circuit measurements can be collected by passing lists of mid-circuit -measurement values to the measurement process: - -.. code-block:: python3 - - import pennylane as qml - - dev = qml.device("default.qubit") - - @qml.qnode(dev) - def circuit(phi, theta): - qml.RX(phi, wires=0) - m_0 = qml.measure(wires=0) - qml.RY(theta, wires=1) - m_1 = qml.measure(wires=1) - return qml.sample([m_0, m_1]) - -Executing this ``QNode``: - -.. code-block:: pycon - - >>> circuit(1.23, 4.56, shots=5) - array([[0, 1], - [1, 1], - [0, 1], - [0, 0], - [1, 1]]) - -Collecting statistics for sequences of mid-circuit measurements is supported with -:func:`~.counts`, :func:`~.probs`, and :func:`~.sample`. - -.. warning:: - - When collecting statistics for a sequence of mid-circuit measurements, the - sequence must not contain arithmetic expressions. - -.. _simulation_techniques: - -Simulation techniques ---------------------- - -PennyLane currently offers three methods to simulate mid-circuit measurements -on classical computers: the deferred measurements principle, dynamic one-shot -sampling, and a tree-traversal approach. These methods differ in their memory requirements -and computational cost, as well as their compatibility with other features such as -shots and differentiation methods. -While the requirements depend on details of the simulation, the expected -scalings with respect to the number of mid-circuit measurements (and shots) are - -.. role:: gr -.. role:: or -.. role:: rd - -.. raw:: html - - - - - -.. rst-class:: tb - -+--------------------------+-------------------------------------------+-----------------------------------------------------------+-------------------------------------------+--------------------+ -| **Simulation technique** | **Memory** | **Time** | **Differentiation** | **shots/analytic** | -+==========================+===========================================+===========================================================+===========================================+====================+ -| Deferred measurements | :rd:`\ ` :math:`\mathcal{O}(2^{n_{MCM}})` | :rd:`\ ` :math:`\mathcal{O}(2^{n_{MCM}})` | :gr:`\ ` yes \ :math:`{}^1` | :gr:`\ ` yes / yes | -+--------------------------+-------------------------------------------+-----------------------------------------------------------+-------------------------------------------+--------------------+ -| Dynamic one-shot | :gr:`\ ` :math:`\mathcal{O}(1)` | :rd:`\ ` :math:`\mathcal{O}(n_{shots})` | :or:`\ ` finite differences\ :math:`{}^2` | :or:`\ ` yes / no | -+--------------------------+-------------------------------------------+-----------------------------------------------------------+-------------------------------------------+--------------------+ -| Tree-traversal | :or:`\ ` :math:`\mathcal{O}(n_{MCM}+1)` | :or:`\ ` :math:`\mathcal{O}(min(n_{shots}, 2^{n_{MCM}}))` | :or:`\ ` finite differences\ :math:`{}^2` | :or:`\ ` yes / no | -+--------------------------+-------------------------------------------+-----------------------------------------------------------+-------------------------------------------+--------------------+ - -:math:`{}^1` Backpropagation and finite differences are fully supported. The adjoint method -and the parameter-shift rule are supported if no postselection is used. -:math:`{}^2` In principle, parameter-shift differentiation is supported as long as no -postselection is used. Parameters within conditionally applied operations will -fall back to finite differences, so a proper value for ``h`` should be provided (see -:func:`~.pennylane.gradients.finite_diff`). - -The strengths and weaknesses of the simulation techniques differ strongly and the best -technique will depend on details of the simulation workflow. As a rule of thumb: - -- dynamic one-shot sampling excels in the many-measurements-few-shots regime, - -- the tree-traversal technique can handle large-scale simulations with many shots - and measurements, and - -- deferred measurements are the generalist solution that enables mid-circuit measurement - support under (almost) all circumstances, but at large memory cost. It is the only method - supporting analytic simulations. - -By default, ``QNode``\ s use deferred measurements and dynamic one-shot sampling (if supported) -when executed without and with shots, respectively. The method can be configured with -the keyword argument ``mcm_method`` at ``QNode`` creation -(see :ref:`"Configuring mid-circuit measurements" `). - -.. _deferred_measurements: - -Deferred measurements -********************* - -A quantum function with mid-circuit measurements can be executed via the -`deferred measurement principle `__. -In PennyLane, this technique is available via ``mcm_method="deferred"`` or as the -transform :func:`~.pennylane.defer_measurements`. - -The deferred measurement principle provides a powerful method to simulate -mid-circuit measurements, conditional operations and measurement statistics -in a differentiable and device-independent way. It adds an auxiliary qubit -to the circuit for each mid-circuit measurement, leading to overheads of both -memory and simulation time that scale exponentially with the number of measurements. - -.. code-block:: pycon - - >>> deferred_qnode = qml.defer_measurements(my_qnode) - >>> pars = np.array([0.643, 0.246]) - >>> deferred_qnode(*pars) - (tensor([0.90165331, 0.09834669], requires_grad=True), - tensor(0.09984972, requires_grad=True)) - -The effect of deferring measurements becomes clear if we draw the ``QNode`` -before and after applying the transform: - -.. code-block:: pycon - - >>> qml.draw(my_qnode)(*pars) - 0: ──RY(0.64)─╭●───────RY(0.25)─┤ Probs - 1: ───────────╰X──┤↗├──║────────┤ - ╚═══╩════════╡ - >>> qml.draw(deferred_qnode)(*pars) - 0: ──RY(0.64)─╭●────╭RY(0.25)─┤ Probs - 1: ───────────╰X─╭●─│─────────┤ - 2: ──────────────╰X─╰●────────┤ - -Mid-circuit measurements are deferred to the end of the circuit, and conditionally applied -operations become (quantumly) controlled operations. - -.. note:: - - This method requires an additional qubit for each mid-circuit measurement, which limits - the number of measurements that can be used both on classical simulators and quantum hardware. - - Postselection with deferred measurements is only supported on - :class:`~.pennylane.devices.DefaultQubit`. - - -.. _one_shot_transform: - -Dynamic one-shot sampling -************************* - -Devices that natively support mid-circuit measurements can evaluate dynamic circuits -by executing them one shot at a time, sampling a dynamic execution path for each shot. - -In PennyLane, this technique is available via the QNode argument ``mcm_method="one-shot"`` -or as the transform :func:`~.pennylane.dynamic_one_shot`. -As the name suggests, this transform only works for a :class:`~.pennylane.QNode` executing -with finite shots and it requires the device to support mid-circuit measurements natively. - -The :func:`~.pennylane.dynamic_one_shot` transform is usually advantageous compared -with the :func:`~.pennylane.defer_measurements` transform in the -many-mid-circuit-measurements and few-shots limit. This is because, unlike the -deferred measurement principle, the method does not need an additional wire for every -mid-circuit measurement in the circuit. - -.. warning:: - - Dynamic circuits executed with shots should be differentiated with the finite difference method. - -.. _tree_traversal: - -Tree-traversal algorithm -************************ - -Dynamic circuit execution is akin to traversing a binary tree where each mid-circuit measurement -corresponds to a node and gates between them correspond to edges. The tree-traversal algorithm -explores this tree depth-first. It improves upon the dynamic one-shot approach above, which -simulates a randomly chosen branch from beginning to end for each shot, by collecting all -samples at a node or leaf at once. - -In PennyLane, this technique is available via the QNode argument ``mcm_method="tree-traversal"``; -it is not a transform. - -The tree-traversal algorithm combines the exponential savings of memory of the one-shot -approach with sampling efficiency of deferred measurements. -Neglecting overheads, simulating all branches requires the same -amount of computations as :func:`~.pennylane.defer_measurements`, but without the -:math:`O(2^{n_{MCM}})` memory cost. To save time, a copy of the state vector -is made at every mid-circuit measurement, requiring :math:`n_{MCM}+1` state -vectors, an exponential improvement over :func:`~.pennylane.defer_measurements`. -Since the counts of many nodes come out to be zero for shot-based simulations, -it is often possible to ignore entire sub-trees, thereby reducing the computational -cost. - -.. warning:: - - The tree-traversal algorithm is only supported by the - :class:`~.pennylane.devices.DefaultQubit` device, and currently does - not support just-in-time (JIT) compilation. - -.. _mcm_config: - -Configuring mid-circuit measurements -************************************ - -As described above, there are multiple simulation techniques for circuits with -mid-circuit measurements in PennyLane. They can be configured when initializing a -:class:`~pennylane.QNode`, using the following keywords: - -* ``mcm_method``: Sets the method used for applying mid-circuit measurements. The options are - ``"deferred"``, ``"one-shot"``, and ``"tree-traversal"`` for the three techniques described above. - The default is ``mcm_method="one-shot"`` when executing with shots, and ``"deferred"`` otherwise. - When using :func:`~pennylane.qjit`, there is the additional (default) option - ``mcm_method="single-branch-statistics"``, which explores a single branch of the execution - tree at random. - - .. warning:: - - If the ``mcm_method`` argument is provided, the transforms for deferred measurements - or dynamic one-shot sampling must not be applied manually to the :class:`~pennylane.QNode`. - -* ``postselect_mode``: Configures how invalid shots are handled when postselecting - mid-circuit measurements with finite-shot circuits. Use ``"hw-like"`` to discard invalid samples. - In this case, fewer than the total number of shots may be used to process results. Use - ``"fill-shots"`` to sample the postselected value unconditionally, creating valid samples - only. This is equivalent to sampling until the number of valid - samples matches the total number of shots. The default is ``"hw-like"``. - - .. code-block:: python3 - - dev = qml.device("default.qubit", wires=3, shots=10) - - def circ(): - qml.Hadamard(0) - m_0 = qml.measure(0, postselect=1) - return qml.sample(qml.PauliZ(0)) - - fill_shots = qml.QNode(circ, dev, mcm_method="one-shot", postselect_mode="fill-shots") - hw_like = qml.QNode(circ, dev, mcm_method="one-shot", postselect_mode="hw-like") - - .. code-block:: pycon - - >>> fill_shots() - array([-1., -1., -1., -1., -1., -1., -1., -1., -1., -1.]) - >>> hw_like() - array([-1., -1., -1., -1., -1., -1., -1.]) - - .. note:: - - When using the ``jax`` interface, the postselection mode ``"hw-like"`` will change - behaviour with the simulation technique. - - * For dynamic one-shot, invalid shots will not be discarded, but will be replaced - by ``np.iinfo(np.int32).min``. They will not be used for processing final results (like - expectation values), but they will appear in the output of ``QNode``\ s that return - samples directly. - - * When using ``jax.jit``, the combination ``"deferred"`` and ``"hw-like"`` is not supported, - due to limitations of the :func:`~pennylane.defer_measurements` transform. This behaviour - will change in the future. - From 4773bd27d8bf68a63e249eb7910a8cc38f2564a9 Mon Sep 17 00:00:00 2001 From: dwierichs Date: Thu, 20 Jun 2024 21:34:01 +0200 Subject: [PATCH 14/15] add file --- doc/introduction/dynamic_quantum_circuits.rst | 461 ++++++++++++++++++ 1 file changed, 461 insertions(+) create mode 100644 doc/introduction/dynamic_quantum_circuits.rst diff --git a/doc/introduction/dynamic_quantum_circuits.rst b/doc/introduction/dynamic_quantum_circuits.rst new file mode 100644 index 00000000000..a1b4b07655c --- /dev/null +++ b/doc/introduction/dynamic_quantum_circuits.rst @@ -0,0 +1,461 @@ +.. role:: html(raw) + :format: html + +.. _mid_circuit_measurements: + +Dynamic quantum circuits +======================== + +.. currentmodule:: pennylane.measure + +PennyLane allows using measurements in the middle of a quantum circuit. +Such measurements are called mid-circuit measurements and can be used to +shape the structure of the circuit dynamically, and to gather information +about the quantum state during the circuit execution. + +Available features +------------------ + +Mid-circuit measurements +************************ + +The function to perform a mid-circuit measurement in PennyLane is +:func:`~.pennylane.measure`, and can be used as follows: + +.. code-block:: python + + dev = qml.device("default.qubit") + + @qml.qnode(dev) + def my_qnode(x, y): + qml.RY(x, wires=0) + qml.CNOT(wires=[0, 1]) + m_0 = qml.measure(1, reset=False, postselect=None) + + qml.cond(m_0, qml.RY)(y, wires=0) + return qml.probs(wires=[0]), qml.expval(m_0) + +See the following sections for details on +:func:`~.pennylane.measure`, :func:`~.pennylane.cond`, and statistics +of mid-circuit measurements, as well as information about simulation +strategies and how to configure them :ref:`further below `. +Additional information can be found in the documentation of the individual +methods. Also consider our +:doc:`Introduction to mid-circuit measurements ` +:doc:`how-to on collecting statistics of mid-circuit measurements `, +and :doc:`how-to on creating dynamic circuits with mid-circuit measurements `. + +Resetting qubits +**************** + +Wires can be reused after making mid-circuit measurements. Moreover, a measured wire can be +reset to the :math:`|0 \rangle` state by setting ``reset=True`` in :func:`~.pennylane.measure`: + +.. code-block:: python3 + + dev = qml.device("default.qubit", wires=3) + + @qml.qnode(dev) + def func(): + qml.PauliX(1) + m_0 = qml.measure(1, reset=True) + qml.PauliX(1) + return qml.probs(wires=[1]) + +Executing this QNode: + +.. code-block:: pycon + + >>> func() + tensor([0., 1.], requires_grad=True) + +Postselecting mid-circuit measurements +************************************** + +PennyLane also supports postselecting on mid-circuit measurement outcomes by specifying the +``postselect`` keyword argument of :func:`~.pennylane.measure`. By default, postselection +discards outcomes that do not match the ``postselect`` argument. +For example, specifying ``postselect=1`` is equivalent to projecting the state vector onto +the :math:`|1\rangle` state, i.e., disregarding all outcomes where :math:`|0\rangle` is measured: + +.. code-block:: python3 + + dev = qml.device("default.qubit") + + @qml.qnode(dev) + def func(x): + qml.RX(x, wires=0) + m_0 = qml.measure(0, postselect=1) + return qml.sample(wires=0) + +By postselecting on ``1``, we only consider results that measured the outcome ``1``. +Executing this QNode with 10 shots yields + +.. code-block:: pycon + + >>> func(np.pi / 2, shots=10) + array([1, 1, 1, 1, 1, 1, 1]) + +Note that only 7 samples are returned. This is because samples that do not meet the postselection criteria are +discarded. This behaviour can be customized, see the section +:ref:`"Configuring mid-circuit measurements" `. + +Conditional operators +********************* + +Users can create conditional operators controlled on mid-circuit measurements using +:func:`~.pennylane.cond`. The condition for a conditional operator may simply be +the measured value returned by a :func:`~.pennylane.measure` call, or we may construct a boolean +condition based on such values and pass it to :func:`~.pennylane.cond`: + +.. code-block:: python + + @qml.qnode(dev) + def qnode_conditional_op_on_zero(x, y): + qml.RY(x, wires=0) + qml.CNOT(wires=[0, 1]) + m_0 = qml.measure(1) + + qml.cond(m_0 == 0, qml.RY)(y, wires=0) + return qml.probs(wires=[0]) + + pars = np.array([0.643, 0.246], requires_grad=True) + +.. code-block:: pycon + + >>> qnode_conditional_op_on_zero(*pars) + tensor([0.88660045, 0.11339955], requires_grad=True) + +For more examples, refer to the :func:`~.pennylane.cond` documentation +and the :doc:`how-to on creating dynamic circuits with mid-circuit measurements +`. + +.. _mid_circuit_measurements_statistics: + +Mid-circuit measurement statistics +********************************** + +Statistics of mid-circuit measurements can be collected along with terminal measurement statistics. +Currently, :func:`~.counts`, :func:`~.expval`, :func:`~.probs`, :func:`~.sample`, and :func:`~.var` +are supported. + +.. code-block:: python3 + + dev = qml.device("default.qubit", wires=2) + + @qml.qnode(dev) + def func(x, y): + qml.RX(x, wires=0) + m_0 = qml.measure(0) + qml.cond(m_0, qml.RY)(y, wires=1) + return qml.probs(wires=1), qml.probs(op=m_0) + +Executing this ``QNode``: + +.. code-block:: pycon + + >>> func(np.pi / 2, np.pi / 4) + (tensor([0.9267767, 0.0732233], requires_grad=True), + tensor([0.5, 0.5], requires_grad=True)) + +Users can also collect statistics on mid-circuit measurements manipulated using arithmetic/boolean operators. +This works for both unary and binary operators. To see a full list of supported operators, refer to the +:func:`~.pennylane.measure` documentation. An example for collecting such statistics is shown below: + +.. code-block:: python3 + + import pennylane as qml + + dev = qml.device("default.qubit") + + @qml.qnode(dev) + def circuit(phi, theta): + qml.RX(phi, wires=0) + m_0 = qml.measure(wires=0) + qml.RY(theta, wires=1) + m_1 = qml.measure(wires=1) + return qml.sample(~m_0 - 2 * m_1) + +Executing this ``QNode``: + +.. code-block:: pycon + + >>> circuit(1.23, 4.56, shots=5) + array([-1, -2, 1, -1, 1]) + +Collecting statistics for mid-circuit measurements manipulated using arithmetic/boolean operators is supported +with :func:`~.counts`, :func:`~.expval`, :func:`~.sample`, and :func:`~.var`. + +Moreover, statistics for multiple mid-circuit measurements can be collected by passing lists of mid-circuit +measurement values to the measurement process: + +.. code-block:: python3 + + import pennylane as qml + + dev = qml.device("default.qubit") + + @qml.qnode(dev) + def circuit(phi, theta): + qml.RX(phi, wires=0) + m_0 = qml.measure(wires=0) + qml.RY(theta, wires=1) + m_1 = qml.measure(wires=1) + return qml.sample([m_0, m_1]) + +Executing this ``QNode``: + +.. code-block:: pycon + + >>> circuit(1.23, 4.56, shots=5) + array([[0, 1], + [1, 1], + [0, 1], + [0, 0], + [1, 1]]) + +Collecting statistics for sequences of mid-circuit measurements is supported with +:func:`~.counts`, :func:`~.probs`, and :func:`~.sample`. + +.. warning:: + + When collecting statistics for a sequence of mid-circuit measurements, the + sequence must not contain arithmetic expressions. + +.. _simulation_techniques: + +Simulation techniques +--------------------- + +PennyLane currently offers three methods to simulate mid-circuit measurements +on classical computers: the deferred measurements principle, dynamic one-shot +sampling, and a tree-traversal approach. These methods differ in their memory requirements +and computational cost, as well as their compatibility with other features such as +shots and differentiation methods. +While the requirements depend on details of the simulation, the expected +scalings with respect to the number of mid-circuit measurements (and shots) are + +.. role:: gr +.. role:: or +.. role:: rd + +.. raw:: html + + + + + +.. rst-class:: tb + ++--------------------------+-------------------------------------------+-----------------------------------------------------------+-------------------------------------------+--------------------+ +| **Simulation technique** | **Memory** | **Time** | **Differentiation** | **shots/analytic** | ++==========================+===========================================+===========================================================+===========================================+====================+ +| Deferred measurements | :rd:`\ ` :math:`\mathcal{O}(2^{n_{MCM}})` | :rd:`\ ` :math:`\mathcal{O}(2^{n_{MCM}})` | :gr:`\ ` yes \ :math:`{}^1` | :gr:`\ ` yes / yes | ++--------------------------+-------------------------------------------+-----------------------------------------------------------+-------------------------------------------+--------------------+ +| Dynamic one-shot | :gr:`\ ` :math:`\mathcal{O}(1)` | :rd:`\ ` :math:`\mathcal{O}(n_{shots})` | :or:`\ ` finite differences\ :math:`{}^2` | :or:`\ ` yes / no | ++--------------------------+-------------------------------------------+-----------------------------------------------------------+-------------------------------------------+--------------------+ +| Tree-traversal | :or:`\ ` :math:`\mathcal{O}(n_{MCM}+1)` | :or:`\ ` :math:`\mathcal{O}(min(n_{shots}, 2^{n_{MCM}}))` | :or:`\ ` finite differences\ :math:`{}^2` | :or:`\ ` yes / no | ++--------------------------+-------------------------------------------+-----------------------------------------------------------+-------------------------------------------+--------------------+ + +:math:`{}^1` Backpropagation and finite differences are fully supported. The adjoint method +and the parameter-shift rule are supported if no postselection is used. + +:math:`{}^2` In principle, parameter-shift differentiation is supported as long as no +postselection is used. Parameters within conditionally applied operations will +fall back to finite differences, so a proper value for ``h`` should be provided (see +:func:`~.pennylane.gradients.finite_diff`). + +The strengths and weaknesses of the simulation techniques differ strongly and the best +technique will depend on details of the simulation workflow. As a rule of thumb: + +- dynamic one-shot sampling excels in the many-measurements-few-shots regime, + +- the tree-traversal technique can handle large-scale simulations with many shots + and measurements, and + +- deferred measurements are the generalist solution that enables mid-circuit measurement + support under (almost) all circumstances, but at large memory cost. It is the only method + supporting analytic simulations. + +By default, ``QNode``\ s use deferred measurements and dynamic one-shot sampling (if supported) +when executed without and with shots, respectively. The method can be configured with +the keyword argument ``mcm_method`` at ``QNode`` creation +(see :ref:`"Configuring mid-circuit measurements" `). + +.. _deferred_measurements: + +Deferred measurements +********************* + +A quantum function with mid-circuit measurements can be executed via the +`deferred measurement principle `__. +In PennyLane, this technique is available via ``mcm_method="deferred"`` or as the +transform :func:`~.pennylane.defer_measurements`. + +The deferred measurement principle provides a powerful method to simulate +mid-circuit measurements, conditional operations and measurement statistics +in a differentiable and device-independent way. It adds an auxiliary qubit +to the circuit for each mid-circuit measurement, leading to overheads of both +memory and simulation time that scale exponentially with the number of measurements. + +.. code-block:: pycon + + >>> deferred_qnode = qml.defer_measurements(my_qnode) + >>> pars = np.array([0.643, 0.246]) + >>> deferred_qnode(*pars) + (tensor([0.90165331, 0.09834669], requires_grad=True), + tensor(0.09984972, requires_grad=True)) + +The effect of deferring measurements becomes clear if we draw the ``QNode`` +before and after applying the transform: + +.. code-block:: pycon + + >>> qml.draw(my_qnode)(*pars) + 0: ──RY(0.64)─╭●───────RY(0.25)─┤ Probs + 1: ───────────╰X──┤↗├──║────────┤ + ╚═══╩════════╡ + >>> qml.draw(deferred_qnode)(*pars) + 0: ──RY(0.64)─╭●────╭RY(0.25)─┤ Probs + 1: ───────────╰X─╭●─│─────────┤ + 2: ──────────────╰X─╰●────────┤ + +Mid-circuit measurements are deferred to the end of the circuit, and conditionally applied +operations become (quantumly) controlled operations. + +.. note:: + + This method requires an additional qubit for each mid-circuit measurement, which limits + the number of measurements that can be used both on classical simulators and quantum hardware. + + Postselection with deferred measurements is only supported on + :class:`~.pennylane.devices.DefaultQubit`. + + +.. _one_shot_transform: + +Dynamic one-shot sampling +************************* + +Devices that natively support mid-circuit measurements can evaluate dynamic circuits +by executing them one shot at a time, sampling a dynamic execution path for each shot. + +In PennyLane, this technique is available via the QNode argument ``mcm_method="one-shot"`` +or as the transform :func:`~.pennylane.dynamic_one_shot`. +As the name suggests, this transform only works for a :class:`~.pennylane.QNode` executing +with finite shots and it requires the device to support mid-circuit measurements natively. + +The :func:`~.pennylane.dynamic_one_shot` transform is usually advantageous compared +with the :func:`~.pennylane.defer_measurements` transform in the +many-mid-circuit-measurements and few-shots limit. This is because, unlike the +deferred measurement principle, the method does not need an additional wire for every +mid-circuit measurement in the circuit. + +.. warning:: + + Dynamic circuits executed with shots should be differentiated with the finite difference method. + +.. _tree_traversal: + +Tree-traversal algorithm +************************ + +Dynamic circuit execution is akin to traversing a binary tree where each mid-circuit measurement +corresponds to a node and gates between them correspond to edges. The tree-traversal algorithm +explores this tree depth-first. It improves upon the dynamic one-shot approach above, which +simulates a randomly chosen branch from beginning to end for each shot, by collecting all +samples at a node or leaf at once. + +In PennyLane, this technique is available via the QNode argument ``mcm_method="tree-traversal"``; +it is not a transform. + +The tree-traversal algorithm combines the exponential savings of memory of the one-shot +approach with sampling efficiency of deferred measurements. +Neglecting overheads, simulating all branches requires the same +amount of computations as :func:`~.pennylane.defer_measurements`, but without the +:math:`O(2^{n_{MCM}})` memory cost. To save time, a copy of the state vector +is made at every mid-circuit measurement, requiring :math:`n_{MCM}+1` state +vectors, an exponential improvement over :func:`~.pennylane.defer_measurements`. +Since the counts of many nodes come out to be zero for shot-based simulations, +it is often possible to ignore entire sub-trees, thereby reducing the computational +cost. + +.. warning:: + + The tree-traversal algorithm is only supported by the + :class:`~.pennylane.devices.DefaultQubit` device, and currently does + not support just-in-time (JIT) compilation. + +.. _mcm_config: + +Configuring mid-circuit measurements +************************************ + +As described above, there are multiple simulation techniques for circuits with +mid-circuit measurements in PennyLane. They can be configured when initializing a +:class:`~pennylane.QNode`, using the following keywords: + +* ``mcm_method``: Sets the method used for applying mid-circuit measurements. The options are + ``"deferred"``, ``"one-shot"``, and ``"tree-traversal"`` for the three techniques described above. + The default is ``mcm_method="one-shot"`` when executing with shots, and ``"deferred"`` otherwise. + When using :func:`~pennylane.qjit`, there is the additional (default) option + ``mcm_method="single-branch-statistics"``, which explores a single branch of the execution + tree at random. + + .. warning:: + + If the ``mcm_method`` argument is provided, the transforms for deferred measurements + or dynamic one-shot sampling must not be applied manually to the :class:`~pennylane.QNode`. + +* ``postselect_mode``: Configures how invalid shots are handled when postselecting + mid-circuit measurements with finite-shot circuits. Use ``"hw-like"`` to discard invalid samples. + In this case, fewer than the total number of shots may be used to process results. Use + ``"fill-shots"`` to sample the postselected value unconditionally, creating valid samples + only. This is equivalent to sampling until the number of valid + samples matches the total number of shots. The default is ``"hw-like"``. + + .. code-block:: python3 + + dev = qml.device("default.qubit", wires=3, shots=10) + + def circ(): + qml.Hadamard(0) + m_0 = qml.measure(0, postselect=1) + return qml.sample(qml.PauliZ(0)) + + fill_shots = qml.QNode(circ, dev, mcm_method="one-shot", postselect_mode="fill-shots") + hw_like = qml.QNode(circ, dev, mcm_method="one-shot", postselect_mode="hw-like") + + .. code-block:: pycon + + >>> fill_shots() + array([-1., -1., -1., -1., -1., -1., -1., -1., -1., -1.]) + >>> hw_like() + array([-1., -1., -1., -1., -1., -1., -1.]) + + .. note:: + + When using the ``jax`` interface, the postselection mode ``"hw-like"`` will change + behaviour with the simulation technique. + + * For dynamic one-shot, invalid shots will not be discarded, but will be replaced + by ``np.iinfo(np.int32).min``. They will not be used for processing final results (like + expectation values), but they will appear in the output of ``QNode``\ s that return + samples directly. + + * When using ``jax.jit``, the combination ``"deferred"`` and ``"hw-like"`` is not supported, + due to limitations of the :func:`~pennylane.defer_measurements` transform. This behaviour + will change in the future. + From f28f9670fdae14c8bf4f6e2526662478308a0789 Mon Sep 17 00:00:00 2001 From: dwierichs Date: Thu, 20 Jun 2024 21:53:22 +0200 Subject: [PATCH 15/15] glossary ref --- pennylane/workflow/qnode.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pennylane/workflow/qnode.py b/pennylane/workflow/qnode.py index 308015faaa9..3ae456d03f8 100644 --- a/pennylane/workflow/qnode.py +++ b/pennylane/workflow/qnode.py @@ -108,8 +108,8 @@ def _to_qfunc_output_type( class QNode: """Represents a quantum node in the hybrid computational graph. - A *quantum node* contains a :ref:`quantum function ` - (corresponding to a :ref:`variational circuit `) + A *quantum node* contains a :ref:`quantum function ` (corresponding to + a `variational circuit `) and the computational device it is executed on. The QNode calls the quantum function to construct a :class:`~.QuantumTape` instance representing