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

Custom boolean functions for supporting noise models #5674

Merged
merged 74 commits into from
Jun 11, 2024
Merged
Show file tree
Hide file tree
Changes from 72 commits
Commits
Show all changes
74 commits
Select commit Hold shift + click to select a range
202c365
init
obliviateandsurrender Apr 26, 2024
4c03e36
init
obliviateandsurrender May 4, 2024
5ff822f
basic structure
obliviateandsurrender May 9, 2024
d49a88b
more versatile support
obliviateandsurrender May 9, 2024
2b8b65d
fix formatting
obliviateandsurrender May 9, 2024
72d25d0
Merge branch 'master' into noise-models-1
obliviateandsurrender May 9, 2024
1892a42
fix `pylint`?
obliviateandsurrender May 9, 2024
3e301a2
more fixes
obliviateandsurrender May 9, 2024
4b6c6b4
format tweaks
obliviateandsurrender May 9, 2024
1317189
add `partial_wires`
obliviateandsurrender May 10, 2024
5332c61
add tests
obliviateandsurrender May 10, 2024
edcb7a4
add repr test
obliviateandsurrender May 10, 2024
59d09de
happy `isort`
obliviateandsurrender May 10, 2024
edb464d
happy `isort`
obliviateandsurrender May 10, 2024
f7465d2
add test
obliviateandsurrender May 10, 2024
09936a6
happy `black`
obliviateandsurrender May 10, 2024
ec1f1c2
happy `isort`
obliviateandsurrender May 10, 2024
d4bfbaf
improve coverage
obliviateandsurrender May 10, 2024
07587a7
improve coverage and simplify bits
obliviateandsurrender May 10, 2024
3d5fe94
Merge branch 'master' into noise-models-1
obliviateandsurrender May 11, 2024
f30501f
happy ci for a while
obliviateandsurrender May 11, 2024
064c6a0
fix test
obliviateandsurrender May 12, 2024
087a007
add docstrings
obliviateandsurrender May 13, 2024
65e158f
add docstrings
obliviateandsurrender May 13, 2024
d806fa3
show in docs
obliviateandsurrender May 13, 2024
72a47e2
tweak
obliviateandsurrender May 13, 2024
75ee072
doc tweak
obliviateandsurrender May 13, 2024
7d92269
doc tweak
obliviateandsurrender May 13, 2024
299a030
Apply suggestions from code review
obliviateandsurrender May 13, 2024
e5d481e
Merge branch 'noise-models-1' of https://github.com/PennyLaneAI/penny…
obliviateandsurrender May 13, 2024
4f27312
add support for arithmetic depth
obliviateandsurrender May 14, 2024
2797a53
make lc support robust
obliviateandsurrender May 14, 2024
7fc2dd2
fix coverage
obliviateandsurrender May 14, 2024
d1192b4
simplify doubts
obliviateandsurrender May 14, 2024
823daad
restructure
obliviateandsurrender May 16, 2024
8a2760d
Merge branch 'master' into noise-models-1
obliviateandsurrender May 16, 2024
6cdfda8
add `changelog`
obliviateandsurrender May 16, 2024
0adc86f
add dunder to bitwise
obliviateandsurrender May 17, 2024
f84811e
Merge branch 'master' into noise-models-1
obliviateandsurrender May 17, 2024
ca5adb8
improve coverage
obliviateandsurrender May 17, 2024
4da09b0
Merge branch 'noise-models-1' of https://github.com/PennyLaneAI/penny…
obliviateandsurrender May 17, 2024
19c6fff
add doc page
obliviateandsurrender May 17, 2024
9dcd7d0
add docs
obliviateandsurrender May 17, 2024
e2e7a31
Merge branch 'master' into noise-models-1
Jaybsoni May 22, 2024
6caae2a
apply suggestions
obliviateandsurrender May 23, 2024
caf9def
Merge branch 'noise-models-1' of https://github.com/PennyLaneAI/penny…
obliviateandsurrender May 23, 2024
dd45dcd
Merge branch 'master' into noise-models-1
obliviateandsurrender May 23, 2024
7e6c332
improve LC comparison
obliviateandsurrender May 26, 2024
d3c5fc6
improve docs
obliviateandsurrender May 27, 2024
0979d6d
add exp check
obliviateandsurrender May 27, 2024
6a565e9
tweak fixes
obliviateandsurrender May 27, 2024
4464bf1
tweak docs
obliviateandsurrender May 28, 2024
8fb77a2
tweak docs
obliviateandsurrender May 28, 2024
eb54e04
docs tweaks
obliviateandsurrender May 29, 2024
2f40fa3
improve docs
obliviateandsurrender May 29, 2024
8922964
little tweaks
obliviateandsurrender May 29, 2024
b08b836
Merge branch 'master' into noise-models-1
Jaybsoni May 29, 2024
c7ca37b
add suggestions
obliviateandsurrender May 29, 2024
4a9e787
Merge branch 'noise-models-1' of https://github.com/PennyLaneAI/penny…
obliviateandsurrender May 29, 2024
85b63d1
add suggestions
obliviateandsurrender May 29, 2024
fe011f3
improve docs
obliviateandsurrender May 30, 2024
55ab757
Merge branch 'master' into noise-models-1
obliviateandsurrender May 30, 2024
72a28fe
tweak docs
obliviateandsurrender May 30, 2024
8e5cddf
Merge branch 'master' into noise-models-1
obliviateandsurrender May 31, 2024
636e2d0
Merge branch 'master' into noise-models-1
obliviateandsurrender May 31, 2024
420b845
Merge branch 'master' into noise-models-1
obliviateandsurrender Jun 4, 2024
1eb5d12
Merge branch 'master' of https://github.com/PennyLaneAI/pennylane int…
obliviateandsurrender Jun 6, 2024
c793e39
adapt for controlled
obliviateandsurrender Jun 6, 2024
7088b9c
minor tweak
obliviateandsurrender Jun 7, 2024
accbeda
Merge branch 'master' into noise-models-1
obliviateandsurrender Jun 10, 2024
da1ffb3
add suggestions to docs
obliviateandsurrender Jun 11, 2024
84e3738
minor fixups
obliviateandsurrender Jun 11, 2024
e34f9ad
hopeful final suggestion
obliviateandsurrender Jun 11, 2024
c93e483
Merge branch 'master' into noise-models-1
obliviateandsurrender Jun 11, 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
187 changes: 187 additions & 0 deletions doc/code/qml_noise.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
qml.noise
=========

