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

Trapped ion decomposer setup and rotation gates improvement #346

Merged
merged 39 commits into from Jan 14, 2020
Merged
Show file tree
Hide file tree
Changes from 31 commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
a37c182
reduced Ry decomposition : modification of restrictedgateset setup to…
Ytterbot Oct 23, 2019
5aab56b
Addition of test file for the rz2rx decomposition
daisyrainsmith Nov 22, 2019
c005469
Revert "Addition of test file for the rz2rx decomposition"
daisyrainsmith Nov 22, 2019
161ceef
Create rz2rx_test file
daisyrainsmith Nov 22, 2019
1de8b0d
Update rz2rx.py
daisyrainsmith Nov 22, 2019
7aee572
Update rz2rx_test.py
daisyrainsmith Nov 22, 2019
1beffdb
Minor update: remove accidental file
daisyrainsmith Nov 25, 2019
3ad1128
Minor update rz2rx.py
daisyrainsmith Nov 26, 2019
6f71faa
Minor update rz2rx_test.py
daisyrainsmith Nov 26, 2019
c29f866
Create h2rx_test.py
daisyrainsmith Dec 5, 2019
ab231de
Improvement of the decomposition chooser; Note that basic restricted …
Ytterbot Dec 6, 2019
5e7e01b
Merge branch 'develop' of https://github.com/dbretaud/ProjectQ into d…
Ytterbot Dec 6, 2019
2b76e64
Updates to h2rx and cnot2rxx
daisyrainsmith Dec 6, 2019
a3d70a1
Merge branch 'develop' of https://github.com/dbretaud/ProjectQ into d…
daisyrainsmith Dec 6, 2019
60427da
Create cnot2rxx_test.py
daisyrainsmith Dec 6, 2019
1880aa8
basic rotation gates at an angle of 0 or 2pi are removed by the optim…
Ytterbot Dec 10, 2019
4748268
Update and create trapped_ion_decomposer_test.py
daisyrainsmith Dec 10, 2019
5ac7318
Minor update
daisyrainsmith Dec 10, 2019
cf6a793
Update on comments regarding Identity gates
Ytterbot Dec 10, 2019
0392ec0
Merge branch 'develop' into develop
dbretaud Dec 11, 2019
acdf41a
Changes 1/2 : command can be printed with unicode symbols only via new
Ytterbot Dec 19, 2019
448ba8e
Merge branch 'develop' of https://github.com/dbretaud/ProjectQ into d…
Ytterbot Dec 19, 2019
27f820c
Work in progress, is commutable
daisyrainsmith Dec 20, 2019
386cdf4
Merge branch 'develop' of https://github.com/dbretaud/ProjectQ into d…
daisyrainsmith Dec 20, 2019
419276c
Update to decomposition test files
daisyrainsmith Dec 20, 2019
ff74c8b
Revert "Work in progress, is commutable"
Ytterbot Dec 20, 2019
0413e10
minor fixes; revert rotation gates to [0;4pi)
Ytterbot Jan 7, 2020
8ea5a5c
fix comments
Ytterbot Jan 10, 2020
7e30ed5
Fix a few issues with the trapped-ion setup
Takishima Jan 13, 2020
f730844
Merge branch 'develop' of https://github.com/dbretaud/ProjectQ into p…
Takishima Jan 13, 2020
aa838ec
Mostly fixing stylistic issues and some code cleanup
Takishima Jan 13, 2020
e8a8961
h2rx decomposition with correct global phase
Takishima Jan 13, 2020
0b97e30
cnot2rxx decomposition with correct global phase
Takishima Jan 13, 2020
1de39d1
Fix non-ASCII character in cnot2rxx.py
Takishima Jan 13, 2020
30674eb
Fix some more files for non-ASCII characters
Takishima Jan 14, 2020
7fe3278
Specify encoding for files with non-ASCII characters
Takishima Jan 14, 2020
9b3d6c1
Fix test errors
Takishima Jan 14, 2020
da021b6
Fix tests for Python 2.7
Takishima Jan 14, 2020
4ffe46b
Complete code coverage for trapped-ion setup
Takishima Jan 14, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
20 changes: 19 additions & 1 deletion docs/projectq.setups.decompositions.rst
Expand Up @@ -10,16 +10,19 @@ The decomposition package is a collection of gate decomposition / replacement ru
projectq.setups.decompositions.barrier
projectq.setups.decompositions.carb1qubit2cnotrzandry
projectq.setups.decompositions.cnot2cz
projectq.setups.decompositions.cnot2rxx
projectq.setups.decompositions.cnu2toffoliandcu
projectq.setups.decompositions.crz2cxandrz
projectq.setups.decompositions.entangle
projectq.setups.decompositions.globalphase
projectq.setups.decompositions.h2rx
projectq.setups.decompositions.ph2r
projectq.setups.decompositions.qft2crandhadamard
projectq.setups.decompositions.qubitop2onequbit
projectq.setups.decompositions.r2rzandph
projectq.setups.decompositions.rx2rz
projectq.setups.decompositions.ry2rz
projectq.setups.decompositions.rz2rx
projectq.setups.decompositions.sqrtswap2cnot
projectq.setups.decompositions.stateprep2cnot
projectq.setups.decompositions.swap2cnot
Expand Down Expand Up @@ -62,6 +65,13 @@ projectq.setups.decompositions.cnot2cz module
:members:
:undoc-members:

