Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Collection of documentation fixes for v0.40 QA #6798

Merged
merged 15 commits into from
Jan 11, 2025
Merged
Show file tree
Hide file tree
Changes from 14 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion doc/code/qml_noise.rst
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ noise-related metadata can also be supplied to construct a noise model using:
~NoiseModel

Each conditional in the ``model_map`` (and ``meas_map``) evaluates the gate operations
(and terminal measurments) in the quantum circuit based on some condition of its attributes
(and terminal measurements) in the quantum circuit based on some condition of its attributes
(e.g., type, parameters, wires, etc.) and uses the corresponding callable to apply the
noise operations, using the user-provided metadata (e.g., hardware topologies or relaxation
times), whenever the condition is true. A noise model, once built, can be attached
Expand Down
10 changes: 5 additions & 5 deletions doc/development/autograph.rst
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ Python control flow:

return qml.expval(qml.PauliZ(0) + qml.PauliZ(3))

While this function cannot be captured directly because there is control flow that depends on the function's inputs' values—the inputs are treated as JAX tracers at capture time, which don't have concrete valuesit can be captured by converting to native PennyLane syntax
While this function cannot be captured directly because there is control flow that depends on the values of the function's inputs (the inputs are treated as JAX tracers at capture time, which don't have concrete values) it can be captured by converting to native PennyLane syntax
via AutoGraph. This is the default behaviour of :func:`~.autograph.make_plxpr`.

>>> weights = jnp.linspace(-1, 1, 20).reshape([5, 4])
Expand Down Expand Up @@ -351,7 +351,7 @@ For example, using a ``for`` loop with static bounds to index a JAX array is str
However, indexing within a ``for`` loop with AutoGraph will require that the object indexed is
a JAX array or dynamic runtime variable.

If the array you are indexing within the for loop is not a JAX array
If the array you are indexing within the ``for`` loop is not a JAX array
or dynamic variable, an error will be raised:

>>> @qml.qnode(dev)
Expand All @@ -378,7 +378,7 @@ a JAX array:
>>> eval_jaxpr(plxpr.jaxpr, plxpr.consts)
[Array(0.99500417, dtype=float64)]

If the object you are indexing **cannot** be converted to a JAX array, it is not possible for AutoGraph to capture this for loop.
If the object you are indexing **cannot** be converted to a JAX array, it is not possible for AutoGraph to capture this ``for`` loop.

If you are updating elements of the array, this must be done using the JAX ``.at`` and ``.set`` syntax.

Expand Down Expand Up @@ -440,7 +440,7 @@ However, like with conditionals, a similar restriction applies: variables
which are updated across iterations of the loop must have a JAX compilable
type (Booleans, Python numeric types, and JAX arrays).

You can also utilize temporary variables within a for loop:
You can also utilize temporary variables within a ``for`` loop:

>>> def f(x):
... for y in [0, 4, 5]:
Expand Down Expand Up @@ -520,7 +520,7 @@ To allow AutoGraph conversion to work in this case, simply convert the list to a
>>> eval_jaxpr(plxpr.jaxpr, plxpr.consts)
[Array(0.99500417, dtype=float64)]

If the object you are indexing **cannot** be converted to a JAX array, it is not possible for AutoGraph to capture this for loop.
If the object you are indexing **cannot** be converted to a JAX array, it is not possible for AutoGraph to capture this ``while`` loop.

If you are updating elements of the array, this must be done using the JAX ``.at`` and ``.set`` syntax.

Expand Down
10 changes: 0 additions & 10 deletions doc/development/deprecations.rst
Original file line number Diff line number Diff line change
Expand Up @@ -47,15 +47,6 @@ Pending deprecations
- Deprecated in v0.40
- Will be removed in v0.41

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

* The ``QubitStateVector`` template is deprecated.
Instead, use ``StatePrep``.

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

* ``op.ops`` and ``op.coeffs`` for ``Sum`` and ``Prod`` will be removed in the future. Use
:meth:`~.Operator.terms` instead.

Expand All @@ -70,7 +61,6 @@ Pending deprecations
values with a bit string. In the future, it will no longer accepts strings as control values.

- Deprecated in v0.36
- Will be removed in v0.37

Completed removal of legacy operator arithmetic
-----------------------------------------------
Expand Down
18 changes: 10 additions & 8 deletions doc/development/plugins.rst
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ For example:

This execute method works in tandem with the optional :meth:`Device.preprocess_transforms <pennylane.devices.Device.preprocess_transforms>`
and :meth:`Device.setup_execution_config`, described below in more detail. Preprocessing transforms
turns generic circuits into ones supported by the device, or raises an error if the circuit is invalid.
turns generic circuits into ones supported by the device or raises an error if the circuit is invalid.
Execution produces numerical results from those supported circuits.