This module contains the functionality for building and manipulating insertion-based noise models,
where noisy gates and channels are inserted based on the target operations.

Overview
--------

Insertion-based noise models in PennyLane are defined via a mapping from conditionals, specified
as :class:`~.BooleanFn` objects, to :ref:`quantum functions <intro_vcirc_qfunc>`-like callables
obliviateandsurrender marked this conversation as resolved.
Show resolved Hide resolved
that contain the noisy operations to be applied, but without any return statements. Additional
noise-related metadata can also be supplied to construct a noise model.

::

NoiseModel: ({Conditional --> Callables}, metadata)

Each ``Conditional`` evaluates the gate operations in the quantum circuit based on some
condition of its attributes (e.g., type, parameters, wires, etc.) and use the corresponding
``Callable`` to apply the noise operations, using the user-provided metadata (e.g., hardware
topologies or relaxation times), whenever the condition results true.
obliviateandsurrender marked this conversation as resolved.
Show resolved Hide resolved

Boolean functions
^^^^^^^^^^^^^^^^^

Each :class:`~.BooleanFn` in the noise model is evaluated on the operations of a given
quantum circuit. One can construct standard Boolean functions using the following helpers:

obliviateandsurrender marked this conversation as resolved.
Show resolved Hide resolved
.. currentmodule:: pennylane.noise

