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

[new opmath] New opmath starting page #5483

Merged
merged 66 commits into from
Apr 23, 2024
Merged
Show file tree
Hide file tree
Changes from 16 commits
Commits
Show all changes
66 commits
Select commit Hold shift + click to select a range
41d5e84
New opmath intro mage
Qottmann Apr 9, 2024
55fa963
[ci skip]
Qottmann Apr 9, 2024
e15ec1b
add file
Qottmann Apr 9, 2024
8519cbf
[ci skip]
Qottmann Apr 9, 2024
0f7a39b
format table
Qottmann Apr 9, 2024
3073171
[ci skip]
Qottmann Apr 9, 2024
8e894e9
Merge branch 'master' of https://github.com/PennyLaneAI/pennylane int…
Qottmann Apr 10, 2024
8ab2efd
encourage to post straight away
Qottmann Apr 10, 2024
4649feb
make comment about not using explicit disable/enable in tests a note
Qottmann Apr 10, 2024
777363a
remove unrelated remnant from copypaste
Qottmann Apr 10, 2024
6cacec9
[ci skip]
Qottmann Apr 10, 2024
4bb80e7
expand examples for new users
Qottmann Apr 10, 2024
160df13
expand on developer questions and testing
Qottmann Apr 10, 2024
dac9a9b
[ci skip]
Qottmann Apr 10, 2024
017e893
typos
Qottmann Apr 10, 2024
025643b
[ci skip]
Qottmann Apr 10, 2024
439a30a
Update doc/introduction/new_opmath.rst
Qottmann Apr 11, 2024
d9c515f
rename page
Qottmann Apr 11, 2024
0fcd587
Merge branch 'newopmathintro' of https://github.com/PennyLaneAI/penny…
Qottmann Apr 11, 2024
c028ac6
Merge branch 'master' of https://github.com/PennyLaneAI/pennylane int…
Qottmann Apr 11, 2024
8361b12
[ci skip]
Qottmann Apr 11, 2024
d535c97
Quick summary update more user facing
Qottmann Apr 11, 2024
56834ec
[ci skip]
Qottmann Apr 11, 2024
29a8c27
Merge branch 'master' of https://github.com/PennyLaneAI/pennylane int…
Qottmann Apr 12, 2024
596b64f
code review
Qottmann Apr 12, 2024
b4d4abb
[ci skip]
Qottmann Apr 12, 2024
13b84ed
table formatting
Qottmann Apr 12, 2024
449796d
[ci skip]
Qottmann Apr 12, 2024
ae23a1f
Merge branch 'master' of https://github.com/PennyLaneAI/pennylane int…
Qottmann Apr 12, 2024
067162f
[ci skip]
Qottmann Apr 12, 2024
ac02399
remove unrelated change
Qottmann Apr 12, 2024
121459f
[ci skip]
Qottmann Apr 12, 2024
ae8f42c
Merge branch 'master' of https://github.com/PennyLaneAI/pennylane int…
Qottmann Apr 15, 2024
157d44b
typo
Qottmann Apr 15, 2024
c4d8405
typo
Qottmann Apr 15, 2024
0469b9c
[ci skip]
Qottmann Apr 15, 2024
1203508
Merge branch 'master' of https://github.com/PennyLaneAI/pennylane int…
Qottmann Apr 18, 2024
12ea204
fix hyperlink
Qottmann Apr 18, 2024
01634ac
make bullet
Qottmann Apr 18, 2024
74057e2
remove last point, add new first one
Qottmann Apr 18, 2024
cb9c8c8
Apply suggestions from code review
Qottmann Apr 18, 2024
1b5c161
black formatting
Qottmann Apr 18, 2024
286e07f
[ci skip]
Qottmann Apr 18, 2024
3566e27
Merge branch 'master' of https://github.com/PennyLaneAI/pennylane int…
Qottmann Apr 18, 2024
0e01fad
fix bullet points
Qottmann Apr 18, 2024
b7a6d11
[ci skip]
Qottmann Apr 18, 2024
0da2fce
admonition tip
Qottmann Apr 18, 2024
f0c5954
[ci skip]
Qottmann Apr 18, 2024
42e4f7d
links
Qottmann Apr 18, 2024
0b45a94
typo
Qottmann Apr 18, 2024
46f059f
[ci skip]
Qottmann Apr 18, 2024
1722183
Apply suggestions from code review
Qottmann Apr 22, 2024
e5b9546
Merge branch 'master' of https://github.com/PennyLaneAI/pennylane int…
Qottmann Apr 22, 2024
51e01e3
[ci skip]
Qottmann Apr 22, 2024
a34262d
expand on pauli_rep
Qottmann Apr 22, 2024
b52b3a8
[ci skip]
Qottmann Apr 22, 2024
bbebd54
update cross-references and sub-headlines in technical details
Qottmann Apr 22, 2024
d179b54
[ci skip]
Qottmann Apr 22, 2024
3005cca
sub-headlines
Qottmann Apr 22, 2024
a29b26c
links
Qottmann Apr 22, 2024
9c6d334
[ci skip]
Qottmann Apr 22, 2024
6438a35
[ci skip]
Qottmann Apr 22, 2024
bd7f89f
links
Qottmann Apr 22, 2024
5108e32
Merge branch 'master' of https://github.com/PennyLaneAI/pennylane int…
Qottmann Apr 22, 2024
d32e837
Merge branch 'master' into newopmathintro
Qottmann Apr 23, 2024
2a7858d
Merge branch 'master' into newopmathintro
trbromley Apr 23, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions doc/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,7 @@ PennyLane is **free** and **open source**, released under the Apache License, Ve
introduction/chemistry
introduction/data
introduction/returns
introduction/new_opmath
introduction/logging

