Пример #1
0
def swap_mapper_layer_update(i, first_layer, best_layout, best_d,
                             best_circ, layer_list):
    """Update the QASM string for an iteration of swap_mapper.

    i = layer number
    first_layer = True if this is the first layer with multi-qubit gates
    best_layout = layout returned from swap algorithm
    best_d = depth returned from swap algorithm
    best_circ = swap circuit returned from swap algorithm
    layer_list = list of circuit objects for each layer

    Return DAGCircuit object to append to the output DAGCircuit.
    """
    layout = best_layout
    layout_max_index = max(map(lambda x: x[1]+1, layout.values()))
    dagcircuit_output = DAGCircuit()
    dagcircuit_output.add_qreg("q", layout_max_index)
    # Identity wire-map for composing the circuits
    identity_wire_map = {('q', j): ('q', j) for j in range(layout_max_index)}

    # If this is the first layer with multi-qubit gates,
    # output all layers up to this point and ignore any
    # swap gates. Set the initial layout.
    if first_layer:
        logger.debug("update_qasm_and_layout: first multi-qubit gate layer")
        # Output all layers up to this point
        for j in range(i + 1):
            dagcircuit_output.compose_back(layer_list[j]["graph"], layout)
    # Otherwise, we output the current layer and the associated swap gates.
    else:
        # Output any swaps
        if best_d > 0:
            logger.debug("update_qasm_and_layout: swaps in this layer, "
                         "depth %d", best_d)
            dagcircuit_output.compose_back(best_circ, identity_wire_map)
        else:
            logger.debug("update_qasm_and_layout: no swaps in this layer")
        # Output this layer
        dagcircuit_output.compose_back(layer_list[i]["graph"], layout)
    return dagcircuit_output
Пример #2
0
    def __init__(self, basis=None):
        """Setup this backend.

        basis is a list of operation name strings.
        """
        super().__init__(basis)
        self.prec = 15
        self.creg = None
        self.cval = None
        self.circuit = DAGCircuit()
        if basis:
            self.basis = basis
        else:
            self.basis = []
        self.listen = True
        self.in_gate = ""
        self.gates = OrderedDict()
Пример #3
0
    def run(self, dag):
        """
        Runs the BasicSwap pass on `dag`.
        Args:
            dag (DAGCircuit): DAG to map.

        Returns:
            DAGCircuit: A mapped DAG.

        Raises:
            TranspilerError: if the coupling map or the layout are not
            compatible with the DAG
        """
        new_dag = DAGCircuit()

        if self.initial_layout is None:
            if self.property_set["layout"]:
                self.initial_layout = self.property_set["layout"]
            else:
                self.initial_layout = Layout.generate_trivial_layout(
                    *dag.qregs.values())

        if len(dag.qubits()) != len(self.initial_layout):
            raise TranspilerError(
                'The layout does not match the amount of qubits in the DAG')

        if len(self.coupling_map.physical_qubits) != len(self.initial_layout):
            raise TranspilerError(
                "Mappers require to have the layout to be the same size as the coupling map"
            )

        current_layout = self.initial_layout.copy()

        for layer in dag.serial_layers():
            subdag = layer['graph']

            for gate in subdag.twoQ_nodes():
                physical_q0 = current_layout[gate['qargs'][0]]
                physical_q1 = current_layout[gate['qargs'][1]]
                if self.coupling_map.distance(physical_q0, physical_q1) != 1:
                    # Insert a new layer with the SWAP(s).
                    swap_layer = DAGCircuit()

                    path = self.coupling_map.shortest_undirected_path(
                        physical_q0, physical_q1)
                    for swap in range(len(path) - 2):
                        connected_wire_1 = path[swap]
                        connected_wire_2 = path[swap + 1]

                        qubit_1 = current_layout[connected_wire_1]
                        qubit_2 = current_layout[connected_wire_2]

                        # create qregs
                        for qreg in current_layout.get_registers():
                            if qreg[0] not in swap_layer.qregs.values():
                                swap_layer.add_qreg(qreg[0])

                        # create the swap operation
                        swap_layer.apply_operation_back(
                            self.swap_gate(qubit_1, qubit_2),
                            qargs=[qubit_1, qubit_2])

                    # layer insertion
                    edge_map = current_layout.combine_into_edge_map(
                        self.initial_layout)
                    new_dag.compose_back(swap_layer, edge_map)

                    # update current_layout
                    for swap in range(len(path) - 2):
                        current_layout.swap(path[swap], path[swap + 1])

            edge_map = current_layout.combine_into_edge_map(
                self.initial_layout)
            new_dag.extend_back(subdag, edge_map)

        return new_dag
Пример #4
0
    def run(self, dag):
        """iterate over each block and replace it with an equivalent Unitary
        on the same wires.
        """
        new_dag = DAGCircuit()
        for qreg in dag.qregs.values():
            new_dag.add_qreg(qreg)
        for creg in dag.cregs.values():
            new_dag.add_creg(creg)

        sim = UnitarySimulatorPy()

        # compute ordered indices for the global circuit wires
        global_index_map = {}
        for wire in dag.wires:
            if not isinstance(wire[0], QuantumRegister):
                continue
            global_qregs = list(dag.qregs.values())
            global_index_map[wire] = global_qregs.index(wire[0]) + wire[1]

        blocks = self.property_set['block_list']
        nodes_seen = set()

        for node in dag.topological_op_nodes():
            # skip already-visited nodes or input/output nodes
            if node in nodes_seen or node.type == 'in' or node.type == 'out':
                continue
            # check if the node belongs to the next block
            if blocks and node in blocks[0]:
                block = blocks[0]
                # find the qubits involved in this block
                block_qargs = set()
                for nd in block:
                    block_qargs |= set(nd.qargs)
                # convert block to a sub-circuit, then simulate unitary and add
                block_width = len(block_qargs)
                q = QuantumRegister(block_width)
                subcirc = QuantumCircuit(q)
                block_index_map = self._block_qargs_to_indices(
                    block_qargs, global_index_map)
                for nd in block:
                    nodes_seen.add(nd)
                    subcirc.append(nd.op,
                                   [q[block_index_map[i]] for i in nd.qargs])
                qobj = assemble_circuits(subcirc)
                unitary_matrix = sim.run(qobj).result().get_unitary()
                unitary = Unitary(unitary_matrix)
                new_dag.apply_operation_back(
                    unitary,
                    sorted(block_qargs, key=lambda x: block_index_map[x]))
                del blocks[0]
            else:
                # the node could belong to some future block, but in that case
                # we simply skip it. It is guaranteed that we will revisit that
                # future block, via its other nodes
                for block in blocks[1:]:
                    if node in block:
                        break
                # freestanding nodes can just be added
                else:
                    nodes_seen.add(node)
                    new_dag.apply_operation_back(node.op, node.qargs,
                                                 node.cargs)

        return new_dag
    def run(self, dag):
        """Run the MergeAdjacentBarriers pass on `dag`."""

        # sorted to so that they are in the order they appear in the DAG
        # so ancestors/descendants makes sense
        barriers = [
            nd for nd in dag.topological_op_nodes() if nd.name == 'barrier'
        ]

        # get dict of barrier merges
        node_to_barrier_qubits = MergeAdjacentBarriers._collect_potential_merges(
            dag, barriers)

        if not node_to_barrier_qubits:
            return dag

        # add the merged barriers to a new DAG
        new_dag = DAGCircuit()

        for qreg in dag.qregs.values():
            new_dag.add_qreg(qreg)
        for creg in dag.cregs.values():
            new_dag.add_creg(creg)

        # go over current nodes, and add them to the new dag
        for node in dag.topological_op_nodes():
            if node.name == 'barrier':
                if node in node_to_barrier_qubits:
                    qubits = node_to_barrier_qubits[node]
                    # qubits are stored as a set, need to convert to a list
                    new_dag.apply_operation_back(Barrier(len(qubits)),
                                                 qargs=list(qubits))
            else:
                # copy the condition over too
                if node.condition:
                    new_dag.apply_operation_back(node.op,
                                                 qargs=node.qargs,
                                                 cargs=node.cargs,
                                                 condition=node.condition)
                else:
                    new_dag.apply_operation_back(node.op,
                                                 qargs=node.qargs,
                                                 cargs=node.cargs)
        return new_dag
Пример #6
0
    def _layer_update(self, i, first_layer, best_layout, best_depth,
                      best_circuit, layer_list):
        """Provide a DAGCircuit for a new mapped layer.

        i (int) = layer number
        first_layer (bool) = True if this is the first layer in the
            circuit with any multi-qubit gates
        best_layout (Layout) = layout returned from _layer_permutation
        best_depth (int) = depth returned from _layer_permutation
        best_circuit (DAGCircuit) = swap circuit returned
            from _layer_permutation
        layer_list (list) = list of DAGCircuit objects for each layer,
            output of DAGCircuit layers() method

        Return a DAGCircuit object to append to the output DAGCircuit
        that the _mapper method is building.
        """
        layout = best_layout
        logger.debug("layer_update: layout = %s", pformat(layout))
        logger.debug("layer_update: self.initial_layout = %s", pformat(self.initial_layout))
        dagcircuit_output = DAGCircuit()
        for register in layout.get_virtual_bits().keys():
            if register[0] not in dagcircuit_output.qregs.values():
                dagcircuit_output.add_qreg(register[0])

        # If this is the first layer with multi-qubit gates,
        # output all layers up to this point and ignore any
        # swap gates. Set the initial layout.
        if first_layer:
            logger.debug("layer_update: first multi-qubit gate layer")
            # Output all layers up to this point
            for j in range(i + 1):
                # Make qubit edge map and extend by classical bits
                edge_map = layout.combine_into_edge_map(self.initial_layout)
                for bit in dagcircuit_output.clbits():
                    edge_map[bit] = bit
                dagcircuit_output.compose_back(layer_list[j]["graph"], edge_map)
        # Otherwise, we output the current layer and the associated swap gates.
        else:
            # Output any swaps
            if best_depth > 0:
                logger.debug("layer_update: there are swaps in this layer, "
                             "depth %d", best_depth)
                dagcircuit_output.extend_back(best_circuit)
            else:
                logger.debug("layer_update: there are no swaps in this layer")
            # Make qubit edge map and extend by classical bits
            edge_map = layout.combine_into_edge_map(self.initial_layout)
            for bit in dagcircuit_output.clbits():
                edge_map[bit] = bit
            # Output this layer
            dagcircuit_output.compose_back(layer_list[i]["graph"], edge_map)

        return dagcircuit_output
Пример #7
0
    def run(self, dag: DAGCircuit) -> DAGCircuit:
        """Run the LocalNoisePass pass on `dag`.
        Args:
            dag: DAG to be changed.
        Returns:
            A changed DAG.
        Raises:
            TranspilerError: if generated operation is not valid.
        """
        qubit_indices = {qubit: idx for idx, qubit in enumerate(dag.qubits)}
        for node in dag.topological_op_nodes():
            if self._ops and not isinstance(node.op, self._ops):
                continue

            qubits = [qubit_indices[q] for q in node.qargs]
            new_op = self._func(node.op, qubits)

            if new_op is None:
                # Edge case where we are replacing a node with nothing (removing node)
                if self._method == "replace":
                    dag.remove_op_node(node)
                continue

            if isinstance(new_op, ReadoutError):
                raise TranspilerError(
                    "Insertions of ReadoutError is not yet supported.")

            # Initialize new node dag
            new_dag = DAGCircuit()
            new_dag.add_qubits(node.qargs)
            new_dag.add_clbits(node.cargs)

            # If appending re-apply original op node first
            if self._method == "append":
                new_dag.apply_operation_back(node.op, qargs=node.qargs)

            # If the new op is not a QuantumCircuit or Instruction, attempt
            # to conver to an Instruction
            if not isinstance(new_op, (QuantumCircuit, Instruction)):
                try:
                    new_op = new_op.to_instruction()
                except AttributeError as att_err:
                    raise TranspilerError(
                        "Function must return an object implementing 'to_instruction' method."
                    ) from att_err

            # Validate the instruction matches the number of qubits and clbits of the node
            if new_op.num_qubits != len(node.qargs):
                raise TranspilerError(
                    f"Number of qubits of generated op {new_op.num_qubits} != "
                    f"{len(node.qargs)} that of a reference op {node.name}")

            # Add the noise op returned by the function
            if isinstance(new_op, QuantumCircuit):
                # If the new op is a quantum circuit, compose its DAG with the new dag
                # so that it is unrolled rather than added as an opaque instruction
                new_dag.compose(circuit_to_dag(new_op),
                                qubits=node.qargs,
                                clbits=node.cargs)
            else:
                # Otherwise append the instruction returned by the function
                new_dag.apply_operation_back(new_op,
                                             qargs=node.qargs,
                                             cargs=node.cargs)

            # If prepending reapply original op node last
            if self._method == "prepend":
                new_dag.apply_operation_back(node.op,
                                             qargs=node.qargs,
                                             cargs=node.cargs)

            dag.substitute_node_with_dag(node, new_dag)

        return dag
