Skip to content

Commit

Permalink
Merge pull request #1257 from qiboteam/clifford-check
Browse files Browse the repository at this point in the history
Update `gate.clifford` value in rotations given a new angle
  • Loading branch information
renatomello authored Mar 20, 2024
2 parents b1f0fe9 + 704cf98 commit d1eecbb
Show file tree
Hide file tree
Showing 5 changed files with 118 additions and 27 deletions.
6 changes: 5 additions & 1 deletion src/qibo/gates/abstract.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,6 @@ def __init__(self):
self.init_args = []
self.init_kwargs = {}

self.clifford = False
self.unitary = False
self._target_qubits = ()
self._control_qubits = ()
Expand All @@ -59,6 +58,11 @@ def __init__(self):
self.device_gates = set()
self.original_gate = None

@property
def clifford(self):
"""Return boolean value representing if a Gate is Clifford or not."""
return False

@property
def raw(self) -> dict:
"""Serialize to dictionary.
Expand Down
108 changes: 86 additions & 22 deletions src/qibo/gates/gates.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,12 @@ def __init__(self, q):
self.draw_label = "H"
self.target_qubits = (q,)
self.init_args = [q]
self.clifford = True
self.unitary = True

@property
def clifford(self):
return True

@property
def qasm_label(self):
return "h"
Expand All @@ -58,9 +61,12 @@ def __init__(self, q):
self.draw_label = "X"
self.target_qubits = (q,)
self.init_args = [q]
self.clifford = True
self.unitary = True

@property
def clifford(self):
return True

@property
def qasm_label(self):
return "x"
Expand Down Expand Up @@ -171,9 +177,12 @@ def __init__(self, q):
self.draw_label = "Y"
self.target_qubits = (q,)
self.init_args = [q]
self.clifford = True
self.unitary = True

@property
def clifford(self):
return True

@property
def qasm_label(self):
return "y"
Expand Down Expand Up @@ -215,9 +224,12 @@ def __init__(self, q):
self.draw_label = "Z"
self.target_qubits = (q,)
self.init_args = [q]
self.clifford = True
self.unitary = True

@property
def clifford(self):
return True

@property
def qasm_label(self):
return "z"
Expand Down Expand Up @@ -256,9 +268,12 @@ def __init__(self, q):
self.draw_label = "SX"
self.target_qubits = (q,)
self.init_args = [q]
self.clifford = True
self.unitary = True

@property
def clifford(self):
return True

@property
def qasm_label(self):
return "sx"
Expand Down Expand Up @@ -303,9 +318,12 @@ def __init__(self, q):
self.draw_label = "SXDG"
self.target_qubits = (q,)
self.init_args = [q]
self.clifford = True
self.unitary = True

@property
def clifford(self):
return True

@property
def qasm_label(self):
return "sxdg"
Expand Down Expand Up @@ -350,9 +368,12 @@ def __init__(self, q):
self.draw_label = "S"
self.target_qubits = (q,)
self.init_args = [q]
self.clifford = True
self.unitary = True

@property
def clifford(self):
return True

@property
def qasm_label(self):
return "s"
Expand Down Expand Up @@ -382,9 +403,12 @@ def __init__(self, q):
self.draw_label = "SDG"
self.target_qubits = (q,)
self.init_args = [q]
self.clifford = True
self.unitary = True

@property
def clifford(self):
return True

@property
def qasm_label(self):
return "sdg"
Expand Down Expand Up @@ -468,9 +492,12 @@ def __init__(self, *q):
self.draw_label = "I"
self.target_qubits = tuple(q)
self.init_args = q
self.clifford = True
self.unitary = True

@property
def clifford(self):
return True

@property
def qasm_label(self):
return "id"
Expand Down Expand Up @@ -502,6 +529,11 @@ def __init__(self, *q, delay: int = 0):
self.target_qubits = tuple(q)


def _is_clifford_given_angle(angle):
"""Helper function to update Clifford boolean condition according to the given angle ``angle``."""
return isinstance(angle, (float, int)) and (angle % (np.pi / 2)).is_integer()


class _Rn_(ParametrizedGate):
"""Abstract class for defining the RX, RY and RZ rotations.
Expand All @@ -524,13 +556,14 @@ def __init__(self, q, theta, trainable=True):
if isinstance(theta, Parameter):
theta = theta()

if isinstance(theta, (float, int)) and (theta % (np.pi / 2)).is_integer():
self.clifford = True

self.parameters = theta
self.init_args = [q]
self.init_kwargs = {"theta": theta, "trainable": trainable}

@property
def clifford(self):
return _is_clifford_given_angle(self.parameters[0])