.. toctree::
Expand Down
368 changes: 368 additions & 0 deletions doc/introduction/new_opmath.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,368 @@
.. _new_opmath:

New opmath
==========
Qottmann marked this conversation as resolved.
Show resolved Hide resolved

Version ``0.36`` of PennyLane updated the default ``Operator`` subclasses, changing the operator arithmetic.
An end-user should not notice any (breaking) changes.
This page should help developers with troubleshooting and provide instructions for legacy support while both systems are supported.
Qottmann marked this conversation as resolved.
Show resolved Hide resolved

.. note::

If you are looking for a quick fix, jump to the :ref:`Troubleshooting` section!
Qottmann marked this conversation as resolved.
Show resolved Hide resolved

After visiting the :ref:`Troubleshooting` section, if you are still stuck then you can:

- Post on the PennyLane `discussion forum <https://discuss.pennylane.ai>`_.

- If you suspect that your issue is due to a bug in PennyLane itself, please open a
`bug report <https://github.com/PennyLaneAI/pennylane/issues/new?labels=bug+%3Abug%3A&template=bug_report.yml&title=[BUG]>`_
on the PennyLane GitHub page.

Summary of the update
---------------------
Qottmann marked this conversation as resolved.
Show resolved Hide resolved

The opt-in feature ``qml.operation.enable_new_opmath()`` is now the default. You can still opt-out via
``qml.operation.disable_new_opmath()`` for backward compatibility.

The changes between the old and new system mainly concern Python operators ``+ - * / @``,
that now create the following ``Operator`` subclass instances.


+----------------------------------+----------------------+---------------------------+
Qottmann marked this conversation as resolved.
Show resolved Hide resolved
| | Legacy | New opmath |
+==================================+======================+===========================+
| tensor products | ``operation.Tensor`` | ``ops.Prod`` |
| ``X(0) @ X(1)`` | | |
+----------------------------------+----------------------+---------------------------+
| sums | ``ops.Hamiltonian`` | ``ops.Sum`` |
| ``X(0) + X(1)`` | | |
+----------------------------------+----------------------+---------------------------+
| scalar products | ``ops.Hamiltonian`` | ``ops.SProd`` |
| ``1.5 * X(1)`` | | |
+----------------------------------+----------------------+---------------------------+
| ``qml.dot(coeffs,ops)`` | ``ops.Sum`` | ``ops.Sum`` |
+----------------------------------+----------------------+---------------------------+
| ``qml.Hamiltonian(coeffs, ops)`` | ``ops.Hamiltonian`` | ``ops.LinearCombination`` |
+----------------------------------+----------------------+---------------------------+


