Пример #1
0
    def test_open_control_cy_unrolling(self):
        """test unrolling of open control gates when gate is in basis"""
        qc = QuantumCircuit(2)
        qc.cy(0, 1, ctrl_state=0)
        dag = circuit_to_dag(qc)
        unroller = Unroller(['u3', 'cy'])
        uqc = dag_to_circuit(unroller.run(dag))

        ref_circuit = QuantumCircuit(2)
        ref_circuit.u3(np.pi, 0, np.pi, 0)
        ref_circuit.cy(0, 1)
        ref_circuit.u3(np.pi, 0, np.pi, 0)
        self.assertEqual(uqc, ref_circuit)
Пример #2
0
    def decompose(self):
        """Call a decomposition pass on this circuit,
        to decompose one level (shallow decompose).

        Returns:
            QuantumCircuit: a circuit one level decomposed
        """
        from qiskit.transpiler.passes.decompose import Decompose
        from qiskit.converters.circuit_to_dag import circuit_to_dag
        from qiskit.converters.dag_to_circuit import dag_to_circuit
        pass_ = Decompose()
        decomposed_dag = pass_.run(circuit_to_dag(self))
        return dag_to_circuit(decomposed_dag)
    def test_composite(self):
        """Test composite gate count."""
        qreg = QuantumRegister(self.num_ctrl + self.num_target)
        qc = QuantumCircuit(qreg, name='composite')
        qc.append(self.grx.control(self.num_ctrl), qreg)
        qc.append(self.gry.control(self.num_ctrl), qreg)
        qc.append(self.gry, qreg[0:self.gry.num_qubits])
        qc.append(self.grz.control(self.num_ctrl), qreg)

        dag = circuit_to_dag(qc)
        unroller = Unroller(['u3', 'cx'])
        uqc = dag_to_circuit(unroller.run(dag))
        self.log.info('%s gate count: %d', uqc.name, uqc.size())
        self.assertTrue(uqc.size() <= 93)  # this limit could be changed
Пример #4
0
    def test_rotation_gates(self):
        """Test controlled rotation gates"""
        import qiskit.extensions.standard.u1 as u1
        import qiskit.extensions.standard.rx as rx
        import qiskit.extensions.standard.ry as ry
        import qiskit.extensions.standard.rz as rz
        num_ctrl = 2
        num_target = 1
        qreg = QuantumRegister(num_ctrl + num_target)

        theta = pi / 2
        gu1 = u1.U1Gate(theta)
        grx = rx.RXGate(theta)
        gry = ry.RYGate(theta)
        grz = rz.RZGate(theta)

        ugu1 = ac._unroll_gate(gu1, ['u1', 'u3', 'cx'])
        ugrx = ac._unroll_gate(grx, ['u1', 'u3', 'cx'])
        ugry = ac._unroll_gate(gry, ['u1', 'u3', 'cx'])
        ugrz = ac._unroll_gate(grz, ['u1', 'u3', 'cx'])
        ugrz.params = grz.params

        cgu1 = ugu1.control(num_ctrl)
        cgrx = ugrx.control(num_ctrl)
        cgry = ugry.control(num_ctrl)
        cgrz = ugrz.control(num_ctrl)

        for gate, cgate in zip([gu1, grx, gry, grz], [cgu1, cgrx, cgry, cgrz]):
            with self.subTest(i=gate.name):
                if gate.name == 'rz':
                    iden = Operator.from_label('I')
                    zgen = Operator.from_label('Z')
                    op_mat = (np.cos(0.5 * theta) * iden -
                              1j * np.sin(0.5 * theta) * zgen).data
                else:
                    op_mat = Operator(gate).data
                ref_mat = Operator(cgate).data
                cop_mat = _compute_control_matrix(op_mat, num_ctrl)
                self.assertTrue(
                    matrix_equal(cop_mat, ref_mat, ignore_phase=True))
                cqc = QuantumCircuit(num_ctrl + num_target)
                cqc.append(cgate, cqc.qregs[0])
                dag = circuit_to_dag(cqc)
                unroller = Unroller(['u3', 'cx'])
                uqc = dag_to_circuit(unroller.run(dag))
                self.log.info('%s gate count: %d', cgate.name, uqc.size())
                self.log.info('\n%s', str(uqc))
                # these limits could be changed
                if gate.name == 'ry':
                    self.assertTrue(uqc.size() <= 32)
                elif gate.name == 'rz':
                    self.assertTrue(uqc.size() <= 40)
                else:
                    self.assertTrue(uqc.size() <= 20)
        qc = QuantumCircuit(qreg, name='composite')
        qc.append(grx.control(num_ctrl), qreg)
        qc.append(gry.control(num_ctrl), qreg)
        qc.append(gry, qreg[0:gry.num_qubits])
        qc.append(grz.control(num_ctrl), qreg)

        dag = circuit_to_dag(qc)
        unroller = Unroller(['u3', 'cx'])
        uqc = dag_to_circuit(unroller.run(dag))
        print(uqc.size())
        self.log.info('%s gate count: %d', uqc.name, uqc.size())
        self.assertTrue(uqc.size() <= 93)  # this limit could be changed
