-
Notifications
You must be signed in to change notification settings - Fork 269
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Adds Uniformly controlled rotations (Ry and Rz) (#253)
- Loading branch information
1 parent
4674e11
commit d3631e2
Showing
8 changed files
with
497 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,147 @@ | ||
# Copyright 2018 ProjectQ-Framework (www.projectq.ch) | ||
# | ||
# 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. | ||
|
||
import math | ||
|
||
from ._basics import ANGLE_PRECISION, ANGLE_TOLERANCE, BasicGate, NotMergeable | ||
|
||
|
||
class UniformlyControlledRy(BasicGate): | ||
""" | ||
Uniformly controlled Ry gate as introduced in arXiv:quant-ph/0312218. | ||
This is an n-qubit gate. There are n-1 control qubits and one target qubit. | ||
This gate applies Ry(angles(k)) to the target qubit if the n-1 control | ||
qubits are in the classical state k. As there are 2^(n-1) classical | ||
states for the control qubits, this gate requires 2^(n-1) (potentially | ||
different) angle parameters. | ||
Example: | ||
.. code-block:: python | ||
controls = eng.allocate_qureg(2) | ||
target = eng.allocate_qubit() | ||
UniformlyControlledRy(angles=[0.1, 0.2, 0.3, 0.4]) | (controls, target) | ||
Note: | ||
The first quantum register contains the control qubits. When converting | ||
the classical state k of the control qubits to an integer, we define | ||
controls[0] to be the least significant (qu)bit. controls can also | ||
be an empty list in which case the gate corresponds to an Ry. | ||
Args: | ||
angles(list[float]): Rotation angles. Ry(angles[k]) is applied | ||
conditioned on the control qubits being in state | ||
k. | ||
""" | ||
def __init__(self, angles): | ||
BasicGate.__init__(self) | ||
rounded_angles = [] | ||
for angle in angles: | ||
new_angle = round(float(angle) % (4. * math.pi), ANGLE_PRECISION) | ||
if new_angle > 4 * math.pi - ANGLE_TOLERANCE: | ||
new_angle = 0. | ||
rounded_angles.append(new_angle) | ||
self.angles = rounded_angles | ||
|
||
def get_inverse(self): | ||
return self.__class__([-1 * angle for angle in self.angles]) | ||
|
||
def get_merged(self, other): | ||
if isinstance(other, self.__class__): | ||
new_angles = [angle1 + angle2 for (angle1, angle2) in | ||
zip(self.angles, other.angles)] | ||
return self.__class__(new_angles) | ||
raise NotMergeable() | ||
|
||
def __str__(self): | ||
return "UniformlyControlledRy(" + str(self.angles) + ")" | ||
|
||
def __eq__(self, other): | ||
""" Return True if same class, same rotation angles.""" | ||
if isinstance(other, self.__class__): | ||
return self.angles == other.angles | ||
else: | ||
return False | ||
|
||
def __ne__(self, other): | ||
return not self.__eq__(other) | ||
|
||
def __hash__(self): | ||
return hash(str(self)) | ||
|
||
|
||
class UniformlyControlledRz(BasicGate): | ||
""" | ||
Uniformly controlled Rz gate as introduced in arXiv:quant-ph/0312218. | ||
This is an n-qubit gate. There are n-1 control qubits and one target qubit. | ||
This gate applies Rz(angles(k)) to the target qubit if the n-1 control | ||
qubits are in the classical state k. As there are 2^(n-1) classical | ||
states for the control qubits, this gate requires 2^(n-1) (potentially | ||
different) angle parameters. | ||
Example: | ||
.. code-block:: python | ||
controls = eng.allocate_qureg(2) | ||
target = eng.allocate_qubit() | ||
UniformlyControlledRz(angles=[0.1, 0.2, 0.3, 0.4]) | (controls, target) | ||
Note: | ||
The first quantum register are the contains qubits. When converting | ||
the classical state k of the control qubits to an integer, we define | ||
controls[0] to be the least significant (qu)bit. controls can also | ||
be an empty list in which case the gate corresponds to an Rz. | ||
Args: | ||
angles(list[float]): Rotation angles. Rz(angles[k]) is applied | ||
conditioned on the control qubits being in state | ||
k. | ||
""" | ||
def __init__(self, angles): | ||
BasicGate.__init__(self) | ||
rounded_angles = [] | ||
for angle in angles: | ||
new_angle = round(float(angle) % (4. * math.pi), ANGLE_PRECISION) | ||
if new_angle > 4 * math.pi - ANGLE_TOLERANCE: | ||
new_angle = 0. | ||
rounded_angles.append(new_angle) | ||
self.angles = rounded_angles | ||
|
||
def get_inverse(self): | ||
return self.__class__([-1 * angle for angle in self.angles]) | ||
|
||
def get_merged(self, other): | ||
if isinstance(other, self.__class__): | ||
new_angles = [angle1 + angle2 for (angle1, angle2) in | ||
zip(self.angles, other.angles)] | ||
return self.__class__(new_angles) | ||
raise NotMergeable() | ||
|
||
def __str__(self): | ||
return "UniformlyControlledRz(" + str(self.angles) + ")" | ||
|
||
def __eq__(self, other): | ||
""" Return True if same class, same rotation angles.""" | ||
if isinstance(other, self.__class__): | ||
return self.angles == other.angles | ||
else: | ||
return False | ||
|
||
def __ne__(self, other): | ||
return not self.__eq__(other) | ||
|
||
def __hash__(self): | ||
return hash(str(self)) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
# Copyright 2018 ProjectQ-Framework (www.projectq.ch) | ||
# | ||
# 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 projectq.ops._uniformly_controlled_rotation.""" | ||
import math | ||
|
||
import pytest | ||
|
||
from projectq.ops import Rx | ||
from ._basics import NotMergeable | ||
|
||
from projectq.ops import _uniformly_controlled_rotation as ucr | ||
|
||
|
||
@pytest.mark.parametrize("gate_class", [ucr.UniformlyControlledRy, | ||
ucr.UniformlyControlledRz]) | ||
def test_init_rounding(gate_class): | ||
gate = gate_class([0.1 + 4 * math.pi, -1e-14]) | ||
assert gate.angles == [0.1, 0.] | ||
|
||
|
||
@pytest.mark.parametrize("gate_class", [ucr.UniformlyControlledRy, | ||
ucr.UniformlyControlledRz]) | ||
def test_get_inverse(gate_class): | ||
gate = gate_class([0.1, 0.2, 0.3, 0.4]) | ||
inverse = gate.get_inverse() | ||
assert inverse == gate_class([-0.1, -0.2, -0.3, -0.4]) | ||
|
||
|
||
@pytest.mark.parametrize("gate_class", [ucr.UniformlyControlledRy, | ||
ucr.UniformlyControlledRz]) | ||
def test_get_merged(gate_class): | ||
gate1 = gate_class([0.1, 0.2, 0.3, 0.4]) | ||
gate2 = gate_class([0.1, 0.2, 0.3, 0.4]) | ||
merged_gate = gate1.get_merged(gate2) | ||
assert merged_gate == gate_class([0.2, 0.4, 0.6, 0.8]) | ||
with pytest.raises(NotMergeable): | ||
gate1.get_merged(Rx(0.1)) | ||
|
||
|
||
def test_str_and_hash(): | ||
gate1 = ucr.UniformlyControlledRy([0.1, 0.2, 0.3, 0.4]) | ||
gate2 = ucr.UniformlyControlledRz([0.1, 0.2, 0.3, 0.4]) | ||
assert str(gate1) == "UniformlyControlledRy([0.1, 0.2, 0.3, 0.4])" | ||
assert str(gate2) == "UniformlyControlledRz([0.1, 0.2, 0.3, 0.4])" | ||
assert hash(gate1) == hash("UniformlyControlledRy([0.1, 0.2, 0.3, 0.4])") | ||
assert hash(gate2) == hash("UniformlyControlledRz([0.1, 0.2, 0.3, 0.4])") | ||
|
||
|
||
@pytest.mark.parametrize("gate_class", [ucr.UniformlyControlledRy, | ||
ucr.UniformlyControlledRz]) | ||
def test_equality(gate_class): | ||
gate1 = gate_class([0.1, 0.2]) | ||
gate2 = gate_class([0.1, 0.2 + 1e-14]) | ||
assert gate1 == gate2 | ||
gate3 = gate_class([0.1, 0.2, 0.1, 0.2]) | ||
assert gate2 != gate3 | ||
gate4 = ucr.UniformlyControlledRz([0.1, 0.2]) | ||
gate5 = ucr.UniformlyControlledRy([0.1, 0.2]) | ||
assert gate4 != gate5 | ||
assert not gate5 == gate4 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.