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

How-to: Quantum Arithmetic #1229

Open
wants to merge 22 commits into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 9 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 Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,6 @@ environment:
$$PYTHON_VENV_PATH/bin/python -m pip install --upgrade git+https://github.com/PennyLaneAI/pennylane-qulacs.git#egg=pennylane-qulacs;\
$$PYTHON_VENV_PATH/bin/python -m pip install --extra-index-url https://test.pypi.org/simple/ PennyLane-Catalyst --pre --upgrade;\
$$PYTHON_VENV_PATH/bin/python -m pip install --extra-index-url https://test.pypi.org/simple/ PennyLane-Lightning --pre --upgrade;\
$$PYTHON_VENV_PATH/bin/python -m pip install --upgrade git+https://github.com/PennyLaneAI/pennylane.git#egg=pennylane;\
$$PYTHON_VENV_PATH/bin/python -m pip install --upgrade git+https://github.com/PennyLaneAI/pennylane.git@qml_outpoly#egg=pennylane;\
fi;\
fi
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
{
"title": "How-to use Quantum Arithmetic Operators",
"authors": [
{
"id": "gmlejarza"
},
{
"id": "KetPuntoG"
}
],
"dateOfPublication": "2024-10-01T00:00:00+00:00",
"dateOfLastModification": "2024-10-01T00:00:00+00:00",
"categories": [
"Quantum Computing",
"Algorithms"
],
"tags": [],
"previewImages": [
{
"type": "thumbnail",
"uri": "/_static/demo_thumbnails/regular_demo_thumbnails/thumbnail_qnn_multivariate_regression.png"
},
{
"type": "large_thumbnail",
"uri": "/_static/demo_thumbnails/large_demo_thumbnails/thumbnail_large_qnn_multivariate_regression.png"
}
],
"seoDescription": "Learn how to use the quantum arithmetic operators of Pennylane.",
"doi": "",
"canonicalURL": "/qml/demos/tutorial_how_to_use_arithmetic_operators",
"references": [
{
"id": "demo_qft_arith",
"type": "other",
"title": "Basic arithmetic with the quantum Fourier transform (QFT)",
"authors": "Guillermo Alonso",
"year": "2024",
"publisher": "",
"url": "https://pennylane.ai/qml/demos/tutorial_qft_arithmetics/"
}
],
"basedOnPapers": [],
"referencedByPapers": [],
"relatedContent": [
{
"type": "demonstration",
"id": "tutorial_qft_arithmetics",
"weight": 1.0
}
]
}
249 changes: 249 additions & 0 deletions demonstrations/tutorial_how_to_use_arithmetic_operators.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,249 @@
r"""
How-to use Quantum Arithmetic Operators
=======================================
gmlejarza marked this conversation as resolved.
Show resolved Hide resolved

Classical computers handle arithmetic operations like addition, subtraction, multiplication, and exponentiation with ease.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like the intro 👍

For instance, you can multiply two numbers on your phone in milliseconds!
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
For instance, you can multiply two numbers on your phone in milliseconds!
For instance, you can multiply large numbers on your phone in milliseconds!


Quantum computers, however, aren't as efficient at basic arithmetic. So why do we need quantum arithmetic?
While it's not "better" for basic operations, it's essential in more complex quantum algorithms. For example:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Too negative and not really true! Quantum hardware currently has a hard time with arithmetic, but in principle a quantum computer can do this with ease. I suggest instead motivating quantum arithmetic by saying that we don't plan to use quantum computers as calculators, but arithmetic is an important building block


1. In Shor's algorithm quantum arithmetic is crucial for performing modular exponentiation.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add link


2. Grover's algorithm might need to use quantum arithmetic to construct oracles, as shown in [#demo_qft_arith]_.

3. Loading functions into quantum computers, which often requires several quantum arithmetic operations.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add link


These arithmetic operations act as building blocks that enable powerful quantum computations when integrated into larger algorithms.
With PennyLane, you'll see how easy it is to build these operations as subroutines for your quantum algorithms!


Loading a function :math:`f(x, y)`
gmlejarza marked this conversation as resolved.
Show resolved Hide resolved
----------------------------------

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

an image showing a sketch of an inplace and outplace operator could look good here :)

In this how-to guide, we will show how we can apply a polynomial function in a quantum computer using basic arithmetic.
We will use as an example the function :math:`f(x,y)= 4 + 3xy + 5 x+ 3 y` where the variables :math:`x` and :math:`y`
are integer values. Therefore, the operator we want to build is:

.. math::

U|x\rangle |y\rangle |0\rangle = |x\rangle |y\rangle |4 + 3xy + 5x + 3y\rangle,

where :math:`x` and :math:`y` are the binary representations of the integers on which we want to apply the function.

gmlejarza marked this conversation as resolved.
Show resolved Hide resolved
We will show how to load this function in two different ways: first, by concatenating simple arithmetic operators,
and finally, using the `qml.OutPoly` operator.

InPlace and OutPlace Operations
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

We can load the target function into the quantum computer using different quantum arithmetic operations.
We will break down into pieces. We'll do a step by step load of the function :math:`f(x, y)`.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is repetitive compared to the previous sentences


The first thing to do is to define the `registers of wires <https://pennylane.ai/qml/demos/tutorial_how_to_use_registers/>`_
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice!

we will work with:"""

