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

Add gate labels #853

Merged
merged 16 commits into from
Apr 24, 2023
Merged
10 changes: 5 additions & 5 deletions doc/source/code-examples/examples.rst
Original file line number Diff line number Diff line change
Expand Up @@ -104,9 +104,9 @@ For example
ccx: 1


The circuit property ``circuit.gate_types`` will also return a ``collections.Counter``
that contains the gate types and the corresponding numbers of appearance. The
method ``circuit.gates_of_type()`` can be used to access gate objects of specific type.
The circuit property ``circuit.gate_types`` (or ``circuit.gate_names``) will return a ``collections.Counter``
that contains the gate types (or names) and the corresponding numbers of appearance. The
method ``circuit.gates_of_type()`` can be used to access gate objects of specific type or name.
For example for the circuit of the previous example:

.. testsetup::
Expand All @@ -124,13 +124,13 @@ For example for the circuit of the previous example:

.. testcode::

common_gates = c.gate_types.most_common()
common_gates = c.gate_names.most_common()
# returns the list [("h", 3), ("cx", 2), ("ccx", 1)]

most_common_gate = common_gates[0][0]
# returns "h"

all_h_gates = c.gates_of_type("h")
all_h_gates = c.gates_of_type(gates.H)
# returns the list [(0, ref to H(0)), (1, ref to H(1)), (4, ref to H(2))]

A circuit may contain multi-controlled or other gates that are not supported by
Expand Down
38 changes: 22 additions & 16 deletions src/qibo/gates/abstract.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
import collections
from abc import ABC, abstractmethod
from collections.abc import Iterable
from typing import List, Sequence, Tuple

