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

Multiplier Operators Quantum Arithmetic #6112

Merged
merged 70 commits into from
Aug 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
70 commits
Select commit Hold shift + click to select a range
9b043c1
in operators and tests
gmlejarza Aug 14, 2024
5abfea5
rename inplace ops
gmlejarza Aug 16, 2024
8dcc9e1
removing multiplier from this PR
gmlejarza Aug 16, 2024
61d8a72
first commit
gmlejarza Aug 16, 2024
348d998
adding test out_mult
gmlejarza Aug 16, 2024
dc64251
fixing some tests
gmlejarza Aug 19, 2024
54aa27a
suggestions and fixing codecov
gmlejarza Aug 19, 2024
91b492e
documentation fixes
gmlejarza Aug 19, 2024
17cd506
test name fix
gmlejarza Aug 19, 2024
1545463
extra minor fixes
gmlejarza Aug 19, 2024
03ba8bd
fix imports
gmlejarza Aug 19, 2024
8aeb859
adding David feedback on tests
gmlejarza Aug 19, 2024
7bac62b
suggestions from code review
gmlejarza Aug 19, 2024
c11b10b
fix documentation adder
gmlejarza Aug 19, 2024
ec5ad75
suggestions from code review
gmlejarza Aug 21, 2024
57d3a10
suggestions code review and fixes
gmlejarza Aug 21, 2024
2848410
small fix
gmlejarza Aug 21, 2024
d6b1357
minor test fix
gmlejarza Aug 21, 2024
d763eb6
import fix
gmlejarza Aug 21, 2024
644a865
updating phase_adder and test
gmlejarza Aug 21, 2024
70cbe03
black
gmlejarza Aug 21, 2024
0445f90
fix init
gmlejarza Aug 21, 2024
98e991c
fix adder tests
gmlejarza Aug 21, 2024
8d20ada
fixing adder
gmlejarza Aug 21, 2024
0d3dbb6
fixing tests multipliers
gmlejarza Aug 21, 2024
054bfda
fix test adder
gmlejarza Aug 21, 2024
6057422
fixing adder
gmlejarza Aug 21, 2024
cda7832
fix local tests
gmlejarza Aug 21, 2024
0724f4d
minor change unflatten
gmlejarza Aug 21, 2024
823f93f
fixing test_adder
gmlejarza Aug 21, 2024
625b0d5
fix test_adder
gmlejarza Aug 21, 2024
7bb4dda
removing primitive_bind
gmlejarza Aug 21, 2024
700c76d
fix capture
gmlejarza Aug 21, 2024
cde0d7e
minor change test_templates
gmlejarza Aug 21, 2024
221c9cb
multiplier passing local tests
gmlejarza Aug 21, 2024
b1ed1d9
out_multiplier passing local tests
gmlejarza Aug 21, 2024
0382654
cleaning templates
KetpuntoG Aug 21, 2024
73bb41c
test_adder
KetpuntoG Aug 21, 2024
34ff2f1
test_phase_adder
KetpuntoG Aug 21, 2024
c6a3af6
adder
KetpuntoG Aug 21, 2024
8ff21f6
cleaning adder phaseadder
gmlejarza Aug 21, 2024
344f7fd
phase_adder
KetpuntoG Aug 21, 2024
10712a6
Merge branch 'in_arithmetics' into multipliers_arithmetic
KetpuntoG Aug 21, 2024
70ae944
Update pennylane/templates/subroutines/adder.py
KetpuntoG Aug 21, 2024
4078b7e
adding test capture
gmlejarza Aug 21, 2024
218897b
fix work_wires errors
gmlejarza Aug 21, 2024
3b6fd0a
fix test_multiplier
gmlejarza Aug 21, 2024
f885553
fix pylint errors
gmlejarza Aug 21, 2024
e21fb5b
test_multiplier import fix
gmlejarza Aug 22, 2024
3d30d71
fixing test_multiplier
gmlejarza Aug 22, 2024
75f201a
changing docstrings mul y outmult
gmlejarza Aug 22, 2024
132e88c
sphinx
KetpuntoG Aug 22, 2024
cb0c4af
cleaning templates
KetpuntoG Aug 22, 2024
ffd8b08
Update multiplier.py
KetpuntoG Aug 22, 2024
a577b68
Update pennylane/templates/subroutines/multiplier.py
KetpuntoG Aug 23, 2024
80e7f38
suggestions from code review
gmlejarza Aug 23, 2024
79d6eca
Update adder.py
KetpuntoG Aug 23, 2024
70020bd
Update phase_adder.py
KetpuntoG Aug 23, 2024
444f40a
Update test_adder.py
KetpuntoG Aug 23, 2024
30e9d4b
Update test_phase_adder.py
KetpuntoG Aug 23, 2024
6365e3e
Update adder.py
KetpuntoG Aug 23, 2024
196bda9
Merge branch 'master' into multipliers_arithmetic
KetpuntoG Aug 23, 2024
ba24892
Update pennylane/templates/subroutines/__init__.py
KetpuntoG Aug 23, 2024
3dfbd9e
update multiplier
soranjh Aug 23, 2024
bc08d58
update outmultiplier
soranjh Aug 23, 2024
31ee581
Merge branch 'multipliers_arithmetic' of https://github.com/PennyLane…
soranjh Aug 23, 2024
bca767c
Merge branch 'master' into multipliers_arithmetic
soranjh Aug 23, 2024
a3423f7
update tests
soranjh Aug 23, 2024
85efee7
update changelog
soranjh Aug 23, 2024
23023e5
Merge branch 'master' into multipliers_arithmetic
soranjh Aug 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
3 changes: 3 additions & 0 deletions doc/releases/changelog-dev.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@
* The `qml.Adder` and `qml.PhaseAdder` templates are added to perform in-place modular addition.
[(#6109)](https://github.com/PennyLaneAI/pennylane/pull/6109)

* The `qml.Multiplier` and `qml.OutMultiplier` templates are added to perform modular multiplication.
[(#6112)](https://github.com/PennyLaneAI/pennylane/pull/6112)

<h4>Creating spin Hamiltonians 🧑‍🎨</h4>

* The function ``transverse_ising`` is added to generate transverse-field Ising Hamiltonian.
Expand Down
2 changes: 2 additions & 0 deletions pennylane/templates/subroutines/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,3 +47,5 @@
from .qrom import QROM
from .phase_adder import PhaseAdder
from .adder import Adder
from .multiplier import Multiplier
from .out_multiplier import OutMultiplier
5 changes: 2 additions & 3 deletions pennylane/templates/subroutines/adder.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,8 @@ class Adder(Operation):

.. note::

Note that :math:`x` must be smaller than :math:`mod` to get the correct result. Also, when
:math:`mod \neq 2^{\text{len(x\_wires)}}` we need :math:`x < 2^{\text{len(x\_wires)}}/2`,
which means that we need one extra wire in ``x_wires``.
Note that :math:`x` must be smaller than :math:`mod` to get the correct result.


Args:
k (int): the number that needs to be added
Expand Down
197 changes: 197 additions & 0 deletions pennylane/templates/subroutines/multiplier.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,197 @@
# 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.
# 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.
"""
Contains the Multiplier template.
"""

import numpy as np

import pennylane as qml
from pennylane.operation import Operation


def _mul_out_k_mod(k, x_wires, mod, work_wire_aux, wires_aux):
"""Performs :math:`x \times k` in the registers wires wires_aux"""
op_list = []

op_list.append(qml.QFT(wires=wires_aux))
op_list.append(
qml.ControlledSequence(qml.PhaseAdder(k, wires_aux, mod, work_wire_aux), control=x_wires)
)
op_list.append(qml.adjoint(qml.QFT(wires=wires_aux)))
return op_list


class Multiplier(Operation):
r"""Performs the in-place modular multiplication operation.

This operator performs the modular multiplication by an integer :math:`k` modulo :math:`mod` in
the computational basis:

.. math::

\text{Multiplier}(k,mod) |x \rangle = | x \cdot k \; \text{modulo} \; \text{mod} \rangle.

The implementation is based on the quantum Fourier transform method presented in
`arXiv:2311.08555 <https://arxiv.org/abs/2311.08555>`_.

.. note::

Note that :math:`x` must be smaller than :math:`mod` to get the correct result. Also, it
is required that :math:`k` has inverse, :math:`k^-1`, modulo :math:`mod`. That means
:math:`k*k^-1 modulo mod is equal to 1`, which will only be possible if :math:`k` and
:math:`mod` are coprime. Furthermore, if :math:`mod \neq 2^{len(x\_wires)}`, two more
auxiliaries must be added.

Args:
k (int): the number that needs to be multiplied
x_wires (Sequence[int]): the wires the operation acts on
mod (int): the modulus for performing the multiplication, default value is :math:`2^{len(x\_wires)}`
work_wires (Sequence[int]): the auxiliary wires to be used for performing the multiplication

**Example**

This example performs the multiplication of two integers :math:`x=3` and :math:`k=4` modulo :math:`mod=7`.

.. code-block::

x = 3
k = 4
mod = 7

x_wires =[0,1,2]
work_wires=[3,4,5,6,7]

dev = qml.device("default.qubit", shots=1)
@qml.qnode(dev)
def circuit(x, k, mod, wires_m, work_wires):
qml.BasisEmbedding(x, wires=wires_m)
qml.Multiplier(k, x_wires, mod, work_wires)
return qml.sample(wires=wires_m)

.. code-block:: pycon

>>> print(circuit(x, k, mod, x_wires, work_wires))
[1 0 1]

The result :math:`[1 0 1]`, is the ket representation of
:math:`3 \cdot 4 \, \text{modulo} \, 12 = 5`.
"""

grad_method = None

def __init__(
self, k, x_wires, mod=None, work_wires=None, id=None
): # pylint: disable=too-many-arguments
if any(wire in work_wires for wire in x_wires):
raise ValueError("None of the wire in work_wires should be included in x_wires.")

if mod is None:
mod = 2 ** len(x_wires)
if mod != 2 ** len(x_wires) and len(work_wires) < (len(x_wires) + 2):
raise ValueError("Multiplier needs as many work_wires as x_wires plus two.")
if len(work_wires) < len(x_wires):
raise ValueError("Multiplier needs as many work_wires as x_wires.")
if (not hasattr(x_wires, "__len__")) or (mod > 2 ** len(x_wires)):
raise ValueError("Multiplier must have enough wires to represent mod.")

k = k % mod
if np.gcd(k, mod) != 1:
raise ValueError("The operator cannot be built because k has no inverse modulo mod.")

self.hyperparameters["k"] = k
self.hyperparameters["mod"] = mod
self.hyperparameters["work_wires"] = qml.wires.Wires(work_wires)
self.hyperparameters["x_wires"] = qml.wires.Wires(x_wires)
all_wires = qml.wires.Wires(x_wires) + qml.wires.Wires(work_wires)
super().__init__(wires=all_wires, id=id)

@property
def num_params(self):
return 0

def _flatten(self):
metadata = tuple((key, value) for key, value in self.hyperparameters.items())
return tuple(), metadata

@classmethod
def _unflatten(cls, data, metadata):
hyperparams_dict = dict(metadata)
return cls(**hyperparams_dict)

def map_wires(self, wire_map: dict):
new_dict = {
key: [wire_map.get(w, w) for w in self.hyperparameters[key]]
for key in ["x_wires", "work_wires"]
}

return Multiplier(
self.hyperparameters["k"],
new_dict["x_wires"],
self.hyperparameters["mod"],
new_dict["work_wires"],
)

@property
def wires(self):
"""All wires involved in the operation."""
return self.hyperparameters["x_wires"] + self.hyperparameters["work_wires"]

def decomposition(self): # pylint: disable=arguments-differ
return self.compute_decomposition(**self.hyperparameters)

@classmethod
def _primitive_bind_call(cls, *args, **kwargs):
return cls._primitive.bind(*args, **kwargs)

@staticmethod
def compute_decomposition(k, x_wires, mod, work_wires): # pylint: disable=arguments-differ
r"""Representation of the operator as a product of other operators.
Args:
k (int): the number that needs to be multiplied
x_wires (Sequence[int]): the wires the operation acts on
mod (int): the modulus for performing the multiplication, default value is :math:`2^{len(x\_wires)}`
work_wires (Sequence[int]): the auxiliary wires to be used for performing the multiplication
Returns:
list[.Operator]: Decomposition of the operator

**Example**

>>> qml.Multiplier.compute_decomposition(k=3, mod=8, x_wires=[0,1,2], work_wires=[3,4,5])
[QFT(wires=[3, 4, 5]),
ControlledSequence(PhaseAdder(wires=[3, 4 , 5 , None]), control=[0, 1, 2]),
Adjoint(QFT(wires=[3, 4, 5])),
SWAP(wires=[0, 3]),
SWAP(wires=[1, 4]),
SWAP(wires=[2, 5]),
Adjoint(Adjoint(QFT(wires=[3, 4, 5]))),
Adjoint(ControlledSequence(PhaseAdder(wires=[3, 4, 5, None]), control=[0, 1, 2])),
Adjoint(QFT(wires=[3, 4, 5]))]
"""

op_list = []
if mod != 2 ** len(x_wires):
work_wire_aux = work_wires[:1]
wires_aux = work_wires[1:]
wires_aux_swap = wires_aux[1:]
else:
work_wire_aux = None
wires_aux = work_wires[: len(x_wires)]
wires_aux_swap = wires_aux
op_list.extend(_mul_out_k_mod(k, x_wires, mod, work_wire_aux, wires_aux))
for x_wire, aux_wire in zip(x_wires, wires_aux_swap):
op_list.append(qml.SWAP(wires=[x_wire, aux_wire]))
inv_k = pow(k, -1, mod)
op_list.extend(qml.adjoint(_mul_out_k_mod)(inv_k, x_wires, mod, work_wire_aux, wires_aux))
return op_list
Loading
Loading