In a more minimal example, for any initial batch of quantum tapes and a config object, we expect to be able to do:
Expand Down Expand Up @@ -376,7 +376,7 @@ Wires
Devices can now either:

1) Strictly use wires provided by the user on initialization: ``device(name, wires=wires)``
2) Infer the number and ordering of wires provided by the submitted circuit.
2) Infer the number and order of wires provided by the submitted circuit
3) Strictly require specific wire labels

Option 2 allows workflows to change the number and labeling of wires over time, but sometimes users want
Expand Down Expand Up @@ -434,18 +434,20 @@ The execution config stores two kinds of information:

Device options are any device specific options used to configure the behavior of an execution. For
example, ``default.qubit`` has ``max_workers``, ``rng``, and ``prng_key``. ``default.tensor`` has
``contract``, ``cutoff``, ``dtype``, ``method``, and ``max_bond_dim``. These options are often set
``contract``, ``contraction_optimizer``, ``cutoff``, ``c_dtype``, ``local_simplify``, ``method``, and ``max_bond_dim``. These options are often set
with default values on initialization. These values should be placed into the ``ExecutionConfig.device_options``
dictionary in :meth:`~.devices.Device.setup_execution_config`. Note that we do provide a default
implementation of this method, but you will most likely need to override it yourself.

>>> dev = qml.device('default.tensor', wires=2, max_bond_dim=4, contract="nonlocal", dtype=np.complex64)
>>> dev = qml.device('default.tensor', wires=2, max_bond_dim=4, contract="nonlocal", c_dtype=np.complex64)
>>> dev.setup_execution_config().device_options
{'contract': 'nonlocal',
'cutoff': 1.1920929e-07,
'dtype': numpy.complex64,
'method': 'mps',
'max_bond_dim': 4}
'contraction_optimizer': 'auto-hq',
'cutoff': None,
'c_dtype': numpy.complex64,
'local_simplify': 'ADCRS',
'max_bond_dim': 4,
'method': 'mps'}

Even if the property is stored as an attribute on the device, execution should pull the value of
these properties from the config instead of from the device instance. While not yet integrated at
Expand Down
10 changes: 5 additions & 5 deletions doc/releases/changelog-0.40.0.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

<h4>Efficient state preparation methods 🦾</h4>

