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

Move mcx synthesis methods with ancillas to the synthesis library #12904

Merged
merged 20 commits into from
Aug 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
138 changes: 16 additions & 122 deletions qiskit/circuit/library/standard_gates/x.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
"""X, CX, CCX and multi-controlled X gates."""
from __future__ import annotations
from typing import Optional, Union, Type
from math import ceil, pi
from math import pi
import numpy
from qiskit.circuit.controlledgate import ControlledGate
from qiskit.circuit.singleton import SingletonGate, SingletonControlledGate, stdlib_singleton_key
Expand Down Expand Up @@ -1371,47 +1371,12 @@ def inverse(self, annotated: bool = False):

def _define(self):
"""Define the MCX gate using recursion."""
# pylint: disable=cyclic-import
from qiskit.circuit.quantumcircuit import QuantumCircuit

q = QuantumRegister(self.num_qubits, name="q")
qc = QuantumCircuit(q, name=self.name)
if self.num_qubits == 4:
qc._append(C3XGate(), q[:], [])
self.definition = qc
elif self.num_qubits == 5:
qc._append(C4XGate(), q[:], [])
self.definition = qc
else:
num_ctrl_qubits = len(q) - 1
q_ancilla = q[-1]
q_target = q[-2]
middle = ceil(num_ctrl_qubits / 2)
first_half = [*q[:middle]]
second_half = [*q[middle : num_ctrl_qubits - 1], q_ancilla]

qc._append(
MCXVChain(num_ctrl_qubits=len(first_half), dirty_ancillas=True),
qargs=[*first_half, q_ancilla, *q[middle : middle + len(first_half) - 2]],
cargs=[],
)
qc._append(
MCXVChain(num_ctrl_qubits=len(second_half), dirty_ancillas=True),
qargs=[*second_half, q_target, *q[: len(second_half) - 2]],
cargs=[],
)
qc._append(
MCXVChain(num_ctrl_qubits=len(first_half), dirty_ancillas=True),
qargs=[*first_half, q_ancilla, *q[middle : middle + len(first_half) - 2]],
cargs=[],
)
qc._append(
MCXVChain(num_ctrl_qubits=len(second_half), dirty_ancillas=True),
qargs=[*second_half, q_target, *q[: len(second_half) - 2]],
cargs=[],
)
# pylint: disable=cyclic-import
from qiskit.synthesis.multi_controlled import synth_mcx_1_clean_b95

self.definition = qc
qc = synth_mcx_1_clean_b95(self.num_ctrl_qubits)
self.definition = qc


class MCXVChain(MCXGate):
Expand Down Expand Up @@ -1513,92 +1478,21 @@ def get_num_ancilla_qubits(num_ctrl_qubits: int, mode: str = "v-chain"):

def _define(self):
"""Define the MCX gate using a V-chain of CX gates."""
# pylint: disable=cyclic-import
from qiskit.circuit.quantumcircuit import QuantumCircuit

q = QuantumRegister(self.num_qubits, name="q")
qc = QuantumCircuit(q, name=self.name)
q_controls = q[: self.num_ctrl_qubits]
q_target = q[self.num_ctrl_qubits]
q_ancillas = q[self.num_ctrl_qubits + 1 :]

if self._dirty_ancillas:
if self.num_ctrl_qubits < 3:
qc.mcx(q_controls, q_target)
elif not self._relative_phase and self.num_ctrl_qubits == 3:
qc._append(C3XGate(), [*q_controls, q_target], [])
else:
num_ancillas = self.num_ctrl_qubits - 2
targets = [q_target] + q_ancillas[:num_ancillas][::-1]

for j in range(2):
for i in range(self.num_ctrl_qubits): # action part
if i < self.num_ctrl_qubits - 2:
if targets[i] != q_target or self._relative_phase:
# gate cancelling

