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

Daisyrainsmith/commutation2 #417

Open
wants to merge 21 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
5026302
Update to add commutation rules
daisyrainsmith Feb 8, 2021
680784c
Update to comments and docstrings
daisyrainsmith Feb 22, 2021
9987f6a
Minor and syntax changes
daisyrainsmith Mar 13, 2021
a51ca91
Medium changes
daisyrainsmith Apr 1, 2021
db22a1b
New commutation rules
daisyrainsmith Apr 7, 2021
47c48e8
Medium changes
daisyrainsmith Apr 13, 2021
dbc219e
Move commutation relation definition outside of gate classes
Takishima Jun 8, 2021
42d7d2f
Fix tests for rotation gates
Takishima Jun 8, 2021
a0e575c
Remove local path from file
Takishima Jun 8, 2021
f5b1199
Fix typos
Takishima Jun 8, 2021
063bc18
Fix test failure due to new metaclass for BasicGate
Takishima Jun 8, 2021
dd96ae5
Fix test failure due to the optimiser able to merge commuting gates
Takishima Jun 8, 2021
e10e64d
Re-add interchangeable indices for Rxx, Ryy and Rzz
Takishima Jun 8, 2021
c293f64
Add missing commutaton relations between Ph and some gates
Takishima Jun 8, 2021
b8b6057
Fix some more errors
Takishima Jun 8, 2021
8ed3997
Merge (manual rebase)
dependabot[bot] Nov 10, 2021
06acfdf
Merge branch 'develop' into daisyrainsmith/commutation2
Takishima Jan 6, 2022
6f1baa9
Merge branch 'develop' into daisyrainsmith/commutation2
Takishima Oct 30, 2022
0cdd9ff
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Oct 30, 2022
acf7bb0
Merge branch 'develop' into daisyrainsmith/commutation2
Takishima Oct 31, 2022
4242d98
Merge branch 'develop' into daisyrainsmith/commutation2
Takishima Apr 2, 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
454 changes: 379 additions & 75 deletions projectq/cengines/_optimize.py

Large diffs are not rendered by default.

660 changes: 654 additions & 6 deletions projectq/cengines/_optimize_test.py

Large diffs are not rendered by default.

4 changes: 3 additions & 1 deletion projectq/cengines/_replacer/_decomposition_rule.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@

"""Module containing the definition of a decomposition rule."""

from projectq.ops import BasicGate
from projectq.ops._basics import BasicGate, BasicGateMeta