Пример #8
0
def direction_mapper(circuit_graph, coupling_graph):
    """Change the direction of CNOT gates to conform to CouplingGraph.

    circuit_graph = input DAGCircuit
    coupling_graph = corresponding CouplingGraph

    Adds "h" to the circuit basis.

    Returns a DAGCircuit object containing a circuit equivalent to
    circuit_graph but with CNOT gate directions matching the edges
    of coupling_graph. Raises an exception if the circuit_graph
    does not conform to the coupling_graph.
    """
    if "cx" not in circuit_graph.basis:
        return circuit_graph
    if circuit_graph.basis["cx"] != (2, 0, 0):
        raise MapperError("cx gate has unexpected signature %s" %
                          circuit_graph.basis["cx"])

    flipped_cx_circuit = DAGCircuit()
    flipped_cx_circuit.add_qreg('q', 2)
    flipped_cx_circuit.add_basis_element("CX", 2)
    flipped_cx_circuit.add_basis_element("U", 1, 0, 3)
    flipped_cx_circuit.add_basis_element("cx", 2)
    flipped_cx_circuit.add_basis_element("u2", 1, 0, 2)
    flipped_cx_circuit.add_basis_element("h", 1)
    flipped_cx_circuit.add_gate_data("cx", cx_data)
    flipped_cx_circuit.add_gate_data("u2", u2_data)
    flipped_cx_circuit.add_gate_data("h", h_data)
    flipped_cx_circuit.apply_operation_back("h", [("q", 0)])
    flipped_cx_circuit.apply_operation_back("h", [("q", 1)])
    flipped_cx_circuit.apply_operation_back("cx", [("q", 1), ("q", 0)])
    flipped_cx_circuit.apply_operation_back("h", [("q", 0)])
    flipped_cx_circuit.apply_operation_back("h", [("q", 1)])

    cg_edges = coupling_graph.get_edges()
    for cx_node in circuit_graph.get_named_nodes("cx"):
        nd = circuit_graph.multi_graph.node[cx_node]
        cxedge = tuple(nd["qargs"])
        if cxedge in cg_edges:
            logger.debug("cx %s[%d], %s[%d] -- OK", cxedge[0][0], cxedge[0][1],
                         cxedge[1][0], cxedge[1][1])
            continue
        elif (cxedge[1], cxedge[0]) in cg_edges:
            circuit_graph.substitute_circuit_one(cx_node,
                                                 flipped_cx_circuit,
                                                 wires=[("q", 0), ("q", 1)])
            logger.debug("cx %s[%d], %s[%d] -FLIP", cxedge[0][0], cxedge[0][1],
                         cxedge[1][0], cxedge[1][1])
        else:
            raise MapperError("circuit incompatible with CouplingGraph: "
                              "cx on %s" % pprint.pformat(cxedge))
    return circuit_graph
Пример #9
0
class TestDagOperations(QiskitTestCase):
    """Test ops inside the dag"""
    def setUp(self):
        self.dag = DAGCircuit()
        qreg = QuantumRegister(3, 'qr')
        creg = ClassicalRegister(2, 'cr')
        self.dag.add_qreg(qreg)
        self.dag.add_creg(creg)

        self.qubit0 = qreg[0]
        self.qubit1 = qreg[1]
        self.qubit2 = qreg[2]
        self.clbit0 = creg[0]
        self.clbit1 = creg[1]
        self.condition = (creg, 3)

    def test_apply_operation_back(self):
        """The apply_operation_back() method."""
        self.dag.apply_operation_back(HGate(self.qubit0), condition=None)
        self.dag.apply_operation_back(CnotGate(self.qubit0, self.qubit1),
                                      condition=None)
        self.dag.apply_operation_back(Measure(self.qubit1, self.clbit1),
                                      condition=None)
        self.dag.apply_operation_back(XGate(self.qubit1),
                                      condition=self.condition)
        self.dag.apply_operation_back(Measure(self.qubit0, self.clbit0),
                                      condition=None)
        self.dag.apply_operation_back(Measure(self.qubit1, self.clbit1),
                                      condition=None)
        self.assertEqual(len(self.dag.multi_graph.nodes), 16)
        self.assertEqual(len(self.dag.multi_graph.edges), 17)

    def test_apply_operation_front(self):
        """The apply_operation_front() method"""
        self.dag.apply_operation_back(HGate(self.qubit0))
        self.dag.apply_operation_front(Reset(self.qubit0))
        h_node = self.dag.op_nodes(op=HGate).pop()
        reset_node = self.dag.op_nodes(op=Reset).pop()

        self.assertIn(reset_node,
                      set(self.dag.multi_graph.predecessors(h_node)))

    def test_get_op_nodes_all(self):
        """The method dag.op_nodes() returns all op nodes"""
        self.dag.apply_operation_back(HGate(self.qubit0))
        self.dag.apply_operation_back(CnotGate(self.qubit0, self.qubit1))
        self.dag.apply_operation_back(Reset(self.qubit0))

        op_nodes = self.dag.op_nodes()
        self.assertEqual(len(op_nodes), 3)

        for node in op_nodes:
            self.assertIsInstance(self.dag.multi_graph.nodes[node]["op"],
                                  Instruction)

    def test_get_op_nodes_particular(self):
        """The method dag.gates_nodes(op=AGate) returns all the AGate nodes"""
        self.dag.apply_operation_back(HGate(self.qubit0))
        self.dag.apply_operation_back(HGate(self.qubit1))
        self.dag.apply_operation_back(Reset(self.qubit0))

        self.dag.apply_operation_back(CnotGate(self.qubit0, self.qubit1))

        op_nodes = self.dag.op_nodes(op=HGate)
        self.assertEqual(len(op_nodes), 2)

        op_node_1 = op_nodes.pop()
        op_node_2 = op_nodes.pop()

        self.assertIsInstance(self.dag.multi_graph.nodes[op_node_1]["op"],
                              HGate)
        self.assertIsInstance(self.dag.multi_graph.nodes[op_node_2]["op"],
                              HGate)

    def test_quantum_successors(self):
        """The method dag.quantum_successors() returns successors connected by quantum edges"""
        self.dag.apply_operation_back(Measure(self.qubit1, self.clbit1))
        self.dag.apply_operation_back(CnotGate(self.qubit0, self.qubit1))
        self.dag.apply_operation_back(Reset(self.qubit0))

        successor_measure = self.dag.quantum_successors(
            self.dag.named_nodes('measure').pop())
        self.assertEqual(len(successor_measure), 1)
        cnot_node = successor_measure[0]
        self.assertIsInstance(self.dag.multi_graph.nodes[cnot_node]["op"],
                              CnotGate)

        successor_cnot = self.dag.quantum_successors(cnot_node)
        self.assertEqual(len(successor_cnot), 2)
        self.assertEqual(self.dag.multi_graph.nodes[successor_cnot[0]]["type"],
                         'out')
        self.assertIsInstance(
            self.dag.multi_graph.nodes[successor_cnot[1]]["op"], Reset)

    def test_get_gates_nodes(self):
        """The method dag.gate_nodes() returns all gate nodes"""
        self.dag.apply_operation_back(HGate(self.qubit0))
        self.dag.apply_operation_back(CnotGate(self.qubit0, self.qubit1))
        self.dag.apply_operation_back(Barrier((self.qubit0, self.qubit1)))
        self.dag.apply_operation_back(Reset(self.qubit0))

        op_nodes = self.dag.gate_nodes()
        self.assertEqual(len(op_nodes), 2)

        op_node_1 = op_nodes.pop()
        op_node_2 = op_nodes.pop()
        self.assertIsInstance(self.dag.multi_graph.nodes[op_node_1]["op"],
                              Gate)
        self.assertIsInstance(self.dag.multi_graph.nodes[op_node_2]["op"],
                              Gate)

    def test_two_q_gates(self):
        """The method dag.twoQ_gates() returns all 2Q gate nodes"""
        self.dag.apply_operation_back(HGate(self.qubit0))
        self.dag.apply_operation_back(CnotGate(self.qubit0, self.qubit1))
        self.dag.apply_operation_back(Barrier((self.qubit0, self.qubit1)))
        self.dag.apply_operation_back(Reset(self.qubit0))

        op_nodes = self.dag.twoQ_gates()
        self.assertEqual(len(op_nodes), 1)

        op_node = op_nodes.pop()
        self.assertIsInstance(op_node["op"], Gate)
        self.assertEqual(len(op_node['qargs']), 2)

    def test_get_named_nodes(self):
        """The get_named_nodes(AName) method returns all the nodes with name AName"""
        self.dag.apply_operation_back(CnotGate(self.qubit0, self.qubit1))
        self.dag.apply_operation_back(HGate(self.qubit0))
        self.dag.apply_operation_back(CnotGate(self.qubit2, self.qubit1))
        self.dag.apply_operation_back(CnotGate(self.qubit0, self.qubit2))
        self.dag.apply_operation_back(HGate(self.qubit2))

        # The ordering is not assured, so we only compare the output (unordered) sets.
        # We use tuples because lists aren't hashable.
        named_nodes = self.dag.named_nodes('cx')
        node_qargs = {
            tuple(self.dag.node(node_id)["op"].qargs)
            for node_id in named_nodes
        }
        expected_qargs = {(self.qubit0, self.qubit1),
                          (self.qubit2, self.qubit1),
                          (self.qubit0, self.qubit2)}
        self.assertEqual(expected_qargs, node_qargs)

    def test_nodes_in_topological_order(self):
        """ The node_nums_in_topological_order() method"""
        self.dag.apply_operation_back(CnotGate(self.qubit0, self.qubit1))
        self.dag.apply_operation_back(HGate(self.qubit0))
        self.dag.apply_operation_back(CnotGate(self.qubit2, self.qubit1))
        self.dag.apply_operation_back(CnotGate(self.qubit0, self.qubit2))
        self.dag.apply_operation_back(HGate(self.qubit2))

        named_nodes = self.dag.node_nums_in_topological_order()
        self.assertEqual([1, 3, 5, 7, 8, 9, 10, 11, 12, 13, 4, 14, 2, 15, 6],
                         [i for i in named_nodes])

    def test_dag_has_edge(self):
        """ Test that existence of edges between nodes is correctly identified"""
        self.assertTrue(self.dag.has_edge(1, 2))
        self.assertTrue(self.dag.has_edge(1, 2, (QuantumRegister(3, 'qr'), 0)))
        self.assertFalse(self.dag.has_edge(1, 2,
                                           (QuantumRegister(3, 'qr'), 1)))

        self.assertFalse(self.dag.has_edge(1, 3))
        self.assertFalse(self.dag.has_edge(1, 3,
                                           (QuantumRegister(3, 'qr'), 0)))

    def test_dag_remove_edge(self):
        """ Test that removing an edge as specified by a wire removes the correct edge"""

        q = QuantumRegister(2, 'qr')
        qc = QuantumCircuit(q)
        qc.cx(q[0], q[1])
        qc.cx(q[0], q[1])

        dag = circuit_to_dag(qc)

        node1 = 5
        node2 = 6
        wire = (QuantumRegister(2, 'qr'), 0)

        self.assertTrue(dag.has_edge(node1, node2, wire))
        dag.remove_edge(node1, node2, wire)
        self.assertFalse(dag.has_edge(node1, node2, wire))

        self.assertRaises(DAGCircuitError, dag.remove_edge, node1, node2, wire)
Пример #10
0
    def run(self, dag):
        """iterate over each block and replace it with an equivalent Unitary
        on the same wires.
        """
        new_dag = DAGCircuit()
        for qreg in dag.qregs.values():
            new_dag.add_qreg(qreg)
        for creg in dag.cregs.values():
            new_dag.add_creg(creg)

        # compute ordered indices for the global circuit wires
        global_index_map = {}
        for wire in dag.wires:
            if not isinstance(wire, Qubit):
                continue
            global_qregs = list(dag.qregs.values())
            global_index_map[wire] = global_qregs.index(
                wire.register) + wire.index

        blocks = self.property_set['block_list']
        # just to make checking if a node is in any block easier
        all_block_nodes = {nd for bl in blocks for nd in bl}

        for node in dag.topological_op_nodes():
            if node not in all_block_nodes:
                # need to add this node to find out where in the list it goes
                preds = [
                    nd for nd in dag.predecessors(node) if nd.type == 'op'
                ]

                block_count = 0
                while preds:
                    if block_count < len(blocks):
                        block = blocks[block_count]

                        # if any of the predecessors are in the block, remove them
                        preds = [p for p in preds if p not in block]
                    else:
                        # should never occur as this would mean not all
                        # nodes before this one topologically had been added
                        # so not all predecessors were removed
                        raise TranspilerError(
                            "Not all predecessors removed due to error"
                            " in topological order")

                    block_count += 1

                # we have now seen all predecessors
                # so update the blocks list to include this block
                blocks = blocks[:block_count] + [[node]] + blocks[block_count:]

        # create the dag from the updated list of blocks
        basis_gate_name = self.decomposer.gate.name
        for block in blocks:

            if len(block) == 1 and block[0].name != 'cx':
                # an intermediate node that was added into the overall list
                new_dag.apply_operation_back(block[0].op, block[0].qargs,
                                             block[0].cargs)
            else:
                # find the qubits involved in this block
                block_qargs = set()
                for nd in block:
                    block_qargs |= set(nd.qargs)
                # convert block to a sub-circuit, then simulate unitary and add
                block_width = len(block_qargs)
                q = QuantumRegister(block_width)
                subcirc = QuantumCircuit(q)
                block_index_map = self._block_qargs_to_indices(
                    block_qargs, global_index_map)
                basis_count = 0
                for nd in block:
                    if nd.op.name == basis_gate_name:
                        basis_count += 1
                    subcirc.append(nd.op,
                                   [q[block_index_map[i]] for i in nd.qargs])
                unitary = UnitaryGate(
                    Operator(subcirc))  # simulates the circuit
                if self.force_consolidate or unitary.num_qubits > 2 or \
                        self.decomposer.num_basis_gates(unitary) != basis_count:

                    new_dag.apply_operation_back(
                        unitary,
                        sorted(block_qargs, key=lambda x: block_index_map[x]))
                else:
                    for nd in block:
                        new_dag.apply_operation_back(nd.op, nd.qargs, nd.cargs)

        return new_dag
Пример #11
0
 def test_create(self):
     qubit0 = ('qr', 0)
     qubit1 = ('qr', 1)
     clbit0 = ('cr', 0)
     clbit1 = ('cr', 1)
     condition = None
     dag = DAGCircuit()
     dag.add_basis_element('h', 1, number_classical=0, number_parameters=0)
     dag.add_basis_element('cx', 2)
     dag.add_basis_element('x', 1)
     dag.add_basis_element('measure', 1, number_classical=1,
                           number_parameters=0)
     dag.add_qreg('qr', 2)
     dag.add_creg('cr', 2)
     dag.apply_operation_back('h', [qubit0], [], [], condition)
     dag.apply_operation_back('cx', [qubit0, qubit1], [],
                              [], condition)
     dag.apply_operation_back('measure', [qubit1], [clbit1], [], condition)
     dag.apply_operation_back('x', [qubit1], [], [], ('cr', 1))
     dag.apply_operation_back('measure', [qubit0], [clbit0], [], condition)
     dag.apply_operation_back('measure', [qubit1], [clbit1], [], condition)