The three main new opmath classes ``SProd``, ``Prod``, and ``Sum`` have already been around for a while.
E.g. ``qml.dot()`` has always returned a ``Sum`` instance.

Usage
~~~~~

Besides the python operators, you can also use the constructors :func:`~s_prod`, :func:`~prod`, and :func:`~sum`.
For composite operators, we can access their constituens via the ``op.operands`` attribute.
Qottmann marked this conversation as resolved.
Show resolved Hide resolved

>>> op = qml.sum(X(0), X(1), X(2))
>>> op.operands
(X(0), X(1), X(2))

In case all terms are composed of operators with a valid ``pauli_rep``, then also the composite
operator has a valid ``pauli_rep`` in terms of a :class:`~pauli.PauliSentence` instance. This is often handy for fast
arithmetic processing.

>>> op.pauli_rep
1.0 * X(0)
+ 1.0 * X(1)
+ 1.0 * X(2)

Further, composite operators can be simplified using :func:`~pennylane.simplify` or the ``op.simplify()`` method.

>>> op = 0.5 * X(0) + 0.5 * Y(0) - 1.5 * X(0) - 0.5 * Y(0) # no simplification by default
>>> op.simplify()
-1.0 * X(0)
>>> qml.simplify(op)
-1.0 * X(0)

Note that the simplification never happens in-place, such that the original operator is left unaltered.

>>> op
(
0.5 * X(0)
+ 0.5 * Y(0)
+ -1 * 1.5 * X(0)
+ -1 * 0.5 * Y(0)
)

We are often interested in obtaining a list of coefficients and `pure` operators.
We can do so by using the ``op.terms()`` method.

>>> op = 0.5 * (X(0) @ X(1) + Y(0) @ Y(1) + 2 * Z(0) @ Z(1)) - 1.5 * I() + 0.5 * I()
>>> op.terms()
([0.5, 0.5, 1.0, -1.0], [X(1) @ X(0), Y(1) @ Y(0), Z(1) @ Z(0), I()])

As seen by this example, this method already takes care of arithmetic simplifications.

qml.Hamiltonian
~~~~~~~~~~~~~~~

The legacy classes :class:`~operation.Tensor` and :class:`~ops.Hamiltonian` will soon be deprecated.
:class:`~ops.LinearCombination` offers the same API as :class:`~ops.Hamiltonian` but works well with new opmath classes.

Depending on whether or not new opmath is active, ``qml.Hamiltonian`` will return either of the two classes.

>>> import pennylane as qml
>>> from pennylane import X
>>> qml.operation.active_new_opmath()
True
>>> H = qml.Hamiltonian([0.5, 0.5], [X(0), X(1)])
>>> type(H)
pennylane.ops.op_math.linear_combination.LinearCombination

>>> qml.operation.disable_new_opmath_()
>>> qml.operation.active_new_opmath()
False
>>> H = qml.Hamiltonian([0.5, 0.5], [X(0), X(1)])
>>> type(H)
pennylane.ops.qubit.hamiltonian.Hamiltonian

Both classes offer the same API and functionality, so a user does not have to worry about those implementation details.

.. _Troubleshooting:

Troubleshooting
---------------

If you are a developer or power-user that explicitly uses ``qml.operation.Tensor`` or ``qml.ops.Hamiltonian``, you
may run into one of the following common issues.
Qottmann marked this conversation as resolved.
Show resolved Hide resolved

.. details::
:title: Sharp bits about the nesting structure of new opmath instances
:href: sharp-bits-nesting

The type of the final operator is determined by the outermost operation. The resulting object is a nested structure (sums of s/prods or s/prods of sums).

>>> qml.operation.enable_new_opmath() # default soon
>>> op = 0.5 * (X(0) @ X(1)) + 0.5 * (Y(0) @ Y(1))
>>> type(op)
pennylane.ops.op_math.sum.Sum

>>> op.operands
(0.5 * (X(0) @ X(1)), 0.5 * (Y(0) @ Y(1)))

>>> type(op.operands[0]), type(op.operands[1])
(pennylane.ops.op_math.sprod.SProd, pennylane.ops.op_math.sprod.SProd)

