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

qml.Reflection Operation #5159

Merged
merged 75 commits into from
Mar 5, 2024
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
Show all changes
75 commits
Select commit Hold shift + click to select a range
8c2af6b
Add Reflection
KetpuntoG Feb 5, 2024
da5faae
updating some docs
KetpuntoG Feb 9, 2024
39a18cf
reflection operator tests
KetpuntoG Feb 9, 2024
49d45d2
Merge branch 'master' into ReflectionOperation
KetpuntoG Feb 9, 2024
20e69af
tests
KetpuntoG Feb 9, 2024
ad20cca
Merge branch 'ReflectionOperation' of https://github.com/PennyLaneAI/…
KetpuntoG Feb 9, 2024
378c368
black
KetpuntoG Feb 9, 2024
82d58de
Update test_reflection.py
KetpuntoG Feb 9, 2024
aa40aad
Docs
KetpuntoG Feb 10, 2024
b1a4e41
queeing test
KetpuntoG Feb 12, 2024
9fabf46
Update reflection.py
KetpuntoG Feb 12, 2024
283a82a
Merge branch 'master' into ReflectionOperation
KetpuntoG Feb 12, 2024
4f1c85c
Update reflection.py
KetpuntoG Feb 17, 2024
91cab7b
Update pennylane/templates/subroutines/reflection.py
KetpuntoG Feb 17, 2024
3800fbb
Update pennylane/templates/subroutines/reflection.py
KetpuntoG Feb 17, 2024
c8032ba
Update tests/templates/test_subroutines/test_reflection.py
KetpuntoG Feb 17, 2024
ece1c63
compute_decomposition
KetpuntoG Feb 17, 2024
7729441
Merge branch 'ReflectionOperation' of https://github.com/PennyLaneAI/…
KetpuntoG Feb 17, 2024
1092c3d
test and feedback
KetpuntoG Feb 17, 2024
8d00fe5
fixing test
KetpuntoG Feb 17, 2024
cc50227
Update test_reflection.py
KetpuntoG Feb 17, 2024
f1478e6
Update test_reflection.py
KetpuntoG Feb 17, 2024
477091b
jax jit - not casting - numpy error
KetpuntoG Feb 18, 2024
8509886
Merge branch 'master' into ReflectionOperation
KetpuntoG Feb 18, 2024
de9bedc
Update reflection.py
KetpuntoG Feb 18, 2024
88bd40c
Merge branch 'ReflectionOperation' of https://github.com/PennyLaneAI/…
KetpuntoG Feb 18, 2024
fb1aea0
docs correction
KetpuntoG Feb 18, 2024
a08a6bb
Update reflection.py
KetpuntoG Feb 20, 2024
8c35834
Update reflection.py
KetpuntoG Feb 20, 2024
e502da9
Update reflection.py
KetpuntoG Feb 20, 2024
814a5d2
Update reflection.py
KetpuntoG Feb 20, 2024
dd842ef
fixing things
KetpuntoG Feb 20, 2024
d3c8457
Update doc/releases/changelog-dev.md
KetpuntoG Feb 20, 2024
ae899c1
Update pennylane/templates/subroutines/reflection.py
KetpuntoG Feb 20, 2024
4bf0acb
Update pennylane/templates/subroutines/reflection.py
KetpuntoG Feb 20, 2024
caf2236
Update reflection.py
KetpuntoG Feb 20, 2024
79c26a1
Merge branch 'ReflectionOperation' of https://github.com/PennyLaneAI/…
KetpuntoG Feb 20, 2024
56236b7
fixing issues
KetpuntoG Feb 20, 2024
5263eb1
docs
KetpuntoG Feb 20, 2024
5d00e44
pylint
KetpuntoG Feb 20, 2024
200d093
Update reflection.py
KetpuntoG Feb 20, 2024
5e7fa4f
Update test_reflection.py
KetpuntoG Feb 20, 2024
c2c1377
hyper parameteres
KetpuntoG Feb 20, 2024
ec4dec3
update reflection op
Jaybsoni Feb 21, 2024
93cc201
lint
Jaybsoni Feb 21, 2024
ad28d6c
Merge branch 'master' into ReflectionOperation
Jaybsoni Feb 21, 2024
b5ff2d0
extra test + image
KetpuntoG Feb 21, 2024
e4e8091
jay suggestions
KetpuntoG Feb 21, 2024
b5d1789
Update pennylane/templates/subroutines/reflection.py
KetpuntoG Feb 21, 2024
40a185b
Update pennylane/templates/subroutines/reflection.py
KetpuntoG Feb 21, 2024
4540d2f
Update pennylane/templates/subroutines/reflection.py
KetpuntoG Feb 21, 2024
39992c8
Merge branch 'ReflectionOperation' of https://github.com/PennyLaneAI/…
KetpuntoG Feb 21, 2024
df96942
Merge branch 'master' into ReflectionOperation
KetpuntoG Feb 21, 2024
cf340fe
pylint
KetpuntoG Feb 21, 2024
1f3726f
black
KetpuntoG Feb 21, 2024
938811e
Merge branch 'master' into ReflectionOperation
Jaybsoni Feb 22, 2024
677e4bf
typo
KetpuntoG Feb 22, 2024
b39713e
Merge branch 'master' into ReflectionOperation
KetpuntoG Feb 22, 2024
a7f0c10
Update tests/templates/test_subroutines/test_reflection.py
KetpuntoG Feb 22, 2024
9dbaf0d
Update pennylane/templates/subroutines/reflection.py
KetpuntoG Feb 22, 2024
e3d3168
Update pennylane/templates/subroutines/reflection.py
KetpuntoG Feb 22, 2024
131228a
Update pennylane/templates/subroutines/reflection.py
KetpuntoG Feb 22, 2024
1bbe0f5
Update pennylane/templates/subroutines/reflection.py
KetpuntoG Feb 22, 2024
c9c8412
Update pennylane/templates/subroutines/reflection.py
KetpuntoG Feb 22, 2024
afb800f
Update pennylane/templates/subroutines/reflection.py
KetpuntoG Feb 22, 2024
5b8e232
Soran suggestions
KetpuntoG Feb 22, 2024
4dbe5ce
Merge branch 'master' into ReflectionOperation
KetpuntoG Feb 22, 2024
720cb54
Update pennylane/templates/subroutines/reflection.py
KetpuntoG Feb 27, 2024
cd16af1
Merge branch 'master' into ReflectionOperation
KetpuntoG Feb 27, 2024
542b2dc
Update pennylane/templates/subroutines/reflection.py
KetpuntoG Mar 5, 2024
ffce70d
Update pennylane/templates/subroutines/reflection.py
KetpuntoG Mar 5, 2024
693e979
Update pennylane/templates/subroutines/reflection.py
KetpuntoG Mar 5, 2024
5424f04
docs
KetpuntoG Mar 5, 2024
5771bfa
Merge branch 'master' into ReflectionOperation
KetpuntoG Mar 5, 2024
352d913
Merge branch 'master' into ReflectionOperation
KetpuntoG Mar 5, 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
6 changes: 6 additions & 0 deletions doc/releases/changelog-dev.md
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,10 @@

