Skip to content

Commit

Permalink
Add the gates ECR, iSwap, CY, SX, SXdg, DCX to the Clifford class (#9623
Browse files Browse the repository at this point in the history
)

* add SX and SXdg gates

* add CY, iSwap and ECR gates

* add new gates to the Clifford docs

* improve ECR

* add more tests for the new gates

* add ECR decomposition into Clifford gates to the equivalence library

* update ECR in equivalence library

* add DCX to clifford_circuits.py

* add test for dcx

* add release notes

* more efficient synthesis of an ECR gate

* change the release notes from features to bug fix

---------

Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
  • Loading branch information
ShellyGarion and mergify[bot] authored Feb 22, 2023
1 parent 72a7eac commit 9128ebd
Show file tree
Hide file tree
Showing 5 changed files with 211 additions and 6 deletions.
20 changes: 20 additions & 0 deletions qiskit/circuit/library/standard_gates/equivalence_library.py
Original file line number Diff line number Diff line change
Expand Up @@ -616,6 +616,26 @@
def_ecr.append(inst, qargs, cargs)
_sel.add_equivalence(ECRGate(), def_ecr)

# ECRGate decomposed to Clifford gates (up to a global phase)
#
# global phase: 7π/4
# ┌──────┐ ┌───┐ ┌───┐
# q_0: ┤0 ├ q_0: ┤ S ├───■──┤ X ├
# │ Ecr │ ≡ ├───┴┐┌─┴─┐└───┘
# q_1: ┤1 ├ q_1: ┤ √X ├┤ X ├─────
# └──────┘ └────┘└───┘

q = QuantumRegister(2, "q")
def_ecr_cliff = QuantumCircuit(q, global_phase=-pi / 4)
for inst, qargs, cargs in [
(SGate(), [q[0]], []),
(SXGate(), [q[1]], []),
(CXGate(), [q[0], q[1]], []),
(XGate(), [q[0]], []),
]:
def_ecr_cliff.append(inst, qargs, cargs)
_sel.add_equivalence(ECRGate(), def_ecr_cliff)

# SGate
#
# ┌───┐ ┌─────────┐
Expand Down
5 changes: 4 additions & 1 deletion qiskit/quantum_info/operators/symplectic/clifford.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,8 +85,11 @@ class Clifford(BaseOperator, AdjointMixin, Operation):
:class:`~qiskit.circuit.library.XGate`, :class:`~qiskit.circuit.library.YGate`,
:class:`~qiskit.circuit.library.ZGate`, :class:`~qiskit.circuit.library.HGate`,
:class:`~qiskit.circuit.library.SGate`, :class:`~qiskit.circuit.library.SdgGate`,
:class:`~qiskit.circuit.library.SXGate`, :class:`~qiskit.circuit.library.SXdgGate`,
:class:`~qiskit.circuit.library.CXGate`, :class:`~qiskit.circuit.library.CZGate`,
:class:`~qiskit.circuit.library.SwapGate`.
:class:`~qiskit.circuit.library.CYGate`, :class:`~qiskit.circuit.library.DXGate`,
:class:`~qiskit.circuit.library.SwapGate`, :class:`~qiskit.circuit.library.iSwapGate`,
:class:`~qiskit.circuit.library.ECRGate`.
They can be converted back into a :class:`~qiskit.circuit.QuantumCircuit`,
or :class:`~qiskit.circuit.Gate` object using the :meth:`~Clifford.to_circuit`
or :meth:`~Clifford.to_instruction` methods respectively. Note that this
Expand Down
120 changes: 119 additions & 1 deletion qiskit/quantum_info/operators/symplectic/clifford_circuits.py
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,42 @@ def _append_sdg(clifford, qubit):
return clifford


def _append_sx(clifford, qubit):
"""Apply an SX gate to a Clifford.
Args:
clifford (Clifford): a Clifford.
qubit (int): gate qubit index.
Returns:
Clifford: the updated Clifford.
"""
x = clifford.x[:, qubit]
z = clifford.z[:, qubit]

clifford.phase ^= ~x & z
x ^= z
return clifford


def _append_sxdg(clifford, qubit):
"""Apply an SXdg gate to a Clifford.
Args:
clifford (Clifford): a Clifford.
qubit (int): gate qubit index.
Returns:
Clifford: the updated Clifford.
"""
x = clifford.x[:, qubit]
z = clifford.z[:, qubit]

clifford.phase ^= x & z
x ^= z
return clifford


