Example #1
0
    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 = dag._copy_circuit_metadata()

        # 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
                new_dag.apply_operation_back(node.op,
                                             qargs=node.qargs,
                                             cargs=node.cargs)
        return new_dag
Example #2
0
    def create_updated_dag(self, layers, barriers):
        """
        Given a set of layers and barries, construct a new dag
        """
        new_dag = DAGCircuit()
        for qreg in self.dag.qregs.values():
            new_dag.add_qreg(qreg)
        for creg in self.dag.cregs.values():
            new_dag.add_creg(creg)
        canonical_register = new_dag.qregs['q']
        for i, layer in enumerate(layers):
            curr_barriers = barriers[i]
            for b in curr_barriers:
                current_qregs = []
                for idx in b:
                    current_qregs.append(canonical_register[idx])
                new_dag.apply_operation_back(Barrier(len(b)), current_qregs,
                                             [])
            for triplet in layer:
                gate = triplet[0]
                new_dag.apply_operation_back(gate.op, gate.qargs, gate.cargs)

        for node in self.dag.op_nodes():
            if isinstance(node.op, Measure):
                new_dag.apply_operation_back(node.op, node.qargs, node.cargs)

        return new_dag
Example #3
0
    def run(self, dag):
        """Run the BarrierBeforeFinalMeasurements pass on `dag`."""
        # 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)

        # Add a barrier across all qubits so swap mapper doesn't add a swap
        # from an unmeasured qubit after a measure.
        final_qubits = dag.qubits()

        barrier_layer.apply_operation_back(Barrier(len(final_qubits)),
                                           list(final_qubits), [])

        # Preserve order of final ops collected earlier from the original DAG.
        ordered_final_nodes = [
            node for node in dag.topological_op_nodes()
            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, final_node.qargs,
                                               final_node.cargs)

        for final_op in final_ops:
            dag.remove_op_node(final_op)

        dag.compose(barrier_layer)

        # Merge the new barrier into any other barriers
        adjacent_pass = MergeAdjacentBarriers()
        return adjacent_pass.run(dag)
Example #4
0
    def test_two_q_gates(self):
        """The method dag.two_qubit_ops() returns all 2Q gate operation nodes"""
        self.dag.apply_operation_back(HGate(), [self.qubit0], [])
        self.dag.apply_operation_back(CXGate(), [self.qubit0, self.qubit1], [])
        self.dag.apply_operation_back(Barrier(2), [self.qubit0, self.qubit1], [])
        self.dag.apply_operation_back(Reset(), [self.qubit0], [])

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

        op_node = op_nodes.pop()
        self.assertIsInstance(op_node.op, Gate)
        self.assertEqual(len(op_node.qargs), 2)
Example #5
0
    def _collect_potential_merges(dag, barriers):
        """Return the potential merges.

        Returns a dict of DAGOpNode: Barrier objects, where the barrier needs to be
        inserted where the corresponding DAGOpNode appears in the main DAG.
        """
        # if only got 1 or 0 barriers then can't merge
        if len(barriers) < 2:
            return None

        # mapping from the node that will be the main barrier to the
        # barrier object that gets built up
        node_to_barrier_qubits = {}

        # Start from the first barrier
        current_barrier = barriers[0]
        end_of_barrier = current_barrier
        current_barrier_nodes = [current_barrier]

        current_qubits = set(current_barrier.qargs)
        current_ancestors = dag.ancestors(current_barrier)
        current_descendants = dag.descendants(current_barrier)

        barrier_to_add = Barrier(len(current_qubits))

        for next_barrier in barriers[1:]:

            # Ensure barriers are adjacent before checking if they are mergeable.
            if dag._multi_graph.has_edge(end_of_barrier._node_id,
                                         next_barrier._node_id):

                # Remove all barriers that have already been included in this new barrier from the
                # set of ancestors/descendants as they will be removed from the new DAG when it is
                # created.
                next_ancestors = {
                    nd
                    for nd in dag.ancestors(next_barrier)
                    if nd not in current_barrier_nodes
                }
                next_descendants = {
                    nd
                    for nd in dag.descendants(next_barrier)
                    if nd not in current_barrier_nodes
                }
                next_qubits = set(next_barrier.qargs)

                if (not current_qubits.isdisjoint(next_qubits)
                        and current_ancestors.isdisjoint(next_descendants)
                        and current_descendants.isdisjoint(next_ancestors)):

                    # can be merged
                    current_ancestors = current_ancestors | next_ancestors
                    current_descendants = current_descendants | next_descendants
                    current_qubits = current_qubits | next_qubits

                    # update the barrier that will be added back to include this barrier
                    barrier_to_add = Barrier(len(current_qubits))

                    end_of_barrier = next_barrier
                    current_barrier_nodes.append(end_of_barrier)

                    continue

            # Fallback if barriers are not adjacent or not mergeable.

            # store the previously made barrier
            if barrier_to_add:
                node_to_barrier_qubits[end_of_barrier] = current_qubits

            # reset the properties
            current_qubits = set(next_barrier.qargs)
            current_ancestors = dag.ancestors(next_barrier)
            current_descendants = dag.descendants(next_barrier)

            barrier_to_add = Barrier(len(current_qubits))
            current_barrier_nodes = []

            end_of_barrier = next_barrier
            current_barrier_nodes.append(end_of_barrier)

        if barrier_to_add:
            node_to_barrier_qubits[end_of_barrier] = current_qubits

        return node_to_barrier_qubits
