How to use quantum arithmetic operators¶
Published: November 5, 2024. Last updated: November 8, 2024.
Note
Go to the end to download the full example code.
Classical computers handle arithmetic operations like addition, subtraction, multiplication, and exponentiation with ease. For instance, you can multiply large numbers on your phone in milliseconds! Quantum computers can handle these operations too, but their true value lies beyond basic calculations.
Quantum arithmetic plays a crucial role in more advanced quantum algorithms, serving as fundamental building blocks in their design and execution. For example:
-
In Shor’s algorithm quantum arithmetic is crucial for performing modular exponentiation 1.
-
Grover’s algorithm might need to use quantum arithmetic to construct oracles, as shown in this related demo.
-
Loading data or preparing initial states on a quantum computer often requires several quantum arithmetic operations 2.
With PennyLane, you will see how easy it is to build quantum arithmetic operations as subroutines for your algorithms. Knowing your way around these operators could be just the thing to streamline your algorithm design!

In-place and out-place arithmetic operations¶
Let’s begin by defining the terms in-place and out-place in the context of arithmetic operators.
In-place operators, like the Adder
and Multiplier
, directly modify the original quantum state by updating a
specific register’s state. In contrast, out-place operators, such as the OutAdder
and OutMultiplier
,
combine multiple states and store the result in a new register, leaving the original states unchanged. Both kinds of operators are
illustrated in the following figure.

In quantum computing, all arithmetic operations are inherently modular. The default behavior in PennyLane is to perform operations modulo $2^n$, where $n$ is the number of wires in the register. For example, if $n=6$, we have $(32 + 43) = 75 = 11 \mod 64$, (since $2^6 = 64$). That means that quantum registers of $n$ wires can represent numbers up to $2^n$. However, users can specify a custom value smaller than this default. It’s important to keep this modular behavior in mind when working with quantum arithmetic, as using too few qubits in a quantum register could lead to overflow issues. We will come back to this point later.
Addition operators¶
There are two addition operators in PennyLane: Adder
and OutAdder
.
The Adder
operator performs an in-place operation, adding an integer value $k$ to the state of the wires $|x \rangle$. It is defined as:
On the other hand, the OutAdder
operator performs an out-place operation, where the states of two
wires, $|x \rangle$ and $|y \rangle$, are
added together and the result is stored in a third register:
To implement these operators in PennyLane, the first step is to define the registers of wires
we will work with. We define wires for input registers, the output register, and also additional work_wires
that will be important when we later discuss the Multiplier
operator.
import pennylane as qml
# we indicate the name of the registers and their number of qubits.
wires = qml.registers({"x": 4, "y":4, "output":6,"work_wires": 4})
Now, we write a circuit to prepare the state $|x \rangle|y \rangle|0 \rangle$, which will be needed for the out-place operation, where we initialize specific values to $x$ and $y$. Note that in this example we use computational basis states, but you could introduce any quantum state as input.
def product_basis_state(x=0,y=0):
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):
product_basis_state(x, y)
return [qml.sample(wires=wires[name]) for name in ["x", "y", "output"]]
Since the arithmetic operations are deterministic, a single shot is enough to sample from the circuit and extract the expected state in the output register. Next, for understandability, we will use an auxiliary function that will take one sample from the circuit and return the associated decimal number.
def state_to_decimal(binary_array):
# Convert a binary array to a decimal number
return sum(bit * (2 ** idx) for idx, bit in enumerate(reversed(binary_array)))
In this example we are setting $x=1$ and $y=4$ and checking that the results are as expected.
output = circuit(x=1,y=4)
print("x register: ", output[0]," (binary) ---> ", state_to_decimal(output[0]), " (decimal)")
print("y register: ", output[1]," (binary) ---> ", state_to_decimal(output[1]), " (decimal)")
print("output register: ", output[2]," (binary) ---> ", state_to_decimal(output[2]), " (decimal)")
x register: [0 0 0 1] (binary) ---> 1 (decimal)
y register: [0 1 0 0] (binary) ---> 4 (decimal)
output register: [0 0 0 0 0 0] (binary) ---> 0 (decimal)
Now we can implement an example for the Adder
operator. We will add the integer $5$ to the wires["x"]
register
that stores the state $|x \rangle=|1 \rangle$.
@qml.qnode(dev)
def circuit(x):
product_basis_state(x) # |x>
qml.Adder(5, wires["x"]) # |x+5>
return qml.sample(wires=wires["x"])
print(circuit(x=1), " (binary) ---> ", state_to_decimal(circuit(x=1))," (decimal)")
[0 1 1 0] (binary) ---> 6 (decimal)
We obtained the result $5+1=6$, as expected. At this point, it’s worth taking a moment to look at the decomposition of the circuit into quantum gates and operators.
fig, _ = qml.draw_mpl(circuit, decimals = 2, style = "pennylane", level='device')(x=1)
fig.show()