def _append_v(clifford, qubit):
"""Apply a V gate to a Clifford.
Expand Down Expand Up @@ -312,6 +348,23 @@ def _append_cz(clifford, control, target):
return clifford


def _append_cy(clifford, control, target):
"""Apply a CY gate to a Clifford.
Args:
clifford (Clifford): a Clifford.
control (int): gate control qubit index.
target (int): gate target qubit index.
Returns:
Clifford: the updated Clifford.
"""
clifford = _append_sdg(clifford, target)
clifford = _append_cx(clifford, control, target)
clifford = _append_s(clifford, target)
return clifford


def _append_swap(clifford, qubit0, qubit1):
"""Apply a Swap gate to a Clifford.
Expand All @@ -328,6 +381,61 @@ def _append_swap(clifford, qubit0, qubit1):
return clifford


def _append_iswap(clifford, qubit0, qubit1):
"""Apply a iSwap gate to a Clifford.
Args:
clifford (Clifford): a Clifford.
qubit0 (int): first qubit index.
qubit1 (int): second qubit index.
Returns:
Clifford: the updated Clifford.
"""
clifford = _append_s(clifford, qubit0)
clifford = _append_h(clifford, qubit0)
clifford = _append_s(clifford, qubit1)
clifford = _append_cx(clifford, qubit0, qubit1)
clifford = _append_cx(clifford, qubit1, qubit0)
clifford = _append_h(clifford, qubit1)
return clifford


def _append_dcx(clifford, qubit0, qubit1):
"""Apply a DCX gate to a Clifford.
Args:
clifford (Clifford): a Clifford.
qubit0 (int): first qubit index.
qubit1 (int): second qubit index.
Returns:
Clifford: the updated Clifford.
"""
clifford = _append_cx(clifford, qubit0, qubit1)
clifford = _append_cx(clifford, qubit1, qubit0)
return clifford


def _append_ecr(clifford, qubit0, qubit1):
"""Apply an ECR gate to a Clifford.
Args:
clifford (Clifford): a Clifford.
qubit0 (int): first qubit index.
qubit1 (int): second qubit index.
Returns:
Clifford: the updated Clifford.
"""
clifford = _append_s(clifford, qubit0)
clifford = _append_sx(clifford, qubit1)
clifford = _append_cx(clifford, qubit0, qubit1)
clifford = _append_x(clifford, qubit0)

return clifford


# Basis Clifford Gates
_BASIS_1Q = {
"i": _append_i,
Expand All @@ -340,9 +448,19 @@ def _append_swap(clifford, qubit0, qubit1):
"s": _append_s,
"sdg": _append_sdg,
"sinv": _append_sdg,
"sx": _append_sx,
"sxdg": _append_sxdg,
"v": _append_v,
"w": _append_w,
}
_BASIS_2Q = {"cx": _append_cx, "cz": _append_cz, "swap": _append_swap}
_BASIS_2Q = {
"cx": _append_cx,
"cz": _append_cz,
"cy": _append_cy,
"swap": _append_swap,
"iswap": _append_iswap,
"ecr": _append_ecr,
"dcx": _append_dcx,
}
# Non-clifford gates
_NON_CLIFFORD = {"t", "tdg", "ccx", "ccz"}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
---
fixes:
- |
Add the following Clifford gates, that already exist in the Circuit Library,
to the :class:`.Clifford` class:
:class:`.SXGate`, :class:`.SXdgGate`, :class:`.CYGate`, :class:`.DCXGate`,
:class:`.iSwapGate` and :class:`.ECRGate`.
- |
Add a decomposition of an :class:`.ECRGate` into Clifford gates (up to a global phase)
into the :class:`.EquivalenceLibrary`.
62 changes: 58 additions & 4 deletions test/python/quantum_info/operators/symplectic/test_clifford.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,17 @@
from qiskit.circuit.library import (
CXGate,
CZGate,
CYGate,
HGate,
IGate,
SdgGate,
SGate,
SXGate,
SXdgGate,
SwapGate,
iSwapGate,
ECRGate,
DCXGate,
XGate,
YGate,
ZGate,
Expand Down Expand Up @@ -82,11 +88,13 @@ def _define(self):
def random_clifford_circuit(num_qubits, num_gates, gates="all", seed=None):
"""Generate a pseudo random Clifford circuit."""

qubits_1_gates = ["i", "x", "y", "z", "h", "s", "sdg", "sx", "sxdg", "v", "w"]
qubits_2_gates = ["cx", "cz", "cy", "swap", "iswap", "ecr", "dcx"]
if gates == "all":
if num_qubits == 1:
gates = ["i", "x", "y", "z", "h", "s", "sdg", "v", "w"]
gates = qubits_1_gates
else:
gates = ["i", "x", "y", "z", "h", "s", "sdg", "v", "w", "cx", "cz", "swap"]
gates = qubits_1_gates + qubits_2_gates

instructions = {
"i": (IGate(), 1),
Expand All @@ -96,11 +104,17 @@ def random_clifford_circuit(num_qubits, num_gates, gates="all", seed=None):
"h": (HGate(), 1),
"s": (SGate(), 1),
"sdg": (SdgGate(), 1),
"sx": (SXGate(), 1),
"sxdg": (SXdgGate(), 1),
"v": (VGate(), 1),
"w": (WGate(), 1),
"cx": (CXGate(), 2),
"cy": (CYGate(), 2),
"cz": (CZGate(), 2),
"swap": (SwapGate(), 2),
"iswap": (iSwapGate(), 2),
"ecr": (ECRGate(), 2),
"dcx": (DCXGate(), 2),
}

if isinstance(seed, np.random.Generator):
Expand Down Expand Up @@ -140,6 +154,8 @@ def test_append_1_qubit_gate(self):
"sinv": np.array([[[True, True], [False, True]]], dtype=bool),
"v": np.array([[[True, True], [True, False]]], dtype=bool),
"w": np.array([[[False, True], [True, True]]], dtype=bool),
"sx": np.array([[[True, False], [True, True]]], dtype=bool),
"sxdg": np.array([[[True, False], [True, True]]], dtype=bool),
}