import sympy
Expand All @@ -18,6 +16,8 @@ def __init__(self):
"""
Attributes:
name (str): Name of the gate.
draw_label (str): Optional label for drawing the gate in a circuit
with :func:`qibo.models.Circuit.draw`.
is_controlled_by (bool): ``True`` if the gate was created using the
:meth:`qibo.gates.abstract.Gate.controlled_by` method,
otherwise ``False``.
Expand All @@ -30,6 +30,7 @@ def __init__(self):
from qibo import config

self.name = None
self.draw_label = None
self.is_controlled_by = False
# args for creating gate
self.init_args = []
Expand Down Expand Up @@ -61,15 +62,22 @@ def qubits(self) -> Tuple[int]:
"""Tuple with ids of all qubits (control and target) that the gate acts."""
return self.control_qubits + self.target_qubits

@property
def qasm_label(self):
"""String corresponding to OpenQASM operation of the gate."""
raise_error(
NotImplementedError,
f"{self.__class__.__name__} is not supported by OpenQASM",
)

def _set_target_qubits(self, qubits: Sequence[int]):
"""Helper method for setting target qubits."""
self._target_qubits = tuple(qubits)
if len(self._target_qubits) != len(set(qubits)):
repeated = self._find_repeated(qubits)
raise_error(
ValueError,
"Target qubit {} was given twice for gate {}."
"".format(repeated, self.name),
f"Target qubit {repeated} was given twice for gate {self.__class__.__name__}.",
)

def _set_control_qubits(self, qubits: Sequence[int]):
Expand All @@ -79,8 +87,7 @@ def _set_control_qubits(self, qubits: Sequence[int]):
repeated = self._find_repeated(qubits)
raise_error(
ValueError,
"Control qubit {} was given twice for gate {}."
"".format(repeated, self.name),
f"Control qubit {repeated} was given twice for gate {self.__class__.__name__}.",
)

@target_qubits.setter
Expand Down Expand Up @@ -123,8 +130,8 @@ def _check_control_target_overlap(self):
if common:
raise_error(
ValueError,
"{} qubits are both targets and controls for "
"gate {}.".format(common, self.name),
f"{common} qubits are both targets and controls "
+ f"for gate {self.__class__.__name__}.",
)

@property
Expand Down Expand Up @@ -213,9 +220,8 @@ def wrapper(self, *args):
raise_error(
RuntimeError,
"Cannot use `controlled_by` method "
"on gate {} because it is already "
"controlled by {}."
"".format(self, self.control_qubits),
+ f"on gate {self} because it is already "
+ f"controlled by {self.control_qubits}.",
)
return func(self, *args) # pylint: disable=E1102

Expand Down Expand Up @@ -267,13 +273,14 @@ def generator_eigenvalue(self):

raise_error(
NotImplementedError,
f"Generator eigenvalue is not implemented for {self.name}",
f"Generator eigenvalue is not implemented for {self.__class__.__name__}",
)

def basis_rotation(self):
"""Transformation required to rotate the basis for measuring the gate."""
raise_error(
NotImplementedError, f"Basis rotation is not implemented for {self.name}"
NotImplementedError,
f"Basis rotation is not implemented for {self.__class__.__name__}",
)

@property
Expand Down Expand Up @@ -346,9 +353,8 @@ def parameters(self, x):
if len(x) != nparams:
raise_error(
ValueError,
"Parametrized gate has {} parameters "
"but {} update values were given."
"".format(nparams, len(x)),
f"Parametrized gate has {nparams} parameters "
+ f"but {len(x)} update values were given.",
)
for i, v in enumerate(x):
if isinstance(v, sympy.Expr):
Expand Down
13 changes: 12 additions & 1 deletion src/qibo/gates/channels.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@ def on_qubits(self, qubit_map): # pragma: no cover

def apply(self, backend, state, nqubits): # pragma: no cover
raise_error(
NotImplementedError, f"{self.name} cannot be applied to state vector."
NotImplementedError,
f"{self.__class__.__name__} cannot be applied to state vector.",
)

def apply_density_matrix(self, backend, state, nqubits):
Expand Down Expand Up @@ -209,6 +210,7 @@ class KrausChannel(Channel):
def __init__(self, ops):
super().__init__()
self.name = "KrausChannel"
self.draw_label = "K"
if isinstance(ops[0], Gate):
self.gates = tuple(ops)
self.target_qubits = tuple(
Expand Down Expand Up @@ -273,6 +275,7 @@ def __init__(self, probabilities, ops):
)
super().__init__(ops)
self.name = "UnitaryChannel"
self.draw_label = "U"
self.coefficients = tuple(probabilities)
self.coefficient_sum = sum(probabilities)
if self.coefficient_sum > 1 + PRECISION_TOL or self.coefficient_sum <= 0:
Expand Down Expand Up @@ -325,6 +328,7 @@ def __init__(self, q, px=0, py=0, pz=0):

super().__init__(probs, gates)
self.name = "PauliNoiseChannel"
self.draw_label = "PN"
assert self.target_qubits == (q,)

self.init_args = [q]
Expand Down Expand Up @@ -407,6 +411,7 @@ def __init__(self, qubits: Tuple[int, list, tuple], operators: list):

super().__init__(probabilities, gates)
self.name = "GeneralizedPauliNoiseChannel"
self.draw_label = "GPN"
self.init_args = qubits
self.init_kwargs = dict(operators)

Expand Down Expand Up @@ -447,6 +452,7 @@ def __init__(self, q, lam: str = 0):
)

self.name = "DepolarizingChannel"
self.draw_label = "D"
self.target_qubits = q

self.init_args = [q]
Expand Down Expand Up @@ -584,6 +590,9 @@ def __init__(self, q, t_1, t_2, time, excited_population=0):
self.init_kwargs["p0"] = preset0
self.init_kwargs["p1"] = preset1

self.name = "ThermalRelaxationChannel"
self.draw_label = "TR"

def apply_density_matrix(self, backend, state, nqubits):
qubit = self.target_qubits[0]

Expand Down Expand Up @@ -655,6 +664,7 @@ def __init__(self, q: Tuple[int, list, tuple], probabilities):

super().__init__(ops=operators)
self.name = "ReadoutErrorChannel"
self.draw_label = "RE"


class ResetChannel(KrausChannel):
Expand Down Expand Up @@ -696,6 +706,7 @@ def __init__(self, q, p0=0.0, p1=0.0):
super().__init__(ops=operators)
self.init_kwargs = {"p0": p0, "p1": p1}
self.name = "ResetChannel"
self.draw_label = "R"

def apply_density_matrix(self, backend, state, nqubits):
return backend.reset_error_density_matrix(self, state, nqubits)
Loading