import pennylane as qml

# we indicate the name of the registers and their number of qubits
wires = qml.registers({"x": 4, "y":4, "output":5,"work_wires": 4})

######################################################################
# Before we start applying our arithmetic operators, we can initialize specific values to :math:`x` and :math:`y`
# which will help us to check that the results obtained are correct.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just say we first write a circuit to prepare |x>|y>|0>, which we should have referenced before as the starting point, e.g., we should say that Adder is |x>|y>|0> --> |x>|y>|x+y>

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So basically I suggest that you first define what addition looks like in quantum and show both versions. Then describe the circuit to implement it


def prepare_initial_state(x,y):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't like the name "prepare initial state". Actually preparing a target quantum state is very difficult. This is just initializing registers to a basis state. Consider a better name. product_basis_state comes to mind

qml.BasisState(x, wires=wires["x"])
qml.BasisState(y, wires=wires["y"])


dev = qml.device("default.qubit", shots=1)
@qml.qnode(dev)
def circuit(x,y):
prepare_initial_state(x, y)
return [qml.sample(wires=wires[name]) for name in ["x", "y", "output"]]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why are we sampling? maybe good to explain in a comment

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can't we just return the state?

Copy link
Collaborator Author

@gmlejarza gmlejarza Oct 15, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The idea is that if we sample we are showing that these results can also be obtained in hardware, where access to the state is not possible. I added an explanation in case it was not clear 👍


output = circuit(x=1,y=4)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suggest that in order to do not make the reader think that this whole cell is for initialization, you could break the cell here and say:
“Next, for understandability, we will use an auxiliary function that will take 1 sample from the circuit and return the associated decimal number."

or similar

You may also move the sampling note of the next paragraph here

print("x register: ", output[0])
print("y register: ", output[1])
print("output register: ", output[2])

######################################################################
# In this example we are setting :math:`x=1` and :math:`y=4` whose binary representation are :math:`[0 0 0 1]` and :math:`[0 1 0 0]` respectively.
#
# Now, the first step to load :math:`f(x, y) =4 + 3xy + 5 x+ 3 y` will be
# to add the constant :math:`4` by using the Inplace addition operator :class:`~.pennylane.Adder` directly on the output wires:
#
# .. math::
#
# \text{Adder}(k) |w \rangle = | w+k \rangle.
#
# In our example, we are considering the input :math:`|w\rangle = |0\rangle` and :math:`k = 4`.
gmlejarza marked this conversation as resolved.
Show resolved Hide resolved
# Let's see how it looks like in code:

@qml.qnode(dev)
def circuit(x,y):

prepare_initial_state(x, y) # |x> |y> |0>
qml.Adder(4, wires["output"]) # |x> |y> |4>

return qml.sample(wires=wires["output"])

print(circuit(x=1,y=4))

######################################################################
# We obtained the state [0 0 1 0 0], i.e. :math:`4`, as expected.
# Note that we have not used the :math:`x` and :math:`y` wires for the moment since the constant term does not depend on these values.
#
# The next step will be to add the term :math:`3xy` by using the
# `Inplace multiplication <https://docs.pennylane.ai/en/stable/code/api/pennylane.Multiplier.html>`_
#
# .. math::
#
# \text{Multiplier}(k) |w \rangle = | kw \rangle,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
# \text{Multiplier}(k) |w \rangle = | kw \rangle,
# \text{Multiplier}(k) |w \rangle = | kw \rangle.

