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
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
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)
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)
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
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