projectq.setups.decompositions.cnot2rxx module
---------------------------------------------

.. automodule:: projectq.setups.decompositions.cnot2rxx
:members:
:undoc-members:

projectq.setups.decompositions.cnu2toffoliandcu module
------------------------------------------------------

Expand Down Expand Up @@ -90,7 +100,8 @@ projectq.setups.decompositions.globalphase module
:members:
:undoc-members:

projectq.setups.decompositions.ph2r module

projectq.setups.decompositions.h2rx module
------------------------------------------

.. automodule:: projectq.setups.decompositions.ph2r
Expand Down Expand Up @@ -132,6 +143,13 @@ projectq.setups.decompositions.ry2rz module
:members:
:undoc-members:

projectq.setups.decompositions.rz2rx module
-------------------------------------------

.. automodule:: projectq.setups.decompositions.rz2rx
:members:
:undoc-members:

projectq.setups.decompositions.sqrtswap2cnot module
---------------------------------------------------

Expand Down
7 changes: 7 additions & 0 deletions docs/projectq.setups.rst
Expand Up @@ -86,6 +86,13 @@ restrictedgateset
:special-members: __init__
:undoc-members:

trapped_ion_decomposer
----------------------
.. automodule:: projectq.setups.trapped_ion_decomposer
:members:
:special-members: __init__
:undoc-members:

Module contents
---------------

Expand Down
16 changes: 15 additions & 1 deletion projectq/cengines/_optimize.py
Expand Up @@ -116,7 +116,7 @@ def _get_gate_indices(self, idx, i, IDs):