#
# and the `Outplace multiplication <https://docs.pennylane.ai/en/stable/code/api/pennylane.OutMultiplier.html>`_
#
# .. math::
#
# \text{OutMultiplier} |w \rangle |z \rangle |0 \rangle = |w \rangle |z \rangle |wz \rangle.
#
# To do this, we first turn :math:`|x\rangle` into :math:`|3x\rangle` with the Inplace multiplication. After that
# we will multiply the result by :math:`|y\rangle`
# using the Outplace operator:

def adding_3xy():
# |x> ---> |3x>
qml.Multiplier(3, wires["x"], work_wires=wires["work_wires"])

# |3x>|y>|0> ---> |3x>|y>|3xy>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You haven't really explained why this is needed. It may also not be super clear to all readers that the uncomputation is done using the adjoint. For example, it may be a good opportunity to mention that we can use adders and multipliers to effectively subtract and divide.

In fact I'm curious, would the multiplier work if we multiply by 1/3? Multiply by sqrt{2}? It's not clear what are the allowed values for such scalars. Seems like only integers make sense

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As suggested, I added an explanation about how the subtraction and division can be achieved by using the adjoint. Regarding the possibility of multiplying by non-integer numbers, the documentation of each operator specifies that the constant used for addition/multiplication is an integer: https://docs.pennylane.ai/en/stable/code/api/pennylane.Adder.html , https://docs.pennylane.ai/en/stable/code/api/pennylane.Multiplier.html

qml.OutMultiplier(wires["x"], wires["y"], wires["output"])

# We return the x-register to its original value
# |3x>|y>|3xy> ---> |x>|y>|3xy>
qml.adjoint(qml.Multiplier)(3, wires["x"], work_wires=wires["work_wires"])

@qml.qnode(dev)
def circuit(x,y):

prepare_initial_state(x, y) # |x> |y> |0>
qml.Adder(4, wires["output"]) # |x> |y> |4>
adding_3xy() # |x> |y> |4 + 3xy>

return qml.sample(wires=wires["output"])

print(circuit(x=1,y=4))

######################################################################
# Nice! The state [1 0 0 0 0] represents :math:`4+3xy = 4 + 12 =16`.
#
# The final step to compute :math:`f(x, y)` is to generate the monomial terms :math:`5x` and :math:`3y` using the previously employed
# :class:`~.pennylane.Multiplier`. These terms are then added to the output register using the :class:`~.pennylane.OutAdder` operator:
#
# .. math::
#
# \text{OutAdder} |w \rangle |z \rangle |0 \rangle = |w \rangle |z \rangle |w + z \rangle,
#
# where in our case, :math:`|w\rangle` and :math:`|z\rangle` are :math:`|5x\rangle` and :math:`|3y\rangle` respectively.
#

def adding_5x_3y():

# |x>|y> ---> |5x>|3y>
qml.Multiplier(5, wires["x"], work_wires=wires["work_wires"])
qml.Multiplier(3, wires["y"], work_wires=wires["work_wires"])

# |5x>|3y>|0> ---> |5x>|3y>|5x + 3y>
qml.OutAdder(wires["x"], wires["y"], wires["output"])

# We return the x and y registers to its original value
# |5x>|3y>|5x + 3y> ---> |x>|y>|5x + 3y>
qml.adjoint(qml.Multiplier)(5, wires["x"], work_wires=wires["work_wires"])
qml.adjoint(qml.Multiplier)(3, wires["y"], work_wires=wires["work_wires"])


@qml.qnode(dev)
def circuit(x,y):

prepare_initial_state(x, y) # |x> |y> |0>
qml.Adder(4, wires["output"]) # |x> |y> |4>
adding_3xy() # |x> |y> |4 + 3xy>
adding_5x_3y() # |x> |y> |4 + 3xy + 5x + 3y>


return qml.sample(wires=wires["output"])

print(circuit(x=1,y=4))

######################################################################
# The result doesn't seem right, as we expect to get :math:`f(x=1, y=4) = 33`. Can you guess why?
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, showing incorrect results is not good in a demo! Much better to clarify from the beginning that all arithmetic is done modulo the largest number that can be supported given the number of qubits. Then you have an example that works, and THEN you can quickly change inputs to showcase overflow, tying back to the how this was mentioned before.