>>> op.operands[0].scalar, op.operands[0].base, type(op.operands[0].base)
(0.5, X(0) @ X(1), pennylane.ops.op_math.prod.Prod)

We could construct an equivalent operator with a different nesting structure.

>>> op = (0.5 * X(0)) @ X(1) + (0.5 * Y(0)) @ Y(1)
>>> op.operands
((0.5 * X(0)) @ X(1), (0.5 * Y(0)) @ Y(1))

>>> type(op.operands[0]), type(op.operands[1])
(pennylane.ops.op_math.prod.Prod, pennylane.ops.op_math.prod.Prod)

>>> op.operands[0].operands
(0.5 * X(0), X(1))

>>> type(op.operands[0].operands[0]), type(op.operands[0].operands[1])
(pennylane.ops.op_math.sprod.SProd,
pennylane.ops.qubit.non_parametric_ops.PauliX)

There is yet another way to construct the same, equivalent, operator.
We can bring all of them to the same format by using ``op.simplify()`` which brings the operator down to
the form :math:`\sum_i c_i \hat{O}_i` where :math:`c_i` is a scalar coefficient and :math:`\hat{O}_i` a pure operator or tensor product of operators.

>>> op1 = 0.5 * (X(0) @ X(1)) + 0.5 * (Y(0) @ Y(1))
>>> op2 = (0.5 * X(0)) @ X(1) + (0.5 * Y(0)) @ Y(1)
>>> op3 = 0.5 * (X(0) @ X(1) + Y(0) @ Y(1))
>>> qml.equal(op1, op2), qml.equal(op2, op3), qml.equal(op3, op1)
(True, False, False)

>>> op1 = op1.simplify()
>>> op2 = op2.simplify()
>>> op3 = op3.simplify()
>>> qml.equal(op1, op2), qml.equal(op2, op3), qml.equal(op3, op1)
(True, True, True)

>>> op1, op2, op3
(0.5 * (X(1) @ X(0)) + 0.5 * (Y(1) @ Y(0)),
0.5 * (X(1) @ X(0)) + 0.5 * (Y(1) @ Y(0)),
0.5 * (X(1) @ X(0)) + 0.5 * (Y(1) @ Y(0)))

We can also obtain those scalar coefficients and tensor product operators via the op.terms() method.

>>> coeffs, ops = op1.terms()
([0.5, 0.5], [X(1) @ X(0), Y(1) @ Y(0)])

.. details::
:title: Sharp bits about the qml.Hamiltonian dispatch
:href: sharp-bits-hamiltonian

One of the reasons that :class:`~ops.LinearCombination` exists is that the old Hamiltonian class is not compatible with new opmath tensor products.
We can try to instantiate a old ``qml.ops.Hamiltonian`` class with a ``X(0) @ X(1)`` tensor product, which returns a :class:`~Prod` instance with opmath enabled.
Qottmann marked this conversation as resolved.
Show resolved Hide resolved

>>> qml.operation.active_new_opmath() # confirm opmath is active (by default)
True
>>> qml.ops.Hamiltonian([0.5], [X(0) @ X(1)])
PennyLaneDeprecationWarning: Using 'qml.ops.Hamiltonian' with new operator arithmetic is deprecated. Instead, use 'qml.Hamiltonian', or use 'qml.operation.disable_new_opmath()' to continue to access the legacy functionality. See https://docs.pennylane.ai/en/stable/development/deprecations.html for more details.
ValueError: Could not create circuits. Some or all observables are not valid.
trbromley marked this conversation as resolved.
Show resolved Hide resolved

However, using ``qml.Hamiltonian`` works as expected.

>>> qml.Hamiltonian([0.5], [X(0) @ X(1)])
0.5 * (X(0) @ X(1))

The API of LinearCombination is identical to that of Hamiltonian. We can group observables or simplify upon initialization.
Qottmann marked this conversation as resolved.
Show resolved Hide resolved

>>> H1 = qml.Hamiltonian([0.5, 0.5, 0.5], [X(0) @ X(1), X(0), Y(0)], grouping_type="qwc", simplify=True)
>>> H2 = qml.ops.LinearCombination([0.5, 0.5, 0.5], [X(0) @ X(1), X(0), Y(0)], grouping_type="qwc", simplify=True)
>>> H1 == H2
True