def _optimize(self, idx, lim=None):
"""
Try to merge or even cancel successive gates using the get_merged and
Try to remove identity gates using the is_identity function, then merge or even cancel successive gates using the get_merged and
get_inverse functions of the gate (see, e.g., BasicRotationGate).

It does so for all qubit command lists.
Expand All @@ -130,6 +130,20 @@ def _optimize(self, idx, lim=None):
new_gateloc = limit

while i < limit - 1:
# can be dropped if the gate is equivalent to an identity gate
if self._l[idx][i].is_identity():
# determine index of this gate on all qubits
qubitids = [qb.id for sublist in self._l[idx][i].all_qubits
for qb in sublist]
gid = self._get_gate_indices(idx, i, qubitids)
for j in range(len(qubitids)):
new_list = (self._l[qubitids[j]][0:gid[j]] +
self._l[qubitids[j]][gid[j] +1:])
self._l[qubitids[j]] = new_list
i = 0
limit -= 1
continue

# can be dropped if two in a row are self-inverses
inv = self._l[idx][i].get_inverse()

Expand Down
18 changes: 18 additions & 0 deletions projectq/cengines/_optimize_test.py
Expand Up @@ -127,3 +127,21 @@ def test_local_optimizer_mergeable_gates():
# Expect allocate, one Rx gate, and flush gate
assert len(backend.received_commands) == 3
assert backend.received_commands[1].gate == Rx(10 * 0.5)

def test_local_optimizer_identity_gates():
local_optimizer = _optimize.LocalOptimizer(m=4)
backend = DummyEngine(save_commands=True)
eng = MainEngine(backend=backend, engine_list=[local_optimizer])
# Test that it merges mergeable gates such as Rx
qb0 = eng.allocate_qubit()
for _ in range(10):
Rx(0.0) | qb0
Ry(0.0) | qb0
Rx(2*math.pi) | qb0
Ry(2*math.pi) | qb0
Rx(0.5) | qb0
assert len(backend.received_commands) == 0
eng.flush()
# Expect allocate, one Rx gate, and flush gate
assert len(backend.received_commands) == 3
assert backend.received_commands[1].gate == Rx(0.5)
1 change: 0 additions & 1 deletion projectq/cengines/_replacer/_replacer.py
Expand Up @@ -175,7 +175,6 @@ def _process_command(self, cmd):

# use decomposition chooser to determine the best decomposition
chosen_decomp = self._decomp_chooser(cmd, decomp_list)

# the decomposed command must have the same tags
# (plus the ones it gets from meta-statements inside the
# decomposition rule).
Expand Down
1 change: 1 addition & 0 deletions projectq/ops/__init__.py
Expand Up @@ -25,6 +25,7 @@
from ._command import apply_command, Command
from ._metagates import (DaggeredGate,
get_inverse,
is_identity,
ControlledGate,
C,
Tensor,
Expand Down
38 changes: 35 additions & 3 deletions projectq/ops/_basics.py
Expand Up @@ -40,6 +40,8 @@
from ._command import Command, apply_command


import unicodedata

ANGLE_PRECISION = 12
ANGLE_TOLERANCE = 10 ** -ANGLE_PRECISION
RTOL = 1e-10
Expand Down Expand Up @@ -224,9 +226,20 @@ def __ne__(self, other):
def __str__(self):
raise NotImplementedError('This gate does not implement __str__.')

def to_string(self,symbols):
"""
String representation

achieve same function as str() but can be extended for configurable representation
"""
return str(self)

def __hash__(self):
return hash(str(self))

def is_identity(self):
return False


class MatrixGate(BasicGate):
"""
Expand Down Expand Up @@ -340,10 +353,23 @@ def __str__(self):
Returns the class name and the angle as

.. code-block:: python

[CLASSNAME]([ANGLE])
"""
return str(self.__class__.__name__) + "(" + str(self.angle) + ")"
return self.to_string()

def to_string(self,symbols=False):
"""
Return the string representation of a BasicRotationGate.

Args:
symbols: uses the pi character and round the angle for a more user friendly display if True, full angle written in radian Otherwise
"""
if symbols:
angle="(" + str(round(self.angle/math.pi,3)) +unicodedata.lookup("GREEK SMALL LETTER PI")+ ")"
else:
angle="(" + str(self.angle) + ")"
return str(self.__class__.__name__) + angle

def tex_str(self):
"""
Expand All @@ -355,7 +381,7 @@ def tex_str(self):

[CLASSNAME]$_[ANGLE]$
"""
return str(self.__class__.__name__) + "$_{" + str(self.angle) + "}$"
return str(self.__class__.__name__) + "$_{" + str(round(self.angle/math.pi,3)) + "\pi}$"

def get_inverse(self):
"""
Expand Down Expand Up @@ -401,6 +427,12 @@ def __ne__(self, other):
def __hash__(self):
return hash(str(self))