In fact getting 1 instead of 33 is not wrong, it's what we want and expect. We just have to remember that this is all modular arithmetic (hence why it's good to clarify from the beginning)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point! I clarified from the beginning that all arithmetic is modular. Then, changed the order to show first the "expected" result (33) and then reduce the number of wires to showcase the overflow and remember that quantum arithmetic is modular.

#
# The issue is overflow. The number 33 exceeds what can be represented with the 5 wires defined in `wires["output"]`.
# With 5 wires, we can only represent numbers up to :math:`2^5 = 32`. Anything larger is reduced modulo :math:`2^5`.
# Quantum arithmetic is modular, so every operation is mod-based.
#
# To fix this and get :math:`f(x=1, y=4) = 33`, we could just add one more wire to the output register.
#

wires = qml.registers({"x": 4, "y": 4, "output": 6,"work_wires": 4})
print(circuit(x=1, y=4))

######################################################################
# Now we get the correct result where :math:`[1 0 0 0 0 1]` is the binary representation of :math:`33`.

######################################################################
# Using OutPoly
# ~~~~~~~~~~~~~
# In the last section, we showed how to use different arithmetic operations to load
# a function onto a quantum computer. But what if I told you there's an easier way to do all this using just one
# PennyLane function that handles the arithmetic for you? Pretty cool, right? I'm talking about :class:`~.pennylane.OutPoly`.
gmlejarza marked this conversation as resolved.
Show resolved Hide resolved
# This handy operator lets you load polynomials directly into quantum states, taking care of all the arithmetic in one go.
# Let's check out how to load a function like :math:`f(x, y)` using :class:`~.pennylane.OutPoly`.
#
# Let's first start by explicitly defining our function:

def f(x, y):
return 4 + 3*x*y + 5*x + 3*y

######################################################################
# Now, we load it into a quantum circuit.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
# Now, we load it into a quantum circuit.
# Now, we create a quantum circuit using :class:`~.pennylane.OutPoly`.


######################################################################

@qml.qnode(dev)
def circuit_with_Poly(x,y):

prepare_initial_state(x, y)
qml.OutPoly(f, registers_wires=[wires["x"], wires["y"], wires["output"]])
KetpuntoG marked this conversation as resolved.
Show resolved Hide resolved

return qml.sample(wires = wires["output"])

print(circuit_with_Poly(x=1,y=4))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

By the way, I'm thinking it may be nice to code up a simple function to convert binary into integers so we can print out the numerical value directly alongside the bitstring description. If I remember correctly, numpy or scipy have these already

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, is there a way to visualize the circuits that doesn't just show OutPoly or something? It would be great to see how they are decomposed into basic operations

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unfortunately, there's no such built-in function that automatically converts a list representing a binary number into its decimal form. However, I've coded a custom function to print out the numerical value alongside the bitstring.

Regarding visualization, I included a printout of the Adder circuit, showing that the addition uses a QFT to switch the basis and then applies rotations to perform the addition in the Fourier basis. I think, this operator is the most pedagogical to showcase, since it serves as a building block to build the others. I also referred the reader to this demo, where the decomposition is further explained: https://pennylane.ai/qml/demos/tutorial_qft_arithmetics/


######################################################################
# Eureka! We've just seen how much easier it can be to implement arithmetic operations in one step.
# Now, it's up to you to decide, depending on the problem you're tackling, whether to go for the versatility
# of defining your own arithmetic operations or the convenience of using the :class:`~.pennylane.OutPoly` function.

######################################################################
# Conclusion
# ------------------------------------------
# Understanding and implementing quantum arithmetic is a key step toward unlocking the full potential
# of quantum computing. While it may not replace classical efficiency for simple tasks, its role in complex algorithms
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove the second sentence

# is undeniable. By leveraging tools like `qml.OutPoly`, you can streamline the coding of your quantum algorithms. So,
# whether you choose to customize your arithmetic operations or take advantage of the built-in convenience offered by PennyLane
# operators, you're now equipped to tackle the exciting quantum challenges ahead.
#
# References
# ----------
#
# .. [#demo_qft_arith]
#
# Guillermo Alonso
# "Basic arithmetic with the quantum Fourier transform (QFT)",
# `Pennylane: Basic arithmetic with the quantum Fourier transform (QFT) <https://pennylane.ai/qml/demos/tutorial_qft_arithmetics/>`__, 2024
#
# About the authors
# -----------------

Loading