def _dagger(self) -> "Gate":
""""""
return self.__class__(
Expand Down Expand Up @@ -974,9 +1007,12 @@ def __init__(self, q0, q1):
self.control_qubits = (q0,)
self.target_qubits = (q1,)
self.init_args = [q0, q1]
self.clifford = True
self.unitary = True

@property
def clifford(self):
return True

@property
def qasm_label(self):
return "cx"
Expand Down Expand Up @@ -1011,9 +1047,12 @@ def __init__(self, q0, q1):
self.control_qubits = (q0,)
self.target_qubits = (q1,)
self.init_args = [q0, q1]
self.clifford = True
self.unitary = True

@property
def clifford(self):
return True

@property
def qasm_label(self):
return "cy"
Expand Down Expand Up @@ -1057,9 +1096,12 @@ def __init__(self, q0, q1):
self.control_qubits = (q0,)
self.target_qubits = (q1,)
self.init_args = [q0, q1]
self.clifford = True
self.unitary = True

@property
def clifford(self):
return True

@property
def qasm_label(self):
return "cz"
Expand Down Expand Up @@ -1186,12 +1228,13 @@ def __init__(self, q0, q1, theta, trainable=True):
self.parameters = theta
self.unitary = True

if isinstance(theta, (float, int)) and (theta % np.pi).is_integer():
self.clifford = True

self.init_args = [q0, q1]
self.init_kwargs = {"theta": theta, "trainable": trainable}

@property
def clifford(self):
return _is_clifford_given_angle(self.parameters[0])

def _dagger(self) -> "Gate":
""""""
q0 = self.control_qubits[0]
Expand Down Expand Up @@ -1484,9 +1527,12 @@ def __init__(self, q0, q1):
self.draw_label = "x"
self.target_qubits = (q0, q1)
self.init_args = [q0, q1]
self.clifford = True
self.unitary = True

@property
def clifford(self):
return True

@property
def qasm_label(self):
return "swap"
Expand Down Expand Up @@ -1516,9 +1562,12 @@ def __init__(self, q0, q1):
self.draw_label = "i"
self.target_qubits = (q0, q1)
self.init_args = [q0, q1]
self.clifford = True
self.unitary = True

@property
def clifford(self):
return True

@property
def qasm_label(self):
return "iswap"
Expand Down Expand Up @@ -1608,9 +1657,12 @@ def __init__(self, q0, q1):
self.draw_label = "fx"
self.target_qubits = (q0, q1)
self.init_args = [q0, q1]
self.clifford = True
self.unitary = True

@property
def clifford(self):
return True

@property
def qasm_label(self):
return "fswap"
Expand Down Expand Up @@ -2158,9 +2210,12 @@ def __init__(self, q0, q1):
self.draw_label = "ECR"
self.target_qubits = (q0, q1)
self.init_args = [q0, q1]
self.clifford = True
self.unitary = True

@property
def clifford(self):
return True

def decompose(self, *free, use_toffolis: bool = True) -> List[Gate]:
"""Decomposition of :math:`\\textup{ECR}` gate up to global phase.
Expand Down Expand Up @@ -2315,6 +2370,7 @@ def __init__(
self.name = "Unitary" if name is None else name
self.draw_label = "U"
self.target_qubits = tuple(q)
self._clifford = False

# TODO: Check that given ``unitary`` has proper shape?
self.parameter_names = "u"
Expand Down Expand Up @@ -2361,6 +2417,14 @@ def parameters(self, x):
for gate in self.device_gates: # pragma: no cover
gate.parameters = x

@property
def clifford(self):
return self._clifford

@clifford.setter
def clifford(self, value):
self._clifford = value

def on_qubits(self, qubit_map):
args = [self.init_args[0]]
args.extend(qubit_map.get(i) for i in self.target_qubits)
Expand Down
2 changes: 1 addition & 1 deletion src/qibo/models/error_mitigation.py
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,7 @@ def sample_training_circuit_cdr(
gates_to_replace = []
for i, gate in enumerate(circuit.queue):
if isinstance(gate, gates.RZ):
if gate.init_kwargs["theta"] % (np.pi / 2) != 0.0:
if not gate.clifford:
gates_to_replace.append((i, gate))

if not gates_to_replace:
Expand Down
27 changes: 26 additions & 1 deletion tests/test_gates_gates.py
Original file line number Diff line number Diff line change
Expand Up @@ -686,7 +686,7 @@ def test_cun(backend, name, params):

if name in ["CRX", "CRY", "CRZ"]:
theta = params["theta"]
if (theta % np.pi).is_integer():
if (theta % (np.pi / 2)).is_integer():
assert gate.clifford
else:
assert not gate.clifford
Expand Down Expand Up @@ -1689,3 +1689,28 @@ def test_x_decomposition_execution(backend, target, controls, free, use_toffolis


###############################################################################

####################### Test Clifford updates #################################


@pytest.mark.parametrize(
"gate",
[
gates.RX(0, 0),
gates.RY(0, 0),
gates.RZ(0, 0),
gates.CRX(0, 1, 0),
gates.CRY(0, 1, 0),
gates.CRZ(0, 1, 0),
],
)
def test_clifford_condition_update(backend, gate):
"""Test clifford condition update if setting new angle into the rotations."""
assert gate.clifford == True
gate.parameters = 0.5
assert gate.clifford == False
gate.parameters = np.pi
assert gate.clifford == True


###############################################################################
2 changes: 0 additions & 2 deletions tests/test_models_error_mitigation.py
Original file line number Diff line number Diff line change
Expand Up @@ -215,8 +215,6 @@ def test_sample_training_circuit(nqubits):
c.add(gates.CNOT(q, q + 1) for q in range(1, nqubits, 2))
c.add(gates.M(q) for q in range(nqubits))

for j in range(len(c.queue)):
c.queue[j].clifford = True
with pytest.raises(ValueError):
sample_training_circuit_cdr(c)
with pytest.raises(ValueError):
Expand Down

0 comments on commit d1eecbb

Please sign in to comment.