One small difference is that ``ham.simplify()`` no longer alters the instance in-place. In either case (legacy/new opmath), the following works.

>>> H1 = qml.Hamiltonian([0.5, 0.5], [X(0) @ X(1), X(0) @ X(1)])
>>> H1 = H1.simplify() # work for new and legacy opmath

.. details::
:title: My old PennyLane script does not run anymore
:href: old-script-broken
Qottmann marked this conversation as resolved.
Show resolved Hide resolved

A quick-and-dirty fix for this issue is to deactivate new opmath at the beginning of the script via ``qml.operation.disable_new_opmath()``.
We recommend to do the following checks instead

* Check explicit use of the legacy :class:`~Tensor` class. If you find it in your script it can just be changed from ``Tensor(*terms)`` to ``qml.prod(*terms)`` with the same signature.

* Check explicit use of ``op.obs`` attribute, where ``op`` is some operator. This is how the terms of a tensor product is accessed in :class:`~Tensor` instances. Use ``op.operands`` instead.

.. code-block:: python

# new opmath enabled (default)
op = X(0) @ X(1)
assert op.operands == (X(0), X(1))

with qml.operation.disable_new_opmath_cm():
# context manager that disables new opmath temporarilly
op = X(0) @ X(1)
assert op.obs == [X(0), X(1)]

* Check explicit use of ``qml.ops.Hamiltonian``. In that case, simply change to ``qml.Hamiltonian``.

>>> op = qml.ops.Hamiltonian([0.5, 0.5], [X(0) @ X(1), X(1) @ X(2)])
ValueError: Could not create circuits. Some or all observables are not valid.
>>> op = qml.Hamiltonian([0.5, 0.5], [X(0) @ X(1), X(1) @ X(2)])
>>> isinstance(op, qml.ops.LinearCombination)
True

* Check if you are explicitly enabling and disabling new opmath somewhere in your script. Mixing both systems is not supported.

If for some unexpected reason your script still breaks, please post on the PennyLane `discussion forum <https://discuss.pennylane.ai>`_ or open a
`bug report <https://github.com/PennyLaneAI/pennylane/issues/new?labels=bug+%3Abug%3A&template=bug_report.yml&title=[BUG]>`_
on the PennyLane GitHub page.

.. details::
:title: I want to contribute to PennyLane and need to provide legacy support in tests
:href: PL-developer

If you want to contribute a new feature to PennyLane or update an existing one, you likely also need to update the tests.

.. note::
Please refrain from explicitly using ``qml.operation.disable_new_opmath()`` and ``qml.operation.enable_new_opmath()`` anywhere in tests as that globally
changes the status of new opmath and thereby can affect other tests.

.. code-block:: python3
Qottmann marked this conversation as resolved.
Show resolved Hide resolved
def test_some_legacy_opmath_behavior():
qml.operation.disable_new_opmath() # dont do this
# testing some legacy behavior things

def test_some_new_opmath_behavior():
assert qml.operation.active_new_opmath()
# will fail because the previous test globally disabled new opmath

Instead, please use the fixtures below, or the context managers ``qml.operation.disable_new_opmath_cm()`` and ``qml.operation.enable_new_opmath_cm()``.

>>> with qml.operation.disable_new_opmath_cm():
... op = qml.Hamiltonian([0.5], [X(0) @ X(1)])
>>> assert isinstance(op, qml.ops.Hamiltonian)

Our continuous integration (CI) test suite is running all tests with the default of new opmath being enabled.
Qottmann marked this conversation as resolved.
Show resolved Hide resolved
We also periodically run the CI test suite with new opmath disabled, as we support both new and legacy systems for some limited time.
Qottmann marked this conversation as resolved.
Show resolved Hide resolved
In case a test needs to be adopted for either case, you can use the following fixtures.

* Use ``@pytest.mark.usefixtures("use_legacy_opmath")`` to test functionality that is explicitly only supported by legacy opmath, e.g. for backward compatibility.
Qottmann marked this conversation as resolved.
Show resolved Hide resolved