class ThisIsNotAGateClassError(TypeError):
Expand Down Expand Up @@ -54,6 +54,8 @@ def __init__(self, gate_class, gate_decomposer, gate_recognizer=lambda cmd: True
"\nDid you pass in someGate instead of someGate.__class__?"
)
if gate_class == type.__class__:
"\nDid you pass in someGate instead of someGate.__class__?"
if gate_class in (type.__class__, BasicGateMeta):
raise ThisIsNotAGateClassError(
"gate_class is type.__class__ instead of a type of BasicGate."
"\nDid you pass in GateType.__class__ instead of GateType?"
Expand Down
59 changes: 56 additions & 3 deletions projectq/ops/_basics.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@

from projectq.types import BasicQubit

from ._command import Command, apply_command
from ._command import Command, Commutability, apply_command

ANGLE_PRECISION = 12
ANGLE_TOLERANCE = 10**-ANGLE_PRECISION
Expand All @@ -63,8 +63,28 @@ class NotInvertible(Exception):
"""


class BasicGate:
"""Base class of all gates. (Don't use it directly but derive from it)."""
class BasicGateMeta(type):
"""
Meta class for all gates; mainly used to ensure that all gate classes have some basic class variable defined.
"""

def __new__(cls, name, bases, attrs):
def get_commutable_gates(self):
return self._commutable_gates

return super().__new__(
cls, name, bases, {**attrs, get_commutable_gates.__name__: get_commutable_gates, '_commutable_gates': set()}
)


class BasicGate(metaclass=BasicGateMeta):
"""
A list of gates that commute with this gate class
"""

"""
Base class of all gates. (Don't use it directly but derive from it)
"""

def __init__(self):
"""
Expand Down Expand Up @@ -123,6 +143,21 @@ def get_merged(self, other):
"""
raise NotMergeable("BasicGate: No get_merged() implemented.")

def get_commutable_gates(self):
return []

def get_commutable_circuit_list(self, n=0):
"""
Args:
n (int): The CNOT gate needs to be able to pass in parameter n in
this method.

Returns:
_commutable_circuit_list (list): the list of commutable circuits
associated with this gate.
"""
return []

@staticmethod
def make_tuple_of_qureg(qubits):
"""
Expand Down Expand Up @@ -234,6 +269,24 @@ def is_identity(self):
"""Return True if the gate is an identity gate. In this base class, always returns False."""
return False

def is_commutable(self, other):
"""Determine whether this gate is commutable with
another gate.

Args:
other (Gate): The other gate.

Returns:
commutability (Commutability) : An enum which
indicates whether the next gate is commutable,
not commutable or maybe commutable.
"""
for gate in self.get_commutable_gates():
if type(other) is gate:
return Commutability.COMMUTABLE
else:
return Commutability.NOT_COMMUTABLE


class MatrixGate(BasicGate):
"""
Expand Down
19 changes: 18 additions & 1 deletion projectq/ops/_basics_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,14 @@
"""Tests for projectq.ops._basics."""

import math
import sys

import numpy as np
import pytest

from projectq import MainEngine
from projectq.cengines import DummyEngine
from projectq.ops import Command, X, _basics
from projectq.ops import NOT, Command, X, _basics, _gates, _metagates
from projectq.types import Qubit, Qureg, WeakQubitRef


Expand Down Expand Up @@ -349,3 +350,19 @@ def test_matrix_gate():
assert X == gate3
assert str(gate3) == "MatrixGate([[0, 1], [1, 0]])"
assert hash(gate3) == hash("MatrixGate([[0, 1], [1, 0]])")


def test_is_commutable():
"""At the gate level is_commutable can return
true or false. Test that is_commutable is working
as expected."""
gate1 = _basics.BasicRotationGate(math.pi)
gate2 = _basics.MatrixGate()
gate3 = _basics.BasicRotationGate(math.pi)
assert not gate1.is_commutable(gate2)
assert not gate1.is_commutable(gate3)
gate4 = _gates.Rz(math.pi)
gate5 = _gates.H
gate6 = _metagates.C(NOT)
assert not gate4.is_commutable(gate5)
assert not gate4.is_commutable(gate6)
50 changes: 50 additions & 0 deletions projectq/ops/_command.py
100755 → 100644
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ def __init__(
self.control_qubits = controls # property
self.engine = engine # property
self.control_state = control_state # property
self._commutable_circuit_list = self.gate.get_commutable_circuit_list(n=len(self.control_qubits)) # property

@property
def qubits(self):
Expand Down Expand Up @@ -163,6 +164,27 @@ def is_identity(self):
"""
return projectq.ops.is_identity(self.gate)

def is_commutable(self, other):
"""
Evaluate if this command is commutable with another command.

Args:
other (Command): The other command.

Returns:
Commutability value (int) : value of the commutability enum
"""
if not overlap(self.all_qubits, other.all_qubits):
return Commutability.NOT_COMMUTABLE
self._commutable_circuit_list = self.gate.get_commutable_circuit_list(len(self.control_qubits))
# If other gate may be part of a list which is
# commutable with gate, return enum MAYBE_COMMUTABLE
for circuit in self._commutable_circuit_list:
if type(other.gate) is type(circuit[0]._gate):
return Commutability.MAYBE_COMMUTABLE
else:
return self.gate.is_commutable(other.gate)

def get_merged(self, other):
"""
Merge this command with another one and return the merged command object.
Expand Down Expand Up @@ -358,3 +380,31 @@ def to_string(self, symbols=False):
qstring = f"{qstring[:-2]} )"
cstring = "C" * len(ctrlqubits)
return f"{cstring + self.gate.to_string(symbols)} | {qstring}"


def overlap(tuple1, tuple2):
"""
Takes two tuples of lists, flattens them and counts the number
of common elements. Used to check if two commands have qubits
or control qubits in common.

i.e. command1.all_qubits = [[control_qubits], [qubits]]
command2.all_qubits = [[control_qubits], [qubits]]
overlap(command1, command2) = 4
means command1 and command2 have 4 qubits or control
qubits in common.

"""
flat_tuple1 = [item for sublist in tuple1 for item in sublist]
flat_tuple2 = [item for sublist in tuple2 for item in sublist]
n = 0
for element in flat_tuple1:
if element in flat_tuple2:
n += 1
return n


class Commutability(IntEnum):
NOT_COMMUTABLE = 0
COMMUTABLE = 1
MAYBE_COMMUTABLE = 2
71 changes: 70 additions & 1 deletion projectq/ops/_command_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,22 @@
from projectq import MainEngine
from projectq.cengines import DummyEngine
from projectq.meta import ComputeTag, canonical_ctrl_state
from projectq.ops import BasicGate, CtrlAll, NotMergeable, Rx, _command
from projectq.ops import (
CNOT,
BasicGate,
CtrlAll,
H,
NotMergeable,
Ph,
Rx,
Rxx,
Ry,
Rz,
SqrtX,
X,
_command,
)
from projectq.ops._command import Commutability
from projectq.types import Qubit, Qureg, WeakQubitRef


Expand Down Expand Up @@ -144,6 +159,54 @@ def test_command_is_identity(main_engine):
assert not cmd2.gate.is_identity()


def test_overlap():
"""Test the overlap function is working as
expected."""
tuple1 = ([1, 2], [3])
tuple2 = ([2], [3, 0])
tuple3 = ([0, 0, 0],)
assert _command.overlap(tuple1, tuple2) == 2
assert _command.overlap(tuple1, tuple3) == 0


def test_command_is_commutable(main_engine):
"""Check is_commutable function returns 0, 1, 2
For False, True and Maybe
'Maybe' refers to the situation where you
might have a commutable circuit
CNOT's commutable circuit wont be recognised at this
level because CNOT.__gate__ = ControlledGate
whereas in the optimizer CNOT.__gate__ = XGate."""
qubit1 = Qureg([Qubit(main_engine, 0)])
qubit2 = Qureg([Qubit(main_engine, 1)])
cmd1 = _command.Command(main_engine, Rx(0.5), (qubit1,))
cmd2 = _command.Command(main_engine, Rx(0.5), (qubit1,))
cmd3 = _command.Command(main_engine, Rx(0.5), (qubit2,))
cmd4 = _command.Command(main_engine, Rxx(0.5), (qubit1, qubit2))
cmd5 = _command.Command(main_engine, Ry(0.2), (qubit1,))
cmd6 = _command.Command(main_engine, Rz(0.2), (qubit1,))
cmd7 = _command.Command(main_engine, H, (qubit1,))
cmd8 = _command.Command(main_engine, CNOT, (qubit2,), qubit1)
cmd9 = _command.Command(main_engine, X, (qubit1,))
cmd10 = _command.Command(main_engine, SqrtX, (qubit1,))
cmd11 = _command.Command(main_engine, Ph(math.pi), (qubit1,))
assert not cmd1.is_commutable(cmd2) # Identical qubits, identical gate
assert _command.overlap(cmd1.all_qubits, cmd3.all_qubits) == 0
assert not cmd1.is_commutable(cmd3) # Different qubits, same gate
assert cmd3.is_commutable(cmd4) # Qubits in common, different but commutable gates
assert not cmd4.is_commutable(cmd5) # Qubits in common, different, non-commutable gates
assert (
cmd6.is_commutable(cmd7) == Commutability.MAYBE_COMMUTABLE.value
) # Rz has a commutable circuit which starts with H
assert not cmd7.is_commutable(cmd8) # H does not have a commutable circuit which starts with CNOT
assert cmd1.is_commutable(cmd9) # Rx commutes with X
assert cmd9.is_commutable(cmd1)
assert cmd10.is_commutable(cmd9) # SqrtX commutes with X
assert cmd9.is_commutable(cmd10)
assert cmd11.is_commutable(cmd9) # Ph commutes with X
assert cmd9.is_commutable(cmd11)


def test_command_order_qubits(main_engine):
qubit0 = Qureg([Qubit(main_engine, 0)])
qubit1 = Qureg([Qubit(main_engine, 1)])
Expand Down Expand Up @@ -278,6 +341,12 @@ def test_command_comparison(main_engine):
cmd6.tags = ["TestTag"]
cmd6.add_control_qubits(ctrl_qubit)
assert cmd6 != cmd1
# Test not equal because of location of ctrl qubits
ctrl_qubit2 = ctrl_qubit = Qureg([Qubit(main_engine, 2)])
cmd7 = _command.Command(main_engine, Rx(0.5), (qubit,))
cmd7.tags = ["TestTag"]
cmd7.add_control_qubits(ctrl_qubit2)
assert not cmd7 == cmd1


def test_command_str(main_engine):
Expand Down
Loading
Loading