def is_identity(self):
Takishima marked this conversation as resolved.
Show resolved Hide resolved
"""
Return True if the gate is equivalent to an Identity gate
"""
return self.angle == 0. or self.angle==4*math.pi


class BasicPhaseGate(BasicGate):
"""
Expand Down
21 changes: 15 additions & 6 deletions projectq/ops/_basics_test.py
Expand Up @@ -163,15 +163,16 @@ def test_basic_rotation_gate_init(input_angle, modulo_angle):


def test_basic_rotation_gate_str():
basic_rotation_gate = _basics.BasicRotationGate(0.5)
assert str(basic_rotation_gate) == "BasicRotationGate(0.5)"

basic_rotation_gate = _basics.BasicRotationGate(math.pi)
assert str(basic_rotation_gate) == "BasicRotationGate(3.14159265359)"
assert basic_rotation_gate.to_string(symbols=True) == "BasicRotationGate(0.5π)"
assert basic_rotation_gate.to_string(symbols=False) == "BasicRotationGate(3.14159265359)"

def test_basic_rotation_tex_str():
basic_rotation_gate = _basics.BasicRotationGate(0.5)
assert basic_rotation_gate.tex_str() == "BasicRotationGate$_{0.5}$"
basic_rotation_gate = _basics.BasicRotationGate(0.5*math.pi)
assert basic_rotation_gate.tex_str() == "BasicRotationGate$_{0.5\pi}$"
basic_rotation_gate = _basics.BasicRotationGate(4 * math.pi - 1e-13)
assert basic_rotation_gate.tex_str() == "BasicRotationGate$_{0.0}$"
assert basic_rotation_gate.tex_str() == "BasicRotationGate$_{0.0\pi}$"


@pytest.mark.parametrize("input_angle, inverse_angle",
Expand All @@ -193,6 +194,14 @@ def test_basic_rotation_gate_get_merged():
merged_gate = basic_rotation_gate1.get_merged(basic_rotation_gate2)
assert merged_gate == basic_rotation_gate3

def test_basic_rotation_gate_is_identity():
basic_gate = _basics.BasicGate()
basic_rotation_gate1 = _basics.BasicRotationGate(0.)
basic_rotation_gate2 = _basics.BasicRotationGate(1.0*math.pi)
basic_rotation_gate3 = _basics.BasicRotationGate(2.*math.pi)
assert basic_rotation_gate1.is_identity()
assert not basic_rotation_gate2.is_identity()
assert basic_rotation_gate3.is_identity()

def test_basic_rotation_gate_comparison_and_hash():
basic_rotation_gate1 = _basics.BasicRotationGate(0.5)
Expand Down
14 changes: 13 additions & 1 deletion projectq/ops/_command.py
Expand Up @@ -149,6 +149,15 @@ def get_inverse(self):
list(self.control_qubits),
deepcopy(self.tags))

def is_identity(self):
"""
Evaluate if the gate called in the command object is an identity gate.

Returns:
True if the gate is equivalent to an Identity gate, False otherwise
"""
return projectq.ops.is_identity(self.gate)

def get_merged(self, other):
"""
Merge this command with another one and return the merged command
Expand Down Expand Up @@ -297,6 +306,9 @@ def __ne__(self, other):
return not self.__eq__(other)

def __str__(self):
return self.to_string()

def to_string(self,symbols=False):
"""
Get string representation of this Command object.
"""
Expand All @@ -314,4 +326,4 @@ def __str__(self):
qstring += ", "
qstring = qstring[:-2] + " )"
cstring = "C" * len(ctrlqubits)
return cstring + str(self.gate) + " | " + qstring
return cstring + self.gate.to_string(symbols) + " | " + qstring
32 changes: 28 additions & 4 deletions projectq/ops/_command_test.py
Expand Up @@ -132,6 +132,18 @@ def test_command_get_merged(main_engine):
with pytest.raises(NotMergeable):
cmd.get_merged(cmd4)