Example #6
0
    def _process_node(self, node):
        """Carry out the action associated with a node."""
        if node.type == "program":
            self._process_children(node)

        elif node.type == "qreg":
            qreg = QuantumRegister(node.index, node.name)
            self.dag.add_qreg(qreg)

        elif node.type == "creg":
            creg = ClassicalRegister(node.index, node.name)
            self.dag.add_creg(creg)

        elif node.type == "id":
            raise QiskitError("internal error: _process_node on id")

        elif node.type == "int":
            raise QiskitError("internal error: _process_node on int")

        elif node.type == "real":
            raise QiskitError("internal error: _process_node on real")

        elif node.type == "indexed_id":
            raise QiskitError("internal error: _process_node on indexed_id")

        elif node.type == "id_list":
            # We process id_list nodes when they are leaves of barriers.
            return [self._process_bit_id(node_children)
                    for node_children in node.children]

        elif node.type == "primary_list":
            # We should only be called for a barrier.
            return [self._process_bit_id(m) for m in node.children]

        elif node.type == "gate":
            self._process_gate(node)

        elif node.type == "custom_unitary":
            self._process_custom_unitary(node)

        elif node.type == "universal_unitary":
            args = self._process_node(node.children[0])
            qid = self._process_bit_id(node.children[1])
            for element in qid:
                u3_gate = U3Gate(*args, element)
                u3_gate.condition = self.condition
                self.dag.apply_operation_back(u3_gate)

        elif node.type == "cnot":
            self._process_cnot(node)

        elif node.type == "expression_list":
            return node.children

        elif node.type == "binop":
            raise QiskitError("internal error: _process_node on binop")

        elif node.type == "prefix":
            raise QiskitError("internal error: _process_node on prefix")

        elif node.type == "measure":
            self._process_measure(node)

        elif node.type == "format":
            self.version = node.version()

        elif node.type == "barrier":
            ids = self._process_node(node.children[0])
            qubits = []
            for qubit in ids:
                for j, _ in enumerate(qubit):
                    qubits.append(qubit[j])
            self.dag.apply_operation_back(Barrier(len(qubits)), qubits, [])

        elif node.type == "reset":
            id0 = self._process_bit_id(node.children[0])
            for i, _ in enumerate(id0):
                reset = Reset()
                reset.condition = self.condition
                self.dag.apply_operation_back(reset, [id0[i]], [])

        elif node.type == "if":
            self._process_if(node)

        elif node.type == "opaque":
            self._process_gate(node, opaque=True)

        elif node.type == "external":
            raise QiskitError("internal error: _process_node on external")

        else:
            raise QiskitError("internal error: undefined node type",
                              node.type, "line=%s" % node.line,
                              "file=%s" % node.file)
        return None