Source code for pennylane.ops.functions.dot
# Copyright 2018-2023 Xanadu Quantum Technologies Inc.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
# http://www.apache.org/licenses/LICENSE-2.0
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""
This file contains the definition of the dot function, which computes the dot product between
a vector and a list of operators.
"""
# pylint: disable=too-many-branches
from collections import defaultdict
from collections.abc import Callable, Sequence
from typing import Union
import pennylane as qml
from pennylane.operation import Operator
from pennylane.pauli import PauliSentence, PauliWord
from pennylane.pulse import ParametrizedHamiltonian
[docs]def dot(
coeffs: Sequence[Union[float, Callable]],
ops: Sequence[Union[Operator, PauliWord, PauliSentence]],
pauli=False,
grouping_type=None,
method="lf",
) -> Union[Operator, ParametrizedHamiltonian, PauliSentence]:
r"""Returns the dot product between the ``coeffs`` vector and the ``ops`` list of operators.
This function returns the following linear combination: :math:`\sum_{k} c_k O_k`, where
:math:`c_k` and :math:`O_k` are the elements inside the ``coeffs`` and ``ops`` arguments, respectively.
Args:
coeffs (Sequence[float, Callable]): sequence containing the coefficients of the linear combination
ops (Sequence[Operator, PauliWord, PauliSentence]): sequence containing the operators of the linear combination.
Can also be ``PauliWord`` or ``PauliSentence`` instances.
pauli (bool, optional): If ``True``, a :class:`~.PauliSentence`
operator is used to represent the linear combination. If False, a :class:`Sum` operator
is returned. Defaults to ``False``. Note that when ``ops`` consists solely of ``PauliWord``
and ``PauliSentence`` instances, the function still returns a PennyLane operator when ``pauli=False``.
grouping_type (str): The type of binary relation between Pauli words used to compute
the grouping. Can be ``'qwc'``, ``'commuting'``, or ``'anticommuting'``. Note that if
``pauli=True``, the grouping will be ignored.
method (str): The graph colouring heuristic to use in solving minimum clique cover for
grouping, which can be ``'lf'`` (Largest First), ``'rlf'`` (Recursive Largest First),
``'dsatur'`` (Degree of Saturation), or ``'gis'`` (Greedy Independent Set).
This keyword argument is ignored if ``grouping_type`` is ``None``. Defaults to ``'lf'`` if no method is provided.
Raises:
ValueError: if the number of coefficients and operators does not match or if they are empty
Returns:
Operator or ParametrizedHamiltonian: operator describing the linear combination
.. note::
If grouping is requested, the computed groupings are stored as a list of list of indices
in ``Sum.grouping_indices``. The indices refer to the operators and coefficients returned
by ``Sum.terms()``, not ``Sum.operands``, as these are not guaranteed to be equivalent.
**Example**
>>> coeffs = np.array([1.1, 2.2])
>>> ops = [qml.X(0), qml.Y(0)]
>>> qml.dot(coeffs, ops)
1.1 * X(0) + 2.2 * Y(0)
>>> qml.dot(coeffs, ops, pauli=True)
1.1 * X(0)
+ 2.2 * Y(0)
Note that additions of the same operator are not executed by default.
>>> qml.dot([1., 1.], [qml.X(0), qml.X(0)])
X(0) + X(0)
You can obtain a cleaner version by simplifying the resulting expression.
>>> qml.dot([1., 1.], [qml.X(0), qml.X(0)]).simplify()
2.0 * X(0)
``pauli=True`` can be used to construct a more efficient, simplified version of the operator.
Note that it returns a :class:`~.PauliSentence`, which is not an :class:`~.Operator`. This
specialized representation can be converted to an operator:
>>> qml.dot([1, 2], [qml.X(0), qml.X(0)], pauli=True).operation()
3.0 * X(0)
Using ``pauli=True`` and then converting the result to an :class:`~.Operator` is much faster
than using ``pauli=False``, but it only works for pauli words
(see :func:`~.is_pauli_word`).
If any of the parameters listed in ``coeffs`` are callables, the resulting dot product will be a
:class:`~.ParametrizedHamiltonian`:
>>> coeffs = [lambda p, t: p * jnp.sin(t) for _ in range(2)]
>>> ops = [qml.X(0), qml.Y(0)]
>>> qml.dot(coeffs, ops)
(
<lambda>(params_0, t) * X(0)
+ <lambda>(params_1, t) * Y(0)
)
.. details::
:title: Grouping
Grouping information can be collected during construction using the ``grouping_type`` and ``method``
keyword arguments. For example:
.. code-block:: python
import pennylane as qml
a = qml.X(0)
b = qml.prod(qml.X(0), qml.X(1))
c = qml.Z(0)
obs = [a, b, c]
coeffs = [1.0, 2.0, 3.0]
op = qml.dot(coeffs, obs, grouping_type="qwc")
>>> op.grouping_indices
((2,), (0, 1))
``grouping_type`` can be ``"qwc"`` (qubit-wise commuting), ``"commuting"``, or ``"anticommuting"``, and
``method`` can be ``'lf'`` (Largest First), ``'rlf'`` (Recursive Largest First),
``'dsatur'`` (Degree of Saturation), or ``'gis'`` (Greedy Independent Set).
To see more details about how these affect grouping, see :ref:`Pauli Graph Colouring<graph_colouring>` and
:func:`~pennylane.pauli.compute_partition_indices`.
"""
for t in (Operator, PauliWord, PauliSentence):
if isinstance(ops, t):
raise ValueError(
f"ops must be an Iterable of {t.__name__}'s, not a {t.__name__} itself."
)
if len(coeffs) != len(ops):
raise ValueError("Number of coefficients and operators does not match.")
if len(coeffs) == 0 and len(ops) == 0:
raise ValueError("Cannot compute the dot product of an empty sequence.")
for t in (Operator, PauliWord, PauliSentence):
if isinstance(ops, t):
raise ValueError(
f"ops must be an Iterable of {t.__name__}'s, not a {t.__name__} itself."
)
if any(callable(c) for c in coeffs):
return ParametrizedHamiltonian(coeffs, ops)
# User-specified Pauli route
if pauli:
if all(isinstance(pauli, (PauliWord, PauliSentence)) for pauli in ops):
# Use pauli arithmetic when ops are just PauliWord and PauliSentence instances
return _dot_pure_paulis(coeffs, ops)
# Else, transform all ops to pauli sentences
return _dot_with_ops_and_paulis(coeffs, ops)
# Convert possible PauliWord and PauliSentence instances to operation
ops = [op.operation() if isinstance(op, (PauliWord, PauliSentence)) else op for op in ops]
operands = [op if coeff == 1 else qml.s_prod(coeff, op) for coeff, op in zip(coeffs, ops)]
return (
operands[0]
if len(operands) == 1
else qml.sum(*operands, grouping_type=grouping_type, method=method)
)
def _dot_with_ops_and_paulis(coeffs: Sequence[float], ops: Sequence[Operator]):
"""Compute dot when operators are a mix of pennylane operators, PauliWord and PauliSentence by turning them all into a PauliSentence instance.
Returns a PauliSentence instance"""
pauli_words = defaultdict(lambda: 0)
for coeff, op in zip(coeffs, ops):
sentence = qml.pauli.pauli_sentence(op)
for pw in sentence:
pauli_words[pw] += sentence[pw] * coeff
return qml.pauli.PauliSentence(pauli_words)
def _dot_pure_paulis(coeffs: Sequence[float], ops: Sequence[Union[PauliWord, PauliSentence]]):
"""Faster computation of dot when all ops are PauliSentences or PauliWords"""
return sum((c * op for c, op in zip(coeffs[1:], ops[1:])), start=coeffs[0] * ops[0])
_modules/pennylane/ops/functions/dot
Download Python script
Download Notebook
View on GitHub