From the Adder
circuit we can see that the addition is performed
in the Fourier basis. This includes a quantum Fourier transformation (QFT) followed by rotations to perform the addition, and
concludes with an inverse QFT transformation. A more detailed explanation on the decomposition of arithmetic operators can be found in
the PennyLane Demo on quantum arithmetic with the QFT.
Now, let’s see an example for the OutAdder
operator to add the states
$|x \rangle$ and $|y \rangle$ to the output register.
@qml.qnode(dev)
def circuit(x,y):
product_basis_state(x, y) # |x> |y> |0>
qml.OutAdder(wires["x"], wires["y"], wires["output"]) # |x> |y> |x+y>
return qml.sample(wires=wires["output"])
print(circuit(x=2,y=3), " (binary) ---> ", state_to_decimal(circuit(x=2,y=3)), " (decimal)")
[0 0 0 1 0 1] (binary) ---> 5 (decimal)
We obtained the result $2+3=5$, as expected.
Multiplication operators¶
There are two multiplication operators in PennyLane: the Multiplier
and the OutMultiplier
.
The class Multiplier
performs an in-place operation, multiplying the state of the wires $|x \rangle$ by an integer $k$. It is defined as:
The OutMultiplier
performs an out-place operation, where the states of two
registers, $|x \rangle$ and $|y \rangle$,
are multiplied together and the result is stored in a third register:
We proceed to implement these operators in PennyLane. First, let’s see an example for the
Multiplier
operator. We will multiply the state $|x \rangle=|2 \rangle$ by
the integer $k=3$:
@qml.qnode(dev)
def circuit(x):
product_basis_state(x) # |x>
qml.Multiplier(3, wires["x"], work_wires=wires["work_wires"]) # |3x>
return qml.sample(wires=wires["x"])
print(circuit(x=2), " (binary) ---> ", state_to_decimal(circuit(x=2))," (decimal)")
[0 1 1 0] (binary) ---> 6 (decimal)
We got the expected result of $3 \cdot 2 = 6$.
Now, let’s look at an example using the OutMultiplier
operator to multiply the states $|x \rangle$ and
$|y \rangle$, storing the result in the output register.
@qml.qnode(dev)
def circuit(x,y):
product_basis_state(x, y) # |x> |y> |0>
qml.OutMultiplier(wires["x"], wires["y"], wires["output"]) # |x> |y> |xy>
return qml.sample(wires=wires["output"])
print(circuit(x=4,y=2), " (binary) ---> ", state_to_decimal(circuit(x=4,y=2))," (decimal)")
[0 0 1 0 0 0] (binary) ---> 8 (decimal)
Nice! Modular subtraction and division can also be implemented as the inverses of addition and
multiplication, respectively. The inverse of a quantum circuit can be implemented with the
adjoint()
operator. Let’s see an example of modular subtraction.
@qml.qnode(dev)
def circuit(x):
product_basis_state(x) # |x>
qml.adjoint(qml.Adder(3, wires["x"])) # |x-3>
return qml.sample(wires=wires["x"])
print(circuit(x=6), " (binary) ---> ", state_to_decimal(circuit(x=6)), " (decimal)")
[0 0 1 1] (binary) ---> 3 (decimal)
As predicted, this gives us $6-3=3$.
Implementing a polynomial on a quantum computer¶
Now that you are familiar with these operations, let’s take it a step further and see how we can use them for something more complicated. We will explore how to implement a polynomial function on a quantum computer using basic arithmetic. In particular, we will use $f(x,y)= 4 + 3xy + 5 x+ 3 y$ as an example, where the variables $x$ and $y$ are integer values. Therefore, the operator we want to build is:
We will show how to implement this circuit in two different ways: first, by concatenating simple modular arithmetic operators,
and finally, using the OutPoly
operator.
Concatenating arithmetic operations¶
Let’s start by defining the arithmetic operations to apply the function $f(x,y) = 4 + 3xy + 5x + 3y$ into a quantum state.
First, we need to define a function that will add the term $3xy$ to the output register. We will use
the Multiplier
and OutMultiplier
operators for this. Also, we will employ the
adjoint function to undo certain multiplications, since $U$ does not modify the input registers.
def adding_3xy():
# |x> ---> |3x>
qml.Multiplier(3, wires["x"], work_wires=wires["work_wires"])
# |3x>|y>|0> ---> |3x>|y>|3xy>
qml.OutMultiplier(wires["x"], wires["y"], wires["output"])
# We return the x-register to its original value using the adjoint operation
# |3x>|y>|3xy> ---> |x>|y>|3xy>
qml.adjoint(qml.Multiplier)(3, wires["x"], work_wires=wires["work_wires"])
Then we need to add the term $5x + 3y$ to the output register, which can be done by using the
Multiplier
and OutAdder
operators.
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 their original value using the adjoint operation
# |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"])
Now we can combine all these circuits to implement the transformation by the polynomial $f(x,y)= 4 + 3xy + 5 x+ 3 y$.
@qml.qnode(dev)
def circuit(x,y):
product_basis_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), " (binary) ---> ", state_to_decimal(circuit(x=1,y=4)), " (decimal)")
[1 0 0 0 0 1] (binary) ---> 33 (decimal)
Cool, we get the correct result, $f(1,4)=33$.
At this point, it’s interesting to consider what would happen if we had chosen a smaller number of wires for the output. For instance, if we had selected one wire fewer, we would have obtained the result $33 \mod 2^5 = 1$.
wires = qml.registers({"x": 4, "y": 4, "output": 5,"work_wires": 4})
print(circuit(x=1,y=4), " (binary) ---> ", state_to_decimal(circuit(x=1,y=4)), " (decimal)")
[0 0 0 0 1] (binary) ---> 1 (decimal)
With one fewer wire, we get $1$, just like we predicted. Remember, we are working with modular arithmetic!
Using the OutPoly function¶
There is a more direct method to apply polynomial transformations in PennyLane:
using OutPoly
.
This operator automatically takes care of all the arithmetic under the hood.
Let’s check out how to apply a function like $f(x, y)$ using OutPoly
.
We will start by explicitly defining our function.
def f(x, y):
return 4 + 3*x*y + 5*x + 3*y
Now, we create a quantum circuit using OutPoly
.
wires = qml.registers({"x": 4, "y":4, "output":6})
@qml.qnode(dev)
def circuit_with_Poly(x,y):
product_basis_state(x, y) # |x> |y> |0>
qml.OutPoly(
f,
input_registers= [wires["x"], wires["y"]],
output_wires = wires["output"]) # |x> |y> |4 + 3xy + 5x + 3y>
return qml.sample(wires = wires["output"])
print(circuit_with_Poly(x=1,y=4), " (binary) ---> ", state_to_decimal(circuit_with_Poly(x=1,y=4)), " (decimal)")
[1 0 0 0 0 1] (binary) ---> 33 (decimal)
You can decide, depending on the problem you are tackling, whether to go for the versatility
of defining your own arithmetic operations or the convenience of using the OutPoly
function.
Conclusion¶
Understanding and implementing quantum arithmetic is a key step toward unlocking the full potential of quantum computing. By leveraging quantum arithmetic operations in PennyLane, 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 are now equipped to tackle the exciting quantum challenges ahead.
References¶
- 1
-
Robert L Singleton Jr “Shor’s Factoring Algorithm and Modular Exponentiation Operators.”, arXiv:2306.09122, 2023.
- 2
-
Yuval R. Sanders, Guang Hao Low, Artur Scherer, Dominic W. Berry “Black-box quantum state preparation without arithmetic.”, arXiv:1807.03206, 2018.
Jorge (Gorka) Martinez de Lejarza
Quantum algorithms for High Energy Physics
Guillermo Alonso
Quantum Scientist specializing in quantum algorithms
Share demo