def test_command_is_identity(main_engine):
qubit = main_engine.allocate_qubit()
qubit2 = main_engine.allocate_qubit()
cmd = _command.Command(main_engine, Rx(0.), (qubit,))
cmd2= _command.Command(main_engine, Rx(0.5), (qubit2,))
inverse_cmd = cmd.get_inverse()
inverse_cmd2 = cmd2.get_inverse()
assert inverse_cmd.gate.is_identity()
assert cmd.gate.is_identity()
assert not inverse_cmd2.gate.is_identity()
assert not cmd2.gate.is_identity()


def test_command_order_qubits(main_engine):
qubit0 = Qureg([Qubit(main_engine, 0)])
Expand Down Expand Up @@ -232,9 +244,21 @@ def test_command_comparison(main_engine):
def test_command_str():
qubit = Qureg([Qubit(main_engine, 0)])
ctrl_qubit = Qureg([Qubit(main_engine, 1)])
cmd = _command.Command(main_engine, Rx(0.5), (qubit,))
cmd = _command.Command(main_engine, Rx(0.5*math.pi), (qubit,))
cmd.tags = ["TestTag"]
cmd.add_control_qubits(ctrl_qubit)
assert str(cmd) == "CRx(0.5) | ( Qureg[1], Qureg[0] )"
cmd2 = _command.Command(main_engine, Rx(0.5), (qubit,))
assert str(cmd2) == "Rx(0.5) | Qureg[0]"
assert str(cmd) == "CRx(1.570796326795‬) | ( Qureg[1], Qureg[0] )"
cmd2 = _command.Command(main_engine, Rx(0.5*math.pi), (qubit,))
assert str(cmd2) == "Rx(1.570796326795‬) | Qureg[0]"

def test_command_to_string():
qubit = Qureg([Qubit(main_engine, 0)])
ctrl_qubit = Qureg([Qubit(main_engine, 1)])
cmd = _command.Command(main_engine, Rx(0.5*math.pi), (qubit,))
cmd.tags = ["TestTag"]
cmd.add_control_qubits(ctrl_qubit)
assert cmd.to_string(symbols=True) == "CRx(0.5π) | ( Qureg[1], Qureg[0] )"
assert cmd.to_string(symbols=False) == "CRx(1.570796326795‬) | ( Qureg[1], Qureg[0] )"
cmd2 = _command.Command(main_engine, Rx(0.5*math.pi), (qubit,))
assert cmd2.to_string(symbols=True) == "Rx(0.5π) | Qureg[0]"
assert cmd2.to_string(symbols=False) == "Rx(1.570796326795‬) | Qureg[0]"
16 changes: 16 additions & 0 deletions projectq/ops/_metagates.py 100755 → 100644
Expand Up @@ -132,6 +132,22 @@ def get_inverse(gate):
except NotInvertible:
return DaggeredGate(gate)

def is_identity(gate):
"""
Return True if the gate is an identity gate.

Tries to call gate.is_identity and, upon failure, returns False

Args:
gate: Gate of which to get the inverse

Example:
.. code-block:: python

get_inverse(Rx(2*math.pi)) # returns True
get_inverse(Rx(math.pi)) # returns False
"""
return gate.is_identity()

class ControlledGate(BasicGate):
"""
Expand Down
10 changes: 10 additions & 0 deletions projectq/ops/_metagates_test.py
Expand Up @@ -122,6 +122,16 @@ def test_get_inverse():
inv2 = _metagates.get_inverse(invertible_gate)
assert inv2 == Y

def test_is_identity():
# Choose gate which is not an identity gate:
non_identity_gate=Rx(0.5)
assert not non_identity_gate.is_identity()
assert not _metagates.is_identity(non_identity_gate)
# Choose gate which is an identity gate:
identity_gate=Rx(0.)
assert identity_gate.is_identity()
assert _metagates.is_identity(identity_gate)


def test_controlled_gate_init():
one_control = _metagates.ControlledGate(Y, 1)
Expand Down