Пример #12
0
class DAGBackend(UnrollerBackend):
    """Backend for the unroller that creates a DAGCircuit object.

    Example::

        qasm = Qasm(filename = "teleport.qasm").parse()
        dagcircuit = Unroller(qasm, DAGBackend()).execute()
        print(dagcircuit.qasm())
    """

    def __init__(self, basis=None):
        """Setup this backend.

        basis is a list of operation name strings.
        """
        super().__init__(basis)
        self.prec = 15
        self.creg = None
        self.cval = None
        self.circuit = DAGCircuit()
        if basis:
            self.basis = basis
        else:
            self.basis = []
        self.listen = True
        self.in_gate = ""
        self.gates = OrderedDict()

    def set_basis(self, basis):
        """Declare the set of user-defined gates to emit."""
        self.basis = basis

    def define_gate(self, name, gatedata):
        """Record and pass down the data for this gate."""
        self.gates[name] = gatedata
        self.circuit.add_gate_data(name, gatedata)

    def version(self, version):
        """Accept the version string.

        v is a version number.
        """
        pass

    def new_qreg(self, name, size):
        """Create a new quantum register.

        name = name of the register
        sz = size of the register
        """
        self.circuit.add_qreg(name, size)

    def new_creg(self, name, size):
        """Create a new classical register.

        name = name of the register
        sz = size of the register
        """
        self.circuit.add_creg(name, size)

    def u(self, arg, qubit, nested_scope=None):
        """Fundamental single qubit gate.

        arg is 3-tuple of Node expression objects.
        qubit is (regname,idx) tuple.
        nested_scope is a list of dictionaries mapping expression variables
        to Node expression objects in order of increasing nesting depth.
        """
        if self.listen:
            if self.creg is not None:
                condition = (self.creg, self.cval)
            else:
                condition = None
            if "U" not in self.basis:
                self.basis.append("U")
                self.circuit.add_basis_element("U", 1, 0, 3)
            self.circuit.apply_operation_back(
                "U", [qubit], [], list(map(lambda x: x.sym(nested_scope),
                                           arg)), condition)

    def cx(self, qubit0, qubit1):
        """Fundamental two-qubit gate.

        qubit0 is (regname, idx) tuple for the control qubit.
        qubit1 is (regname, idx) tuple for the target qubit.
        """
        if self.listen:
            if self.creg is not None:
                condition = (self.creg, self.cval)
            else:
                condition = None
            if "CX" not in self.basis:
                self.basis.append("CX")
                self.circuit.add_basis_element("CX", 2)
            self.circuit.apply_operation_back("CX", [qubit0, qubit1], [],
                                              [], condition)

    def measure(self, qubit, bit):
        """Measurement operation.

        qubit is (regname, idx) tuple for the input qubit.
        bit is (regname, idx) tuple for the output bit.
        """
        if self.creg is not None:
            condition = (self.creg, self.cval)
        else:
            condition = None
        if "measure" not in self.basis:
            self.basis.append("measure")
        if "measure" not in self.circuit.basis:
            self.circuit.add_basis_element("measure", 1, 1)
        self.circuit.apply_operation_back(
            "measure", [qubit], [bit], [], condition)

    def barrier(self, qubitlists):
        """Barrier instruction.

        qubitlists is a list of lists of (regname, idx) tuples.
        """
        if self.listen:
            names = []
            for qubit in qubitlists:
                for j, _ in enumerate(qubit):
                    names.append(qubit[j])
            if "barrier" not in self.basis:
                self.basis.append("barrier")
                self.circuit.add_basis_element("barrier", -1)
            self.circuit.apply_operation_back("barrier", names)

    def reset(self, qubit):
        """Reset instruction.

        qubit is a (regname, idx) tuple.
        """
        if self.creg is not None:
            condition = (self.creg, self.cval)
        else:
            condition = None
        if "reset" not in self.basis:
            self.basis.append("reset")
            self.circuit.add_basis_element("reset", 1)
        self.circuit.apply_operation_back("reset", [qubit], [], [], condition)

    def set_condition(self, creg, cval):
        """Attach a current condition.

        creg is a name string.
        cval is the integer value for the test.
        """
        self.creg = creg
        self.cval = cval

    def drop_condition(self):
        """Drop the current condition."""
        self.creg = None
        self.cval = None

    def start_gate(self, name, args, qubits, nested_scope=None):
        """Begin a custom gate.

        name is name string.
        args is list of Node expression objects.
        qubits is list of (regname, idx) tuples.
        nested_scope is a list of dictionaries mapping expression variables
        to Node expression objects in order of increasing nesting depth.
        """
        if self.listen and name not in self.basis \
                and self.gates[name]["opaque"]:
            raise BackendError("opaque gate %s not in basis" % name)
        if self.listen and name in self.basis:
            if self.creg is not None:
                condition = (self.creg, self.cval)
            else:
                condition = None
            self.in_gate = name
            self.listen = False
            self.circuit.add_basis_element(name, len(qubits), 0, len(args))
            self.circuit.apply_operation_back(
                name, qubits, [], list(map(lambda x: x.sym(nested_scope),
                                           args)), condition)

    def end_gate(self, name, args, qubits, nested_scope=None):
        """End a custom gate.

        name is name string.
        args is list of Node expression objects.
        qubits is list of (regname, idx) tuples.
        nested_scope is a list of dictionaries mapping expression variables
        to Node expression objects in order of increasing nesting depth.
        """
        if name == self.in_gate:
            self.in_gate = ""
            self.listen = True

    def get_output(self):
        """Returns the generated circuit."""
        return self.circuit
Пример #13
0
def swap_mapper(circuit_graph, coupling_graph,
                initial_layout=None,
                basis="cx,u1,u2,u3,id", trials=20, seed=None):
    """Map a DAGCircuit onto a CouplingGraph using swap gates.

    Args:
        circuit_graph (DAGCircuit): input DAG circuit
        coupling_graph (CouplingGraph): coupling graph to map onto
        initial_layout (dict): dict from qubits of circuit_graph to qubits
            of coupling_graph (optional)
        basis (str): basis string specifying basis of output DAGCircuit
        trials (int): number of trials.
        seed (int): initial seed.

    Returns:
        DAGCircuit: object containing a circuit equivalent to
        circuit_graph that respects couplings in coupling_graph, and
        a layout dict mapping qubits of circuit_graph into qubits
        of coupling_graph. The layout may differ from the initial_layout
        if the first layer of gates cannot be executed on the
        initial_layout.
    Raises:
        MapperError: if there was any error during the mapping or with the
            parameters.
    """
    if circuit_graph.width() > coupling_graph.size():
        raise MapperError("Not enough qubits in CouplingGraph")

    # Schedule the input circuit
    layerlist = list(circuit_graph.layers())
    logger.debug("schedule:")
    for i, v in enumerate(layerlist):
        logger.debug("    %d: %s", i, v["partition"])

    if initial_layout is not None:
        # Check the input layout
        circ_qubits = circuit_graph.get_qubits()
        coup_qubits = coupling_graph.get_qubits()
        qubit_subset = []
        for k, v in initial_layout.items():
            qubit_subset.append(v)
            if k not in circ_qubits:
                raise MapperError("initial_layout qubit %s[%d] not in input "
                                  "DAGCircuit" % (k[0], k[1]))
            if v not in coup_qubits:
                raise MapperError("initial_layout qubit %s[%d] not in input "
                                  "CouplingGraph" % (v[0], v[1]))
    else:
        # Supply a default layout
        qubit_subset = coupling_graph.get_qubits()
        qubit_subset = qubit_subset[0:circuit_graph.width()]
        initial_layout = {a: b for a, b in
                          zip(circuit_graph.get_qubits(), qubit_subset)}

    # Find swap circuit to preceed to each layer of input circuit
    layout = initial_layout.copy()
    layout_max_index = max(map(lambda x: x[1]+1, layout.values()))

    # Construct an empty DAGCircuit with one qreg "q"
    # and the same set of cregs as the input circuit
    dagcircuit_output = DAGCircuit()
    dagcircuit_output.add_qreg("q", layout_max_index)
    for name, size in circuit_graph.cregs.items():
        dagcircuit_output.add_creg(name, size)

    # Make a trivial wire mapping between the subcircuits
    # returned by swap_mapper_layer_update and the circuit
    # we are building
    identity_wire_map = {}
    for j in range(layout_max_index):
        identity_wire_map[("q", j)] = ("q", j)
    for name, size in circuit_graph.cregs.items():
        for j in range(size):
            identity_wire_map[(name, j)] = (name, j)

    first_layer = True  # True until first layer is output
    logger.debug("initial_layout = %s", layout)

    # Iterate over layers
    for i, layer in enumerate(layerlist):

        # Attempt to find a permutation for this layer
        success_flag, best_circ, best_d, best_layout, trivial_flag \
            = layer_permutation(layer["partition"], layout,
                                qubit_subset, coupling_graph, trials, seed)
        logger.debug("swap_mapper: layer %d", i)
        logger.debug("swap_mapper: success_flag=%s,best_d=%s,trivial_flag=%s",
                     success_flag, str(best_d), trivial_flag)

        # If this fails, try one gate at a time in this layer
        if not success_flag:
            logger.debug("swap_mapper: failed, layer %d, "
                         "retrying sequentially", i)
            serial_layerlist = list(layer["graph"].serial_layers())

            # Go through each gate in the layer
            for j, serial_layer in enumerate(serial_layerlist):

                success_flag, best_circ, best_d, best_layout, trivial_flag \
                    = layer_permutation(serial_layer["partition"],
                                        layout, qubit_subset, coupling_graph,
                                        trials, seed)
                logger.debug("swap_mapper: layer %d, sublayer %d", i, j)
                logger.debug("swap_mapper: success_flag=%s,best_d=%s,"
                             "trivial_flag=%s",
                             success_flag, str(best_d), trivial_flag)

                # Give up if we fail again
                if not success_flag:
                    raise MapperError("swap_mapper failed: " +
                                      "layer %d, sublayer %d" % (i, j) +
                                      ", \"%s\"" %
                                      serial_layer["graph"].qasm(
                                          no_decls=True,
                                          aliases=layout))

                # If this layer is only single-qubit gates,
                # and we have yet to see multi-qubit gates,
                # continue to the next inner iteration
                if trivial_flag and first_layer:
                    logger.debug("swap_mapper: skip to next sublayer")
                    continue

                # Update the record of qubit positions for each inner iteration
                layout = best_layout
                # Update the QASM
                dagcircuit_output.compose_back(
                    swap_mapper_layer_update(j,
                                             first_layer,
                                             best_layout,
                                             best_d,
                                             best_circ,
                                             serial_layerlist),
                    identity_wire_map)
                # Update initial layout
                if first_layer:
                    initial_layout = layout
                    first_layer = False

        else:
            # Update the record of qubit positions for each iteration
            layout = best_layout

            # Update the QASM
            dagcircuit_output.compose_back(
                swap_mapper_layer_update(i,
                                         first_layer,
                                         best_layout,
                                         best_d,
                                         best_circ,
                                         layerlist),
                identity_wire_map)
            # Update initial layout
            if first_layer:
                initial_layout = layout
                first_layer = False

    # If first_layer is still set, the circuit only has single-qubit gates
    # so we can use the initial layout to output the entire circuit
    if first_layer:
        layout = initial_layout
        for i, layer in enumerate(layerlist):
            dagcircuit_output.compose_back(layer["graph"], layout)

    # Parse openqasm_output into DAGCircuit object
    dag_unrrolled = DagUnroller(dagcircuit_output,
                                DAGBackend(basis.split(",")))
    dagcircuit_output = dag_unrrolled.expand_gates()
    return dagcircuit_output, initial_layout
Пример #14
0
 def test_dag_get_qubits(self):
     """ get_qubits() method """
     dag = DAGCircuit()
     dag.add_qreg(QuantumRegister(1, 'qr1'))
     dag.add_qreg(QuantumRegister(1, 'qr10'))
     dag.add_qreg(QuantumRegister(1, 'qr0'))
     dag.add_qreg(QuantumRegister(1, 'qr3'))
     dag.add_qreg(QuantumRegister(1, 'qr4'))
     dag.add_qreg(QuantumRegister(1, 'qr6'))
     self.assertListEqual(dag.qubits(), [(QuantumRegister(1, 'qr1'), 0),
                                         (QuantumRegister(1, 'qr10'), 0),
                                         (QuantumRegister(1, 'qr0'), 0),
                                         (QuantumRegister(1, 'qr3'), 0),
                                         (QuantumRegister(1, 'qr4'), 0),
                                         (QuantumRegister(1, 'qr6'), 0)])
 def _units_used_in_delays(dag: DAGCircuit) -> Set[str]:
     units_used = set()
     for node in dag.op_nodes(op=Delay):
         units_used.add(node.op.unit)
     return units_used
Пример #16
0
 def test_add_reg_duplicate(self):
     """ add_qreg with the same register twice is not allowed."""
     dag = DAGCircuit()
     qr = QuantumRegister(2)
     dag.add_qreg(qr)
     self.assertRaises(DAGCircuitError, dag.add_qreg, qr)