.. autosummary::
:toctree: api

~op_eq
~op_in
~wires_eq
~wires_in

obliviateandsurrender marked this conversation as resolved.
Show resolved Hide resolved
For example, a Boolean function that checks of an operation is on wire ``0`` can be created
like so:

>>> fn = wires_eq(0)
>>> op1, op2 = qml.PauliX(0), qml.PauliX(1)
>>> fn(op1)
False
>>> fn(op2)
True

Arbitrary Boolean functions can also be defined by wrapping the functional form
of custom conditions with the following decorator:

.. currentmodule:: pennylane

.. autosummary::
:toctree: api

~BooleanFn

obliviateandsurrender marked this conversation as resolved.
Show resolved Hide resolved
For example, a Boolean function can be created to identify an :class:`~.RX` gate
with a maximum parameter value:

.. code-block:: python

@qml.BooleanFn
def rx_condition(op, **metadata):
return isinstance(op, qml.RX) and op.parameters[0] < 1.0

obliviateandsurrender marked this conversation as resolved.
Show resolved Hide resolved
Boolean functions can be combined using standard bit-wise operators, such as
``&``, ``|``, ``^``, or ``~``. The result will be another Boolean function. It
is important to note that as Python will evaluate the expression in the order
of their combination, i.e., left to right, the order of composition could matter,
even though bitwise operations are symmetric by definition.

Noisy quantum functions
^^^^^^^^^^^^^^^^^^^^^^^

If a Boolean function evaluates to ``True`` on a given operation in the quantum circuit,
the corresponding quantum function is evaluated that inserts the noise directly after
the operation. The quantum function should have signature ``fn(op, **metadata)``,
allowing for dependency on both the preceding operation and metadata specified in
the noise model. For example, the following noise model adds an over-rotation
to the ``RX`` gate:

.. code-block:: python

def noisy_rx(op, **metadata):
qml.RX(op.parameters[0] * 0.05, op.wires)

noise_model = qml.NoiseModel({rx_condition: noisy_rx})

A common use case is to have a single-operation :ref:`noise channel <intro_ref_ops_channels>`
whose wires are the same as the preceding operation. This can be constructed using:

.. currentmodule:: pennylane.noise

.. autosummary::
:toctree: api

~partial_wires

obliviateandsurrender marked this conversation as resolved.
Show resolved Hide resolved
For example, a constant-valued over-rotation can be created using:

>>> rx_constant = qml.noise.partial_wires(qml.RX(0.1, wires=[0]))
>>> rx_constant(2)
RX(0.1, 2)
>>> qml.NoiseModel({rx_condition: rx_constant})
NoiseModel({
BooleanFn(rx_condition): RX(phi=0.1)
})

obliviateandsurrender marked this conversation as resolved.
Show resolved Hide resolved
Example noise model
^^^^^^^^^^^^^^^^^^^

The following example shows how to set up an artificial noise model in PennyLane:

.. code-block:: python

# Set up the conditions
c0 = qml.noise.op_eq(qml.PauliX) | qml.noise.op_eq(qml.PauliY)
c1 = qml.noise.op_eq(qml.Hadamard) & qml.noise.wires_in([0, 1])
c2 = qml.noise.op_eq(qml.RX)

@qml.BooleanFn
def c3(op, **metadata):
return isinstance(op, qml.RY) and op.parameters[0] >= 0.5

# Set up noisy ops
n0 = qml.noise.partial_wires(qml.AmplitudeDamping, 0.4)

def n1(op, **metadata):
ThermalRelaxationError(0.4, metadata["t1"], 0.2, 0.6, op.wires)

def n2(op, **metadata):
qml.RX(op.parameters[0] * 0.05, op.wires)

n3 = qml.noise.partial_wires(qml.PhaseDamping, 0.9)