target_phase = {
Expand All @@ -155,6 +171,8 @@ def test_append_1_qubit_gate(self):
"sinv": np.array([[True, False]], dtype=bool),
"v": np.array([[False, False]], dtype=bool),
"w": np.array([[False, False]], dtype=bool),
"sx": np.array([[False, True]], dtype=bool),
"sxdg": np.array([[False, False]], dtype=bool),
}

target_stabilizer = {
Expand All @@ -170,6 +188,8 @@ def test_append_1_qubit_gate(self):
"sinv": "+Z",
"v": "+X",
"w": "+Y",
"sx": "-Y",
"sxdg": "+Y",
}

target_destabilizer = {
Expand All @@ -185,9 +205,25 @@ def test_append_1_qubit_gate(self):
"sinv": "-Y",
"v": "+Y",
"w": "+Z",
"sx": "+X",
"sxdg": "+X",
}

for gate_name in ("i", "id", "iden", "x", "y", "z", "h", "s", "sdg", "v", "w"):
for gate_name in (
"i",
"id",
"iden",
"x",
"y",
"z",
"h",
"s",
"sdg",
"v",
"w",
"sx",
"sxdg",
):
with self.subTest(msg="append gate %s" % gate_name):
cliff = Clifford([[1, 0], [0, 1]])
cliff = _append_operation(cliff, gate_name, [0])
Expand Down Expand Up @@ -273,6 +309,8 @@ def test_1_qubit_conj_relations(self):
"w * x * v = y",
"w * y * v = z",
"w * z * v = x",
"sdg * h * sdg = sx",
"s * h * s = sxdg",
]

for rel in rels:
Expand Down Expand Up @@ -410,6 +448,14 @@ def test_2_qubit_relations(self):
cliff = _append_operation(cliff, "sdg", [0])
self.assertEqual(cliff, cliff1)

with self.subTest(msg="relation between cx and dcx"):
cliff = Clifford(np.eye(4))
cliff1 = cliff.copy()
cliff = _append_operation(cliff, "cx", [0, 1])
cliff = _append_operation(cliff, "cx", [1, 0])
cliff1 = _append_operation(cliff1, "dcx", [0, 1])
self.assertEqual(cliff, cliff1)

def test_barrier_delay_sim(self):
"""Test barrier and delay instructions can be simulated"""
target_circ = QuantumCircuit(2)
Expand Down Expand Up @@ -591,7 +637,9 @@ class TestCliffordDecomposition(QiskitTestCase):
["h", "s", "sdg"],
["h", "s", "v"],
["h", "s", "w"],
["h", "s", "sdg", "i", "x", "y", "z", "v", "w"],
["h", "sx", "sxdg"],
["s", "sx", "sxdg"],
["h", "s", "sdg", "i", "x", "y", "z", "v", "w", "sx", "sxdg"],
]
)
def test_to_operator_1qubit_gates(self, gates):
Expand All @@ -609,11 +657,17 @@ def test_to_operator_1qubit_gates(self, gates):
gates=[
["cx"],
["cz"],
["cy"],
["swap"],
["iswap"],
["ecr"],
["dcx"],
["cx", "cz"],
["cx", "cz", "cy"],
["cx", "swap"],
["cz", "swap"],
["cx", "cz", "swap"],
["cx", "cz", "cy", "swap", "iswap", "ecr", "dcx"],
]
)
def test_to_operator_2qubit_gates(self, gates):
Expand Down

0 comments on commit 9128ebd

Please sign in to comment.