Skip to content

Commit

Permalink
Trapped ion decomposer setup and rotation gates improvement (#346)
Browse files Browse the repository at this point in the history
* reduced Ry decomposition : modification of restrictedgateset setup to include compiler chooser, extension of available decompositions, new setup based on restrictedgateset but adapted for trapped ion configurations

* Addition of test file for the rz2rx decomposition

Addition of test file for the rz2rx decomposition and edit to comments in the rz2rx file

* Revert "Addition of test file for the rz2rx decomposition"

This reverts commit 5aab56b.

* Create rz2rx_test file

Addition of test file for the rz2rx decomposition

* Update rz2rx.py

Update to comments

* Update rz2rx_test.py

Update to comments

* Minor update: remove accidental file

* Minor update rz2rx.py

Corrected an angle.

* Minor update rz2rx_test.py

Edited comments.

* Create h2rx_test.py

Updated method for test_decomposition which tests that decomposition produces identical wave-state up to a global phase.

* Improvement of the decomposition chooser; Note that basic restricted gate
set will fail decomposing into Rxx because of the default chooser

* Updates to h2rx and cnot2rxx

* Create cnot2rxx_test.py

Testing file for cnot2rxx decomposition. Includes updated method for test_decomposition which tests that decomposition produces identical wave-state up to a global phase.

* basic rotation gates at an angle of 0 or 2pi are removed by the optimizer.
basic rotation gates ignore now the global phase and are defined over 0:2pi

* Update and create trapped_ion_decomposer_test.py

* Minor update

Documentation and comments.

* Update on comments regarding Identity gates

* Changes 1/2 : command can be printed with unicode symbols only via new
to_String method; syntax correction;

* Work in progress, is commutable

* Update to decomposition test files

rz2rx_test now tests each decomposition defined in rz2rx.all_defined_decomposition_rules
Similar for cnot2rxx_test and h2rx_test

* Revert "Work in progress, is commutable"

This reverts commit 27f820c.

* minor fixes; revert rotation gates to [0;4pi)

* fix comments

* Fix a few issues with the trapped-ion setup

- Store the sign of the last Ry gate on an engine-by-engine basis
- Cleanup of some remaining print statements
- Some stylistic issues fixed

* Mostly fixing stylistic issues and some code cleanup

* h2rx decomposition with correct global phase

* cnot2rxx decomposition with correct global phase

* Fix non-ASCII character in cnot2rxx.py

* Fix some more files for non-ASCII characters

* Specify encoding for files with non-ASCII characters

* Fix test errors

* Fix tests for Python 2.7

* Complete code coverage for trapped-ion setup

Co-authored-by: Nguyen Damien <ngn.damien@gmail.com>
  • Loading branch information
dbretaud and Takishima committed Jan 14, 2020
1 parent 4bd6e6f commit 93f2d79
Show file tree
Hide file tree
Showing 23 changed files with 1,157 additions and 125 deletions.
20 changes: 19 additions & 1 deletion docs/projectq.setups.decompositions.rst
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
20 changes: 20 additions & 0 deletions projectq/cengines/_optimize_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

import pytest

import math
from projectq import MainEngine
from projectq.cengines import DummyEngine
from projectq.ops import (CNOT, H, Rx, Ry, AllocateQubitGate, X,
Expand Down Expand Up @@ -127,3 +128,22 @@ 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(4*math.pi) | qb0
Ry(4*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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
64 changes: 50 additions & 14 deletions projectq/ops/_basics.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
# 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.

"""
Defines the BasicGate class, the base class of all gates, the
BasicRotationGate class, the SelfInverseGate, the FastForwardingGate, the
Expand Down Expand Up @@ -39,9 +38,10 @@
from projectq.types import BasicQubit
from ._command import Command, apply_command

import unicodedata

ANGLE_PRECISION = 12
ANGLE_TOLERANCE = 10 ** -ANGLE_PRECISION
ANGLE_TOLERANCE = 10**-ANGLE_PRECISION
RTOL = 1e-10
ATOL = 1e-12

Expand Down Expand Up @@ -157,7 +157,7 @@ def make_tuple_of_qureg(qubits):
(or list of Qubits) objects.
"""
if not isinstance(qubits, tuple):
qubits = (qubits,)
qubits = (qubits, )

qubits = list(qubits)

Expand Down Expand Up @@ -208,8 +208,9 @@ def __eq__(self, other):
Equality comparision
Return True if instance of the same class, unless other is an instance
of :class:MatrixGate, in which case equality is to be checked by testing
for existence and (approximate) equality of matrix representations.
of :class:MatrixGate, in which case equality is to be checked by
testing for existence and (approximate) equality of matrix
representations.
"""
if isinstance(other, self.__class__):
return True
Expand All @@ -224,9 +225,21 @@ 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 @@ -271,20 +284,19 @@ def __eq__(self, other):
"""
if not hasattr(other, 'matrix'):
return False
if (not isinstance(self.matrix, np.matrix) or
not isinstance(other.matrix, np.matrix)):
if (not isinstance(self.matrix, np.matrix)
or not isinstance(other.matrix, np.matrix)):
raise TypeError("One of the gates doesn't have the correct "
"type (numpy.matrix) for the matrix "
"attribute.")
if (self.matrix.shape == other.matrix.shape and
np.allclose(self.matrix, other.matrix,
rtol=RTOL, atol=ATOL,
equal_nan=False)):
if (self.matrix.shape == other.matrix.shape and np.allclose(
self.matrix, other.matrix, rtol=RTOL, atol=ATOL,
equal_nan=False)):
return True
return False

def __str__(self):
return("MatrixGate(" + str(self.matrix.tolist()) + ")")
return ("MatrixGate(" + str(self.matrix.tolist()) + ")")

def __hash__(self):
return hash(str(self))
Expand Down Expand Up @@ -343,7 +355,23 @@ def __str__(self):
[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 (bool): 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 +383,8 @@ 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 +430,12 @@ def __ne__(self, other):
def __hash__(self):
return hash(str(self))

def is_identity(self):
"""
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 Expand Up @@ -597,6 +632,7 @@ def math_fun(a):

def math_function(x):
return list(math_fun(*x))

self._math_function = math_function

def __str__(self):
Expand Down
Loading

0 comments on commit 93f2d79

Please sign in to comment.