Пример #5
0
def layer_parser(circ, two_qubit_gate='cx', coupling_map=None):
    """
    Tranforms general circuits into a nice form for a qotp.

    Args:
        circ (QuantumCircuit): A generic quantum circuit
        two_qubit_gate (str): a flag as to which 2 qubit
            gate to compile with, can be cx or cz
        coupling_map (list): some particular device topology as list
            of list (e.g. [[0,1],[1,2],[2,0]])
    Returns:
        dict: A dictionary of the parsed layers with the following keys:
            ``singlequbit_layers`` (lsit):  a list of circuits describing
                the single qubit gates
            ``cz_layers`` (list): a list of circuits describing the cz layers
            ``meas_layer`` (QuantumCircuit): a circuit describing the final measurement

    Raises:
        QiskitError: If a circuit element is not implemented in qotp
    """

    # transpile to single qubits and cx
    # TODO: replace cx with cz when that is available
    circ_internal = transpile(circ,
                              optimization_level=2,
                              basis_gates=['u1', 'u2', 'u3', 'cx'],
                              coupling_map=coupling_map)
    # quantum and classial registers
    qregs = circ_internal.qregs[0]
    cregs = circ_internal.cregs[0]
    # conatiners for the eventual output passed to the accred code
    singlequbitlayers = [
        QuantumCircuit(qregs, cregs),
        QuantumCircuit(qregs, cregs)
    ]
    twoqubitlayers = [QuantumCircuit(qregs, cregs)]
    measlayer = QuantumCircuit(qregs, cregs)
    # some flags for simplicity
    current2qs = []
    # loop through circuit (best to use the dag object)
    dag_internal = circuit_to_dag(circ_internal)
    for dag_layer in dag_internal.layers():
        circuit_layer = dag_to_circuit(dag_layer['graph'])
        for circelem, qsub, csub in circuit_layer:
            n = circelem.name
            if n == "barrier":
                # if a barrier separates any two qubit gates
                # start a new layer
                if current2qs != []:
                    singlequbitlayers.append(QuantumCircuit(qregs, cregs))
                    twoqubitlayers.append(QuantumCircuit(qregs, cregs))
                    current2qs = []
                singlequbitlayers[-2].append(circelem, qsub, csub)
            elif n in ('u1', 'u2', 'u3'):
                # single qubit gate
                q = qsub[0]
                if q in current2qs:
                    singlequbitlayers[-1].append(circelem, qsub, csub)
                else:
                    singlequbitlayers[-2].append(circelem, qsub, csub)
            elif n == "cx":
                # cx indices
                q_0 = qsub[0]
                q_1 = qsub[1]
                # check if new cnot satisfies overlap criteria
                if q_0 in current2qs or q_1 in current2qs:
                    singlequbitlayers.append(QuantumCircuit(qregs, cregs))
                    twoqubitlayers.append(QuantumCircuit(qregs, cregs))
                    current2qs = []
                if two_qubit_gate == 'cx':
                    # append cx
                    twoqubitlayers[-1].cx(q_0, q_1)
                elif two_qubit_gate == 'cz':
                    # append and correct to cz with h gates
                    twoqubitlayers[-1].cz(q_0, q_1)
                    singlequbitlayers[-1].h(qsub[1])
                    singlequbitlayers[-2].h(qsub[1])
                else:
                    raise QiskitError(
                        "Two qubit gate {0}".format(two_qubit_gate) +
                        " is not implemented in qotp")
                # add to current
                current2qs.append(q_0)
                current2qs.append(q_1)
            elif n == "measure":
                measlayer.append(circelem, qsub, csub)
            else:
                raise QiskitError("Circuit element {0}".format(n) +
                                  " is not implemented in qotp")
    if current2qs == []:
        del singlequbitlayers[-1]
        del twoqubitlayers[-1]
    for ind, circlayer in enumerate(singlequbitlayers):
        singlequbitlayers[ind] = transpile(circlayer,
                                           basis_gates=['u1', 'u2', 'u3'])
    parsedlayers = {
        'singlequbitlayers': singlequbitlayers,
        'twoqubitlayers': twoqubitlayers,
        'measlayer': measlayer,
        'twoqubitgate': two_qubit_gate,
        'qregs': qregs,
        'cregs': cregs
    }
    return parsedlayers
    n_qubits = int(math.log(dim, 2))
    converter.n_qubits = n_qubits
    n_cnots = (n_layers // 2) * (n_qubits // 2) + ((n_qubits - 1) // 2) * (
        (n_layers + 1) // 2)
    r = RewriteTket(Circuit(n_qubits), [], [])
    r.set_target_unitary(unitary)
    skips = []
    circuit, distance = approximate_scipy(unitary, n_layers, r, [])
    for k in range(10):
        new_skip = random.choice([i for i in range(n_cnots) if i not in skips])
        print("maybe I will skip", new_skip)
        new_circ, new_d = approximate_scipy(unitary, n_layers, r,
                                            skips + [new_skip])
        if new_d <= distance:  # or random.random() < (1 - new_d + distance) / (5 * k + 1):
            print("skipping", new_skip)
            circuit, distance, skips = new_circ, new_d, skips + [new_skip]
        else:
            print("not skipping", new_skip)
        print(circuit.n_gates, circuit.n_gates_of_type(OpType.CX))
        if distance < 0.01:
            print("returning early", distance)
            return circuit, r.fidelity(circuit)
    print(distance)
    return circuit, r.fidelity(circuit)


circuit, fid = approximate_scipy(unitary, n_layers, r)
print("final fidelity", fid)
print(circuit.n_gates, circuit.n_gates_of_type(OpType.CX))
print(dag_to_circuit(tk_to_dagcircuit(circuit)))
Пример #7
0
    def run(self, dag):
        """Run the Unroller pass on `dag`.

        Args:
            dag (DAGCircuit): input dag

        Raises:
            QiskitError: if unable to unroll given the basis due to undefined
            decomposition rules (such as a bad basis) or excessive recursion.

        Returns:
            DAGCircuit: output unrolled dag
        """
        if self.basis is None:
            return dag
        # Walk through the DAG and expand each non-basis node
        basic_insts = ["measure", "reset", "barrier", "snapshot", "delay"]
        for node in dag.op_nodes():
            if getattr(node.op, "_directive", False):
                continue

            if node.name in basic_insts:
                # TODO: this is legacy behavior.Basis_insts should be removed that these
                #  instructions should be part of the device-reported basis. Currently, no
                #  backend reports "measure", for example.
                continue

            if node.name in self.basis:  # If already a base, ignore.
                if isinstance(node.op, ControlledGate) and node.op._open_ctrl:
                    pass
                else:
                    continue

            if isinstance(node.op, ControlFlowOp):
                unrolled_blocks = []
                for block in node.op.blocks:
                    dag_block = circuit_to_dag(block)
                    unrolled_dag_block = self.run(dag_block)
                    unrolled_circ_block = dag_to_circuit(unrolled_dag_block)
                    unrolled_blocks.append(unrolled_circ_block)
                node.op = node.op.replace_blocks(unrolled_blocks)
                continue
            try:
                phase = node.op.definition.global_phase
                rule = node.op.definition.data
            except (TypeError, AttributeError) as err:
                raise QiskitError(
                    f"Error decomposing node of instruction '{node.name}': {err}. "
                    f"Unable to define instruction '{node.name}' in the given basis."
                ) from err

            # Isometry gates definitions can have widths smaller than that of the
            # original gate, in which case substitute_node will raise. Fall back
            # to substitute_node_with_dag if an the width of the definition is
            # different that the width of the node.
            while rule and len(rule) == 1 and len(node.qargs) == len(
                    rule[0].qubits) == 1:
                if rule[0].operation.name in self.basis:
                    dag.global_phase += phase
                    dag.substitute_node(node, rule[0].operation, inplace=True)
                    break
                try:
                    phase += rule[0].operation.definition.global_phase
                    rule = rule[0].operation.definition.data
                except (TypeError, AttributeError) as err:
                    raise QiskitError(
                        f"Error decomposing node of instruction '{node.name}': {err}. "
                        f"Unable to define instruction '{rule[0].operation.name}' in the basis."
                    ) from err

            else:
                if not rule:
                    if rule == []:  # empty node
                        dag.remove_op_node(node)
                        dag.global_phase += phase
                        continue
                    # opaque node
                    raise QiskitError(
                        "Cannot unroll the circuit to the given basis, %s. "
                        "No rule to expand instruction %s." %
                        (str(self.basis), node.op.name))
                decomposition = circuit_to_dag(node.op.definition)
                unrolled_dag = self.run(
                    decomposition)  # recursively unroll ops
                dag.substitute_node_with_dag(node, unrolled_dag)
        return dag
Пример #8
0
def ha_mapping(
    quantum_circuit: QuantumCircuit,
    initial_mapping: ty.Dict[Qubit, int],
    hardware: IBMQHardwareArchitecture,
    swap_cost_heuristic: ty.Callable[
        [
            IBMQHardwareArchitecture,  # Hardware information
            QuantumLayer,  # Current front layer
            ty.List[DAGNode],  # Topologically sorted list of nodes
            int,  # Index of the first non-processed gate.
            ty.Dict[Qubit, int],  # The mapping before applying the tested SWAP/Bridge
            ty.Dict[Qubit, int],  # The initial mapping
            ty.Dict[Qubit, int],  # The trans mapping
            numpy.ndarray,  # The distance matrix between each qubits
            TwoQubitGate,  # The SWAP/Bridge we want to rank
        ],
        float,
    ] = sabre_heuristic,
    get_candidates: ty.Callable[
        [QuantumLayer, IBMQHardwareArchitecture, ty.Dict[Qubit, int], ty.Dict[Qubit, int], ty.Dict[Qubit, int], ty.Set[str],],
        ty.List[TwoQubitGate],
    ] = get_all_swap_bridge_candidates,
    get_distance_matrix: ty.Callable[
        [IBMQHardwareArchitecture], numpy.ndarray
    ] = get_distance_matrix_swap_number_and_error,
) -> ty.Tuple[QuantumCircuit, ty.Dict[Qubit, int]]:
    """Map the given quantum circuit to the hardware topology provided.

    :param quantum_circuit: the quantum circuit to map.
    :param initial_mapping: the initial mapping used to start the iterative mapping
        algorithm.
    :param hardware: hardware data such as connectivity, gate time, gate errors, ...
    :param swap_cost_heuristic: the heuristic cost function that will estimate the cost
        of a given SWAP/Bridge according to the current state of the circuit.
    :param get_candidates: a function that takes as input the current front
        layer and the hardware description and that returns a list of tuples
        representing the SWAP/Bridge that should be considered by the heuristic.
    :param get_distance_matrix: a function that takes as first (and only) parameter the
        hardware representation and that outputs a numpy array containing the cost of
        performing a SWAP between each pair of qubits.
    :return: The final circuit along with the mapping obtained at the end of the
        iterative procedure.
    """
    _adapt_quantum_circuit_and_mapping_arity(quantum_circuit, initial_mapping, hardware)
    # Creating the internal data structures that will be used in this function.
    dag_circuit = circuit_to_dag(quantum_circuit)
    distance_matrix = get_distance_matrix(hardware)
    resulting_dag_quantum_circuit = _create_empty_dagcircuit_from_existing(dag_circuit)
    current_mapping = initial_mapping
    explored_mappings: ty.Set[str] = set()
    # Sorting all the quantum operations in topological order once for all.
    # May require significant memory on large circuits...
    topological_nodes: ty.List[DAGNode] = list(dag_circuit.topological_op_nodes())
    current_node_index = 0
    # Creating the initial front layer.
    front_layer = QuantumLayer()
    current_node_index = update_layer(
        front_layer, topological_nodes, current_node_index
    )
    trans_mapping = initial_mapping.copy()

    # Start of the iterative algorithm
    while not front_layer.is_empty():
        execute_gate_list = QuantumLayer()
        for op in front_layer.ops:
            if hardware.can_natively_execute_operation(op, current_mapping):
                execute_gate_list.add_operation(op)
                # Delaying the remove operation because we do not want to remove from
                # a container we are iterating on.
                # front_layer.remove_operation(op)
        if not execute_gate_list.is_empty():
            front_layer.remove_operations_from_layer(execute_gate_list)
            execute_gate_list.apply_back_to_dag_circuit(
                resulting_dag_quantum_circuit, initial_mapping, trans_mapping
            )
            # Empty the explored mappings because at least one gate has been executed.
            explored_mappings.clear()
        else:
            inverse_mapping = {val: key for key, val in initial_mapping.items()}
            # We cannot execute any gate, that means that we should insert at least
            # one SWAP/Bridge to make some gates executable.
            # First list all the SWAPs/Bridges that may help us make some gates
            # executable.
            swap_candidates = get_candidates(
                front_layer, hardware, initial_mapping, current_mapping, trans_mapping, explored_mappings
            )
            # Then rank the SWAPs/Bridge and take the best one.
            best_swap_qubits = None
            best_cost = float("inf")
            for potential_swap in swap_candidates:
                cost = swap_cost_heuristic(
                    hardware,
                    front_layer,
                    topological_nodes,
                    current_node_index,
                    current_mapping,
                    initial_mapping,
                    trans_mapping,
                    distance_matrix,
                    potential_swap,
                )
                if cost < best_cost:
                    best_cost = cost
                    best_swap_qubits = potential_swap
            # We now have our best SWAP/Bridge, let's perform it!
            current_mapping = best_swap_qubits.update_mapping(current_mapping)
            if isinstance(best_swap_qubits, SwapTwoQubitGate):
                control, target = current_mapping[best_swap_qubits.left], current_mapping[best_swap_qubits.right]
                swap_control, swap_target = inverse_mapping[control], inverse_mapping[target]
                best_swap_qubits = SwapTwoQubitGate(
                    swap_control, swap_target
                )
                #print("swap gates is :", best_swap_qubits.left, best_swap_qubits.right)
                trans_mapping[best_swap_qubits.left], trans_mapping[best_swap_qubits.right] = (
                    trans_mapping[best_swap_qubits.right],
                    trans_mapping[best_swap_qubits.left],
                )
            else:
                #print("brige gate is :", best_swap_qubits.left, best_swap_qubits.middle, best_swap_qubits.right)
                pass
            explored_mappings.add(mapping_to_str(current_mapping))
            best_swap_qubits.apply(resulting_dag_quantum_circuit, front_layer, initial_mapping, trans_mapping)
        # Anyway, update the current front_layer
        current_node_index = update_layer(
            front_layer, topological_nodes, current_node_index
        )

    # We are done here, we just need to return the results
    # resulting_dag_quantum_circuit.draw(scale=1, filename="qcirc.dot")
    resulting_circuit = dag_to_circuit(resulting_dag_quantum_circuit)
    return resulting_circuit, current_mapping
Пример #9
0
def ha_mapping_paper_compliant(
    quantum_circuit: QuantumCircuit,
    initial_mapping: ty.Dict[Qubit, int],
    hardware: IBMQHardwareArchitecture,
    swap_cost_and_effect_heuristic: ty.Callable[
        [
            IBMQHardwareArchitecture,  # Hardware information
            QuantumLayer,  # Current front layer
            ty.List[DAGNode],  # Topologically sorted list of nodes
            int,  # Index of the first non-processed gate.
            ty.Dict[Qubit, int],  # The mapping before applying the tested SWAP/Bridge
            ty.Dict[Qubit, int],  # The initial mapping
            ty.Dict[Qubit, int],  # The trans mapping
            numpy.ndarray,  # The distance matrix between each qubits
            TwoQubitGate,  # The SWAP/Bridge we want to rank
        ],
        ty.Tuple[
            float,  # Cost of the SWAP pair
            float,  # Effect of the SWAP pair on the other gates
        ],
    ] = sabre_heuristic_with_effect,
    get_candidates: ty.Callable[
        [QuantumLayer, IBMQHardwareArchitecture, ty.Dict[Qubit, int], ty.Set[str],],
        ty.List[SwapTwoQubitGate],
    ] = get_all_swap_candidates,
    get_distance_matrix: ty.Callable[
        [IBMQHardwareArchitecture], numpy.ndarray
    ] = get_distance_matrix_swap_number_and_error,
) -> ty.Tuple[QuantumCircuit, ty.Dict[Qubit, int]]:
    """Map the given quantum circuit to the hardware topology provided.

    This implementation uses the exact same algorithm described in the associated
    scientific paper. Another implementation using a different method to choose
    between SWAP and Bridge is available as `:py:func:`~hamap.mapping.ha_mapping`.

    :param quantum_circuit: the quantum circuit to map.
    :param initial_mapping: the initial mapping used to start the iterative mapping
        algorithm.
    :param hardware: hardware data such as connectivity, gate time, gate errors, ...
    :param swap_cost_and_effect_heuristic: the heuristic cost function that will
        estimate the cost of a given SWAP according to the current state of the circuit
        and the effect of the SWAP on the following quantum gates. The two floats
        should be returned as a tuple (cost, effect).
    :param get_candidates: a function that takes as input the current front
        layer and the hardware description and that returns a list of tuples
        representing the SWAP that should be considered by the heuristic.
    :param get_distance_matrix: a function that takes as first (and only) parameter the
        hardware representation and that outputs a numpy array containing the cost of
        performing a SWAP between each pair of qubits.
    :return: The final circuit along with the mapping obtained at the end of the
        iterative procedure.
    """
    _adapt_quantum_circuit_and_mapping_arity(quantum_circuit, initial_mapping, hardware)
    # Creating the internal data structures that will be used in this function.
    dag_circuit = circuit_to_dag(quantum_circuit)
    distance_matrix = get_distance_matrix(hardware)
    resulting_dag_quantum_circuit = _create_empty_dagcircuit_from_existing(dag_circuit)
    current_mapping = initial_mapping
    explored_mappings: ty.Set[str] = set()
    # Sorting all the quantum operations in topological order once for all.
    # May require significant memory on large circuits...
    topological_nodes: ty.List[DAGNode] = list(dag_circuit.topological_op_nodes())
    current_node_index = 0
    # Creating the initial front layer.
    front_layer = QuantumLayer()
    current_node_index = update_layer(
        front_layer, topological_nodes, current_node_index
    )

    swap_distance_matrix = get_distance_matrix_swap_number(hardware)
    trans_mapping = initial_mapping.copy()
    # Start of the iterative algorithm
    while not front_layer.is_empty():
        execute_gate_list = QuantumLayer()
        for op in front_layer.ops:
            if hardware.can_natively_execute_operation(op, current_mapping):
                execute_gate_list.add_operation(op)
                # Delaying the remove operation because we do not want to remove from
                # a container we are iterating on.
                # front_layer.remove_operation(op)
        if not execute_gate_list.is_empty():
            front_layer.remove_operations_from_layer(execute_gate_list)
            execute_gate_list.apply_back_to_dag_circuit(
                resulting_dag_quantum_circuit, initial_mapping, trans_mapping
            )
            # Empty the explored mappings because at least one gate has been executed.
            explored_mappings.clear()
        else:
            # We cannot execute any gate, that means that we should insert at least
            # one SWAP/Bridge to make some gates executable.
            # First list all the SWAPs/Bridges that may help us make some gates
            # executable.
            inverse_mapping = {val: key for key, val in initial_mapping.items()}
            swap_candidates = get_candidates(
                front_layer, hardware, current_mapping, explored_mappings
            )
            # Then rank the SWAPs/Bridge and take the best one.
            best_swap_qubits = None
            best_cost = float("inf")
            best_effect = 0.0
            for potential_swap in swap_candidates:
                cost, swap_effect = swap_cost_and_effect_heuristic(
                    hardware,
                    front_layer,
                    topological_nodes,
                    current_node_index,
                    current_mapping,
                    initial_mapping,
                    trans_mapping,
                    distance_matrix,
                    potential_swap,
                )
                if cost < best_cost:
                    best_cost = cost
                    best_effect = swap_effect
                    best_swap_qubits = potential_swap
            # We now have our best SWAP, let's check if a Bridge is not better
            # if (
            #     best_effect < 0
            #     and swap_distance_matrix[best_swap_qubits.left][best_swap_qubits.right]
            #     == 2
            # ):
            #     i, j = best_swap_qubits
            #     common_neighbours = set(hardware.neighbors(i)) & set(
            #         hardware.neighbors(j)
            #     )
            #     if len(common_neighbours) < 1:
            #         raise RuntimeError("Less than one common neighbour")
            #     common_neighbour = list(common_neighbours)[0]
            #     best_gate = BridgeTwoQubitGate(i, common_neighbour, j)
            if best_effect < 0:
                inverse_trans_mapping = {val: key for key, val in trans_mapping.items()}
                for op in front_layer.ops:
                    if len(op.qargs) < 2:
                        # We just pass 1 qubit gates because they do not participate in the
                        # Bridge operation
                        continue
                    if len(op.qargs) != 2:
                        logger.warning("A 3-qubit or more gate has been found in the circuit.")
                        continue
                    control, target = op.qargs
                    control_index = initial_mapping[inverse_trans_mapping[initial_mapping[control]]]
                    target_index = initial_mapping[inverse_trans_mapping[initial_mapping[target]]]
                    for _, potential_middle_index in hardware.out_edges(control_index):
                        for _, potential_target_index in hardware.out_edges(potential_middle_index):
                            if potential_target_index == target_index:
                                best_swap_qubits = BridgeTwoQubitGate(
                                    inverse_trans_mapping[initial_mapping[control]],
                                    inverse_mapping[potential_middle_index],
                                    inverse_trans_mapping[initial_mapping[target]],
                                )

            current_mapping = best_swap_qubits.update_mapping(current_mapping)
            if isinstance(best_swap_qubits, SwapTwoQubitGate):
                control, target = current_mapping[best_swap_qubits.left], current_mapping[best_swap_qubits.right]
                swap_control, swap_target = inverse_mapping[control], inverse_mapping[target]
                best_swap_qubits = SwapTwoQubitGate(
                    swap_control, swap_target
                )
                #print("swap gates is :", best_swap_qubits.left, best_swap_qubits.right)
                trans_mapping[best_swap_qubits.left], trans_mapping[best_swap_qubits.right] = (
                    trans_mapping[best_swap_qubits.right],
                    trans_mapping[best_swap_qubits.left],
                )
            else:
                #print("brige gate is :", best_swap_qubits.left, best_swap_qubits.middle, best_swap_qubits.right)
                pass

            explored_mappings.add(mapping_to_str(current_mapping))
            best_swap_qubits.apply(resulting_dag_quantum_circuit, front_layer, initial_mapping, trans_mapping)
        # Anyway, update the current front_layer
        current_node_index = update_layer(
            front_layer, topological_nodes, current_node_index
        )

    # We are done here, we just need to return the results
    # resulting_dag_quantum_circuit.draw(scale=1, filename="qcirc.dot")
    resulting_circuit = dag_to_circuit(resulting_dag_quantum_circuit)
    return resulting_circuit, current_mapping