def run(self, dag): """run the layout method""" qubits = dag.qubits qubit_indices = {qubit: index for index, qubit in enumerate(qubits)} interactions = [] for node in dag.op_nodes(include_directives=False): len_args = len(node.qargs) if len_args == 2: interactions.append((qubit_indices[node.qargs[0]], qubit_indices[node.qargs[1]])) if len_args >= 3: raise TranspilerError( "VF2Layout only can handle 2-qubit gates or less. Node " f"{node.name} ({node}) is {len_args}-qubit") if self.strict_direction: cm_graph = self.coupling_map.graph im_graph = PyDiGraph(multigraph=False) else: cm_graph = self.coupling_map.graph.to_undirected() im_graph = PyGraph(multigraph=False) cm_nodes = list(cm_graph.node_indexes()) if self.seed != -1: random.Random(self.seed).shuffle(cm_nodes) shuffled_cm_graph = type(cm_graph)() shuffled_cm_graph.add_nodes_from(cm_nodes) new_edges = [(cm_nodes[edge[0]], cm_nodes[edge[1]]) for edge in cm_graph.edge_list()] shuffled_cm_graph.add_edges_from_no_data(new_edges) cm_nodes = [ k for k, v in sorted(enumerate(cm_nodes), key=lambda item: item[1]) ] cm_graph = shuffled_cm_graph im_graph.add_nodes_from(range(len(qubits))) im_graph.add_edges_from_no_data(interactions) mappings = vf2_mapping(cm_graph, im_graph, subgraph=True, id_order=False, induced=False) try: mapping = next(mappings) stop_reason = "solution found" layout = Layout({ qubits[im_i]: cm_nodes[cm_i] for cm_i, im_i in mapping.items() }) self.property_set["layout"] = layout for reg in dag.qregs.values(): self.property_set["layout"].add_register(reg) except StopIteration: stop_reason = "nonexistent solution" self.property_set["VF2Layout_stop_reason"] = stop_reason
def run(self, dag): """run the layout method""" if self.target is None and (self.coupling_map is None or self.properties is None): raise TranspilerError( "A target must be specified or a coupling map and properties must be provided" ) if not self.strict_direction and self.avg_error_map is None: self.avg_error_map = vf2_utils.build_average_error_map( self.target, self.properties, self.coupling_map ) result = vf2_utils.build_interaction_graph(dag, self.strict_direction) if result is None: self.property_set["VF2PostLayout_stop_reason"] = VF2PostLayoutStopReason.MORE_THAN_2Q return im_graph, im_graph_node_map, reverse_im_graph_node_map = result if self.target is not None: if self.strict_direction: cm_graph = PyDiGraph(multigraph=False) else: cm_graph = PyGraph(multigraph=False) cm_graph.add_nodes_from( [self.target.operation_names_for_qargs((i,)) for i in range(self.target.num_qubits)] ) for qargs in self.target.qargs: len_args = len(qargs) # If qargs == 1 we already populated it and if qargs > 2 there are no instructions # using those in the circuit because we'd have already returned by this point if len_args == 2: cm_graph.add_edge( qargs[0], qargs[1], self.target.operation_names_for_qargs(qargs) ) cm_nodes = list(cm_graph.node_indexes()) else: cm_graph, cm_nodes = vf2_utils.shuffle_coupling_graph( self.coupling_map, self.seed, self.strict_direction ) logger.debug("Running VF2 to find post transpile mappings") if self.target and self.strict_direction: mappings = vf2_mapping( cm_graph, im_graph, node_matcher=_target_match, edge_matcher=_target_match, subgraph=True, id_order=False, induced=False, call_limit=self.call_limit, ) else: mappings = vf2_mapping( cm_graph, im_graph, subgraph=True, id_order=False, induced=False, call_limit=self.call_limit, ) chosen_layout = None initial_layout = Layout(dict(enumerate(dag.qubits))) try: if self.strict_direction: chosen_layout_score = self._score_layout( initial_layout, im_graph_node_map, reverse_im_graph_node_map, im_graph ) else: chosen_layout_score = vf2_utils.score_layout( self.avg_error_map, initial_layout, im_graph_node_map, reverse_im_graph_node_map, im_graph, self.strict_direction, ) # Circuit not in basis so we have nothing to compare against return here except KeyError: self.property_set[ "VF2PostLayout_stop_reason" ] = VF2PostLayoutStopReason.NO_SOLUTION_FOUND return logger.debug("Initial layout has score %s", chosen_layout_score) start_time = time.time() trials = 0 for mapping in mappings: trials += 1 logger.debug("Running trial: %s", trials) stop_reason = VF2PostLayoutStopReason.SOLUTION_FOUND layout = Layout( {reverse_im_graph_node_map[im_i]: cm_nodes[cm_i] for cm_i, im_i in mapping.items()} ) if self.strict_direction: layout_score = self._score_layout( layout, im_graph_node_map, reverse_im_graph_node_map, im_graph ) else: layout_score = vf2_utils.score_layout( self.avg_error_map, layout, im_graph_node_map, reverse_im_graph_node_map, im_graph, self.strict_direction, ) logger.debug("Trial %s has score %s", trials, layout_score) if layout_score < chosen_layout_score: logger.debug( "Found layout %s has a lower score (%s) than previous best %s (%s)", layout, layout_score, chosen_layout, chosen_layout_score, ) chosen_layout = layout chosen_layout_score = layout_score elapsed_time = time.time() - start_time if self.time_limit is not None and elapsed_time >= self.time_limit: logger.debug( "VFPostLayout has taken %s which exceeds configured max time: %s", elapsed_time, self.time_limit, ) break if chosen_layout is None: stop_reason = VF2PostLayoutStopReason.NO_SOLUTION_FOUND else: existing_layout = self.property_set["layout"] # If any ancillas in initial layout map them back to the final layout output if existing_layout is not None and len(existing_layout) > len(chosen_layout): virtual_bits = chosen_layout.get_virtual_bits() used_bits = set(virtual_bits.values()) num_qubits = len(cm_graph) for bit in dag.qubits: if len(chosen_layout) == len(existing_layout): break if bit not in virtual_bits: for i in range(num_qubits): if i not in used_bits: used_bits.add(i) chosen_layout.add(bit, i) break self.property_set["post_layout"] = chosen_layout self.property_set["VF2PostLayout_stop_reason"] = stop_reason
def run(self, dag): """run the layout method""" if self.coupling_map is None: raise TranspilerError("coupling_map or target must be specified.") qubits = dag.qubits qubit_indices = {qubit: index for index, qubit in enumerate(qubits)} interactions = [] for node in dag.op_nodes(include_directives=False): len_args = len(node.qargs) if len_args == 2: interactions.append((qubit_indices[node.qargs[0]], qubit_indices[node.qargs[1]])) if len_args >= 3: self.property_set[ "VF2Layout_stop_reason"] = VF2LayoutStopReason.MORE_THAN_2Q return if self.strict_direction: cm_graph = self.coupling_map.graph im_graph = PyDiGraph(multigraph=False) else: cm_graph = self.coupling_map.graph.to_undirected() im_graph = PyGraph(multigraph=False) cm_nodes = list(cm_graph.node_indexes()) if self.seed != -1: random.Random(self.seed).shuffle(cm_nodes) shuffled_cm_graph = type(cm_graph)() shuffled_cm_graph.add_nodes_from(cm_nodes) new_edges = [(cm_nodes[edge[0]], cm_nodes[edge[1]]) for edge in cm_graph.edge_list()] shuffled_cm_graph.add_edges_from_no_data(new_edges) cm_nodes = [ k for k, v in sorted(enumerate(cm_nodes), key=lambda item: item[1]) ] cm_graph = shuffled_cm_graph im_graph.add_nodes_from(range(len(qubits))) im_graph.add_edges_from_no_data(interactions) # To avoid trying to over optimize the result by default limit the number # of trials based on the size of the graphs. For circuits with simple layouts # like an all 1q circuit we don't want to sit forever trying every possible # mapping in the search space if self.max_trials is None: im_graph_edge_count = len(im_graph.edge_list()) cm_graph_edge_count = len(cm_graph.edge_list()) self.max_trials = max(im_graph_edge_count, cm_graph_edge_count) + 15 logger.debug("Running VF2 to find mappings") mappings = vf2_mapping( cm_graph, im_graph, subgraph=True, id_order=False, induced=False, call_limit=self.call_limit, ) chosen_layout = None chosen_layout_score = None start_time = time.time() trials = 0 for mapping in mappings: trials += 1 logger.debug("Running trial: %s", trials) stop_reason = VF2LayoutStopReason.SOLUTION_FOUND layout = Layout({ qubits[im_i]: cm_nodes[cm_i] for cm_i, im_i in mapping.items() }) # If the graphs have the same number of nodes we don't need to score or do multiple # trials as the score heuristic currently doesn't weigh nodes based on gates on a # qubit so the scores will always all be the same if len(cm_graph) == len(im_graph): chosen_layout = layout break layout_score = self._score_layout(layout) logger.debug("Trial %s has score %s", trials, layout_score) if chosen_layout is None: chosen_layout = layout chosen_layout_score = layout_score elif layout_score < chosen_layout_score: logger.debug( "Found layout %s has a lower score (%s) than previous best %s (%s)", layout, layout_score, chosen_layout, chosen_layout_score, ) chosen_layout = layout chosen_layout_score = layout_score if self.max_trials > 0 and trials >= self.max_trials: logger.debug("Trial %s is >= configured max trials %s", trials, self.max_trials) break elapsed_time = time.time() - start_time if self.time_limit is not None and elapsed_time >= self.time_limit: logger.debug( "VF2Layout has taken %s which exceeds configured max time: %s", elapsed_time, self.time_limit, ) break if chosen_layout is None: stop_reason = VF2LayoutStopReason.NO_SOLUTION_FOUND else: self.property_set["layout"] = chosen_layout for reg in dag.qregs.values(): self.property_set["layout"].add_register(reg) self.property_set["VF2Layout_stop_reason"] = stop_reason
def run(self, dag): """run the layout method""" if self.coupling_map is None: raise TranspilerError("coupling_map or target must be specified.") if self.avg_error_map is None: self.avg_error_map = vf2_utils.build_average_error_map( self.target, self.properties, self.coupling_map) result = vf2_utils.build_interaction_graph(dag, self.strict_direction) if result is None: self.property_set[ "VF2Layout_stop_reason"] = VF2LayoutStopReason.MORE_THAN_2Q return im_graph, im_graph_node_map, reverse_im_graph_node_map = result cm_graph, cm_nodes = vf2_utils.shuffle_coupling_graph( self.coupling_map, self.seed, self.strict_direction) # To avoid trying to over optimize the result by default limit the number # of trials based on the size of the graphs. For circuits with simple layouts # like an all 1q circuit we don't want to sit forever trying every possible # mapping in the search space if no other limits are set if self.max_trials is None and self.call_limit is None and self.time_limit is None: im_graph_edge_count = len(im_graph.edge_list()) cm_graph_edge_count = len(self.coupling_map.graph.edge_list()) self.max_trials = max(im_graph_edge_count, cm_graph_edge_count) + 15 logger.debug("Running VF2 to find mappings") mappings = vf2_mapping( cm_graph, im_graph, subgraph=True, id_order=False, induced=False, call_limit=self.call_limit, ) chosen_layout = None chosen_layout_score = None start_time = time.time() trials = 0 for mapping in mappings: trials += 1 logger.debug("Running trial: %s", trials) stop_reason = VF2LayoutStopReason.SOLUTION_FOUND layout = Layout({ reverse_im_graph_node_map[im_i]: cm_nodes[cm_i] for cm_i, im_i in mapping.items() }) # If the graphs have the same number of nodes we don't need to score or do multiple # trials as the score heuristic currently doesn't weigh nodes based on gates on a # qubit so the scores will always all be the same if len(cm_graph) == len(im_graph): chosen_layout = layout break layout_score = vf2_utils.score_layout( self.avg_error_map, layout, im_graph_node_map, reverse_im_graph_node_map, im_graph, self.strict_direction, ) logger.debug("Trial %s has score %s", trials, layout_score) if chosen_layout is None: chosen_layout = layout chosen_layout_score = layout_score elif layout_score < chosen_layout_score: logger.debug( "Found layout %s has a lower score (%s) than previous best %s (%s)", layout, layout_score, chosen_layout, chosen_layout_score, ) chosen_layout = layout chosen_layout_score = layout_score if self.max_trials is not None and self.max_trials > 0 and trials >= self.max_trials: logger.debug("Trial %s is >= configured max trials %s", trials, self.max_trials) break elapsed_time = time.time() - start_time if self.time_limit is not None and elapsed_time >= self.time_limit: logger.debug( "VF2Layout has taken %s which exceeds configured max time: %s", elapsed_time, self.time_limit, ) break if chosen_layout is None: stop_reason = VF2LayoutStopReason.NO_SOLUTION_FOUND else: self.property_set["layout"] = chosen_layout for reg in dag.qregs.values(): self.property_set["layout"].add_register(reg) self.property_set["VF2Layout_stop_reason"] = stop_reason