Пример #17
0
    def run(self, dag):
        """Run the BasicSwap pass on `dag`.

        Args:
            dag (DAGCircuit): DAG to map.

        Returns:
            DAGCircuit: A mapped DAG.

        Raises:
            TranspilerError: if the coupling map or the layout are not
            compatible with the DAG.
        """
        if self.fake_run:
            return self.fake_run(dag)

        new_dag = dag.copy_empty_like()

        if len(dag.qregs) != 1 or dag.qregs.get("q", None) is None:
            raise TranspilerError("Basic swap runs on physical circuits only")

        if len(dag.qubits) > len(self.coupling_map.physical_qubits):
            raise TranspilerError(
                "The layout does not match the amount of qubits in the DAG")

        canonical_register = dag.qregs["q"]
        trivial_layout = Layout.generate_trivial_layout(canonical_register)
        current_layout = trivial_layout.copy()

        for layer in dag.serial_layers():
            subdag = layer["graph"]

            for gate in subdag.two_qubit_ops():
                physical_q0 = current_layout[gate.qargs[0]]
                physical_q1 = current_layout[gate.qargs[1]]
                if self.coupling_map.distance(physical_q0, physical_q1) != 1:
                    # Insert a new layer with the SWAP(s).
                    swap_layer = DAGCircuit()
                    swap_layer.add_qreg(canonical_register)

                    path = self.coupling_map.shortest_undirected_path(
                        physical_q0, physical_q1)
                    for swap in range(len(path) - 2):
                        connected_wire_1 = path[swap]
                        connected_wire_2 = path[swap + 1]

                        qubit_1 = current_layout[connected_wire_1]
                        qubit_2 = current_layout[connected_wire_2]

                        # create the swap operation
                        swap_layer.apply_operation_back(
                            SwapGate(), qargs=[qubit_1, qubit_2], cargs=[])

                    # layer insertion
                    order = current_layout.reorder_bits(new_dag.qubits)
                    new_dag.compose(swap_layer, qubits=order)

                    # update current_layout
                    for swap in range(len(path) - 2):
                        current_layout.swap(path[swap], path[swap + 1])

            order = current_layout.reorder_bits(new_dag.qubits)
            new_dag.compose(subdag, qubits=order)

        return new_dag
Пример #18
0
def direction_mapper(circuit_graph, coupling_graph):
    """Change the direction of CNOT gates to conform to CouplingGraph.

    circuit_graph = input DAGCircuit
    coupling_graph = corresponding CouplingGraph

    Adds "h" to the circuit basis.

    Returns a DAGCircuit object containing a circuit equivalent to
    circuit_graph but with CNOT gate directions matching the edges
    of coupling_graph. Raises an exception if the circuit_graph
    does not conform to the coupling_graph.
    """
    if "cx" not in circuit_graph.basis:
        return circuit_graph
    if circuit_graph.basis["cx"] != (2, 0, 0):
        raise MapperError("cx gate has unexpected signature %s" %
                          circuit_graph.basis["cx"])

    flipped_cx_circuit = DAGCircuit()
    flipped_cx_circuit.add_qreg('q', 2)
    flipped_cx_circuit.add_basis_element("CX", 2)
    flipped_cx_circuit.add_basis_element("U", 1, 0, 3)
    flipped_cx_circuit.add_basis_element("cx", 2)
    flipped_cx_circuit.add_basis_element("u2", 1, 0, 2)
    flipped_cx_circuit.add_basis_element("h", 1)
    flipped_cx_circuit.add_gate_data("cx", cx_data)
    flipped_cx_circuit.add_gate_data("u2", u2_data)
    flipped_cx_circuit.add_gate_data("h", h_data)
    flipped_cx_circuit.apply_operation_back("h", [("q", 0)])
    flipped_cx_circuit.apply_operation_back("h", [("q", 1)])
    flipped_cx_circuit.apply_operation_back("cx", [("q", 1), ("q", 0)])
    flipped_cx_circuit.apply_operation_back("h", [("q", 0)])
    flipped_cx_circuit.apply_operation_back("h", [("q", 1)])

    cx_node_list = circuit_graph.get_named_nodes("cx")
    cg_edges = coupling_graph.get_edges()
    for cx_node in cx_node_list:
        nd = circuit_graph.multi_graph.node[cx_node]
        cxedge = tuple(nd["qargs"])
        if cxedge in cg_edges:
            logger.debug("cx %s[%d], %s[%d] -- OK",
                         cxedge[0][0], cxedge[0][1],
                         cxedge[1][0], cxedge[1][1])
            continue
        elif (cxedge[1], cxedge[0]) in cg_edges:
            circuit_graph.substitute_circuit_one(cx_node,
                                                 flipped_cx_circuit,
                                                 wires=[("q", 0), ("q", 1)])
            logger.debug("cx %s[%d], %s[%d] -FLIP",
                         cxedge[0][0], cxedge[0][1],
                         cxedge[1][0], cxedge[1][1])
        else:
            raise MapperError("circuit incompatible with CouplingGraph: "
                              "cx on %s" % pprint.pformat(cxedge))
    return circuit_graph
    def run(self, dag: DAGCircuit) -> DAGCircuit:
        """Run the RemoveBarriers pass on `dag`."""

        dag.remove_all_ops_named("barrier")

        return dag
Пример #20
0
def swap_mapper(circuit_graph,
                coupling_graph,
                initial_layout=None,
                basis="cx,u1,u2,u3,id",
                trials=20,
                seed=None):
    """Map a DAGCircuit onto a CouplingGraph using swap gates.

    Args:
        circuit_graph (DAGCircuit): input DAG circuit
        coupling_graph (CouplingGraph): coupling graph to map onto
        initial_layout (dict): dict from qubits of circuit_graph to qubits
            of coupling_graph (optional)
        basis (str): basis string specifying basis of output DAGCircuit
        trials (int): number of trials.
        seed (int): initial seed.

    Returns:
        DAGCircuit: object containing a circuit equivalent to
        circuit_graph that respects couplings in coupling_graph, and
        a layout dict mapping qubits of circuit_graph into qubits
        of coupling_graph. The layout may differ from the initial_layout
        if the first layer of gates cannot be executed on the
        initial_layout. Finally, returned is the final layer qubit
        permutation that is needed to add measurements back in.

    Raises:
        MapperError: if there was any error during the mapping or with the
            parameters.
    """
    if circuit_graph.width() > coupling_graph.size():
        raise MapperError("Not enough qubits in CouplingGraph")

    # Schedule the input circuit
    layerlist = list(circuit_graph.layers())
    logger.debug("schedule:")
    for i, v in enumerate(layerlist):
        logger.debug("    %d: %s", i, v["partition"])

    if initial_layout is not None:
        # Check the input layout
        circ_qubits = circuit_graph.get_qubits()
        coup_qubits = coupling_graph.get_qubits()
        qubit_subset = []
        for k, v in initial_layout.items():
            qubit_subset.append(v)
            if k not in circ_qubits:
                raise MapperError("initial_layout qubit %s[%d] not in input "
                                  "DAGCircuit" % (k[0], k[1]))
            if v not in coup_qubits:
                raise MapperError("initial_layout qubit %s[%d] not in input "
                                  "CouplingGraph" % (v[0], v[1]))
    else:
        # Supply a default layout
        qubit_subset = coupling_graph.get_qubits()
        qubit_subset = qubit_subset[0:circuit_graph.width()]
        initial_layout = {
            a: b
            for a, b in zip(circuit_graph.get_qubits(), qubit_subset)
        }

    # Find swap circuit to preceed to each layer of input circuit
    layout = initial_layout.copy()
    layout_max_index = max(map(lambda x: x[1] + 1, layout.values()))

    # Construct an empty DAGCircuit with one qreg "q"
    # and the same set of cregs as the input circuit
    dagcircuit_output = DAGCircuit()
    dagcircuit_output.name = circuit_graph.name
    dagcircuit_output.add_qreg("q", layout_max_index)
    for name, size in circuit_graph.cregs.items():
        dagcircuit_output.add_creg(name, size)

    # Make a trivial wire mapping between the subcircuits
    # returned by swap_mapper_layer_update and the circuit
    # we are building
    identity_wire_map = {}
    for j in range(layout_max_index):
        identity_wire_map[("q", j)] = ("q", j)
    for name, size in circuit_graph.cregs.items():
        for j in range(size):
            identity_wire_map[(name, j)] = (name, j)

    first_layer = True  # True until first layer is output
    logger.debug("initial_layout = %s", layout)

    # Iterate over layers
    for i, layer in enumerate(layerlist):

        # Attempt to find a permutation for this layer
        success_flag, best_circ, best_d, best_layout, trivial_flag \
            = layer_permutation(layer["partition"], layout,
                                qubit_subset, coupling_graph, trials, seed)
        logger.debug("swap_mapper: layer %d", i)
        logger.debug("swap_mapper: success_flag=%s,best_d=%s,trivial_flag=%s",
                     success_flag, str(best_d), trivial_flag)

        # If this fails, try one gate at a time in this layer
        if not success_flag:
            logger.debug(
                "swap_mapper: failed, layer %d, "
                "retrying sequentially", i)
            serial_layerlist = list(layer["graph"].serial_layers())

            # Go through each gate in the layer
            for j, serial_layer in enumerate(serial_layerlist):

                success_flag, best_circ, best_d, best_layout, trivial_flag \
                    = layer_permutation(serial_layer["partition"],
                                        layout, qubit_subset, coupling_graph,
                                        trials, seed)
                logger.debug("swap_mapper: layer %d, sublayer %d", i, j)
                logger.debug(
                    "swap_mapper: success_flag=%s,best_d=%s,"
                    "trivial_flag=%s", success_flag, str(best_d), trivial_flag)

                # Give up if we fail again
                if not success_flag:
                    raise MapperError("swap_mapper failed: " +
                                      "layer %d, sublayer %d" % (i, j) +
                                      ", \"%s\"" % serial_layer["graph"].qasm(
                                          no_decls=True, aliases=layout))

                # If this layer is only single-qubit gates,
                # and we have yet to see multi-qubit gates,
                # continue to the next inner iteration
                if trivial_flag and first_layer:
                    logger.debug("swap_mapper: skip to next sublayer")
                    continue

                # Update the record of qubit positions for each inner iteration
                layout = best_layout
                # Update the QASM
                dagcircuit_output.compose_back(
                    swap_mapper_layer_update(j, first_layer, best_layout,
                                             best_d, best_circ,
                                             serial_layerlist),
                    identity_wire_map)
                # Update initial layout
                if first_layer:
                    initial_layout = layout
                    first_layer = False

        else:
            # Update the record of qubit positions for each iteration
            layout = best_layout

            # Update the QASM
            dagcircuit_output.compose_back(
                swap_mapper_layer_update(i, first_layer, best_layout, best_d,
                                         best_circ, layerlist),
                identity_wire_map)
            # Update initial layout
            if first_layer:
                initial_layout = layout
                first_layer = False

    # This is the final layout that we need to correctly replace
    # any measurements that needed to be removed before the swap
    last_layout = layout

    # If first_layer is still set, the circuit only has single-qubit gates
    # so we can use the initial layout to output the entire circuit
    if first_layer:
        layout = initial_layout
        for i, layer in enumerate(layerlist):
            dagcircuit_output.compose_back(layer["graph"], layout)

    # Parse openqasm_output into DAGCircuit object
    dag_unrrolled = DagUnroller(dagcircuit_output,
                                DAGBackend(basis.split(",")))
    dagcircuit_output = dag_unrrolled.expand_gates()
    return dagcircuit_output, initial_layout, last_layout
Пример #21
0
    def run(self, dag):
        """Run the ConsolidateBlocks pass on `dag`.

        Iterate over each block and replace it with an equivalent Unitary
        on the same wires.
        """

        if self.decomposer is None:
            return dag

        new_dag = DAGCircuit()
        for qreg in dag.qregs.values():
            new_dag.add_qreg(qreg)
        for creg in dag.cregs.values():
            new_dag.add_creg(creg)

        # compute ordered indices for the global circuit wires
        global_index_map = {wire: idx for idx, wire in enumerate(dag.qubits)}

        blocks = self.property_set['block_list']
        # just to make checking if a node is in any block easier
        all_block_nodes = {nd for bl in blocks for nd in bl}

        for node in dag.topological_op_nodes():
            if node not in all_block_nodes:
                # need to add this node to find out where in the list it goes
                preds = [
                    nd for nd in dag.predecessors(node) if nd.type == 'op'
                ]

                block_count = 0
                while preds:
                    if block_count < len(blocks):
                        block = blocks[block_count]

                        # if any of the predecessors are in the block, remove them
                        preds = [p for p in preds if p not in block]
                    else:
                        # should never occur as this would mean not all
                        # nodes before this one topologically had been added
                        # so not all predecessors were removed
                        raise TranspilerError(
                            "Not all predecessors removed due to error"
                            " in topological order")

                    block_count += 1

                # we have now seen all predecessors
                # so update the blocks list to include this block
                blocks = blocks[:block_count] + [[node]] + blocks[block_count:]

        # create the dag from the updated list of blocks
        basis_gate_name = self.decomposer.gate.name
        for block in blocks:
            if len(block) == 1 and (block[0].name != basis_gate_name
                                    or block[0].op.is_parameterized()):
                # an intermediate node that was added into the overall list
                new_dag.apply_operation_back(block[0].op, block[0].qargs,
                                             block[0].cargs)
            else:
                # find the qubits involved in this block
                block_qargs = set()
                block_cargs = set()
                for nd in block:
                    block_qargs |= set(nd.qargs)
                    if nd.condition:
                        block_cargs |= set(nd.condition[0])
                # convert block to a sub-circuit, then simulate unitary and add
                q = QuantumRegister(len(block_qargs))
                # if condition in node, add clbits to circuit
                if len(block_cargs) > 0:
                    c = ClassicalRegister(len(block_cargs))
                    subcirc = QuantumCircuit(q, c)
                else:
                    subcirc = QuantumCircuit(q)
                block_index_map = self._block_qargs_to_indices(
                    block_qargs, global_index_map)
                basis_count = 0
                for nd in block:
                    if nd.op.name == basis_gate_name:
                        basis_count += 1
                    subcirc.append(nd.op,
                                   [q[block_index_map[i]] for i in nd.qargs])
                unitary = UnitaryGate(
                    Operator(subcirc))  # simulates the circuit

                max_2q_depth = 20  # If depth > 20, there will be 1q gates to consolidate.
                if (  # pylint: disable=too-many-boolean-expressions
                        self.force_consolidate or unitary.num_qubits > 2 or
                        self.decomposer.num_basis_gates(unitary) < basis_count
                        or len(subcirc) > max_2q_depth or
                    (self.basis_gates is not None and
                     not set(subcirc.count_ops()).issubset(self.basis_gates))):
                    new_dag.apply_operation_back(
                        UnitaryGate(unitary),
                        sorted(block_qargs, key=lambda x: block_index_map[x]))
                else:
                    for nd in block:
                        new_dag.apply_operation_back(nd.op, nd.qargs, nd.cargs)

        return new_dag