# Set up noise model
noise_model = qml.NoiseModel({c0: n0, c1: n1, c2: n2}, t1=0.04)
noise_model += {c3: n3} # One-at-a-time construction

>>> noise_model
NoiseModel({
OpEq(PauliX) | OpEq(PauliY): AmplitudeDamping(gamma=0.4)
OpEq(Hadamard) & WiresIn([0, 1]): n1
OpEq(RX): n2
BooleanFn(c3): PhaseDamping(gamma=0.9)
}, t1 = 0.04)

API overview
^^^^^^^^^^^^

The following are the ``BooleanFn`` objects created by calling the helper functions
above, such as :func:`~.op_eq`. These objects do not need to be instantiated directly

.. currentmodule:: pennylane.noise.conditionals

.. autosummary::
:toctree: api

~OpEq
~OpIn
~WiresEq
~WiresIn

obliviateandsurrender marked this conversation as resolved.
Show resolved Hide resolved
Bitwise operations like `And` and `Or` are represented with the following classes in the
:mod:`boolean_fn` module:

.. currentmodule:: pennylane.boolean_fn

.. autosummary::
:toctree: api

~And
~Or
~Xor
~Not

Class Inheritence Diagram
^^^^^^^^^^^^^^^^^^^^^^^^^

.. inheritance-diagram:: pennylane.noise.conditionals
:parts: 1
obliviateandsurrender marked this conversation as resolved.
Show resolved Hide resolved