* The module `pennylane/math/quantum.py` has now support for the min-entropy.
[(#3959)](https://github.com/PennyLaneAI/pennylane/pull/3959/)

* Create the `qml.Reflection` operator, useful for amplitude amplification and its variants.
[(##5159)](https://github.com/PennyLaneAI/pennylane/pull/5159)
KetpuntoG marked this conversation as resolved.
Show resolved Hide resolved


* A function called `apply_operation` has been added to the new `qutrit_mixed` module found in `qml.devices` that applies operations to device-compatible states.
[(#5032)](https://github.com/PennyLaneAI/pennylane/pull/5032)
Expand Down Expand Up @@ -497,7 +501,9 @@

This release contains contributions from (in alphabetical order):


KetpuntoG marked this conversation as resolved.
Show resolved Hide resolved
KetpuntoG marked this conversation as resolved.
Show resolved Hide resolved
Abhishek Abhishek,
Guillermo Alonso,
Utkarsh Azad,
Gabriel Bottrill,
Astral Cai,
Expand Down
1 change: 0 additions & 1 deletion pennylane/ops/op_math/controlled_ops.py
Original file line number Diff line number Diff line change
Expand Up @@ -353,7 +353,6 @@ def decomposition(self):


def _check_and_convert_control_values(control_values, control_wires):

if isinstance(control_values, str):
# Make sure all values are either 0 or 1
if not set(control_values).issubset({"1", "0"}):
Expand Down
1 change: 1 addition & 0 deletions pennylane/templates/subroutines/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,3 +39,4 @@
from .controlled_sequence import ControlledSequence
from .trotter import TrotterProduct
from .aqft import AQFT
from .reflection import Reflection
134 changes: 134 additions & 0 deletions pennylane/templates/subroutines/reflection.py
KetpuntoG marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
# Copyright 2018-2023 Xanadu Quantum Technologies Inc.
KetpuntoG marked this conversation as resolved.
Show resolved Hide resolved

# 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.

"""
This submodule contains the template for the Reflection operation.
"""

import numpy as np
import pennylane as qml
from pennylane.operation import Operation
from pennylane.ops import SymbolicOp


class Reflection(SymbolicOp, Operation):
KetpuntoG marked this conversation as resolved.
Show resolved Hide resolved
r"""Reflection(U, alpha = np.pi, reflection_wires = None)
Apply a :math:`\alpha`-reflection over the state :math:`|\Psi\rangle = U|0\rangle`.

.. math::

\text{Reflection}(U, \alpha) = -\mathbb{I} + (1 - e^{i\alpha}) |\Psi\rangle \langle \Psi|
KetpuntoG marked this conversation as resolved.
Show resolved Hide resolved
KetpuntoG marked this conversation as resolved.
Show resolved Hide resolved


Args:
U (qml.ops.op_math.prod.Prod): the product of operations that generate the state :math:`|\Psi\rangle`.
KetpuntoG marked this conversation as resolved.
Show resolved Hide resolved
alpha (float): the reflection angle. Default is :math:`\pi`.
reflection_wires (Any or Iterable[Any]): Subsystem of wires on which to reflect. The default is None and the reflection will be applied on the U wires.
KetpuntoG marked this conversation as resolved.
Show resolved Hide resolved

**Example**

The reflection :math:`-\mathbb{I} + 2|+\rangle \langle +|` applied to the state :math:`|1\rangle` would be as follows:
KetpuntoG marked this conversation as resolved.
Show resolved Hide resolved

.. code-block::
KetpuntoG marked this conversation as resolved.
Show resolved Hide resolved

@qml.prod
KetpuntoG marked this conversation as resolved.
Show resolved Hide resolved
def generator(wires):
qml.Hadamard(wires=wires)

U = generator(wires=0)

dev = qml.device('default.qubit')

KetpuntoG marked this conversation as resolved.
Show resolved Hide resolved
@qml.qnode(dev)
def circuit():

# Initialize to the state |1>
qml.qml.PauliX(wires=0)
KetpuntoG marked this conversation as resolved.
Show resolved Hide resolved

# Apply the reflection
qml.Reflection(U)

return qml.state()

circuit()
KetpuntoG marked this conversation as resolved.
Show resolved Hide resolved


.. details::
:title: Theory

The operator is built as follows:

.. math::

\text{Reflection}(U, \alpha) = -\mathbb{I} + (1 - e^{i\alpha}) |\Psi\rangle \langle \Psi| = U(-\mathbb{I} + (1 - e^{i\alpha}) |0\rangle \langle 0|)U^{\dagger}.

The central block is obtained through a PhaseShift controlled operator.
KetpuntoG marked this conversation as resolved.
Show resolved Hide resolved

In the case of specifying the reflection wires, the operator would have the following expression.
KetpuntoG marked this conversation as resolved.
Show resolved Hide resolved

.. math::

U(\mathbb{I} - (1 - e^{i\alpha}) |0\rangle^{\otimes m} \langle 0|^{\otimes m}\otimes \mathbb{I}^{n-m}})U^{\dagger},
KetpuntoG marked this conversation as resolved.
Show resolved Hide resolved

KetpuntoG marked this conversation as resolved.
Show resolved Hide resolved
where :math:`m` is the number of reflection wires and :math:`n` is the total number of wires.

"""

def __init__(self, U, alpha=np.pi, reflection_wires=None, id=None):
self.U = U
self.alpha = alpha

if reflection_wires is None:
self.reflection_wires = U.wires
else:
self.reflection_wires = reflection_wires

if not set(self.reflection_wires).issubset(set(U.wires)):
raise ValueError("The reflection_wires must be a subset of the U wires.")
KetpuntoG marked this conversation as resolved.
Show resolved Hide resolved

self._name = "Reflection"

super().__init__(base=U, id=id)

@property
def has_matrix(self):
return False
KetpuntoG marked this conversation as resolved.
Show resolved Hide resolved

def decomposition(self):
KetpuntoG marked this conversation as resolved.
Show resolved Hide resolved
wires = qml.wires.Wires(self.reflection_wires)

ops = []

ops.append(qml.GlobalPhase(np.pi))
ops.append(qml.adjoint(self.U))

if len(wires) > 1:
KetpuntoG marked this conversation as resolved.
Show resolved Hide resolved
ops.append(qml.PauliX(wires=wires[-1]))
ops.append(
qml.ctrl(
qml.PhaseShift(self.alpha, wires=wires[-1]),
control=wires[:-1],
control_values=[0] * (len(wires) - 1),
)
)
ops.append(qml.PauliX(wires=wires[-1]))

else:
ops.append(qml.PauliX(wires=wires))
ops.append(qml.PhaseShift(self.alpha, wires=wires))
ops.append(qml.PauliX(wires=wires))

ops.append(self.U)

return ops
206 changes: 206 additions & 0 deletions tests/templates/test_subroutines/test_reflection.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,206 @@
# Copyright 2018-2021 Xanadu Quantum Technologies Inc.
KetpuntoG marked this conversation as resolved.
Show resolved Hide resolved

# 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.
"""
Tests for the Reflection Operator template
"""

import pytest
import numpy as np
import pennylane as qml


@pytest.mark.parametrize(
("prod", "reflection_wires"),
[
(qml.QFT([0, 1, 4]), [0, 1, 2]),
(qml.QFT([0, 1, 2]), [3]),
(qml.QFT([0, 1, 2]), [0, 1, 2, 3]),
],
)
def test_reflection_wires(prod, reflection_wires):
"""Assert reflection_wires is a subset of the U wires"""
KetpuntoG marked this conversation as resolved.
Show resolved Hide resolved
with pytest.raises(ValueError, match="The reflection_wires must be a subset of the U wires."):
qml.Reflection(prod, 0.5, reflection_wires=reflection_wires)


def test_decomposition_one_wire():
KetpuntoG marked this conversation as resolved.
Show resolved Hide resolved
"""Test that the decomposition of the Reflection operator is correct"""
op = qml.Reflection(qml.Hadamard(wires=0), 0.5, reflection_wires=[0])
decomp = op.decomposition()
expected = [
qml.GlobalPhase(np.pi),
qml.adjoint(qml.Hadamard(0)),
qml.PauliX(wires=[0]),
qml.PhaseShift(0.5, wires=[0]),
qml.PauliX(wires=[0]),
qml.Hadamard(0),
]
assert decomp == expected


def test_decomposition_two_wire():
"""Test that the decomposition of the Reflection operator is correct"""
op = qml.Reflection(qml.QFT(wires=[0, 1]), 0.5)

decomp = op.decomposition()
expected = [
qml.GlobalPhase(np.pi),
qml.adjoint(qml.QFT(wires=[0, 1])),
qml.PauliX(wires=[1]),
qml.ctrl(qml.PhaseShift(0.5, wires=[1]), control=0, control_values=[0]),
qml.PauliX(wires=[1]),
qml.QFT(wires=[0, 1]),
]

assert decomp == expected


def test_default_values():
"""Test that the default values are correct"""

U = qml.QFT(wires=[0, 1, 4])
op = qml.Reflection(U)

assert op.alpha == np.pi
assert op.reflection_wires == U.wires


@pytest.mark.parametrize("n_wires", [3, 4, 5])
def test_grover_as_reflection(n_wires):
"""Test that the GroverOperator can be used as a Reflection operator"""

@qml.prod
def hadamards(wires):
for wire in wires:
qml.Hadamard(wires=wire)

grover_matrix = qml.matrix(qml.GroverOperator(wires=range(n_wires)))
reflection_matrix = qml.matrix(qml.Reflection(hadamards(wires=range(n_wires))))

assert np.allclose(grover_matrix, reflection_matrix)


@pytest.mark.tf
@pytest.mark.jax
@pytest.mark.torch
@pytest.mark.parametrize("value", [1.2, 2.1, 3.4])
def test_gradients_all_interfaces(value):
KetpuntoG marked this conversation as resolved.
Show resolved Hide resolved
import torch
import jax
import pennylane.numpy as pnp
import tensorflow as tf

n_wires = 3

@qml.prod
def hadamards(wires):
for wire in wires:
qml.Hadamard(wires=wire)

dev = qml.device("default.qubit")

@qml.qnode(dev)
def circuit(alpha):
qml.RY(1.2, wires=0)
qml.RY(-1.4, wires=1)
qml.RX(-2, wires=0)
qml.CRX(1, wires=[0, 1])
qml.Reflection(hadamards(range(n_wires)), alpha)

return qml.expval(qml.PauliZ(0))

def cost(alpha):
return circuit(alpha)

alpha = pnp.array([value], requires_grad=True)
grad_autograd = qml.grad(cost)(alpha)

grad_jax = jax.grad(cost)(jax.numpy.array(value, dtype=jax.numpy.float32))

x = torch.tensor([value], requires_grad=True)

y = cost(x)
y.backward()
grad_torch = x.grad

x = tf.Variable([value], dtype=tf.float32)

with tf.GradientTape() as tape:
y = cost(x)

grad_tf = tape.gradient(y, x)

assert np.allclose(grad_autograd, grad_jax, atol=1e-3)
assert np.allclose(grad_autograd, grad_torch, atol=1e-3)
assert np.allclose(grad_autograd, grad_tf, atol=1e-3)


def test_lightning_qubit():
dev1 = qml.device("lightning.qubit", wires=2)

@qml.qnode(dev1)
def circuit1():
qml.RX(2, wires=0)
qml.CRY(1, wires=[0, 1])
qml.Reflection(U=qml.PauliX(wires=0), alpha=2.0)
return qml.probs(wires=[0, 1])

dev2 = qml.device("default.qubit", wires=2)

@qml.qnode(dev2)
def circuit2():
qml.RX(2, wires=0)
qml.CRY(1, wires=[0, 1])
qml.Reflection(U=qml.PauliX(wires=0), alpha=2.0)
return qml.probs(wires=[0, 1])

assert np.allclose(circuit1(), circuit2())


def test_correct_queueing():
"""Test that the Reflection operator is correctly queued in the circuit"""
dev = qml.device("default.qubit", wires=2)

@qml.qnode(dev)
def circuit1():
qml.Hadamard(wires=0)
qml.RY(2, wires=0)
qml.CRY(1, wires=[0, 1])
qml.Reflection(U=qml.Hadamard(wires=0), alpha=2.0)
return qml.state()

@qml.prod
soranjh marked this conversation as resolved.
Show resolved Hide resolved
def generator(wires):
qml.Hadamard(wires=wires)

@qml.qnode(dev)
def circuit2():
generator(wires=0)
qml.RY(2, wires=0)
qml.CRY(1, wires=[0, 1])
qml.Reflection(U=generator(wires=0), alpha=2.0)
return qml.state()

U = generator(0)

@qml.qnode(dev)
def circuit3():
generator(wires=0)
qml.RY(2, wires=0)
qml.CRY(1, wires=[0, 1])
qml.Reflection(U=U, alpha=2.0)
return qml.state()

assert np.allclose(circuit1(), circuit2())
assert np.allclose(circuit1(), circuit3())
Loading