* Added new ``MPSPrep`` template to prepare quantum states in tensor simulators.
* Added new ``MPSPrep`` template to prepare quantum states in tensor network simulators.
[(#6431)](https://github.com/PennyLaneAI/pennylane/pull/6431)

* Users can prepare a linear combination of basis states using `qml.Superposition`.
Expand Down Expand Up @@ -164,7 +164,7 @@
True
```

* Devices that extends `qml.devices.Device` now has an optional class attribute `capabilities`
* A device that extends `qml.devices.Device` now has an optional class attribute `capabilities`
that is an instance of the `DeviceCapabilities` data class, constructed from the configuration
file if it exists. Otherwise, it is set to `None`.
[(#6433)](https://github.com/PennyLaneAI/pennylane/pull/6433)
Expand All @@ -184,7 +184,7 @@
```

* Default implementations of `Device.setup_execution_config` and `Device.preprocess_transforms`
are added to the device API for devices that provides a TOML configuration file and thus have
are added to the device API for devices that provide a TOML configuration file, thereby having
a `capabilities` property.
[(#6632)](https://github.com/PennyLaneAI/pennylane/pull/6632)
[(#6653)](https://github.com/PennyLaneAI/pennylane/pull/6653)
Expand Down Expand Up @@ -337,7 +337,7 @@
[(#6567)](https://github.com/PennyLaneAI/pennylane/pull/6567)

* The `diagonalize_measurements` transform no longer raises an error for unknown observables. Instead,
they are left undiagonalized, with the expectation that observable validation will catch any undiagonalized
they are left un-diagonalized, with the expectation that observable validation will catch any un-diagonalized
observables that are also unsupported by the device.
[(#6653)](https://github.com/PennyLaneAI/pennylane/pull/6653)

Expand All @@ -360,7 +360,7 @@

* `qml.execute` can now be used with `diff_method="best"`.
Classical cotransform information is now handled lazily by the workflow. Gradient method
validation and program setup is now handled inside of `qml.execute`, instead of in `QNode`.
validation and program setup are now handled inside of `qml.execute`, instead of in `QNode`.
[(#6716)](https://github.com/PennyLaneAI/pennylane/pull/6716)

* Added PyTree support for measurements in a circuit.
Expand Down
28 changes: 14 additions & 14 deletions pennylane/bose/bosonic_mapping.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,8 @@ def binary_mapping(
The mapping procedure is described in equations :math:`27-29` in `arXiv:1507.03271 <https://arxiv.org/pdf/1507.03271>`_.

Args:
bose_operator(BoseWord, BoseSentence): the bosonic operator
n_states(int): maximum number of allowed bosonic states
bose_operator (BoseWord, BoseSentence): the bosonic operator
n_states (int): Maximum number of allowed bosonic states. Defaults to ``2``.
ps (bool): Whether to return the result as a ``PauliSentence`` instead of an
operator. Defaults to ``False``.
wire_map (dict): A dictionary defining how to map the states of
Expand All @@ -61,7 +61,7 @@ def binary_mapping(
tol (float): tolerance for discarding the imaginary part of the coefficients

Returns:
Union[PauliSentence, Operator]: A linear combination of qubit operators
Union[PauliSentence, Operator]: a linear combination of qubit operators

**Example**

Expand Down Expand Up @@ -175,16 +175,16 @@ def unary_mapping(

Args:
bose_operator(BoseWord, BoseSentence): the bosonic operator
n_states(int): maximum number of allowed bosonic states
ps (bool): Whether to return the result as a PauliSentence instead of an
operator. Defaults to False.
n_states(int): Maximum number of allowed bosonic states. Defaults to ``2``.
ps (bool): Whether to return the result as a ``PauliSentence`` instead of an
operator. Defaults to ``False``.
wire_map (dict): A dictionary defining how to map the states of
the Bose operator to qubit wires. If None, integers used to
label the bosonic states will be used as wire labels. Defaults to None.
the Bose operator to qubit wires. If ``None``, integers used to
label the bosonic states will be used as wire labels. Defaults to ``None``.
tol (float): tolerance for discarding the imaginary part of the coefficients

Returns:
Union[PauliSentence, Operator]: A linear combination of qubit operators.
Union[PauliSentence, Operator]: a linear combination of qubit operators

**Example**

Expand Down Expand Up @@ -325,15 +325,15 @@ def christiansen_mapping(

Args:
bose_operator(BoseWord, BoseSentence): the bosonic operator
ps (bool): Whether to return the result as a PauliSentence instead of an
operator. Defaults to False.
ps (bool): Whether to return the result as a ``PauliSentence`` instead of an
operator. Defaults to ``False``.
wire_map (dict): A dictionary defining how to map the states of
the Bose operator to qubit wires. If None, integers used to
label the bosonic states will be used as wire labels. Defaults to None.
the Bose operator to qubit wires. If ``None``, integers used to
label the bosonic states will be used as wire labels. Defaults to ``None``.
tol (float): tolerance for discarding the imaginary part of the coefficients

Returns:
Union[PauliSentence, Operator]: A linear combination of qubit operators.
Union[PauliSentence, Operator]: a linear combination of qubit operators
"""

qubit_operator = _christiansen_mapping_dispatch(bose_operator, tol)
Expand Down
4 changes: 2 additions & 2 deletions pennylane/capture/autograph/transformer.py
Original file line number Diff line number Diff line change
Expand Up @@ -125,8 +125,8 @@ def run_autograph(fn):
).

Args:
fn (Callable): the callable to be converted. This could be a function, a QNode, or another callable object.
For a QNode, the ``Qnode.func`` will be converted. For another callable object, a function calling the
fn (Callable): The callable to be converted. This could be a function, a QNode, or another callable object.
For a QNode, the ``QNode.func`` will be converted. For another callable object, a function calling the
object will be converted.

Returns:
Expand Down
29 changes: 16 additions & 13 deletions pennylane/capture/base_interpreter.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,27 +84,27 @@ def interpret_measurement(self, measurement):

Now the interpreter can be used to transform functions and jaxpr:

>>> qml.capture.enable()
>>> interpreter = SimplifyInterpreter()
>>> def f(x):
... qml.RX(x, 0)**2
... qml.adjoint(qml.Z(0))
... return qml.expval(qml.X(0) + qml.X(0))
>>> simplified_f = interpreter(f)
>>> print(qml.draw(simplified_f)(0.5)
>>> print(qml.draw(simplified_f)(0.5))
0: ──RX(1.00)──Z─┤ <2.00*X>
>>> jaxpr = jax.make_jaxpr(f)(0.5)
>>> interpreter.eval(jaxpr.jaxpr, [], 0.5)
[expval(2.0 * X(0))]

**Handling higher order primitives:**

Two main strategies exist for handling higher order primitives (primitives with jaxpr as metatdata).

1) Structure preserving. Tracing the execution preserves the higher order primitive.
2) Structure flattening. Tracing the execution eliminates the higher order primitive.
Two main strategies exist for handling higher order primitives (primitives with jaxpr as metadata).
The first one is structure preserving (tracing the execution preserves the higher order primitive),
and the second one is structure flattening (tracing the execution eliminates the higher order primitive).

Compilation transforms, like the above ``SimplifyInterpreter``, may prefer to handle higher order primitives
via a structure preserving method. After transforming the jaxpr, the `for_loop` still exists. This maintains
via a structure-preserving method. After transforming the jaxpr, the `for_loop` still exists. This maintains
the compact structure of the jaxpr and reduces the size of the program. This behavior is the default.

>>> def g(x):
Expand All @@ -117,23 +117,26 @@ def interpret_measurement(self, measurement):
>>> jax.make_jaxpr(interpreter(g))(0.5)
{ lambda ; a:f32[]. let
_:f32[] = for_loop[
jaxpr_body_fn={ lambda ; b:i32[] c:f32[]. let
args_slice=slice(0, None, None)
consts_slice=slice(0, 0, None)
jaxpr_body_fn={ lambda ; b:i32[] c:f32[]. let
d:f32[] = convert_element_type[new_dtype=float32 weak_type=True] b
e:f32[] = mul c d
_:AbstractOperator() = RX[n_wires=1] e 0
in (c,) }
n_consts=0
in (c,) }
] 0 3 1 1.0
f:AbstractOperator() = PauliZ[n_wires=1] 0
g:AbstractOperator() = SProd[_pauli_rep=4.0 * Z(0)] 4.0 f
h:AbstractMeasurement(n_wires=None) = expval_obs g
in (h,) }
in (h,) }

Accumulation transforms, like device execution or conversion to tapes, may need to flatten out
the higher order primitive to execute it.

.. code-block:: python

import copy

class AccumulateOps(PlxprInterpreter):

def __init__(self, ops=None):
Expand All @@ -152,14 +155,14 @@ def _(self, start, stop, step, *invals, jaxpr_body_fn, consts_slice, args_slice)
state = invals[args_slice]

for i in range(start, stop, step):
state = copy(self).eval(jaxpr_body_fn, consts, i, *state)
state = copy.copy(self).eval(jaxpr_body_fn, consts, i, *state)
return state

>>> @qml.for_loop(3)
... def loop(i, x):
... qml.RX(x, i)
... return x
>>> accumulator = AccumlateOps()
>>> accumulator = AccumulateOps()
>>> accumulator(loop)(0.5)
>>> accumulator.ops
[RX(0.5, wires=[0]), RX(0.5, wires=[1]), RX(0.5, wires=[2])]
Expand Down Expand Up @@ -222,7 +225,7 @@ def setup(self) -> None:
def cleanup(self) -> None:
"""Perform any final steps after iterating through all equations.

Blank by default, this method can clean up instance variables. Particularily,
Blank by default, this method can clean up instance variables. Particularly,
this method can be used to deallocate qubits and registers when converting to
a Catalyst variant jaxpr.
"""
Expand Down
2 changes: 1 addition & 1 deletion pennylane/debugging/snapshot.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ def snapshots(tape: QuantumScript) -> tuple[QuantumScriptBatch, PostprocessingFn

If tape splitting is carried out, the transform will be conservative about the wires that it includes in each tape.
So, if all operations preceding a snapshot in a 3-qubit circuit has been applied to only one wire,
the tape would only be looking at this wire. This can be overriden by the configuration of the execution device
the tape would only be looking at this wire. This can be overridden by the configuration of the execution device
and its nature.

Regardless of the transform's behaviour, the output is a dictionary where each key is either
Expand Down
1 change: 1 addition & 0 deletions pennylane/devices/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
.. autosummary::
:toctree: api

capabilities
default_qubit
default_gaussian
default_mixed
Expand Down
4 changes: 2 additions & 2 deletions pennylane/devices/_qubit_device.py
Original file line number Diff line number Diff line change
Expand Up @@ -634,15 +634,15 @@ def statistics(
results = []

for m in measurements:
# TODO: Remove this when all overriden measurements support the `MeasurementProcess` class
# TODO: Remove this when all overridden measurements support the `MeasurementProcess` class
if isinstance(m.mv, list):
# MeasurementProcess stores information needed for processing if terminal measurement
# uses a list of mid-circuit measurement values
obs = m # pragma: no cover
else:
obs = m.obs or m.mv
obs = m if obs is None else obs
# Check if there is an overriden version of the measurement process
# Check if there is an overridden version of the measurement process
if method := getattr(self, self.measurement_map[type(m)], False):
if isinstance(m, MeasurementTransform):
result = method(tape=circuit)
Expand Down
Loading
Loading