.. inheritance-diagram:: pennylane.boolean_fn
:parts: 1
1 change: 1 addition & 0 deletions doc/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,7 @@ PennyLane is **free** and **open source**, released under the Apache License, Ve
code/qml_kernels
code/qml_logging
code/qml_math
code/qml_noise
code/qml_numpy
code/qml_ops_op_math
code/qml_pauli
Expand Down
3 changes: 3 additions & 0 deletions doc/releases/changelog-dev.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@
* The `default.tensor` device is introduced to perform tensor network simulation of a quantum circuit.
[(#5699)](https://github.com/PennyLaneAI/pennylane/pull/5699)

* A new `qml.noise` module which contains utililty functions for building `NoiseModels`.
[(#5674)](https://github.com/PennyLaneAI/pennylane/pull/5674)

<h3>Improvements 🛠</h3>

Expand Down Expand Up @@ -366,6 +368,7 @@
This release contains contributions from (in alphabetical order):

Guillermo Alonso-Linaje,
Utkarsh Azad,
Lillian M. A. Frederiksen,
Gabriel Bottrill,
Astral Cai,
Expand Down
1 change: 1 addition & 0 deletions pennylane/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@
import pennylane.compiler

import pennylane.data
import pennylane.noise

from packaging.specifiers import SpecifierSet
from packaging.version import Version
Expand Down
135 changes: 125 additions & 10 deletions pennylane/boolean_fn.py
trbromley marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Copyright 2018-2021 Xanadu Quantum Technologies Inc.
# Copyright 2018-2024 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.
Expand All @@ -19,13 +19,14 @@
import functools


# pylint: disable=unnecessary-lambda
class BooleanFn:
r"""Wrapper for simple callables with boolean output that can be
r"""Wrapper for simple callables with Boolean output that can be
manipulated and combined with bit-wise operators.

Args:
fn (callable): Function to be wrapped. It must accept a single
argument, and must return a boolean.
fn (callable): Function to be wrapped. It can accept any number
of arguments, and must return a Boolean.

**Example**

Expand Down Expand Up @@ -77,18 +78,132 @@ class BooleanFn:

"""

def __init__(self, fn):
def __init__(self, fn, name=None):
self.fn = fn
self.name = name or self.fn.__name__
functools.update_wrapper(self, fn)

def __and__(self, other):
return BooleanFn(lambda obj: self.fn(obj) and other.fn(obj))
return And(self, other)

def __or__(self, other):
return BooleanFn(lambda obj: self.fn(obj) or other.fn(obj))
return Or(self, other)

def __xor__(self, other):
return Xor(self, other)

def __invert__(self):
return BooleanFn(lambda obj: not self.fn(obj))
return Not(self)

def __call__(self, *args, **kwargs):
return self.fn(*args, **kwargs)

def __repr__(self):
return f"BooleanFn({self.name})" if not (self.bitwise or self.conditional) else self.name

@property
def bitwise(self):
"""Determine whether wrapped callable performs a bit-wise operation or not.
This checks for the ``operands`` attribute that should be defined by it."""
return bool(getattr(self, "operands", tuple()))

@property
def conditional(self):
"""Determine whether wrapped callable is for a conditional or not.
This checks for the ``condition`` attribute that should be defined by it."""
return bool(getattr(self, "condition", None))


class And(BooleanFn):
"""Developer facing class for implemeting bit-wise ``AND`` for callables
wrapped up with :class:`BooleanFn <pennylane.BooleanFn>`.

Args:
left (~.BooleanFn): Left operand in the bit-wise expression.
right (~.BooleanFn): Right operand in the bit-wise expression.
"""

def __init__(self, left, right):
self.operands = (left, right)

if any(getattr(opr, "condition", None) for opr in self.operands):
self.condition = tuple(getattr(opr, "condition", ()) for opr in self.operands)

super().__init__(
lambda *args, **kwargs: left(*args, **kwargs) and right(*args, **kwargs),
f"And({left.name}, {right.name})",
)

def __str__(self):
return f"{self.operands[0].name} & {self.operands[1].name}"


class Or(BooleanFn):
"""Developer facing class for implemeting bit-wise ``OR`` for callables
wrapped up with :class:`BooleanFn <pennylane.BooleanFn>`.

Args:
left (~.BooleanFn): Left operand in the bit-wise expression.
right (~.BooleanFn): Right operand in the bit-wise expression.
"""

def __init__(self, left, right):
self.operands = (left, right)

if any(getattr(opr, "condition", None) for opr in self.operands):
self.condition = tuple(getattr(opr, "condition", ()) for opr in self.operands)

super().__init__(
lambda *args, **kwargs: left(*args, **kwargs) or right(*args, **kwargs),
f"Or({left.name}, {right.name})",
)

def __str__(self):
return f"{self.operands[0].name} | {self.operands[1].name}"


class Xor(BooleanFn):
"""Developer facing class for implemeting bit-wise ``XOR`` for callables
wrapped up with :class:`BooleanFn <pennylane.BooleanFn>`.

Args:
left (~.BooleanFn): Left operand in the bit-wise expression.
right (~.BooleanFn): Right operand in the bit-wise expression.
"""

def __init__(self, left, right):
self.operands = (left, right)

if any(getattr(opr, "condition", None) for opr in self.operands):
self.condition = tuple(getattr(opr, "condition", ()) for opr in self.operands)

super().__init__(
lambda *args, **kwargs: left(*args, **kwargs) ^ right(*args, **kwargs),
f"Xor({left.name}, {right.name})",
)

def __str__(self):
return f"{self.operands[0].name} ^ {self.operands[1].name}"


class Not(BooleanFn):
"""Developer facing class for implemeting bit-wise ``NOT`` for callables
wrapped up with :class:`BooleanFn <pennylane.BooleanFn>`.

Args:
left (~.BooleanFn): Left operand in the bit-wise expression.
"""

def __init__(self, left):
self.operands = (left,)

if any(getattr(opr, "condition", None) for opr in self.operands):
self.condition = tuple(getattr(opr, "condition", ()) for opr in self.operands)

super().__init__(
lambda *args, **kwargs: not left(*args, **kwargs),
f"Not({left.name})",
)

def __call__(self, obj):
return self.fn(obj)
def __str__(self):
return f"~{self.operands[0].name}"
Loading
Loading