Пример #22
0
    def _layer_permutation(self, layer_partition, layout, qubit_subset,
                           coupling, trials, seed=None):
        """Find a swap circuit that implements a permutation for this layer.

        The goal is to swap qubits such that qubits in the same two-qubit gates
        are adjacent.

        Based on S. Bravyi's algorithm.

        layer_partition (list): The layer_partition is a list of (qu)bit
            lists and each qubit is a tuple (qreg, index).
        layout (Layout): The layout is a Layout object mapping virtual
            qubits in the input circuit to physical qubits in the coupling
            graph. It reflects the current positions of the data.
        qubit_subset (list): The qubit_subset is the set of qubits in
            the coupling graph that we have chosen to map into, as tuples
            (Register, index).
        coupling (CouplingMap): Directed graph representing a coupling map.
            This coupling map should be one that was provided to the
            stochastic mapper.
        trials (int): Number of attempts the randomized algorithm makes.
        seed (int): Optional seed for the random number generator. If it is
            None we do not reseed.

        Returns:
             Tuple: success_flag, best_circuit, best_depth, best_layout, trivial_flag

        If success_flag is True, then best_circuit contains a DAGCircuit with
        the swap circuit, best_depth contains the depth of the swap circuit,
        and best_layout contains the new positions of the data qubits after the
        swap circuit has been applied. The trivial_flag is set if the layer
        has no multi-qubit gates.

        Raises:
            TranspilerError: if anything went wrong.
        """
        if seed is not None:
            np.random.seed(seed)

        logger.debug("layer_permutation: layer_partition = %s",
                     pformat(layer_partition))
        logger.debug("layer_permutation: layout = %s",
                     pformat(layout.get_virtual_bits()))
        logger.debug("layer_permutation: qubit_subset = %s",
                     pformat(qubit_subset))
        logger.debug("layer_permutation: trials = %s", trials)

        gates = []  # list of lists of tuples [[(register, index), ...], ...]
        for gate_args in layer_partition:
            if len(gate_args) > 2:
                raise TranspilerError("Layer contains > 2-qubit gates")
            elif len(gate_args) == 2:
                gates.append(tuple(gate_args))
        logger.debug("layer_permutation: gates = %s", pformat(gates))

        # Can we already apply the gates? If so, there is no work to do.
        dist = sum([coupling.distance(layout[g[0]], layout[g[1]])
                    for g in gates])
        logger.debug("layer_permutation: distance = %s", dist)
        if dist == len(gates):
            logger.debug("layer_permutation: nothing to do")
            circ = DAGCircuit()
            for register in layout.get_virtual_bits().keys():
                if register[0] not in circ.qregs.values():
                    circ.add_qreg(register[0])
            return True, circ, 0, layout, (not bool(gates))

        # Begin loop over trials of randomized algorithm
        num_qubits = len(layout)
        best_depth = inf  # initialize best depth
        best_circuit = None  # initialize best swap circuit
        best_layout = None  # initialize best final layout

        cdist2 = coupling._dist_matrix**2
        # Scaling matrix
        scale = np.zeros((num_qubits, num_qubits))
        utri_idx = np.triu_indices(num_qubits)

        for trial in range(trials):
            logger.debug("layer_permutation: trial %s", trial)
            trial_layout = layout.copy()
            trial_circuit = DAGCircuit()  # SWAP circuit for this trial
            for register in trial_layout.get_virtual_bits().keys():
                if register[0] not in trial_circuit.qregs.values():
                    trial_circuit.add_qreg(register[0])

            # Compute randomized distance
            data = 1 + np.random.normal(0, 1/num_qubits,
                                        size=num_qubits*(num_qubits+1)//2)
            scale[utri_idx] = data
            xi = (scale+scale.T)*cdist2  # pylint: disable=invalid-name

            slice_circuit = DAGCircuit()  # circuit for this swap slice
            for register in trial_layout.get_virtual_bits().keys():
                if register[0] not in slice_circuit.qregs.values():
                    slice_circuit.add_qreg(register[0])

            # Loop over depths from 1 up to a maximum depth
            depth_step = 1
            depth_max = 2 * num_qubits + 1
            while depth_step < depth_max:
                qubit_set = set(qubit_subset)
                # While there are still qubits available
                while qubit_set:
                    # Compute the objective function
                    min_cost = sum(xi[trial_layout[g[0]]][trial_layout[g[1]]] for g in gates)
                    # Try to decrease objective function
                    cost_reduced = False

                    # Loop over edges of coupling graph
                    need_copy = True
                    for edge in coupling.get_edges():
                        qubits = (trial_layout[edge[0]], trial_layout[edge[1]])
                        # Are the qubits available?
                        if qubits[0] in qubit_set and qubits[1] in qubit_set:
                            # Try this edge to reduce the cost
                            if need_copy:
                                new_layout = trial_layout.copy()
                                need_copy = False
                            new_layout.swap(edge[0], edge[1])

                            # Compute the objective function
                            new_cost = sum(xi[new_layout[g[0]]][new_layout[g[1]]] for g in gates)
                            # Record progress if we succceed
                            if new_cost < min_cost:
                                logger.debug("layer_permutation: min_cost "
                                             "improved to %s", min_cost)
                                cost_reduced = True
                                min_cost = new_cost
                                optimal_layout = new_layout
                                optimal_edge = (self.initial_layout[edge[0]],
                                                self.initial_layout[edge[1]])
                                optimal_qubits = qubits
                                need_copy = True
                            else:
                                new_layout.swap(edge[0], edge[1])

                    # Were there any good swap choices?
                    if cost_reduced:
                        qubit_set.remove(optimal_qubits[0])
                        qubit_set.remove(optimal_qubits[1])
                        trial_layout = optimal_layout
                        slice_circuit.apply_operation_back(
                            SwapGate(optimal_edge[0],
                                     optimal_edge[1]))
                        logger.debug("layer_permutation: swap the pair %s",
                                     pformat(optimal_edge))
                    else:
                        break

                # We have either run out of swap pairs to try or
                # failed to improve the cost.

                # Compute the coupling graph distance
                dist = sum(coupling.distance(trial_layout[g[0]],
                                             trial_layout[g[1]])
                           for g in gates)
                logger.debug("layer_permutation: new swap distance = %s", dist)
                # If all gates can be applied now, we are finished.
                # Otherwise we need to consider a deeper swap circuit
                if dist == len(gates):
                    logger.debug("layer_permutation: all gates can be "
                                 "applied now in this layer")
                    trial_circuit.extend_back(slice_circuit)
                    break

                # Increment the depth
                depth_step += 1
                logger.debug("layer_permutation: increment depth to %s", depth_step)

            # Either we have succeeded at some depth d < dmax or failed
            dist = sum(coupling.distance(trial_layout[g[0]],
                                         trial_layout[g[1]])
                       for g in gates)
            logger.debug("layer_permutation: final distance for this trial = %s", dist)
            if dist == len(gates):
                if depth_step < best_depth:
                    logger.debug("layer_permutation: got circuit with improved depth %s",
                                 depth_step)
                    best_circuit = trial_circuit
                    best_layout = trial_layout
                    best_depth = min(best_depth, depth_step)

            # Break out of trial loop if we found a depth 1 circuit
            # since we can't improve it further
            if best_depth == 1:
                break

        # If we have no best circuit for this layer, all of the
        # trials have failed
        if best_circuit is None:
            logger.debug("layer_permutation: failed!")
            return False, None, None, None, False

        # Otherwise, we return our result for this layer
        logger.debug("layer_permutation: success!")
        return True, best_circuit, best_depth, best_layout, False
Пример #23
0
    def run(self, dag):
        """Run the ApplyLayout pass on `dag`.

        Args:
            dag (DAGCircuit): DAG to map.

        Returns:
            DAGCircuit: A mapped DAG (with physical qubits).

        Raises:
            TranspilerError: if no layout is found in `property_set` or no full physical qubits.
        """
        layout = self.property_set["layout"]
        if not layout:
            raise TranspilerError(
                "No 'layout' is found in property_set. Please run a Layout pass in advance."
            )
        if len(layout) != (1 + max(layout.get_physical_bits())):
            raise TranspilerError("The 'layout' must be full (with ancilla).")

        for qreg in dag.qregs.values():
            self.property_set["layout"].add_register(qreg)

        q = QuantumRegister(len(layout), "q")

        new_dag = DAGCircuit()
        new_dag.add_qreg(q)
        new_dag.metadata = dag.metadata
        new_dag.add_clbits(dag.clbits)
        for creg in dag.cregs.values():
            new_dag.add_creg(creg)
        virtual_phsyical_map = layout.get_virtual_bits()
        for node in dag.topological_op_nodes():
            qargs = [q[virtual_phsyical_map[qarg]] for qarg in node.qargs]
            new_dag.apply_operation_back(node.op, qargs, node.cargs)
        new_dag._global_phase = dag._global_phase

        return new_dag
Пример #24
0
    def _mapper(self, circuit_graph, coupling_graph,
                trials=20, seed=None):
        """Map a DAGCircuit onto a CouplingMap using swap gates.

        Use self.initial_layout for the initial layout.

        Args:
            circuit_graph (DAGCircuit): input DAG circuit
            coupling_graph (CouplingMap): coupling graph to map onto
            trials (int): number of trials.
            seed (int): initial seed.

        Returns:
            DAGCircuit: object containing a circuit equivalent to
                circuit_graph that respects couplings in coupling_graph
            Layout: a layout object mapping qubits of circuit_graph into
                qubits of coupling_graph. The layout may differ from the
                initial_layout if the first layer of gates cannot be
                executed on the initial_layout, since in this case
                it is more efficient to modify the layout instead of swapping
            Dict: a final-layer qubit permutation

        Raises:
            TranspilerError: if there was any error during the mapping
                or with the parameters.
        """
        # Schedule the input circuit by calling layers()
        layerlist = list(circuit_graph.layers())
        logger.debug("schedule:")
        for i, v in enumerate(layerlist):
            logger.debug("    %d: %s", i, v["partition"])

        if self.initial_layout is not None:
            qubit_subset = self.initial_layout.get_virtual_bits().keys()
        else:
            # Supply a default layout for this dag
            self.initial_layout = Layout()
            physical_qubit = 0
            for qreg in circuit_graph.qregs.values():
                for index in range(qreg.size):
                    self.initial_layout[(qreg, index)] = physical_qubit
                    physical_qubit += 1
            qubit_subset = self.initial_layout.get_virtual_bits().keys()
            # Restrict the coupling map to the image of the layout
            coupling_graph = coupling_graph.subgraph(
                self.initial_layout.get_physical_bits().keys())
            if coupling_graph.size() < len(self.initial_layout):
                raise TranspilerError("Coupling map too small for default layout")
            self.input_layout = self.initial_layout.copy()

        # Find swap circuit to preceed to each layer of input circuit
        layout = self.initial_layout.copy()

        # Construct an empty DAGCircuit with the same set of
        # qregs and cregs as the input circuit
        dagcircuit_output = DAGCircuit()
        dagcircuit_output.name = circuit_graph.name
        for qreg in circuit_graph.qregs.values():
            dagcircuit_output.add_qreg(qreg)
        for creg in circuit_graph.cregs.values():
            dagcircuit_output.add_creg(creg)

        # Make a trivial wire mapping between the subcircuits
        # returned by _layer_update and the circuit we build
        identity_wire_map = {}
        for qubit in circuit_graph.qubits():
            identity_wire_map[qubit] = qubit
        for bit in circuit_graph.clbits():
            identity_wire_map[bit] = bit

        first_layer = True  # True until first layer is output
        logger.debug("initial_layout = %s", layout)

        # Iterate over layers
        for i, layer in enumerate(layerlist):

            # Attempt to find a permutation for this layer
            success_flag, best_circuit, best_depth, best_layout, trivial_flag \
                = self._layer_permutation(layer["partition"], layout,
                                          qubit_subset, coupling_graph,
                                          trials, seed)
            logger.debug("mapper: layer %d", i)
            logger.debug("mapper: success_flag=%s,best_depth=%s,trivial_flag=%s",
                         success_flag, str(best_depth), trivial_flag)

            # If this fails, try one gate at a time in this layer
            if not success_flag:
                logger.debug("mapper: failed, layer %d, "
                             "retrying sequentially", i)
                serial_layerlist = list(layer["graph"].serial_layers())

                # Go through each gate in the layer
                for j, serial_layer in enumerate(serial_layerlist):

                    success_flag, best_circuit, best_depth, best_layout, trivial_flag = \
                        self._layer_permutation(
                            serial_layer["partition"],
                            layout, qubit_subset,
                            coupling_graph,
                            trials, seed)
                    logger.debug("mapper: layer %d, sublayer %d", i, j)
                    logger.debug("mapper: success_flag=%s,best_depth=%s,"
                                 "trivial_flag=%s",
                                 success_flag, str(best_depth), trivial_flag)

                    # Give up if we fail again
                    if not success_flag:
                        raise TranspilerError("swap mapper failed: " +
                                              "layer %d, sublayer %d" % (i, j))

                    # If this layer is only single-qubit gates,
                    # and we have yet to see multi-qubit gates,
                    # continue to the next inner iteration
                    if trivial_flag and first_layer:
                        logger.debug("mapper: skip to next sublayer")
                        continue

                    if first_layer:
                        self.initial_layout = layout

                    # Update the record of qubit positions
                    # for each inner iteration
                    layout = best_layout
                    # Update the DAG
                    dagcircuit_output.extend_back(
                        self._layer_update(j,
                                           first_layer,
                                           best_layout,
                                           best_depth,
                                           best_circuit,
                                           serial_layerlist),
                        identity_wire_map)
                    if first_layer:
                        first_layer = False

            else:
                # Update the record of qubit positions for each iteration
                layout = best_layout

                if first_layer:
                    self.initial_layout = layout

                # Update the DAG
                dagcircuit_output.extend_back(
                    self._layer_update(i,
                                       first_layer,
                                       best_layout,
                                       best_depth,
                                       best_circuit,
                                       layerlist),
                    identity_wire_map)

                if first_layer:
                    first_layer = False

        # This is the final edgemap. We might use it to correctly replace
        # any measurements that needed to be removed earlier.
        logger.debug("mapper: self.initial_layout = %s", pformat(self.initial_layout))
        logger.debug("mapper: layout = %s", pformat(layout))
        last_edgemap = layout.combine_into_edge_map(self.initial_layout)
        logger.debug("mapper: last_edgemap = %s", pformat(last_edgemap))

        # If first_layer is still set, the circuit only has single-qubit gates
        # so we can use the initial layout to output the entire circuit
        # This code is dead due to changes to first_layer above.
        if first_layer:
            logger.debug("mapper: first_layer flag still set")
            layout = self.initial_layout
            for i, layer in enumerate(layerlist):
                edge_map = layout.combine_into_edge_map(self.initial_layout)
                dagcircuit_output.compose_back(layer["graph"], edge_map)

        return dagcircuit_output
Пример #25
0
    def run(self, dag):
        """Run the CommutativeCancellation pass on `dag`.

        Args:
            dag (DAGCircuit): the DAG to be optimized.

        Returns:
            DAGCircuit: the optimized DAG.

        Raises:
            TranspilerError: when the 1-qubit rotation gates are not found
        """
        var_z_gate = None
        z_var_gates = [
            gate for gate in dag.count_ops().keys() if gate in self._var_z_map
        ]
        if z_var_gates:
            # priortize z gates in circuit
            var_z_gate = self._var_z_map[next(iter(z_var_gates))]
        else:
            z_var_gates = [
                gate for gate in self.basis if gate in self._var_z_map
            ]
            if z_var_gates:
                var_z_gate = self._var_z_map[next(iter(z_var_gates))]

        # Now the gates supported are hard-coded
        q_gate_list = ["cx", "cy", "cz", "h", "y"]

        # Gate sets to be cancelled
        cancellation_sets = defaultdict(lambda: [])

        # Traverse each qubit to generate the cancel dictionaries
        # Cancel dictionaries:
        #  - For 1-qubit gates the key is (gate_type, qubit_id, commutation_set_id),
        #    the value is the list of gates that share the same gate type, qubit, commutation set.
        #  - For 2qbit gates the key: (gate_type, first_qbit, sec_qbit, first commutation_set_id,
        #    sec_commutation_set_id), the value is the list gates that share the same gate type,
        #    qubits and commutation sets.

        for wire in dag.wires:
            wire_commutation_set = self.property_set["commutation_set"][wire]

            for com_set_idx, com_set in enumerate(wire_commutation_set):
                if isinstance(com_set[0], (DAGInNode, DAGOutNode)):
                    continue
                for node in com_set:
                    num_qargs = len(node.qargs)
                    if num_qargs == 1 and node.name in q_gate_list:
                        cancellation_sets[(node.name, wire,
                                           com_set_idx)].append(node)
                    if num_qargs == 1 and node.name in [
                            "p", "z", "u1", "rz", "t", "s"
                    ]:
                        cancellation_sets[("z_rotation", wire,
                                           com_set_idx)].append(node)
                    if num_qargs == 1 and node.name in ["rx", "x"]:
                        cancellation_sets[("x_rotation", wire,
                                           com_set_idx)].append(node)
                    # Don't deal with Y rotation, because Y rotation doesn't commute with CNOT, so
                    # it should be dealt with by optimized1qgate pass
                    elif num_qargs == 2 and node.qargs[0] == wire:
                        second_qarg = node.qargs[1]
                        q2_key = (
                            node.name,
                            wire,
                            second_qarg,
                            com_set_idx,
                            self.property_set["commutation_set"][(
                                node, second_qarg)],
                        )
                        cancellation_sets[q2_key].append(node)

        for cancel_set_key in cancellation_sets:
            if cancel_set_key[0] == "z_rotation" and var_z_gate is None:
                continue
            set_len = len(cancellation_sets[cancel_set_key])
            if set_len > 1 and cancel_set_key[0] in q_gate_list:
                gates_to_cancel = cancellation_sets[cancel_set_key]
                for c_node in gates_to_cancel[:(set_len // 2) * 2]:
                    dag.remove_op_node(c_node)

            elif set_len > 1 and cancel_set_key[0] in [
                    "z_rotation", "x_rotation"
            ]:
                run = cancellation_sets[cancel_set_key]
                run_qarg = run[0].qargs[0]
                total_angle = 0.0  # lambda
                total_phase = 0.0
                for current_node in run:
                    if (getattr(current_node.op, "condition", None) is not None
                            or len(current_node.qargs) != 1
                            or current_node.qargs[0] != run_qarg):
                        raise TranspilerError("internal error")

                    if current_node.name in ["p", "u1", "rz", "rx"]:
                        current_angle = float(current_node.op.params[0])
                    elif current_node.name in ["z", "x"]:
                        current_angle = np.pi
                    elif current_node.name == "t":
                        current_angle = np.pi / 4
                    elif current_node.name == "s":
                        current_angle = np.pi / 2

                    # Compose gates
                    total_angle = current_angle + total_angle
                    if current_node.op.definition:
                        total_phase += current_node.op.definition.global_phase

                # Replace the data of the first node in the run
                if cancel_set_key[0] == "z_rotation":
                    new_op = var_z_gate(total_angle)
                elif cancel_set_key[0] == "x_rotation":
                    new_op = RXGate(total_angle)

                new_op_phase = 0
                if np.mod(total_angle, (2 * np.pi)) > _CUTOFF_PRECISION:
                    new_qarg = QuantumRegister(1, "q")
                    new_dag = DAGCircuit()
                    new_dag.add_qreg(new_qarg)
                    new_dag.apply_operation_back(new_op, [new_qarg[0]])
                    dag.substitute_node_with_dag(run[0], new_dag)
                    if new_op.definition:
                        new_op_phase = new_op.definition.global_phase

                dag.global_phase = total_phase - new_op_phase

                # Delete the other nodes in the run
                for current_node in run[1:]:
                    dag.remove_op_node(current_node)

                if np.mod(total_angle, (2 * np.pi)) < _CUTOFF_PRECISION:
                    dag.remove_op_node(run[0])

        return dag
Пример #26
0
def _compile_single_circuit(circuit,
                            backend,
                            config=None,
                            basis_gates=None,
                            coupling_map=None,
                            initial_layout=None,
                            seed=None,
                            pass_manager=None):
    """Compile a single circuit into a QobjExperiment.

    Args:
        circuit (QuantumCircuit): circuit to compile
        backend (BaseBackend): a backend to compile for
        config (dict): dictionary of parameters (e.g. noise) used by runner
        basis_gates (str): comma-separated basis gate set to compile to
        coupling_map (list): coupling map (perhaps custom) to target in mapping
        initial_layout (list): initial layout of qubits in mapping
        seed (int): random seed for simulators
        pass_manager (PassManager): a pass_manager for the transpiler stage

    Returns:
        QobjExperiment: the QobjExperiment to be run on the backends
    """
    # TODO: A better solution is to have options to enable/disable optimizations
    num_qubits = sum((len(qreg) for qreg in circuit.get_qregs().values()))
    if num_qubits == 1 or coupling_map == "all-to-all":
        coupling_map = None
    # Step 2a: circuit -> dag
    dag_circuit = DAGCircuit.fromQuantumCircuit(circuit)

    # TODO: move this inside the mapper pass
    # pick a good initial layout if coupling_map is not already satisfied
    # otherwise keep it as q[i]->q[i]
    if (initial_layout is None and not backend.configuration['simulator']
            and not _matches_coupling_map(circuit.data, coupling_map)):
        initial_layout = _pick_best_layout(backend, num_qubits,
                                           circuit.get_qregs())

    # Step 2b: transpile (dag -> dag)
    dag_circuit, final_layout = transpile(dag_circuit,
                                          basis_gates=basis_gates,
                                          coupling_map=coupling_map,
                                          initial_layout=initial_layout,
                                          get_layout=True,
                                          seed=seed,
                                          pass_manager=pass_manager)

    # Step 2c: dag -> json
    # the compiled circuit to be run saved as a dag
    # we assume that transpile() has already expanded gates
    # to the target basis, so we just need to generate json
    list_layout = [[k, v]
                   for k, v in final_layout.items()] if final_layout else None

    json_circuit = DagUnroller(dag_circuit,
                               JsonBackend(dag_circuit.basis)).execute()

    # Step 3a: create the Experiment based on json_circuit
    experiment = QobjExperiment.from_dict(json_circuit)
    # Step 3b: populate the Experiment configuration and header
    experiment.header.name = circuit.name
    # TODO: place in header or config?
    experiment_config = deepcopy(config or {})
    experiment_config.update({
        'coupling_map':
        coupling_map,
        'basis_gates':
        basis_gates,
        'layout':
        list_layout,
        'memory_slots':
        sum(register.size for register in circuit.get_cregs().values())
    })
    experiment.config = QobjItem(**experiment_config)

    # set eval_symbols=True to evaluate each symbolic expression
    # TODO after transition to qobj, we can drop this
    experiment.header.compiled_circuit_qasm = dag_circuit.qasm(
        qeflag=True, eval_symbols=True)

    return experiment
Пример #27
0
def _layer_permutation(layer_partition, initial_layout, layout, qubit_subset,
                       coupling, trials, qregs, rng):
    """Find a swap circuit that implements a permutation for this layer.

    Args:
        layer_partition (list): The layer_partition is a list of (qu)bit
            lists and each qubit is a tuple (qreg, index).
        initial_layout (Layout): The initial layout passed.
        layout (Layout): The layout is a Layout object mapping virtual
            qubits in the input circuit to physical qubits in the coupling
            graph. It reflects the current positions of the data.
        qubit_subset (list): The qubit_subset is the set of qubits in
            the coupling graph that we have chosen to map into, as tuples
            (Register, index).
        coupling (CouplingMap): Directed graph representing a coupling map.
            This coupling map should be one that was provided to the
            stochastic mapper.
        trials (int): Number of attempts the randomized algorithm makes.
        qregs (OrderedDict): Ordered dict of registers from input DAG.
        rng (RandomState): Random number generator.

    Returns:
        Tuple: success_flag, best_circuit, best_depth, best_layout, trivial_flag

    Raises:
        TranspilerError: if anything went wrong.
     """
    logger.debug("layer_permutation: layer_partition = %s",
                 pformat(layer_partition))
    logger.debug("layer_permutation: layout = %s",
                 pformat(layout.get_virtual_bits()))
    logger.debug("layer_permutation: qubit_subset = %s", pformat(qubit_subset))
    logger.debug("layer_permutation: trials = %s", trials)

    gates = []  # list of lists of tuples [[(register, index), ...], ...]
    for gate_args in layer_partition:
        if len(gate_args) > 2:
            raise TranspilerError("Layer contains > 2-qubit gates")
        if len(gate_args) == 2:
            gates.append(tuple(gate_args))
    logger.debug("layer_permutation: gates = %s", pformat(gates))

    # Can we already apply the gates? If so, there is no work to do.
    dist = sum([coupling.distance(layout[g[0]], layout[g[1]]) for g in gates])
    logger.debug("layer_permutation: distance = %s", dist)
    if dist == len(gates):
        logger.debug("layer_permutation: nothing to do")
        circ = DAGCircuit()
        for register in layout.get_virtual_bits().keys():
            if register.register not in circ.qregs.values():
                circ.add_qreg(register.register)
        return True, circ, 0, layout, (not bool(gates))

    # Begin loop over trials of randomized algorithm
    num_qubits = len(layout)
    best_depth = inf  # initialize best depth
    best_edges = None  # best edges found
    best_circuit = None  # initialize best swap circuit
    best_layout = None  # initialize best final layout

    cdist2 = coupling._dist_matrix**2
    # Scaling matrix
    scale = np.zeros((num_qubits, num_qubits))

    int_qubit_subset = regtuple_to_numeric(qubit_subset, qregs)
    int_gates = gates_to_idx(gates, qregs)
    int_layout = nlayout_from_layout(layout, qregs, coupling.size())

    trial_circuit = DAGCircuit()  # SWAP circuit for this trial
    for qubit in layout.get_virtual_bits().keys():
        if qubit.register not in trial_circuit.qregs.values():
            trial_circuit.add_qreg(qubit.register)

    slice_circuit = DAGCircuit()  # circuit for this swap slice
    for qubit in layout.get_virtual_bits().keys():
        if qubit.register not in slice_circuit.qregs.values():
            slice_circuit.add_qreg(qubit.register)
    edges = np.asarray(coupling.get_edges(), dtype=np.int32).ravel()
    cdist = coupling._dist_matrix
    for trial in range(trials):
        logger.debug("layer_permutation: trial %s", trial)
        # This is one Trial --------------------------------------
        dist, optim_edges, trial_layout, depth_step = swap_trial(
            num_qubits, int_layout, int_qubit_subset, int_gates, cdist2, cdist,
            edges, scale, rng)

        logger.debug("layer_permutation: final distance for this trial = %s",
                     dist)
        if dist == len(gates) and depth_step < best_depth:
            logger.debug(
                "layer_permutation: got circuit with improved depth %s",
                depth_step)
            best_edges = optim_edges
            best_layout = trial_layout
            best_depth = min(best_depth, depth_step)

        # Break out of trial loop if we found a depth 1 circuit
        # since we can't improve it further
        if best_depth == 1:
            break

    # If we have no best circuit for this layer, all of the
    # trials have failed
    if best_layout is None:
        logger.debug("layer_permutation: failed!")
        return False, None, None, None, False

    edgs = best_edges.edges()
    for idx in range(best_edges.size // 2):
        slice_circuit.apply_operation_back(
            SwapGate(),
            [initial_layout[edgs[2 * idx]], initial_layout[edgs[2 * idx + 1]]],
            [])
    trial_circuit.extend_back(slice_circuit)
    best_circuit = trial_circuit

    # Otherwise, we return our result for this layer
    logger.debug("layer_permutation: success!")
    best_lay = best_layout.to_layout(qregs)
    return True, best_circuit, best_depth, best_lay, False
Пример #28
0
 def _define_decompositions(self):
     """
     gate cy a,b { sdg b; cx a,b; s b; }
     """
     decomposition = DAGCircuit()
     q = QuantumRegister(2, "q")
     decomposition.add_qreg(q)
     decomposition.add_basis_element("s", 1, 0, 0)
     decomposition.add_basis_element("sdg", 1, 0, 0)
     decomposition.add_basis_element("cx", 2, 0, 0)
     rule = [SdgGate(q[1]), CnotGate(q[0], q[1]), SGate(q[1])]
     for inst in rule:
         decomposition.apply_operation_back(inst)
     self._decompositions = [decomposition]
Пример #29
0
    def run(self, dag):
        """
        Runs the BasicSwap pass on `dag`.
        Args:
            dag (DAGCircuit): DAG to map.

        Returns:
            DAGCircuit: A mapped DAG.
        """
        new_dag = DAGCircuit()

        if self.initial_layout is None:
            if self.property_set["layout"]:
                self.initial_layout = self.property_set["layout"]
            else:
                self.initial_layout = Layout.generate_trivial_layout(*dag.qregs.values())

        current_layout = copy(self.initial_layout)

        for layer in dag.serial_layers():
            subdag = layer['graph']

            for gate in subdag.get_2q_nodes():
                physical_q0 = current_layout[gate['qargs'][0]]
                physical_q1 = current_layout[gate['qargs'][1]]
                if self.coupling_map.distance(physical_q0, physical_q1) != 1:
                    # Insert a new layer with the SWAP(s).
                    swap_layer = DAGCircuit()

                    path = self.coupling_map.shortest_undirected_path(physical_q0, physical_q1)
                    for swap in range(len(path) - 2):
                        connected_wire_1 = path[swap]
                        connected_wire_2 = path[swap + 1]

                        qubit_1 = current_layout[connected_wire_1]
                        qubit_2 = current_layout[connected_wire_2]

                        # create qregs
                        for qreg in current_layout.get_registers():
                            if qreg[0] not in swap_layer.qregs.values():
                                swap_layer.add_qreg(qreg[0])

                        # create the swap operation
                        swap_layer.add_basis_element('swap', 2, 0, 0)
                        swap_layer.apply_operation_back(self.swap_gate(qubit_1, qubit_2),
                                                        qargs=[qubit_1, qubit_2])

                    # layer insertion
                    edge_map = current_layout.combine_into_edge_map(self.initial_layout)
                    new_dag.compose_back(swap_layer, edge_map)

                    # update current_layout
                    for swap in range(len(path) - 2):
                        current_layout.swap(path[swap], path[swap + 1])

            edge_map = current_layout.combine_into_edge_map(self.initial_layout)
            new_dag.extend_back(subdag, edge_map)

        return new_dag
Пример #30
0
    def test_layers_basic(self):
        """ The layers() method returns a list of layers, each of them with a list of nodes."""
        qreg = QuantumRegister(2, 'qr')
        creg = ClassicalRegister(2, 'cr')
        qubit0 = qreg[0]
        qubit1 = qreg[1]
        clbit0 = creg[0]
        clbit1 = creg[1]
        condition = (creg, 3)
        dag = DAGCircuit()
        dag.add_qreg(qreg)
        dag.add_creg(creg)
        dag.apply_operation_back(HGate(qubit0))
        dag.apply_operation_back(CnotGate(qubit0, qubit1), condition=None)
        dag.apply_operation_back(Measure(qubit1, clbit1), condition=None)
        dag.apply_operation_back(XGate(qubit1), condition=condition)
        dag.apply_operation_back(Measure(qubit0, clbit0), condition=None)
        dag.apply_operation_back(Measure(qubit1, clbit1), condition=None)

        layers = list(dag.layers())
        self.assertEqual(5, len(layers))

        name_layers = [[
            node[1]["op"].name
            for node in layer["graph"].multi_graph.nodes(data=True)
            if node[1]["type"] == "op"
        ] for layer in layers]

        self.assertEqual(
            [['h'], ['cx'], ['measure'], ['x'], ['measure', 'measure']],
            name_layers)
Пример #31
0
    def run(self, dag):
        new_dag = DAGCircuit()

        for qreg in dag.qregs.values():
            new_dag.add_qreg(qreg)
        for creg in dag.cregs.values():
            new_dag.add_creg(creg)
        for node in dag.op_nodes():
            new_dag.apply_operation_back(node.op, node.qargs, node.cargs)
            logger.info('SequentialPass: adding node {node.name}')
            if node.name in ['barrier', 'measure']:
                continue
            new_dag.apply_operation_back(Barrier(new_dag.num_qubits()),
                                         list(new_dag.qubits), [])

        return new_dag
Пример #32
0
class TestDagSubstitute(QiskitTestCase):
    """Test substitutuing a dag node with a sub-dag"""
    def setUp(self):
        self.dag = DAGCircuit()
        qreg = QuantumRegister(3, 'qr')
        creg = ClassicalRegister(2, 'cr')
        self.dag.add_qreg(qreg)
        self.dag.add_creg(creg)

        self.qubit0 = qreg[0]
        self.qubit1 = qreg[1]
        self.qubit2 = qreg[2]
        self.clbit0 = creg[0]
        self.clbit1 = creg[1]
        self.condition = (creg, 3)

        self.dag.apply_operation_back(HGate(self.qubit0))
        self.dag.apply_operation_back(CnotGate(self.qubit0, self.qubit1))
        self.dag.apply_operation_back(XGate(self.qubit1))

    def test_substitute_circuit_one_middle(self):
        """The method substitute_node_with_dag() replaces a in-the-middle node with a DAG."""
        cx_node = self.dag.op_nodes(op=CnotGate).pop()

        flipped_cx_circuit = DAGCircuit()
        v = QuantumRegister(2, "v")
        flipped_cx_circuit.add_qreg(v)
        flipped_cx_circuit.apply_operation_back(HGate(v[0]))
        flipped_cx_circuit.apply_operation_back(HGate(v[1]))
        flipped_cx_circuit.apply_operation_back(CnotGate(v[1], v[0]))
        flipped_cx_circuit.apply_operation_back(HGate(v[0]))
        flipped_cx_circuit.apply_operation_back(HGate(v[1]))

        self.dag.substitute_node_with_dag(cx_node,
                                          flipped_cx_circuit,
                                          wires=[v[0], v[1]])

        self.assertEqual(self.dag.count_ops()['h'], 5)

    def test_substitute_circuit_one_front(self):
        """The method substitute_node_with_dag() replaces a leaf-in-the-front node with a DAG."""
        pass

    def test_substitute_circuit_one_back(self):
        """The method substitute_node_with_dag() replaces a leaf-in-the-back node with a DAG."""
        pass
Пример #33
0
    def run(self, dag):
        new_dag = DAGCircuit()

        for qreg in dag.qregs.values():
            new_dag.add_qreg(qreg)
        for creg in dag.cregs.values():
            new_dag.add_creg(creg)

        for ii, layer in enumerate(dag.layers()):
            gates_1q = []
            gates_2q = []
            other_gates = []
            for node in layer['graph'].op_nodes():
                if len(node.qargs) == 2:
                    gates_2q.append(node)
                elif len(node.qargs) == 1:
                    gates_1q.append(node)
                else:
                    logging.info(f'layer {ii}: other type of node {node}')
                    other_gates.append(node)

            even = []
            odd = []
            for node in gates_1q:
                if node.qargs[0].index % 2 == 0:
                    even.append(node)
                else:
                    odd.append(node)
            logging.info(
                f'layer {ii}: 2q gates {len(gates_2q)}, even {len(even)} odd {len(odd)}, other {len(other_gates)}'
            )

            if len(even) > 0:
                for node in even:
                    new_dag.apply_operation_back(node.op, node.qargs,
                                                 node.cargs)
                if not isinstance(node.op, Barrier):
                    new_dag.apply_operation_back(Barrier(new_dag.num_qubits()),
                                                 list(new_dag.qubits), [])

            if len(odd) > 0:
                for node in odd:
                    new_dag.apply_operation_back(node.op, node.qargs,
                                                 node.cargs)
                if not isinstance(node.op, Barrier):
                    new_dag.apply_operation_back(Barrier(new_dag.num_qubits()),
                                                 list(new_dag.qubits), [])

            for node in gates_2q:
                new_dag.apply_operation_back(node.op, node.qargs, node.cargs)
                if not isinstance(node.op, Barrier):
                    new_dag.apply_operation_back(Barrier(new_dag.num_qubits()),
                                                 list(new_dag.qubits), [])

            for node in other_gates:
                new_dag.apply_operation_back(node.op, node.qargs, node.cargs)

                if not isinstance(node.op, Barrier):
                    new_dag.apply_operation_back(Barrier(new_dag.num_qubits()),
                                                 list(new_dag.qubits), [])

        return new_dag
Пример #34
0
 def test_add_reg_bad_type(self):
     """ add_qreg with a classical register is not allowed."""
     dag = DAGCircuit()
     cr = ClassicalRegister(2)
     self.assertRaises(DAGCircuitError, dag.add_qreg, cr)
Пример #35
0
    def run(self, dag):
        """Return a circuit with a barrier before last measurments."""

        # Collect DAG nodes which are followed only by barriers or other measures.
        final_op_types = ['measure', 'barrier']
        final_ops = []
        for candidate_node in dag.named_nodes(*final_op_types):
            is_final_op = True

            for _, child_successors in dag.bfs_successors(candidate_node):

                if any(suc.type == 'op' and suc.name not in final_op_types
                       for suc in child_successors):
                    is_final_op = False
                    break

            if is_final_op:
                final_ops.append(candidate_node)

        if not final_ops:
            return dag

        # Create a layer with the barrier and add registers from the original dag.
        barrier_layer = DAGCircuit()
        for qreg in dag.qregs.values():
            barrier_layer.add_qreg(qreg)
        for creg in dag.cregs.values():
            barrier_layer.add_creg(creg)

        final_qubits = set(final_op.qargs[0] for final_op in final_ops)

        new_barrier_node = barrier_layer.apply_operation_back(
            Barrier(qubits=final_qubits))

        # Preserve order of final ops collected earlier from the original DAG.
        ordered_final_nodes = [
            node for node in dag.nodes_in_topological_order()
            if node in set(final_ops)
        ]

        # Move final ops to the new layer and append the new layer to the DAG.
        for final_node in ordered_final_nodes:
            barrier_layer.apply_operation_back(final_node.op)

        for final_op in final_ops:
            dag._remove_op_node(final_op)

        # Check to see if the new barrier added to the DAG is equivalent to any
        # existing barriers, and if so, consolidate the two.
        our_ancestors = barrier_layer.ancestors(new_barrier_node)
        our_descendants = barrier_layer.descendants(new_barrier_node)
        our_qubits = final_qubits

        existing_barriers = sorted(barrier_layer.named_nodes('barrier'))
        # remove element from the list
        for i, node in enumerate(existing_barriers):
            if node == new_barrier_node:
                del existing_barriers[i]
                break

        for candidate_barrier in existing_barriers:
            their_ancestors = barrier_layer.ancestors(candidate_barrier)
            their_descendants = barrier_layer.descendants(candidate_barrier)

            their_qubits = set(candidate_barrier.qargs)

            if (not our_qubits.isdisjoint(their_qubits)
                    and our_ancestors.isdisjoint(their_descendants)
                    and our_descendants.isdisjoint(their_ancestors)):
                merge_barrier = Barrier(qubits=(our_qubits | their_qubits))
                merge_barrier_node = barrier_layer.apply_operation_front(
                    merge_barrier)

                our_ancestors = our_ancestors | their_ancestors
                our_descendants = our_descendants | their_descendants

                barrier_layer._remove_op_node(candidate_barrier)
                barrier_layer._remove_op_node(new_barrier_node)

                new_barrier_node = merge_barrier_node

        dag.extend_back(barrier_layer)

        return dag
Пример #36
0
def layer_permutation(layer_partition, layout, qubit_subset, coupling, trials,
                      seed=None):
    """Find a swap circuit that implements a permutation for this layer.

    The goal is to swap qubits such that qubits in the same two-qubit gates
    are adjacent.

    Based on Sergey Bravyi's algorithm.

    The layer_partition is a list of (qu)bit lists and each qubit is a
    tuple (qreg, index).
    The layout is a dict mapping qubits in the circuit to qubits in the
    coupling graph and represents the current positions of the data.
    The qubit_subset is the subset of qubits in the coupling graph that
    we have chosen to map into.
    The coupling is a CouplingGraph.
    TRIALS is the number of attempts the randomized algorithm makes.

    Returns: success_flag, best_circ, best_d, best_layout, trivial_flag

    If success_flag is True, then best_circ contains a DAGCircuit with
    the swap circuit, best_d contains the depth of the swap circuit, and
    best_layout contains the new positions of the data qubits after the
    swap circuit has been applied. The trivial_flag is set if the layer
    has no multi-qubit gates.
    """
    if seed is not None:
        np.random.seed(seed)
    logger.debug("layer_permutation: ----- enter -----")
    logger.debug("layer_permutation: layer_partition = %s",
                 pprint.pformat(layer_partition))
    logger.debug("layer_permutation: layout = %s",
                 pprint.pformat(layout))
    logger.debug("layer_permutation: qubit_subset = %s",
                 pprint.pformat(qubit_subset))
    logger.debug("layer_permutation: trials = %s", trials)
    rev_layout = {b: a for a, b in layout.items()}
    gates = []
    for layer in layer_partition:
        if len(layer) > 2:
            raise MapperError("Layer contains >2 qubit gates")
        elif len(layer) == 2:
            gates.append(tuple(layer))

    logger.debug("layer_permutation: gates = %s", pprint.pformat(gates))

    # Find layout maximum index
    layout_max_index = max(map(lambda x: x[1] + 1, layout.values()))

    # Can we already apply the gates?
    dist = sum([coupling.distance(layout[g[0]][1], layout[g[1]][1]) for g in gates])
    logger.debug("layer_permutation: dist = %s", dist)
    if dist == len(gates):
        logger.debug("layer_permutation: done already")
        logger.debug("layer_permutation: ----- exit -----")
        circ = DAGCircuit()
        circ.add_qreg(QuantumRegister(coupling.size(), "q"))
        circ.add_basis_element("CX", 2)
        circ.add_basis_element("cx", 2)
        circ.add_basis_element("swap", 2)
        circ.add_gate_data("cx", cx_data)
        circ.add_gate_data("swap", swap_data)
        return True, circ, 0, layout, bool(gates)

    # Begin loop over trials of randomized algorithm
    n = coupling.size()
    best_d = sys.maxsize  # initialize best depth
    best_circ = None  # initialize best swap circuit
    best_layout = None  # initialize best final layout
    for trial in range(trials):

        logger.debug("layer_permutation: trial %s", trial)
        trial_layout = layout.copy()
        rev_trial_layout = rev_layout.copy()
        # SWAP circuit constructed this trial
        trial_circ = DAGCircuit()
        trial_circ.add_qreg(QuantumRegister(coupling.size(), "q"))

        # Compute Sergey's randomized distance
        xi = {}
        for i in coupling.physical_qubits:
            xi[(QuantumRegister(coupling.size(), 'q'), i)] = {}
        for i in coupling.physical_qubits:
            i = (QuantumRegister(coupling.size(), 'q'), i)
            for j in coupling.physical_qubits:
                j = (QuantumRegister(coupling.size(), 'q'), j)
                scale = 1 + np.random.normal(0, 1 / n)
                xi[i][j] = scale * coupling.distance(i[1], j[1]) ** 2
                xi[j][i] = xi[i][j]

        # Loop over depths d up to a max depth of 2n+1
        d = 1
        # Circuit for this swap slice
        circ = DAGCircuit()
        circ.add_qreg(QuantumRegister(coupling.size(), "q"))
        circ.add_basis_element("CX", 2)
        circ.add_basis_element("cx", 2)
        circ.add_basis_element("swap", 2)
        circ.add_gate_data("cx", cx_data)
        circ.add_gate_data("swap", swap_data)

        # Identity wire-map for composing the circuits
        q = QuantumRegister(coupling.size(), 'q')
        identity_wire_map = {(q, j): (q, j) for j in range(layout_max_index)}

        while d < 2 * n + 1:
            # Set of available qubits
            qubit_set = set(qubit_subset)
            # While there are still qubits available
            while qubit_set:
                # Compute the objective function
                min_cost = sum([xi[trial_layout[g[0]]][trial_layout[g[1]]]
                                for g in gates])
                # Try to decrease objective function
                progress_made = False
                # Loop over edges of coupling graph
                for e in coupling.get_edges():
                    e = [(QuantumRegister(coupling.size(), 'q'), edge) for edge in e]
                    # Are the qubits available?
                    if e[0] in qubit_set and e[1] in qubit_set:
                        # Try this edge to reduce the cost
                        new_layout = trial_layout.copy()
                        new_layout[rev_trial_layout[e[0]]] = e[1]
                        new_layout[rev_trial_layout[e[1]]] = e[0]
                        rev_new_layout = rev_trial_layout.copy()
                        rev_new_layout[e[0]] = rev_trial_layout[e[1]]
                        rev_new_layout[e[1]] = rev_trial_layout[e[0]]
                        # Compute the objective function
                        new_cost = sum([xi[new_layout[g[0]]][new_layout[g[1]]]
                                        for g in gates])
                        # Record progress if we succceed
                        if new_cost < min_cost:
                            logger.debug("layer_permutation: progress! "
                                         "min_cost = %s", min_cost)
                            progress_made = True
                            min_cost = new_cost
                            opt_layout = new_layout
                            rev_opt_layout = rev_new_layout
                            opt_edge = e

                # Were there any good choices?
                if progress_made:
                    qubit_set.remove(opt_edge[0])
                    qubit_set.remove(opt_edge[1])
                    trial_layout = opt_layout
                    rev_trial_layout = rev_opt_layout
                    circ.apply_operation_back(
                        SwapGate((opt_edge[0][0], opt_edge[0][1]),
                                 (opt_edge[1][0], opt_edge[1][1])))
                    logger.debug("layer_permutation: chose pair %s",
                                 pprint.pformat(opt_edge))
                else:
                    break

            # We have either run out of qubits or failed to improve
            # Compute the coupling graph distance_qubits
            dist = sum([coupling.distance(trial_layout[g[0]][1],
                                          trial_layout[g[1]][1]) for g in gates])
            logger.debug("layer_permutation: dist = %s", dist)
            # If all gates can be applied now, we are finished
            # Otherwise we need to consider a deeper swap circuit
            if dist == len(gates):
                logger.debug("layer_permutation: all can be applied now")
                trial_circ.compose_back(circ, identity_wire_map)
                break

            # Increment the depth
            d += 1
            logger.debug("layer_permutation: increment depth to %s", d)

        # Either we have succeeded at some depth d < dmax or failed
        dist = sum([coupling.distance(trial_layout[g[0]][1],
                                      trial_layout[g[1]][1]) for g in gates])
        logger.debug("layer_permutation: dist = %s", dist)
        if dist == len(gates):
            if d < best_d:
                logger.debug("layer_permutation: got circuit with depth %s", d)
                best_circ = trial_circ
                best_layout = trial_layout
            best_d = min(best_d, d)

    if best_circ is None:
        logger.debug("layer_permutation: failed!")
        logger.debug("layer_permutation: ----- exit -----")
        return False, None, None, None, False

    logger.debug("layer_permutation: done")
    logger.debug("layer_permutation: ----- exit -----")
    return True, best_circ, best_d, best_layout, False
Пример #37
0
def layer_permutation(layer_partition, layout, qubit_subset, coupling, trials,
                      seed=None):
    """Find a swap circuit that implements a permutation for this layer.

    The goal is to swap qubits such that qubits in the same two-qubit gates
    are adjacent.

    Based on Sergey Bravyi's algorithm.

    The layer_partition is a list of (qu)bit lists and each qubit is a
    tuple (qreg, index).
    The layout is a dict mapping qubits in the circuit to qubits in the
    coupling graph and represents the current positions of the data.
    The qubit_subset is the subset of qubits in the coupling graph that
    we have chosen to map into.
    The coupling is a CouplingGraph.
    TRIALS is the number of attempts the randomized algorithm makes.

    Returns: success_flag, best_circ, best_d, best_layout, trivial_flag

    If success_flag is True, then best_circ contains a DAGCircuit with
    the swap circuit, best_d contains the depth of the swap circuit, and
    best_layout contains the new positions of the data qubits after the
    swap circuit has been applied. The trivial_flag is set if the layer
    has no multi-qubit gates.
    """
    if seed is not None:
        np.random.seed(seed)
    logger.debug("layer_permutation: ----- enter -----")
    logger.debug("layer_permutation: layer_partition = %s",
                 pprint.pformat(layer_partition))
    logger.debug("layer_permutation: layout = %s",
                 pprint.pformat(layout))
    logger.debug("layer_permutation: qubit_subset = %s",
                 pprint.pformat(qubit_subset))
    logger.debug("layer_permutation: trials = %s", trials)
    rev_layout = {b: a for a, b in layout.items()}
    gates = []
    for layer in layer_partition:
        if len(layer) > 2:
            raise MapperError("Layer contains >2 qubit gates")
        elif len(layer) == 2:
            gates.append(tuple(layer))

    logger.debug("layer_permutation: gates = %s", pprint.pformat(gates))

    # Find layout maximum index
    layout_max_index = max(map(lambda x: x[1]+1, layout.values()))

    # Can we already apply the gates?
    dist = sum([coupling.distance(layout[g[0]],
                                  layout[g[1]]) for g in gates])
    logger.debug("layer_permutation: dist = %s", dist)
    if dist == len(gates):
        logger.debug("layer_permutation: done already")
        logger.debug("layer_permutation: ----- exit -----")
        circ = DAGCircuit()
        circ.add_qreg('q', layout_max_index)
        circ.add_basis_element("CX", 2)
        circ.add_basis_element("cx", 2)
        circ.add_basis_element("swap", 2)
        circ.add_gate_data("cx", cx_data)
        circ.add_gate_data("swap", swap_data)
        return True, circ, 0, layout, bool(gates)

    # Begin loop over trials of randomized algorithm
    n = coupling.size()
    best_d = sys.maxsize  # initialize best depth
    best_circ = None  # initialize best swap circuit
    best_layout = None  # initialize best final layout
    for trial in range(trials):

        logger.debug("layer_permutation: trial %s", trial)
        trial_layout = layout.copy()
        rev_trial_layout = rev_layout.copy()
        # SWAP circuit constructed this trial
        trial_circ = DAGCircuit()
        trial_circ.add_qreg('q', layout_max_index)

        # Compute Sergey's randomized distance
        xi = {}
        for i in coupling.get_qubits():
            xi[i] = {}
        for i in coupling.get_qubits():
            for j in coupling.get_qubits():
                scale = 1 + np.random.normal(0, 1 / n)
                xi[i][j] = scale * coupling.distance(i, j) ** 2
                xi[j][i] = xi[i][j]

        # Loop over depths d up to a max depth of 2n+1
        d = 1
        # Circuit for this swap slice
        circ = DAGCircuit()
        circ.add_qreg('q', layout_max_index)
        circ.add_basis_element("CX", 2)
        circ.add_basis_element("cx", 2)
        circ.add_basis_element("swap", 2)
        circ.add_gate_data("cx", cx_data)
        circ.add_gate_data("swap", swap_data)

        # Identity wire-map for composing the circuits
        identity_wire_map = {('q', j): ('q', j) for j in range(layout_max_index)}

        while d < 2 * n + 1:
            # Set of available qubits
            qubit_set = set(qubit_subset)
            # While there are still qubits available
            while qubit_set:
                # Compute the objective function
                min_cost = sum([xi[trial_layout[g[0]]][trial_layout[g[1]]]
                                for g in gates])
                # Try to decrease objective function
                progress_made = False
                # Loop over edges of coupling graph
                for e in coupling.get_edges():
                    # Are the qubits available?
                    if e[0] in qubit_set and e[1] in qubit_set:
                        # Try this edge to reduce the cost
                        new_layout = trial_layout.copy()
                        new_layout[rev_trial_layout[e[0]]] = e[1]
                        new_layout[rev_trial_layout[e[1]]] = e[0]
                        rev_new_layout = rev_trial_layout.copy()
                        rev_new_layout[e[0]] = rev_trial_layout[e[1]]
                        rev_new_layout[e[1]] = rev_trial_layout[e[0]]
                        # Compute the objective function
                        new_cost = sum([xi[new_layout[g[0]]][new_layout[g[1]]]
                                        for g in gates])
                        # Record progress if we succceed
                        if new_cost < min_cost:
                            logger.debug("layer_permutation: progress! "
                                         "min_cost = %s", min_cost)
                            progress_made = True
                            min_cost = new_cost
                            opt_layout = new_layout
                            rev_opt_layout = rev_new_layout
                            opt_edge = e

                # Were there any good choices?
                if progress_made:
                    qubit_set.remove(opt_edge[0])
                    qubit_set.remove(opt_edge[1])
                    trial_layout = opt_layout
                    rev_trial_layout = rev_opt_layout
                    circ.apply_operation_back("swap", [(opt_edge[0][0],
                                                        opt_edge[0][1]),
                                                       (opt_edge[1][0],
                                                        opt_edge[1][1])])
                    logger.debug("layer_permutation: chose pair %s",
                                 pprint.pformat(opt_edge))
                else:
                    break

            # We have either run out of qubits or failed to improve
            # Compute the coupling graph distance
            dist = sum([coupling.distance(trial_layout[g[0]],
                                          trial_layout[g[1]]) for g in gates])
            logger.debug("layer_permutation: dist = %s", dist)
            # If all gates can be applied now, we are finished
            # Otherwise we need to consider a deeper swap circuit
            if dist == len(gates):
                logger.debug("layer_permutation: all can be applied now")
                trial_circ.compose_back(circ, identity_wire_map)
                break

            # Increment the depth
            d += 1
            logger.debug("layer_permutation: increment depth to %s", d)

        # Either we have succeeded at some depth d < dmax or failed
        dist = sum([coupling.distance(trial_layout[g[0]],
                                      trial_layout[g[1]]) for g in gates])
        logger.debug("layer_permutation: dist = %s", dist)
        if dist == len(gates):
            if d < best_d:
                logger.debug("layer_permutation: got circuit with depth %s", d)
                best_circ = trial_circ
                best_layout = trial_layout
            best_d = min(best_d, d)

    if best_circ is None:
        logger.debug("layer_permutation: failed!")
        logger.debug("layer_permutation: ----- exit -----")
        return False, None, None, None, False

    logger.debug("layer_permutation: done")
    logger.debug("layer_permutation: ----- exit -----")
    return True, best_circ, best_d, best_layout, False