.. code-bock:: python3
@pytest.mark.usefixtures("use_legacy_opmath")
def test_qml_hamiltonian_legacy_opmath():
assert qml.Hamiltonian == qml.ops.Hamiltonian

def test_qml_hamiltonian()
assert qml.Hamiltonian == qml.ops.LinearCombination

* Use ``@pytest.mark.usefixtures("use_new_opmath")`` to test functionality that `only` works with new opmath. That is because for the intermittent period
of supporting both systems, we periodically run the test suite with new opmath disabled.

.. code-bock:: python3
@pytest.mark.usefixtures("use_new_opmath")
def test_qml_hamiltonian_new_opmath():
assert qml.Hamiltonian == qml.ops.LinearCombination

* Use ``@pytest.mark.usefixtures("use_legacy_and_new_opmath")`` if you want to test support for both systems in one single test. You can use ``qml.operation.active_new_opmath``
inside the test to account for minor differences between both systems.

.. code-bock:: python3
@pytest.mark.usefixtures("use_legacy_and_new_opmath")
def test_qml_hamiltonian_new_opmath():
if qml.operation.active_new_opmath():
assert qml.Hamiltonian == qml.ops.LinearCombination

if not qml.operation.active_new_opmath():
assert qml.Hamiltonian == qml.ops.Hamiltonian

One sharp bit about testing is that ``pytest`` runs collection and test execution separately. That means that instances generated outside the test, e.g. for parametrization, have been created
Qottmann marked this conversation as resolved.
Show resolved Hide resolved
using the respective system. So you may need to also put that creation in the appropriate context manager.

.. code-block:: python3
Qottmann marked this conversation as resolved.
Show resolved Hide resolved
# in some test file
with qml.operation.disable_new_opmath():
legacy_ham_example = qml.Hamiltonian(coeffs, ops) # creates a Hamiltonian instance

@pytest.mark.usefixtures("use_legacy_opmath")
@pytest.marl.parametrize("ham", [legacy_ham_example])
def test_qml_hamiltonian_legacy_opmath(ham):
assert isinstance(ham, qml.Hamiltonian) # True
assert isinstance(ham, qml.ops.Hamiltonian) # True

Alternatively, you can convert them back to legacy Hamiltonian instances using ``qml.operation.convert_to_legacy_H()``.

.. code-block:: python3
Qottmann marked this conversation as resolved.
Show resolved Hide resolved
ham_example = qml.Hamiltonian(coeffs, ops) # creates a LinearCombination instance

@pytest.mark.usefixtures("use_legacy_opmath")
@pytest.marl.parametrize("ham", [ham_example])
def test_qml_hamiltonian_legacy_opmath(ham):
assert isinstance(ham, qml.Hamiltonian) # True
assert not isinstance(ham, qml.ops.Hamiltonian) # True

@pytest.mark.usefixtures("use_legacy_opmath")
@pytest.marl.parametrize("ham", [ham_example])
def test_qml_hamiltonian_legacy_opmath(ham):
# Most likely you wanted to test things with an Hamiltonian instance
legacy_ham_example = convert_to_legacy_H(ham)
assert isinstance(legacy_ham_example, qml.ops.Hamiltonian) # True
assert isinstance(legacy_ham_example, qml.Hamiltonian) # True because we are in legacy opmath context
assert not isinstance(legacy_ham_example, qml.ops.LinearCombination) # True

For all that, keep in mind that ``qml.Hamiltonian`` points to :class:`~Hamiltonian` and :class:`LinearCombination` depending on the status of ``qml.operation.active_new_opmath()``.
So if you want to test something specifically for the old :class:`~Hamiltonian`` class, use ``qml.ops.Hamiltonian`` instead.


.. details::
:title: I am unsure what to do
:href: unsure

Please carefully read through the options above. If you are still stuck, you can:

- Post on the PennyLane `discussion forum <https://discuss.pennylane.ai>`_. Please include
a complete block of code demonstrating your issue so that we can quickly troubleshoot.

- If you suspect that your issue is due to a bug in PennyLane itself, please open a
`bug report <https://github.com/PennyLaneAI/pennylane/issues/new?labels=bug+%3Abug%3A&template=bug_report.yml&title=[BUG]>`_
on the PennyLane GitHub page.