# cancel rightmost gates of action part
# with leftmost gates of reset part
if self._relative_phase and targets[i] == q_target and j == 1:
qc.cx(q_ancillas[num_ancillas - i - 1], targets[i])
qc.t(targets[i])
qc.cx(q_controls[self.num_ctrl_qubits - i - 1], targets[i])
qc.tdg(targets[i])
qc.h(targets[i])
else:
qc.h(targets[i])
qc.t(targets[i])
qc.cx(q_controls[self.num_ctrl_qubits - i - 1], targets[i])
qc.tdg(targets[i])
qc.cx(q_ancillas[num_ancillas - i - 1], targets[i])
else:
controls = [
q_controls[self.num_ctrl_qubits - i - 1],
q_ancillas[num_ancillas - i - 1],
]

qc.ccx(controls[0], controls[1], targets[i])
else:
# implements an optimized toffoli operation
# up to a diagonal gate, akin to lemma 6 of arXiv:1501.06911
qc.h(targets[i])
qc.t(targets[i])
qc.cx(q_controls[self.num_ctrl_qubits - i - 2], targets[i])
qc.tdg(targets[i])
qc.cx(q_controls[self.num_ctrl_qubits - i - 1], targets[i])
qc.t(targets[i])
qc.cx(q_controls[self.num_ctrl_qubits - i - 2], targets[i])
qc.tdg(targets[i])
qc.h(targets[i])

break

for i in range(num_ancillas - 1): # reset part
qc.cx(q_ancillas[i], q_ancillas[i + 1])
qc.t(q_ancillas[i + 1])
qc.cx(q_controls[2 + i], q_ancillas[i + 1])
qc.tdg(q_ancillas[i + 1])
qc.h(q_ancillas[i + 1])

if self._action_only:
qc.ccx(q_controls[-1], q_ancillas[-1], q_target)

break
else:
qc.rccx(q_controls[0], q_controls[1], q_ancillas[0])
i = 0
for j in range(2, self.num_ctrl_qubits - 1):
qc.rccx(q_controls[j], q_ancillas[i], q_ancillas[i + 1])
# pylint: disable=cyclic-import
from qiskit.synthesis.multi_controlled import synth_mcx_n_dirty_i15

i += 1

qc.ccx(q_controls[-1], q_ancillas[i], q_target)

for j in reversed(range(2, self.num_ctrl_qubits - 1)):
qc.rccx(q_controls[j], q_ancillas[i - 1], q_ancillas[i])
qc = synth_mcx_n_dirty_i15(
self.num_ctrl_qubits,
self._relative_phase,
self._action_only,
)

i -= 1
else: # use clean ancillas
# pylint: disable=cyclic-import
from qiskit.synthesis.multi_controlled import synth_mcx_n_clean_m15

qc.rccx(q_controls[0], q_controls[1], q_ancillas[i])
qc = synth_mcx_n_clean_m15(self.num_ctrl_qubits)

self.definition = qc
12 changes: 12 additions & 0 deletions qiskit/synthesis/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,13 @@

.. autofunction:: two_qubit_cnot_decompose

Multi Controlled Synthesis
==========================

.. autofunction:: synth_mcx_n_dirty_i15
.. autofunction:: synth_mcx_n_clean_m15
.. autofunction:: synth_mcx_1_clean_b95

"""

from .evolution import (
Expand Down Expand Up @@ -173,3 +180,8 @@
two_qubit_cnot_decompose,
TwoQubitWeylDecomposition,
)
from .multi_controlled.mcx_with_ancillas_synth import (
synth_mcx_n_dirty_i15,
synth_mcx_n_clean_m15,
synth_mcx_1_clean_b95,
)
19 changes: 19 additions & 0 deletions qiskit/synthesis/multi_controlled/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# This code is part of Qiskit.
#
# (C) Copyright IBM 2024.
#
# This code is licensed under the Apache License, Version 2.0. You may
# obtain a copy of this license in the LICENSE.txt file in the root directory
# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
#
# Any modifications or derivative works of this code must retain this
# copyright notice, and modified files need to carry a notice indicating
# that they have been altered from the originals.

"""Module containing multi-controlled circuits synthesis"""

from .mcx_with_ancillas_synth import (
synth_mcx_n_dirty_i15,
synth_mcx_n_clean_m15,
synth_mcx_1_clean_b95,
)
Loading
Loading