def constrain_gate_input_adjacency(model_input: ModelInput, model_variables: ModelVariables) -> Goal: goal = Goal() for node in topological_gate_nodes(model_input.circuit): gate_qubit_mapping = model_variables['gate_qubit_mapping'][node] if len(node.qargs) == 1: pass elif len(node.qargs) == 2: adjacency_constraints = [] for physical_qubit0, virtual_qubit0 in gate_qubit_mapping.items(): for physical_qubit1, virtual_qubit1 in gate_qubit_mapping.items( ): if physical_qubit0 != physical_qubit1: if model_input.coupling_graph.distance( physical_qubit0, physical_qubit1) == 1: adjacency_constraints.append( And(virtual_qubit0 == node.qargs[0].index, virtual_qubit1 == node.qargs[1].index)) else: goal.append( Not( And(virtual_qubit0 == node.qargs[0].index, virtual_qubit1 == node.qargs[1].index))) goal.add(Or(adjacency_constraints)) else: # TODO: Support topologies which accept 3+ qubit gate implementations. raise TranspilerError('3+ qubit gates are not permitted.') return goal
def constrain_gate_duration(model_input: ModelInput, model_variables: ModelVariables) -> Goal: goal = Goal() gate_durations = model_variables['gate_duration'] swap_gate_durations = model_variables['swap_gate_duration'] gate_start_times = model_variables['gate_start_time'] gate_qubit_mappings = model_variables['gate_qubit_mapping'] num_physical_qubits = model_input.coupling_graph.size() for node in topological_gate_nodes(model_input.circuit): gate_duration = gate_durations[node] swap_gate_duration = swap_gate_durations[node] gate_start_time = gate_start_times[node] gate_qubit_mapping = gate_qubit_mappings[node] # Constrain all of the gate durations to have a threshold at least as high as the swap gate durations. for physical_qubit in range(num_physical_qubits): goal.add(gate_duration[physical_qubit] >= swap_gate_duration[physical_qubit]) if len(node.qargs) == 1: for physical_qubit, virtual_qubit in gate_qubit_mapping.items(): goal.add( Implies( virtual_qubit == node.qargs[0].index, gate_duration[physical_qubit] >= 1 + swap_gate_duration[physical_qubit])) elif len(node.qargs) == 2: for physical_qubit0, virtual_qubit0 in gate_qubit_mapping.items(): for physical_qubit1, virtual_qubit1 in gate_qubit_mapping.items( ): if physical_qubit0 != physical_qubit1: swap_gate_qubit_duration0 = swap_gate_duration[ physical_qubit0] swap_gate_qubit_duration1 = swap_gate_duration[ physical_qubit1] goal.add( Implies( And(virtual_qubit0 == node.qargs[0].index, virtual_qubit1 == node.qargs[1].index), And( And( gate_duration[physical_qubit0] >= 1 + swap_gate_qubit_duration0, gate_duration[physical_qubit1] >= 1 + swap_gate_qubit_duration1, gate_duration[physical_qubit0] >= 1 + swap_gate_qubit_duration1, gate_duration[physical_qubit1] >= 1 + swap_gate_qubit_duration0), And(gate_start_time[physical_qubit0] + gate_duration[physical_qubit0] == gate_start_time[physical_qubit1] + gate_duration[physical_qubit1])))) # TODO fix, should calc distance to correct mapping? else: # TODO: Support topologies which accept 3+ qubit gate implementations. raise TranspilerError('3+ qubit gates are not permitted.') return goal
def __init__(self, coupling_map, backend_prop=None, readout=False): """ChainLayout initializer. Args: coupling_map (CouplingMap or list): directed graph representing a coupling chain. backend_prop (BackendProperties): backend properties object. Raises: TranspilerError: if invalid options. """ super().__init__() if isinstance(coupling_map, list): self.coupling_map = CouplingMap(coupling_map) elif isinstance(coupling_map, CouplingMap): self.coupling_map = coupling_map else: raise TranspilerError('Coupling map of type %s is not valid' % coupling_map.__class__) self.coupling_graph = self.coupling_map.graph.to_undirected() self.backend_prop = backend_prop # collect cx reliability data if self.backend_prop is not None: if readout: self.readout_reliability = dict() i = 0 for q in backend_prop.qubits: for info in q: if info.name == 'readout_error': self.readout_reliability[i] = 1.0 - info.value i += 1 self.cx_reliab = dict() for ginfo in self.backend_prop.gates: if ginfo.gate == 'cx': for item in ginfo.parameters: if item.name == 'gate_error': g_reliab = max(1.0 - item.value, 10**(-10)) break else: g_reliab = 1.0 self.cx_reliab[(ginfo.qubits[0], ginfo.qubits[1])] = g_reliab self.cx_reliab[(ginfo.qubits[1], ginfo.qubits[0])] = g_reliab if readout: qubits_readout_reliab = self.readout_reliability[ ginfo.qubits[0]] * self.readout_reliability[ ginfo.qubits[1]] self.cx_reliab[( ginfo.qubits[0], ginfo.qubits[1])] *= qubits_readout_reliab self.cx_reliab[( ginfo.qubits[1], ginfo.qubits[0])] *= qubits_readout_reliab
def __init__(self): """TransformCxCascade initializer. Raises: TranspilerError: if run after the layout has been set. """ super().__init__() if self.property_set['layout']: raise TranspilerError('TransformCxCascade pass must be run before any layout has been set.') self.requires.append(Unroller(['u1', 'u2', 'u3', 'cx', 'id'])) self._num_qubits = None self._wires_to_id = {} self._id_to_wires = {} self._layers = None self._extra_layers = None self._skip = []
def _check_edges(self, node: DAGOpNode, swap_strategy: SwapStrategy): """Check if the swap strategy can create the required connectivity. Args: node: The dag node for which to check if the swap strategy provides enough connectivity. swap_strategy: The swap strategy that is being used. Raises: TranspilerError: If there is an edge that the swap strategy cannot accommodate and if the pass has been configured to raise on such issues. """ required_edges = set() for sub_node in node.op: edge = (self._bit_indices[sub_node.qargs[0]], self._bit_indices[sub_node.qargs[1]]) required_edges.add(edge) # Check that the swap strategy supports all required edges if not required_edges.issubset(swap_strategy.possible_edges): raise TranspilerError( f"{swap_strategy} cannot implement all edges in {required_edges}." )
def run(self, dag): """Sets the layout property set. Args: dag (DAGCircuit): DAG to find layout for. Raises: TranspilerError: if dag wider than the coupling_map. """ num_dag_qubits = sum([qreg.size for qreg in dag.qregs.values()]) if num_dag_qubits > self.coupling_map.size(): raise TranspilerError('Number of qubits greater than device.') # get the chain of qubits as list of integers chain = self.chain(num_dag_qubits) logger.info('Chain: %s' % str(chain)) layout = Layout() chain_iter = 0 # produce a layout from the chain for qreg in dag.qregs.values(): for i in range(qreg.size): layout[qreg[i]] = chain[chain_iter] chain_iter += 1 self.property_set['layout'] = layout logger.info(self.property_set['layout'])
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]) circ.add_basis_element("swap", 2) 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 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 xi = {} # pylint: disable=invalid-name for i in range(num_qubits): xi[i] = {} for i in range(num_qubits): for j in range(i, num_qubits): scale = 1 + np.random.normal(0, 1 / num_qubits) xi[i][j] = scale * coupling.distance(i, j) ** 2 xi[j][i] = xi[i][j] 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]) slice_circuit.add_basis_element("swap", 2) # 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 for edge in coupling.get_edges(): qubits = [trial_layout[e] for e in edge] # Are the qubits available? if qubits[0] in qubit_set and qubits[1] in qubit_set: # Try this edge to reduce the cost new_layout = trial_layout.copy() 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 = qubits # Were there any good swap choices? if cost_reduced: qubit_set.remove(optimal_edge[0]) qubit_set.remove(optimal_edge[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
def level_3_pass_manager(pass_manager_config: PassManagerConfig) -> PassManager: """Level 3 pass manager: heavy optimization by noise adaptive qubit mapping and gate cancellation using commutativity rules and unitary synthesis. This pass manager applies the user-given initial layout. If none is given, a search for a perfect layout (i.e. one that satisfies all 2-qubit interactions) is conducted. If no such layout is found, and device calibration information is available, the circuit is mapped to the qubits with best readouts and to CX gates with highest fidelity. The pass manager then transforms the circuit to match the coupling constraints. It is then unrolled to the basis, and any flipped cx directions are fixed. Finally, optimizations in the form of commutative gate cancellation, resynthesis of two-qubit unitary blocks, and redundant reset removal are performed. Note: In simulators where ``coupling_map=None``, only the unrolling and optimization stages are done. Args: pass_manager_config: configuration of the pass manager. Returns: a level 3 pass manager. Raises: TranspilerError: if the passmanager config is invalid. """ basis_gates = pass_manager_config.basis_gates coupling_map = pass_manager_config.coupling_map initial_layout = pass_manager_config.initial_layout layout_method = pass_manager_config.layout_method or 'dense' routing_method = pass_manager_config.routing_method or 'stochastic' seed_transpiler = pass_manager_config.seed_transpiler backend_properties = pass_manager_config.backend_properties # 1. Unroll to the basis first, to prepare for noise-adaptive layout _unroll = Unroller(basis_gates) # 2. Layout on good qubits if calibration info available, otherwise on dense links _given_layout = SetLayout(initial_layout) def _choose_layout_condition(property_set): return not property_set['layout'] _choose_layout_1 = CSPLayout(coupling_map, call_limit=10000, time_limit=60) if layout_method == 'trivial': _choose_layout_2 = TrivialLayout(coupling_map) elif layout_method == 'dense': _choose_layout_2 = DenseLayout(coupling_map, backend_properties) elif layout_method == 'noise_adaptive': _choose_layout_2 = NoiseAdaptiveLayout(backend_properties) else: raise TranspilerError("Invalid layout method %s." % layout_method) # 3. Extend dag/layout with ancillas using the full coupling map _embed = [FullAncillaAllocation(coupling_map), EnlargeWithAncilla(), ApplyLayout()] # 4. Unroll to 1q or 2q gates, swap to fit the coupling map _swap_check = CheckMap(coupling_map) def _swap_condition(property_set): return not property_set['is_swap_mapped'] _swap = [BarrierBeforeFinalMeasurements(), Unroll3qOrMore()] if routing_method == 'basic': _swap += [BasicSwap(coupling_map)] elif routing_method == 'stochastic': _swap += [StochasticSwap(coupling_map, trials=200, seed=seed_transpiler)] elif routing_method == 'lookahead': _swap += [LookaheadSwap(coupling_map, search_depth=5, search_width=6)] else: raise TranspilerError("Invalid routing method %s." % routing_method) # 5. 1q rotation merge and commutative cancellation iteratively until no more change in depth _depth_check = [Depth(), FixedPoint('depth')] def _opt_control(property_set): return not property_set['depth_fixed_point'] _opt = [RemoveResetInZeroState(), Collect2qBlocks(), ConsolidateBlocks(), Unroller(basis_gates), # unroll unitaries Optimize1qGates(), CommutativeCancellation(), OptimizeSwapBeforeMeasure(), RemoveDiagonalGatesBeforeMeasure()] # 6. Fix any CX direction mismatch _direction_check = [CheckCXDirection(coupling_map)] def _direction_condition(property_set): return not property_set['is_direction_mapped'] _direction = [CXDirection(coupling_map)] # Build pass manager pm3 = PassManager() pm3.append(_unroll) if coupling_map: pm3.append(_given_layout) pm3.append(_choose_layout_1, condition=_choose_layout_condition) pm3.append(_choose_layout_2, condition=_choose_layout_condition) pm3.append(_embed) pm3.append(_swap_check) pm3.append(_swap, condition=_swap_condition) pm3.append(_depth_check + _opt, do_while=_opt_control) if coupling_map and not coupling_map.is_symmetric: pm3.append(_direction_check) pm3.append(_direction, condition=_direction_condition) return pm3
def level_1_pass_manager( pass_manager_config: PassManagerConfig) -> PassManager: """Level 1 pass manager: light optimization by simple adjacent gate collapsing. This pass manager applies the user-given initial layout. If none is given, and a trivial layout (i-th virtual -> i-th physical) makes the circuit fit the coupling map, that is used. Otherwise, the circuit is mapped to the most densely connected coupling subgraph, and swaps are inserted to map. Any unused physical qubit is allocated as ancilla space. The pass manager then unrolls the circuit to the desired basis, and transforms the circuit to match the coupling map. Finally, optimizations in the form of adjacent gate collapse and redundant reset removal are performed. Note: In simulators where ``coupling_map=None``, only the unrolling and optimization stages are done. Args: pass_manager_config: configuration of the pass manager. Returns: a level 1 pass manager. Raises: TranspilerError: if the passmanager config is invalid. """ basis_gates = pass_manager_config.basis_gates inst_map = pass_manager_config.inst_map coupling_map = pass_manager_config.coupling_map initial_layout = pass_manager_config.initial_layout layout_method = pass_manager_config.layout_method or "dense" routing_method = pass_manager_config.routing_method or "stochastic" translation_method = pass_manager_config.translation_method or "translator" scheduling_method = pass_manager_config.scheduling_method instruction_durations = pass_manager_config.instruction_durations seed_transpiler = pass_manager_config.seed_transpiler backend_properties = pass_manager_config.backend_properties approximation_degree = pass_manager_config.approximation_degree unitary_synthesis_method = pass_manager_config.unitary_synthesis_method unitary_synthesis_plugin_config = pass_manager_config.unitary_synthesis_plugin_config timing_constraints = pass_manager_config.timing_constraints or TimingConstraints( ) target = pass_manager_config.target # 1. Use trivial layout if no layout given _given_layout = SetLayout(initial_layout) _choose_layout_and_score = [ TrivialLayout(coupling_map), Layout2qDistance(coupling_map, property_name="trivial_layout_score"), ] def _choose_layout_condition(property_set): return not property_set["layout"] # 2. Decompose so only 1-qubit and 2-qubit gates remain _unroll3q = [ # Use unitary synthesis for basis aware decomposition of UnitaryGates UnitarySynthesis( basis_gates, approximation_degree=approximation_degree, method=unitary_synthesis_method, min_qubits=3, plugin_config=unitary_synthesis_plugin_config, ), Unroll3qOrMore(), ] # 3. Use a better layout on densely connected qubits, if circuit needs swaps if layout_method == "trivial": _improve_layout = TrivialLayout(coupling_map) elif layout_method == "dense": _improve_layout = DenseLayout(coupling_map, backend_properties) elif layout_method == "noise_adaptive": _improve_layout = NoiseAdaptiveLayout(backend_properties) elif layout_method == "sabre": _improve_layout = SabreLayout(coupling_map, max_iterations=2, seed=seed_transpiler) else: raise TranspilerError("Invalid layout method %s." % layout_method) def _not_perfect_yet(property_set): return (property_set["trivial_layout_score"] is not None and property_set["trivial_layout_score"] != 0) # 4. Extend dag/layout with ancillas using the full coupling map _embed = [ FullAncillaAllocation(coupling_map), EnlargeWithAncilla(), ApplyLayout() ] # 5. Swap to fit the coupling map _swap_check = CheckMap(coupling_map) def _swap_condition(property_set): return not property_set["is_swap_mapped"] _swap = [BarrierBeforeFinalMeasurements()] if routing_method == "basic": _swap += [BasicSwap(coupling_map)] elif routing_method == "stochastic": _swap += [ StochasticSwap(coupling_map, trials=20, seed=seed_transpiler) ] elif routing_method == "lookahead": _swap += [LookaheadSwap(coupling_map, search_depth=4, search_width=4)] elif routing_method == "sabre": _swap += [ SabreSwap(coupling_map, heuristic="lookahead", seed=seed_transpiler) ] elif routing_method == "none": _swap += [ Error( msg= ("No routing method selected, but circuit is not routed to device. " "CheckMap Error: {check_map_msg}"), action="raise", ) ] else: raise TranspilerError("Invalid routing method %s." % routing_method) # 6. Unroll to the basis if translation_method == "unroller": _unroll = [Unroller(basis_gates)] elif translation_method == "translator": from qiskit.circuit.equivalence_library import SessionEquivalenceLibrary as sel _unroll = [ # Use unitary synthesis for basis aware decomposition of UnitaryGates before # custom unrolling UnitarySynthesis( basis_gates, approximation_degree=approximation_degree, coupling_map=coupling_map, method=unitary_synthesis_method, backend_props=backend_properties, plugin_config=unitary_synthesis_plugin_config, ), UnrollCustomDefinitions(sel, basis_gates), BasisTranslator(sel, basis_gates, target), ] elif translation_method == "synthesis": _unroll = [ # Use unitary synthesis for basis aware decomposition of UnitaryGates before # collection UnitarySynthesis( basis_gates, approximation_degree=approximation_degree, coupling_map=coupling_map, method=unitary_synthesis_method, backend_props=backend_properties, min_qubits=3, ), Unroll3qOrMore(), Collect2qBlocks(), ConsolidateBlocks(basis_gates=basis_gates), UnitarySynthesis( basis_gates, approximation_degree=approximation_degree, coupling_map=coupling_map, method=unitary_synthesis_method, backend_props=backend_properties, plugin_config=unitary_synthesis_plugin_config, ), ] else: raise TranspilerError("Invalid translation method %s." % translation_method) # 7. Fix any bad CX directions _direction_check = [CheckGateDirection(coupling_map, target)] def _direction_condition(property_set): return not property_set["is_direction_mapped"] _direction = [GateDirection(coupling_map, target)] # 8. Remove zero-state reset _reset = RemoveResetInZeroState() # 9. Merge 1q rotations and cancel CNOT gates iteratively until no more change in depth _depth_check = [Depth(), FixedPoint("depth")] def _opt_control(property_set): return not property_set["depth_fixed_point"] _opt = [Optimize1qGatesDecomposition(basis_gates), CXCancellation()] # 10. Unify all durations (either SI, or convert to dt if known) # Schedule the circuit only when scheduling_method is supplied _time_unit_setup = [ContainsInstruction("delay")] _time_unit_conversion = [TimeUnitConversion(instruction_durations)] def _contains_delay(property_set): return property_set["contains_delay"] _scheduling = [] if scheduling_method: _scheduling += _time_unit_conversion if scheduling_method in {"alap", "as_late_as_possible"}: _scheduling += [ALAPSchedule(instruction_durations)] elif scheduling_method in {"asap", "as_soon_as_possible"}: _scheduling += [ASAPSchedule(instruction_durations)] else: raise TranspilerError("Invalid scheduling method %s." % scheduling_method) # 11. Call measure alignment. Should come after scheduling. if (timing_constraints.granularity != 1 or timing_constraints.min_length != 1 or timing_constraints.acquire_alignment != 1): _alignments = [ ValidatePulseGates(granularity=timing_constraints.granularity, min_length=timing_constraints.min_length), AlignMeasures(alignment=timing_constraints.acquire_alignment), ] else: _alignments = [] # Build pass manager pm1 = PassManager() if coupling_map or initial_layout: pm1.append(_given_layout) pm1.append(_unroll3q) pm1.append(_choose_layout_and_score, condition=_choose_layout_condition) pm1.append(_improve_layout, condition=_not_perfect_yet) pm1.append(_embed) pm1.append(_swap_check) pm1.append(_swap, condition=_swap_condition) pm1.append(_unroll) if (coupling_map and not coupling_map.is_symmetric) or ( target is not None and target.get_non_global_operation_names(strict_direction=True)): pm1.append(_direction_check) pm1.append(_direction, condition=_direction_condition) pm1.append(_reset) pm1.append(_depth_check + _opt + _unroll, do_while=_opt_control) if inst_map and inst_map.has_custom_gate(): pm1.append(PulseGates(inst_map=inst_map)) if scheduling_method: pm1.append(_scheduling) elif instruction_durations: pm1.append(_time_unit_setup) pm1.append(_time_unit_conversion, condition=_contains_delay) pm1.append(_alignments) return pm1
def level_1_pass_manager( pass_manager_config: PassManagerConfig) -> PassManager: """Level 1 pass manager: light optimization by simple adjacent gate collapsing. This pass manager applies the user-given initial layout. If none is given, and a trivial layout (i-th virtual -> i-th physical) makes the circuit fit the coupling map, that is used. Otherwise, the circuit is mapped to the most densely connected coupling subgraph, and swaps are inserted to map. Any unused physical qubit is allocated as ancilla space. The pass manager then unrolls the circuit to the desired basis, and transforms the circuit to match the coupling map. Finally, optimizations in the form of adjacent gate collapse and redundant reset removal are performed. Note: In simulators where ``coupling_map=None``, only the unrolling and optimization stages are done. Args: pass_manager_config: configuration of the pass manager. Returns: a level 1 pass manager. Raises: TranspilerError: if the passmanager config is invalid. """ basis_gates = pass_manager_config.basis_gates inst_map = pass_manager_config.inst_map coupling_map = pass_manager_config.coupling_map initial_layout = pass_manager_config.initial_layout layout_method = pass_manager_config.layout_method or "dense" routing_method = pass_manager_config.routing_method or "stochastic" translation_method = pass_manager_config.translation_method or "translator" scheduling_method = pass_manager_config.scheduling_method instruction_durations = pass_manager_config.instruction_durations seed_transpiler = pass_manager_config.seed_transpiler backend_properties = pass_manager_config.backend_properties approximation_degree = pass_manager_config.approximation_degree unitary_synthesis_method = pass_manager_config.unitary_synthesis_method unitary_synthesis_plugin_config = pass_manager_config.unitary_synthesis_plugin_config timing_constraints = pass_manager_config.timing_constraints or TimingConstraints( ) target = pass_manager_config.target # 1. Use trivial layout if no layout given if that isn't perfect use vf2 layout _given_layout = SetLayout(initial_layout) def _choose_layout_condition(property_set): return not property_set["layout"] def _trivial_not_perfect(property_set): # Verify that a trivial layout is perfect. If trivial_layout_score > 0 # the layout is not perfect. The layout is unconditionally set by trivial # layout so we need to clear it before contuing. if (property_set["trivial_layout_score"] is not None and property_set["trivial_layout_score"] != 0): return True return False def _vf2_match_not_found(property_set): # If a layout hasn't been set by the time we run vf2 layout we need to # run layout if property_set["layout"] is None: return True # if VF2 layout stopped for any reason other than solution found we need # to run layout since VF2 didn't converge. if (property_set["VF2Layout_stop_reason"] is not None and property_set["VF2Layout_stop_reason"] is not VF2LayoutStopReason.SOLUTION_FOUND): return True return False _choose_layout_0 = ([] if pass_manager_config.layout_method else [ TrivialLayout(coupling_map), Layout2qDistance(coupling_map, property_name="trivial_layout_score"), ]) _choose_layout_1 = ([] if pass_manager_config.layout_method else VF2Layout( coupling_map, seed=seed_transpiler, call_limit=int(5e4), # Set call limit to ~100ms with retworkx 0.10.2 time_limit=0.1, properties=backend_properties, target=target, )) # 2. Decompose so only 1-qubit and 2-qubit gates remain _unroll3q = [ # Use unitary synthesis for basis aware decomposition of UnitaryGates UnitarySynthesis( basis_gates, approximation_degree=approximation_degree, method=unitary_synthesis_method, min_qubits=3, plugin_config=unitary_synthesis_plugin_config, target=target, ), Unroll3qOrMore(), ] # 3. Use a better layout on densely connected qubits, if circuit needs swaps if layout_method == "trivial": _improve_layout = TrivialLayout(coupling_map) elif layout_method == "dense": _improve_layout = DenseLayout(coupling_map, backend_properties, target=target) elif layout_method == "noise_adaptive": _improve_layout = NoiseAdaptiveLayout(backend_properties) elif layout_method == "sabre": _improve_layout = SabreLayout(coupling_map, max_iterations=2, seed=seed_transpiler) else: raise TranspilerError("Invalid layout method %s." % layout_method) # 4. Extend dag/layout with ancillas using the full coupling map _embed = [ FullAncillaAllocation(coupling_map), EnlargeWithAncilla(), ApplyLayout() ] # 5. Swap to fit the coupling map _swap_check = CheckMap(coupling_map) def _swap_condition(property_set): return not property_set["is_swap_mapped"] _swap = [BarrierBeforeFinalMeasurements()] if routing_method == "basic": _swap += [BasicSwap(coupling_map)] elif routing_method == "stochastic": _swap += [ StochasticSwap(coupling_map, trials=20, seed=seed_transpiler) ] elif routing_method == "lookahead": _swap += [LookaheadSwap(coupling_map, search_depth=4, search_width=4)] elif routing_method == "sabre": _swap += [ SabreSwap(coupling_map, heuristic="lookahead", seed=seed_transpiler) ] elif routing_method == "none": _swap += [ Error( msg= ("No routing method selected, but circuit is not routed to device. " "CheckMap Error: {check_map_msg}"), action="raise", ) ] else: raise TranspilerError("Invalid routing method %s." % routing_method) # 6. Unroll to the basis if translation_method == "unroller": _unroll = [Unroller(basis_gates)] elif translation_method == "translator": from qiskit.circuit.equivalence_library import SessionEquivalenceLibrary as sel _unroll = [ # Use unitary synthesis for basis aware decomposition of UnitaryGates before # custom unrolling UnitarySynthesis( basis_gates, approximation_degree=approximation_degree, coupling_map=coupling_map, method=unitary_synthesis_method, backend_props=backend_properties, plugin_config=unitary_synthesis_plugin_config, target=target, ), UnrollCustomDefinitions(sel, basis_gates), BasisTranslator(sel, basis_gates, target), ] elif translation_method == "synthesis": _unroll = [ # Use unitary synthesis for basis aware decomposition of UnitaryGates before # collection UnitarySynthesis( basis_gates, approximation_degree=approximation_degree, coupling_map=coupling_map, method=unitary_synthesis_method, backend_props=backend_properties, min_qubits=3, target=target, ), Unroll3qOrMore(), Collect2qBlocks(), ConsolidateBlocks(basis_gates=basis_gates, target=target), UnitarySynthesis( basis_gates, approximation_degree=approximation_degree, coupling_map=coupling_map, method=unitary_synthesis_method, backend_props=backend_properties, plugin_config=unitary_synthesis_plugin_config, target=target, ), ] else: raise TranspilerError("Invalid translation method %s." % translation_method) # 7. Fix any bad CX directions _direction_check = [CheckGateDirection(coupling_map, target)] def _direction_condition(property_set): return not property_set["is_direction_mapped"] _direction = [GateDirection(coupling_map, target)] # 8. Remove zero-state reset _reset = RemoveResetInZeroState() # 9. Merge 1q rotations and cancel CNOT gates iteratively until no more change in depth # or size of circuit _depth_check = [Depth(), FixedPoint("depth")] _size_check = [Size(), FixedPoint("size")] def _opt_control(property_set): return (not property_set["depth_fixed_point"]) or ( not property_set["size_fixed_point"]) _opt = [Optimize1qGatesDecomposition(basis_gates), CXCancellation()] # Build pass manager pm1 = PassManager() if coupling_map or initial_layout: pm1.append(_given_layout) pm1.append(_unroll3q) pm1.append(_choose_layout_0, condition=_choose_layout_condition) pm1.append(_choose_layout_1, condition=_trivial_not_perfect) pm1.append(_improve_layout, condition=_vf2_match_not_found) pm1.append(_embed) pm1.append(_swap_check) pm1.append(_swap, condition=_swap_condition) pm1.append(_unroll) if (coupling_map and not coupling_map.is_symmetric) or ( target is not None and target.get_non_global_operation_names(strict_direction=True)): pm1.append(_direction_check) pm1.append(_direction, condition=_direction_condition) pm1.append(_reset) pm1.append(_depth_check + _size_check) pm1.append(_opt + _unroll + _depth_check + _size_check, do_while=_opt_control) if inst_map and inst_map.has_custom_gate(): pm1.append(PulseGates(inst_map=inst_map)) # 10. Unify all durations (either SI, or convert to dt if known) # Schedule the circuit only when scheduling_method is supplied # Apply alignment analysis regardless of scheduling for delay validation. if scheduling_method: # Do scheduling after unit conversion. scheduler = { "alap": ALAPScheduleAnalysis, "as_late_as_possible": ALAPScheduleAnalysis, "asap": ASAPScheduleAnalysis, "as_soon_as_possible": ASAPScheduleAnalysis, } pm1.append(TimeUnitConversion(instruction_durations)) try: pm1.append(scheduler[scheduling_method](instruction_durations)) except KeyError as ex: raise TranspilerError("Invalid scheduling method %s." % scheduling_method) from ex elif instruction_durations: # No scheduling. But do unit conversion for delays. def _contains_delay(property_set): return property_set["contains_delay"] pm1.append(ContainsInstruction("delay")) pm1.append(TimeUnitConversion(instruction_durations), condition=_contains_delay) if (timing_constraints.granularity != 1 or timing_constraints.min_length != 1 or timing_constraints.acquire_alignment != 1 or timing_constraints.pulse_alignment != 1): # Run alignment analysis regardless of scheduling. def _require_alignment(property_set): return property_set["reschedule_required"] pm1.append( InstructionDurationCheck( acquire_alignment=timing_constraints.acquire_alignment, pulse_alignment=timing_constraints.pulse_alignment, )) pm1.append( ConstrainedReschedule( acquire_alignment=timing_constraints.acquire_alignment, pulse_alignment=timing_constraints.pulse_alignment, ), condition=_require_alignment, ) pm1.append( ValidatePulseGates( granularity=timing_constraints.granularity, min_length=timing_constraints.min_length, )) if scheduling_method: # Call padding pass if circuit is scheduled pm1.append(PadDelay()) return pm1
def level_1_pass_manager(pass_manager_config: PassManagerConfig) -> StagedPassManager: """Level 1 pass manager: light optimization by simple adjacent gate collapsing. This pass manager applies the user-given initial layout. If none is given, and a trivial layout (i-th virtual -> i-th physical) makes the circuit fit the coupling map, that is used. Otherwise, the circuit is mapped to the most densely connected coupling subgraph, and swaps are inserted to map. Any unused physical qubit is allocated as ancilla space. The pass manager then unrolls the circuit to the desired basis, and transforms the circuit to match the coupling map. Finally, optimizations in the form of adjacent gate collapse and redundant reset removal are performed. Args: pass_manager_config: configuration of the pass manager. Returns: a level 1 pass manager. Raises: TranspilerError: if the passmanager config is invalid. """ basis_gates = pass_manager_config.basis_gates inst_map = pass_manager_config.inst_map coupling_map = pass_manager_config.coupling_map initial_layout = pass_manager_config.initial_layout layout_method = pass_manager_config.layout_method or "dense" routing_method = pass_manager_config.routing_method or "stochastic" translation_method = pass_manager_config.translation_method or "translator" scheduling_method = pass_manager_config.scheduling_method instruction_durations = pass_manager_config.instruction_durations seed_transpiler = pass_manager_config.seed_transpiler backend_properties = pass_manager_config.backend_properties approximation_degree = pass_manager_config.approximation_degree unitary_synthesis_method = pass_manager_config.unitary_synthesis_method unitary_synthesis_plugin_config = pass_manager_config.unitary_synthesis_plugin_config timing_constraints = pass_manager_config.timing_constraints or TimingConstraints() target = pass_manager_config.target # Use trivial layout if no layout given _given_layout = SetLayout(initial_layout) def _choose_layout_condition(property_set): return not property_set["layout"] def _trivial_not_perfect(property_set): # Verify that a trivial layout is perfect. If trivial_layout_score > 0 # the layout is not perfect. The layout is unconditionally set by trivial # layout so we need to clear it before contuing. if ( property_set["trivial_layout_score"] is not None and property_set["trivial_layout_score"] != 0 ): return True return False # Use a better layout on densely connected qubits, if circuit needs swaps def _vf2_match_not_found(property_set): # If a layout hasn't been set by the time we run vf2 layout we need to # run layout if property_set["layout"] is None: return True # if VF2 layout stopped for any reason other than solution found we need # to run layout since VF2 didn't converge. if ( property_set["VF2Layout_stop_reason"] is not None and property_set["VF2Layout_stop_reason"] is not VF2LayoutStopReason.SOLUTION_FOUND ): return True return False _choose_layout_0 = ( [] if pass_manager_config.layout_method else [ TrivialLayout(coupling_map), Layout2qDistance(coupling_map, property_name="trivial_layout_score"), ] ) _choose_layout_1 = ( [] if pass_manager_config.layout_method else VF2Layout( coupling_map, seed=seed_transpiler, call_limit=int(5e4), # Set call limit to ~100ms with retworkx 0.10.2 properties=backend_properties, target=target, ) ) if layout_method == "trivial": _improve_layout = TrivialLayout(coupling_map) elif layout_method == "dense": _improve_layout = DenseLayout(coupling_map, backend_properties, target=target) elif layout_method == "noise_adaptive": _improve_layout = NoiseAdaptiveLayout(backend_properties) elif layout_method == "sabre": _improve_layout = SabreLayout(coupling_map, max_iterations=2, seed=seed_transpiler) else: raise TranspilerError("Invalid layout method %s." % layout_method) toqm_pass = False if routing_method == "basic": routing_pass = BasicSwap(coupling_map) elif routing_method == "stochastic": routing_pass = StochasticSwap(coupling_map, trials=20, seed=seed_transpiler) elif routing_method == "lookahead": routing_pass = LookaheadSwap(coupling_map, search_depth=4, search_width=4) elif routing_method == "sabre": routing_pass = SabreSwap(coupling_map, heuristic="lookahead", seed=seed_transpiler) elif routing_method == "toqm": HAS_TOQM.require_now("TOQM-based routing") from qiskit_toqm import ToqmSwap, ToqmStrategyO1, latencies_from_target if initial_layout: raise TranspilerError("Initial layouts are not supported with TOQM-based routing.") toqm_pass = True # Note: BarrierBeforeFinalMeasurements is skipped intentionally since ToqmSwap # does not yet support barriers. routing_pass = ToqmSwap( coupling_map, strategy=ToqmStrategyO1( latencies_from_target( coupling_map, instruction_durations, basis_gates, backend_properties, target ) ), ) elif routing_method == "none": routing_pass = Error( msg="No routing method selected, but circuit is not routed to device. " "CheckMap Error: {check_map_msg}", action="raise", ) else: raise TranspilerError("Invalid routing method %s." % routing_method) # Build optimization loop: merge 1q rotations and cancel CNOT gates iteratively # until no more change in depth _depth_check = [Depth(), FixedPoint("depth")] _size_check = [Size(), FixedPoint("size")] def _opt_control(property_set): return (not property_set["depth_fixed_point"]) or (not property_set["size_fixed_point"]) _opt = [Optimize1qGatesDecomposition(basis_gates), CXCancellation()] unroll_3q = None # Build full pass manager if coupling_map or initial_layout: unroll_3q = common.generate_unroll_3q( target, basis_gates, approximation_degree, unitary_synthesis_method, unitary_synthesis_plugin_config, ) layout = PassManager() layout.append(_given_layout) layout.append(_choose_layout_0, condition=_choose_layout_condition) layout.append(_choose_layout_1, condition=_trivial_not_perfect) layout.append(_improve_layout, condition=_vf2_match_not_found) layout += common.generate_embed_passmanager(coupling_map) vf2_call_limit = None if pass_manager_config.layout_method is None and pass_manager_config.initial_layout is None: vf2_call_limit = int(5e4) # Set call limit to ~100ms with retworkx 0.10.2 routing = common.generate_routing_passmanager( routing_pass, target, coupling_map, vf2_call_limit=vf2_call_limit, backend_properties=backend_properties, seed_transpiler=seed_transpiler, check_trivial=True, use_barrier_before_measurement=not toqm_pass, ) else: layout = None routing = None translation = common.generate_translation_passmanager( target, basis_gates, translation_method, approximation_degree, coupling_map, backend_properties, unitary_synthesis_method, unitary_synthesis_plugin_config, ) pre_routing = None if toqm_pass: pre_routing = translation if (coupling_map and not coupling_map.is_symmetric) or ( target is not None and target.get_non_global_operation_names(strict_direction=True) ): pre_optimization = common.generate_pre_op_passmanager(target, coupling_map, True) else: pre_optimization = common.generate_pre_op_passmanager(remove_reset_in_zero=True) optimization = PassManager() unroll = [pass_ for x in translation.passes() for pass_ in x["passes"]] optimization.append(_depth_check + _size_check) opt_loop = _opt + unroll + _depth_check + _size_check optimization.append(opt_loop, do_while=_opt_control) sched = common.generate_scheduling( instruction_durations, scheduling_method, timing_constraints, inst_map ) return StagedPassManager( init=unroll_3q, layout=layout, pre_routing=pre_routing, routing=routing, translation=translation, pre_optimization=pre_optimization, optimization=optimization, scheduling=sched, )
def noise_pass_manager(basis_gates=None, initial_layout=None, coupling_map=None, layout_method=None, translation_method=None, seed_transpiler=None, backend=None, routing_method=None, backend_properties=None, transform=False, readout=True, alpha=0.5, next_gates=5, front=True) -> PassManager: """Level 3 pass manager: heavy optimization by noise adaptive qubit mapping and gate cancellation using commutativity rules and unitary synthesis. This pass manager applies the user-given initial layout. If none is given, a search for a perfect layout (i.e. one that satisfies all 2-qubit interactions) is conducted. If no such layout is found, and device calibration information is available, the circuit is mapped to the qubits with best readouts and to CX gates with highest fidelity. The pass manager then transforms the circuit to match the coupling constraints. It is then unrolled to the basis, and any flipped cx directions are fixed. Finally, optimizations in the form of commutative gate cancellation, resynthesis of two-qubit unitary blocks, and redundant reset removal are performed. Note: In simulators where ``coupling_map=None``, only the unrolling and optimization stages are done. Args: backend (BaseBackend) Returns: a level 3 pass manager. Raises: TranspilerError: if the passmanager config is invalid. """ if basis_gates is None: if getattr(backend, 'configuration', None): basis_gates = getattr(backend.configuration(), 'basis_gates', None) # basis_gates could be None, or a list of basis, e.g. ['u3', 'cx'] if isinstance(basis_gates, list) and all( isinstance(i, str) for i in basis_gates): basis_gates = basis_gates if basis_gates is None: basis_gates = ['u3', 'cx', 'id'] # basis_gates = ['u3', 'cx', 'id'] backend = backend if backend is None or backend.configuration().simulator: if backend_properties is None or coupling_map is None: raise QiskitError( "Backend is simulator or not specified, provide backend properties and coupling map." ) coupling_map = coupling_map backend_properties = backend_properties else: if backend_properties is not None or coupling_map is not None: warnings.warn( "A backend was provide, ignoring backend properties and coupling map", UserWarning) coupling_map = backend.configuration().coupling_map backend_properties = backend.properties() if isinstance(coupling_map, list): coupling_map = CouplingMap(couplinglist=coupling_map) initial_layout = initial_layout layout_method = layout_method or 'dense' routing_method = routing_method or 'stochastic' translation_method = translation_method or 'translator' seed_transpiler = seed_transpiler # 1. Unroll to 1q or 2q gates _unroll3q = Unroll3qOrMore() # 2. Layout on good qubits if calibration info available, otherwise on dense links _given_layout = SetLayout(initial_layout) def _choose_layout_condition(property_set): return not property_set['layout'] _choose_layout_1 = CSPLayout(coupling_map, call_limit=10000, time_limit=60) if layout_method == 'trivial': _choose_layout_2 = TrivialLayout(coupling_map) elif layout_method == 'dense': _choose_layout_2 = DenseLayout(coupling_map, backend_properties) elif layout_method == 'noise_adaptive': _choose_layout_2 = NoiseAdaptiveLayout(backend_properties) elif layout_method == 'sabre': _choose_layout_2 = SabreLayout(coupling_map, max_iterations=4, seed=seed_transpiler) elif layout_method == 'chain': _choose_layout_2 = ChainLayout(coupling_map, backend_properties, readout=readout) else: raise TranspilerError("Invalid layout method %s." % layout_method) # 3. Extend dag/layout with ancillas using the full coupling map _embed = [ FullAncillaAllocation(coupling_map), EnlargeWithAncilla(), ApplyLayout() ] # 4. Swap to fit the coupling map _swap_check = CheckMap(coupling_map) def _swap_condition(property_set): return not property_set['is_swap_mapped'] _swap = [BarrierBeforeFinalMeasurements()] if routing_method == 'basic': _swap += [BasicSwap(coupling_map)] elif routing_method == 'stochastic': _swap += [ StochasticSwap(coupling_map, trials=200, seed=seed_transpiler) ] elif routing_method == 'lookahead': _swap += [LookaheadSwap(coupling_map, search_depth=5, search_width=6)] elif routing_method == 'sabre': _swap += [ SabreSwap(coupling_map, heuristic='decay', seed=seed_transpiler) ] elif routing_method == 'noise_adaptive': _swap += [ NoiseAdaptiveSwap(coupling_map, backend_properties, invert_score=invert_score, swap_score=swap_score, readout=readout, alpha=alpha, next_gates=next_gates, front=front) ] else: raise TranspilerError("Invalid routing method %s." % routing_method) # 5. Unroll to the basis if translation_method == 'unroller': _unroll = [Unroller(basis_gates)] elif translation_method == 'translator': from qiskit.circuit.equivalence_library import SessionEquivalenceLibrary as sel _unroll = [ UnrollCustomDefinitions(sel, basis_gates), BasisTranslator(sel, basis_gates) ] elif translation_method == 'synthesis': _unroll = [ Unroll3qOrMore(), Collect2qBlocks(), ConsolidateBlocks(basis_gates=basis_gates), UnitarySynthesis(basis_gates), ] else: raise TranspilerError("Invalid translation method %s." % translation_method) # 6. Fix any CX direction mismatch _direction_check = [CheckCXDirection(coupling_map)] def _direction_condition(property_set): return not property_set['is_direction_mapped'] _direction = [CXDirection(coupling_map)] # 8. Optimize iteratively until no more change in depth. Removes useless gates # after reset and before measure, commutes gates and optimizes continguous blocks. _depth_check = [Depth(), FixedPoint('depth')] def _opt_control(property_set): return not property_set['depth_fixed_point'] _reset = [RemoveResetInZeroState()] _meas = [OptimizeSwapBeforeMeasure(), RemoveDiagonalGatesBeforeMeasure()] _opt = [ Collect2qBlocks(), ConsolidateBlocks(basis_gates=basis_gates), UnitarySynthesis(basis_gates), Optimize1qGates(basis_gates), CommutativeCancellation(), ] # Build pass manager pm3 = PassManager() pm3.append(_unroll3q) if transform: _transform = TransformCxCascade() pm3.append(_transform) pm3.append(_reset + _meas) if coupling_map: pm3.append(_given_layout) pm3.append(_choose_layout_1, condition=_choose_layout_condition) pm3.append(_choose_layout_2, condition=_choose_layout_condition) pm3.append(_embed) pm3.append(_swap_check) pm3.append(_swap, condition=_swap_condition) pm3.append(_unroll) pm3.append(_depth_check + _opt + _unroll, do_while=_opt_control) if coupling_map and not coupling_map.is_symmetric: pm3.append(_direction_check) pm3.append(_direction, condition=_direction_condition) pm3.append(_reset) return pm3
def level_2_pass_manager(pass_manager_config: PassManagerConfig) -> PassManager: """Level 2 pass manager: medium optimization by initial layout selection and gate cancellation using commutativity rules. This pass manager applies the user-given initial layout. If none is given, a search for a perfect layout (i.e. one that satisfies all 2-qubit interactions) is conducted. If no such layout is found, qubits are laid out on the most densely connected subset which also exhibits the best gate fidelitites. The pass manager then transforms the circuit to match the coupling constraints. It is then unrolled to the basis, and any flipped cx directions are fixed. Finally, optimizations in the form of commutative gate cancellation and redundant reset removal are performed. Note: In simulators where ``coupling_map=None``, only the unrolling and optimization stages are done. Args: pass_manager_config: configuration of the pass manager. Returns: a level 2 pass manager. Raises: TranspilerError: if the passmanager config is invalid. """ basis_gates = pass_manager_config.basis_gates coupling_map = pass_manager_config.coupling_map initial_layout = pass_manager_config.initial_layout layout_method = pass_manager_config.layout_method or 'dense' routing_method = pass_manager_config.routing_method or 'stochastic' translation_method = pass_manager_config.translation_method or 'translator' scheduling_method = pass_manager_config.scheduling_method instruction_durations = pass_manager_config.instruction_durations seed_transpiler = pass_manager_config.seed_transpiler backend_properties = pass_manager_config.backend_properties # 1. Search for a perfect layout, or choose a dense layout, if no layout given _given_layout = SetLayout(initial_layout) def _choose_layout_condition(property_set): return not property_set['layout'] _choose_layout_1 = CSPLayout(coupling_map, call_limit=1000, time_limit=10) if layout_method == 'trivial': _choose_layout_2 = TrivialLayout(coupling_map) elif layout_method == 'dense': _choose_layout_2 = DenseLayout(coupling_map, backend_properties) elif layout_method == 'noise_adaptive': _choose_layout_2 = NoiseAdaptiveLayout(backend_properties) elif layout_method == 'sabre': _choose_layout_2 = SabreLayout(coupling_map, max_iterations=2, seed=seed_transpiler) else: raise TranspilerError("Invalid layout method %s." % layout_method) # 2. Extend dag/layout with ancillas using the full coupling map _embed = [FullAncillaAllocation(coupling_map), EnlargeWithAncilla(), ApplyLayout()] # 3. Unroll to 1q or 2q gates _unroll3q = Unroll3qOrMore() # 4. Swap to fit the coupling map _swap_check = CheckMap(coupling_map) def _swap_condition(property_set): return not property_set['is_swap_mapped'] _swap = [BarrierBeforeFinalMeasurements()] if routing_method == 'basic': _swap += [BasicSwap(coupling_map)] elif routing_method == 'stochastic': _swap += [StochasticSwap(coupling_map, trials=20, seed=seed_transpiler)] elif routing_method == 'lookahead': _swap += [LookaheadSwap(coupling_map, search_depth=5, search_width=5)] elif routing_method == 'sabre': _swap += [SabreSwap(coupling_map, heuristic='decay', seed=seed_transpiler)] else: raise TranspilerError("Invalid routing method %s." % routing_method) # 5. Unroll to the basis if translation_method == 'unroller': _unroll = [Unroller(basis_gates)] elif translation_method == 'translator': from qiskit.circuit.equivalence_library import SessionEquivalenceLibrary as sel _unroll = [UnrollCustomDefinitions(sel, basis_gates), BasisTranslator(sel, basis_gates)] elif translation_method == 'synthesis': _unroll = [ Unroll3qOrMore(), Collect2qBlocks(), ConsolidateBlocks(basis_gates=basis_gates), UnitarySynthesis(basis_gates), ] else: raise TranspilerError("Invalid translation method %s." % translation_method) # 6. Fix any bad CX directions _direction_check = [CheckCXDirection(coupling_map)] def _direction_condition(property_set): return not property_set['is_direction_mapped'] _direction = [CXDirection(coupling_map)] # 7. Remove zero-state reset _reset = RemoveResetInZeroState() # 8. 1q rotation merge and commutative cancellation iteratively until no more change in depth _depth_check = [Depth(), FixedPoint('depth')] def _opt_control(property_set): return not property_set['depth_fixed_point'] _opt = [Optimize1qGates(basis_gates), CommutativeCancellation()] # 9. Schedule the circuit only when scheduling_method is supplied if scheduling_method: _scheduling = [TimeUnitAnalysis(instruction_durations)] if scheduling_method in {'alap', 'as_late_as_possible'}: _scheduling += [ALAPSchedule(instruction_durations)] elif scheduling_method in {'asap', 'as_soon_as_possible'}: _scheduling += [ASAPSchedule(instruction_durations)] else: raise TranspilerError("Invalid scheduling method %s." % scheduling_method) # Build pass manager pm2 = PassManager() if coupling_map: pm2.append(_given_layout) pm2.append(_choose_layout_1, condition=_choose_layout_condition) pm2.append(_choose_layout_2, condition=_choose_layout_condition) pm2.append(_embed) pm2.append(_unroll3q) pm2.append(_swap_check) pm2.append(_swap, condition=_swap_condition) pm2.append(_unroll) if coupling_map and not coupling_map.is_symmetric: pm2.append(_direction_check) pm2.append(_direction, condition=_direction_condition) pm2.append(_reset) pm2.append(_depth_check + _opt, do_while=_opt_control) if scheduling_method: pm2.append(_scheduling) return pm2
def level_2_pass_manager(pass_manager_config: PassManagerConfig) -> PassManager: """Level 2 pass manager: medium optimization by initial layout selection and gate cancellation using commutativity rules. This pass manager applies the user-given initial layout. If none is given, a search for a perfect layout (i.e. one that satisfies all 2-qubit interactions) is conducted. If no such layout is found, qubits are laid out on the most densely connected subset which also exhibits the best gate fidelities. The pass manager then transforms the circuit to match the coupling constraints. It is then unrolled to the basis, and any flipped cx directions are fixed. Finally, optimizations in the form of commutative gate cancellation and redundant reset removal are performed. Note: In simulators where ``coupling_map=None``, only the unrolling and optimization stages are done. Args: pass_manager_config: configuration of the pass manager. Returns: a level 2 pass manager. Raises: TranspilerError: if the passmanager config is invalid. """ basis_gates = pass_manager_config.basis_gates coupling_map = pass_manager_config.coupling_map initial_layout = pass_manager_config.initial_layout layout_method = pass_manager_config.layout_method or "dense" routing_method = pass_manager_config.routing_method or "stochastic" translation_method = pass_manager_config.translation_method or "translator" scheduling_method = pass_manager_config.scheduling_method instruction_durations = pass_manager_config.instruction_durations seed_transpiler = pass_manager_config.seed_transpiler backend_properties = pass_manager_config.backend_properties approximation_degree = pass_manager_config.approximation_degree timing_constraints = pass_manager_config.timing_constraints or TimingConstraints() # 1. Search for a perfect layout, or choose a dense layout, if no layout given _given_layout = SetLayout(initial_layout) def _choose_layout_condition(property_set): # layout hasn't been set yet return not property_set["layout"] # 1a. If layout_method is not set, first try a trivial layout _choose_layout_0 = ( [] if pass_manager_config.layout_method else [ TrivialLayout(coupling_map), Layout2qDistance(coupling_map, property_name="trivial_layout_score"), ] ) # 1b. If a trivial layout wasn't perfect (ie no swaps are needed) then try using # CSP layout to find a perfect layout _choose_layout_1 = ( [] if pass_manager_config.layout_method else CSPLayout(coupling_map, call_limit=1000, time_limit=10, seed=seed_transpiler) ) def _trivial_not_perfect(property_set): # Verify that a trivial layout is perfect. If trivial_layout_score > 0 # the layout is not perfect. The layout is unconditionally set by trivial # layout so we need to clear it before contuing. if property_set["trivial_layout_score"] is not None: if property_set["trivial_layout_score"] != 0: property_set["layout"]._wrapped = None return True return False def _csp_not_found_match(property_set): # If a layout hasn't been set by the time we run csp we need to run layout if property_set["layout"] is None: return True # if CSP layout stopped for any reason other than solution found we need # to run layout since CSP didn't converge. if ( property_set["CSPLayout_stop_reason"] is not None and property_set["CSPLayout_stop_reason"] != "solution found" ): return True return False # 1c. if CSP layout doesn't converge on a solution use layout_method (dense) to get a layout if layout_method == "trivial": _choose_layout_2 = TrivialLayout(coupling_map) elif layout_method == "dense": _choose_layout_2 = DenseLayout(coupling_map, backend_properties) elif layout_method == "noise_adaptive": _choose_layout_2 = NoiseAdaptiveLayout(backend_properties) elif layout_method == "sabre": _choose_layout_2 = SabreLayout(coupling_map, max_iterations=2, seed=seed_transpiler) else: raise TranspilerError("Invalid layout method %s." % layout_method) # 2. Extend dag/layout with ancillas using the full coupling map _embed = [FullAncillaAllocation(coupling_map), EnlargeWithAncilla(), ApplyLayout()] # 3. Unroll to 1q or 2q gates _unroll3q = Unroll3qOrMore() # 4. Swap to fit the coupling map _swap_check = CheckMap(coupling_map) def _swap_condition(property_set): return not property_set["is_swap_mapped"] _swap = [BarrierBeforeFinalMeasurements()] if routing_method == "basic": _swap += [BasicSwap(coupling_map)] elif routing_method == "stochastic": _swap += [StochasticSwap(coupling_map, trials=20, seed=seed_transpiler)] elif routing_method == "lookahead": _swap += [LookaheadSwap(coupling_map, search_depth=5, search_width=5)] elif routing_method == "sabre": _swap += [SabreSwap(coupling_map, heuristic="decay", seed=seed_transpiler)] elif routing_method == "none": _swap += [ Error( msg="No routing method selected, but circuit is not routed to device. " "CheckMap Error: {check_map_msg}", action="raise", ) ] else: raise TranspilerError("Invalid routing method %s." % routing_method) # 5. Unroll to the basis if translation_method == "unroller": _unroll = [Unroller(basis_gates)] elif translation_method == "translator": from qiskit.circuit.equivalence_library import SessionEquivalenceLibrary as sel _unroll = [UnrollCustomDefinitions(sel, basis_gates), BasisTranslator(sel, basis_gates)] elif translation_method == "synthesis": _unroll = [ Unroll3qOrMore(), Collect2qBlocks(), ConsolidateBlocks(basis_gates=basis_gates), UnitarySynthesis(basis_gates, approximation_degree=approximation_degree), ] else: raise TranspilerError("Invalid translation method %s." % translation_method) # 6. Fix any bad CX directions _direction_check = [CheckGateDirection(coupling_map)] def _direction_condition(property_set): return not property_set["is_direction_mapped"] _direction = [GateDirection(coupling_map)] # 7. Remove zero-state reset _reset = RemoveResetInZeroState() # 8. 1q rotation merge and commutative cancellation iteratively until no more change in depth _depth_check = [Depth(), FixedPoint("depth")] def _opt_control(property_set): return not property_set["depth_fixed_point"] _opt = [ Optimize1qGatesDecomposition(basis_gates), CommutativeCancellation(basis_gates=basis_gates), ] # 9. Unify all durations (either SI, or convert to dt if known) # Schedule the circuit only when scheduling_method is supplied _scheduling = [TimeUnitConversion(instruction_durations)] if scheduling_method: if scheduling_method in {"alap", "as_late_as_possible"}: _scheduling += [ALAPSchedule(instruction_durations)] elif scheduling_method in {"asap", "as_soon_as_possible"}: _scheduling += [ASAPSchedule(instruction_durations)] else: raise TranspilerError("Invalid scheduling method %s." % scheduling_method) # 10. Call measure alignment. Should come after scheduling. _alignments = [ ValidatePulseGates( granularity=timing_constraints.granularity, min_length=timing_constraints.min_length ), AlignMeasures(alignment=timing_constraints.acquire_alignment), ] # Build pass manager pm2 = PassManager() if coupling_map or initial_layout: pm2.append(_given_layout) pm2.append(_choose_layout_0, condition=_choose_layout_condition) pm2.append(_choose_layout_1, condition=_trivial_not_perfect) pm2.append(_choose_layout_2, condition=_csp_not_found_match) pm2.append(_embed) pm2.append(_unroll3q) pm2.append(_swap_check) pm2.append(_swap, condition=_swap_condition) pm2.append(_unroll) if coupling_map and not coupling_map.is_symmetric: pm2.append(_direction_check) pm2.append(_direction, condition=_direction_condition) pm2.append(_reset) pm2.append(_depth_check + _opt + _unroll, do_while=_opt_control) pm2.append(_scheduling) pm2.append(_alignments) return pm2
def level_0_pass_manager( pass_manager_config: PassManagerConfig) -> PassManager: """Level 0 pass manager: no explicit optimization other than mapping to backend. This pass manager applies the user-given initial layout. If none is given, a trivial layout consisting of mapping the i-th virtual qubit to the i-th physical qubit is used. Any unused physical qubit is allocated as ancilla space. The pass manager then unrolls the circuit to the desired basis, and transforms the circuit to match the coupling map. Note: In simulators where ``coupling_map=None``, only the unrolling and optimization stages are done. Args: pass_manager_config: configuration of the pass manager. Returns: a level 0 pass manager. Raises: TranspilerError: if the passmanager config is invalid. """ basis_gates = pass_manager_config.basis_gates coupling_map = pass_manager_config.coupling_map initial_layout = pass_manager_config.initial_layout layout_method = pass_manager_config.layout_method or 'trivial' routing_method = pass_manager_config.routing_method or 'stochastic' translation_method = pass_manager_config.translation_method or 'translator' scheduling_method = pass_manager_config.scheduling_method instruction_durations = pass_manager_config.instruction_durations seed_transpiler = pass_manager_config.seed_transpiler backend_properties = pass_manager_config.backend_properties # 1. Choose an initial layout if not set by user (default: trivial layout) _given_layout = SetLayout(initial_layout) def _choose_layout_condition(property_set): return not property_set['layout'] if layout_method == 'trivial': _choose_layout = TrivialLayout(coupling_map) elif layout_method == 'dense': _choose_layout = DenseLayout(coupling_map, backend_properties) elif layout_method == 'noise_adaptive': _choose_layout = NoiseAdaptiveLayout(backend_properties) elif layout_method == 'sabre': _choose_layout = SabreLayout(coupling_map, max_iterations=1, seed=seed_transpiler) else: raise TranspilerError("Invalid layout method %s." % layout_method) # 2. Extend dag/layout with ancillas using the full coupling map _embed = [ FullAncillaAllocation(coupling_map), EnlargeWithAncilla(), ApplyLayout() ] # 3. Decompose so only 1-qubit and 2-qubit gates remain _unroll3q = Unroll3qOrMore() # 4. Swap to fit the coupling map _swap_check = CheckMap(coupling_map) def _swap_condition(property_set): return not property_set['is_swap_mapped'] _swap = [BarrierBeforeFinalMeasurements()] if routing_method == 'basic': _swap += [BasicSwap(coupling_map)] elif routing_method == 'stochastic': _swap += [ StochasticSwap(coupling_map, trials=20, seed=seed_transpiler) ] elif routing_method == 'lookahead': _swap += [LookaheadSwap(coupling_map, search_depth=2, search_width=2)] elif routing_method == 'sabre': _swap += [ SabreSwap(coupling_map, heuristic='basic', seed=seed_transpiler) ] else: raise TranspilerError("Invalid routing method %s." % routing_method) # 5. Unroll to the basis if translation_method == 'unroller': _unroll = [Unroller(basis_gates)] elif translation_method == 'translator': from qiskit.circuit.equivalence_library import SessionEquivalenceLibrary as sel _unroll = [ UnrollCustomDefinitions(sel, basis_gates), BasisTranslator(sel, basis_gates) ] elif translation_method == 'synthesis': _unroll = [ Unroll3qOrMore(), Collect2qBlocks(), ConsolidateBlocks(basis_gates=basis_gates), UnitarySynthesis(basis_gates), ] else: raise TranspilerError("Invalid translation method %s." % translation_method) # 6. Fix any bad CX directions _direction_check = [CheckCXDirection(coupling_map)] def _direction_condition(property_set): return not property_set['is_direction_mapped'] _direction = [CXDirection(coupling_map)] # 7. Schedule the circuit only when scheduling_method is supplied if scheduling_method: _scheduling = [TimeUnitAnalysis(instruction_durations)] if scheduling_method in {'alap', 'as_late_as_possible'}: _scheduling += [ALAPSchedule(instruction_durations)] elif scheduling_method in {'asap', 'as_soon_as_possible'}: _scheduling += [ASAPSchedule(instruction_durations)] else: raise TranspilerError("Invalid scheduling method %s." % scheduling_method) # Build pass manager pm0 = PassManager() if coupling_map: pm0.append(_given_layout) pm0.append(_choose_layout, condition=_choose_layout_condition) pm0.append(_embed) pm0.append(_unroll3q) pm0.append(_swap_check) pm0.append(_swap, condition=_swap_condition) pm0.append(_unroll) if coupling_map and not coupling_map.is_symmetric: pm0.append(_direction_check) pm0.append(_direction, condition=_direction_condition) if scheduling_method: pm0.append(_scheduling) return pm0
def run(self, dag: DAGCircuit) -> DAGCircuit: """Run the pass by decomposing the nodes it applies on. Args: dag: The dag to which we will add swaps. Returns: A dag where swaps have been added for the intended gate type. Raises: TranspilerError: If the swap strategy was not given at init time and there is no swap strategy in the property set. TranspilerError: If the quantum circuit contains more than one qubit register. TranspilerError: If there are qubits that are not contained in the quantum register. """ if self._swap_strategy is None: swap_strategy = self.property_set["swap_strategy"] if swap_strategy is None: raise TranspilerError( "No swap strategy given at init or in the property set.") else: swap_strategy = self._swap_strategy if len(dag.qregs) != 1: raise TranspilerError( f"{self.__class__.__name__} runs on circuits with one quantum register." ) if len(dag.qubits) != next(iter(dag.qregs.values())).size: raise TranspilerError( "Circuit has qubits not contained in the qubit register.") new_dag = dag.copy_empty_like() current_layout = Layout.generate_trivial_layout(*dag.qregs.values()) # Used to keep track of nodes that do not decompose using swap strategies. accumulator = new_dag.copy_empty_like() self._bit_indices = { bit: index for index, bit in enumerate(dag.qubits) } for node in dag.topological_op_nodes(): if isinstance(node.op, Commuting2qBlock): # Check that the swap strategy creates enough connectivity for the node. self._check_edges(node, swap_strategy) # Compose any accumulated non-swap strategy gates to the dag accumulator = self._compose_non_swap_nodes( accumulator, current_layout, new_dag) # Decompose the swap-strategy node and add to the dag. new_dag.compose( self.swap_decompose(dag, node, current_layout, swap_strategy)) else: accumulator.apply_operation_back(node.op, node.qargs, node.cargs) self._compose_non_swap_nodes(accumulator, current_layout, new_dag) return new_dag
def level_3_with_contant_pure(pass_manager_config: PassManagerConfig) -> PassManager: """ Args: pass_manager_config: configuration of the pass manager. Returns: a level 3 pass manager. """ basis_gates = pass_manager_config.basis_gates coupling_map = pass_manager_config.coupling_map initial_layout = pass_manager_config.initial_layout layout_method = pass_manager_config.layout_method or 'dense' routing_method = pass_manager_config.routing_method or 'stochastic' seed_transpiler = pass_manager_config.seed_transpiler backend_properties = pass_manager_config.backend_properties # 1. Unroll to the basis first, to prepare for noise-adaptive layout _unroll = Unroller(basis_gates + ['annotation']) # 2. Layout on good qubits if calibration info available, otherwise on dense links _given_layout = SetLayout(initial_layout) def _choose_layout_condition(property_set): return not property_set['layout'] _choose_layout_1 = CSPLayout(coupling_map, call_limit=10000, time_limit=60) if layout_method == 'trivial': _choose_layout_2 = TrivialLayout(coupling_map) elif layout_method == 'dense': _choose_layout_2 = DenseLayout(coupling_map, backend_properties) elif layout_method == 'noise_adaptive': _choose_layout_2 = NoiseAdaptiveLayout(backend_properties) else: raise TranspilerError("Invalid layout method %s." % layout_method) # 3. Extend dag/layout with ancillas using the full coupling map _embed = [FullAncillaAllocation(coupling_map), EnlargeWithAncilla(), ApplyLayout()] # 4. Unroll to 1q or 2q gates, swap to fit the coupling map _swap_check = CheckMap(coupling_map) def _swap_condition(property_set): return not property_set['is_swap_mapped'] _swap = [BarrierBeforeFinalMeasurements(), Unroll3qOrMore()] if routing_method == 'basic': _swap += [BasicSwap(coupling_map)] elif routing_method == 'stochastic': _swap += [StochasticSwap(coupling_map, trials=200, seed=seed_transpiler)] elif routing_method == 'lookahead': _swap += [LookaheadSwap(coupling_map, search_depth=5, search_width=6)] else: raise TranspilerError("Invalid routing method %s." % routing_method) # 5. 1q rotation merge and commutative cancellation iteratively until no more change in depth _depth_check = [Depth(), FixedPoint('depth')] def _opt_control(property_set): return not property_set['depth_fixed_point'] _opt = [RemoveResetInZeroState(), Collect2qBlocks(), ConsolidateBlocks(), Unroller(basis_gates), # unroll unitaries Optimize1qGates(basis_gates), CommutativeCancellation(), OptimizeSwapBeforeMeasure(), RemoveDiagonalGatesBeforeMeasure()] # 6. Fix any CX direction mismatch _direction_check = [CheckCXDirection(coupling_map)] def _direction_condition(property_set): return not property_set['is_direction_mapped'] _direction = [CXDirection(coupling_map)] # Build pass manager pm = PassManager() pm.append(ConstantsStateOptimization()) pm.append(_unroll) if coupling_map: pm.append(_given_layout) pm.append(_choose_layout_1, condition=_choose_layout_condition) pm.append(_choose_layout_2, condition=_choose_layout_condition) pm.append(_embed) pm.append(_swap_check) pm.append(_swap, condition=_swap_condition) pm.append(ConstantsStateOptimization()) pm.append([Unroller(basis_gates+['swap', 'aswap', 'annotation']), Optimize1qGates(), PureStateOnU()]) pm.append(_depth_check + _opt, do_while=_opt_control) if coupling_map and not coupling_map.is_symmetric: pm.append(_direction_check) pm.append(_direction, condition=_direction_condition) return pm
def level_2_pass_manager( pass_manager_config: PassManagerConfig) -> PassManager: """Level 2 pass manager: medium optimization by initial layout selection and gate cancellation using commutativity rules. This pass manager applies the user-given initial layout. If none is given, a search for a perfect layout (i.e. one that satisfies all 2-qubit interactions) is conducted. If no such layout is found, qubits are laid out on the most densely connected subset which also exhibits the best gate fidelities. The pass manager then transforms the circuit to match the coupling constraints. It is then unrolled to the basis, and any flipped cx directions are fixed. Finally, optimizations in the form of commutative gate cancellation and redundant reset removal are performed. Note: In simulators where ``coupling_map=None``, only the unrolling and optimization stages are done. Args: pass_manager_config: configuration of the pass manager. Returns: a level 2 pass manager. Raises: TranspilerError: if the passmanager config is invalid. """ basis_gates = pass_manager_config.basis_gates inst_map = pass_manager_config.inst_map coupling_map = pass_manager_config.coupling_map initial_layout = pass_manager_config.initial_layout layout_method = pass_manager_config.layout_method or "dense" routing_method = pass_manager_config.routing_method or "stochastic" translation_method = pass_manager_config.translation_method or "translator" scheduling_method = pass_manager_config.scheduling_method instruction_durations = pass_manager_config.instruction_durations seed_transpiler = pass_manager_config.seed_transpiler backend_properties = pass_manager_config.backend_properties approximation_degree = pass_manager_config.approximation_degree unitary_synthesis_method = pass_manager_config.unitary_synthesis_method timing_constraints = pass_manager_config.timing_constraints or TimingConstraints( ) unitary_synthesis_plugin_config = pass_manager_config.unitary_synthesis_plugin_config target = pass_manager_config.target # 1. Unroll to 1q or 2q gates _unroll3q = [ # Use unitary synthesis for basis aware decomposition of UnitaryGates UnitarySynthesis( basis_gates, approximation_degree=approximation_degree, method=unitary_synthesis_method, min_qubits=3, plugin_config=unitary_synthesis_plugin_config, ), Unroll3qOrMore(), ] # 2. Search for a perfect layout, or choose a dense layout, if no layout given _given_layout = SetLayout(initial_layout) def _choose_layout_condition(property_set): # layout hasn't been set yet return not property_set["layout"] def _vf2_match_not_found(property_set): # If a layout hasn't been set by the time we run vf2 layout we need to # run layout if property_set["layout"] is None: return True # if VF2 layout stopped for any reason other than solution found we need # to run layout since VF2 didn't converge. if (property_set["VF2Layout_stop_reason"] is not None and property_set["VF2Layout_stop_reason"] is not VF2LayoutStopReason.SOLUTION_FOUND): return True return False # 2a. Try using VF2 layout to find a perfect layout _choose_layout_0 = ([] if pass_manager_config.layout_method else VF2Layout( coupling_map, seed=seed_transpiler, call_limit=int(5e6), # Set call limit to ~10 sec with retworkx 0.10.2 time_limit=10.0, properties=backend_properties, )) # 2b. if VF2 layout doesn't converge on a solution use layout_method (dense) to get a layout if layout_method == "trivial": _choose_layout_1 = TrivialLayout(coupling_map) elif layout_method == "dense": _choose_layout_1 = DenseLayout(coupling_map, backend_properties) elif layout_method == "noise_adaptive": _choose_layout_1 = NoiseAdaptiveLayout(backend_properties) elif layout_method == "sabre": _choose_layout_1 = SabreLayout(coupling_map, max_iterations=2, seed=seed_transpiler) else: raise TranspilerError("Invalid layout method %s." % layout_method) # 3. Extend dag/layout with ancillas using the full coupling map _embed = [ FullAncillaAllocation(coupling_map), EnlargeWithAncilla(), ApplyLayout() ] # 4. Swap to fit the coupling map _swap_check = CheckMap(coupling_map) def _swap_condition(property_set): return not property_set["is_swap_mapped"] _swap = [BarrierBeforeFinalMeasurements()] if routing_method == "basic": _swap += [BasicSwap(coupling_map)] elif routing_method == "stochastic": _swap += [ StochasticSwap(coupling_map, trials=20, seed=seed_transpiler) ] elif routing_method == "lookahead": _swap += [LookaheadSwap(coupling_map, search_depth=5, search_width=5)] elif routing_method == "sabre": _swap += [ SabreSwap(coupling_map, heuristic="decay", seed=seed_transpiler) ] elif routing_method == "none": _swap += [ Error( msg= ("No routing method selected, but circuit is not routed to device. " "CheckMap Error: {check_map_msg}"), action="raise", ) ] else: raise TranspilerError("Invalid routing method %s." % routing_method) # 5. Unroll to the basis if translation_method == "unroller": _unroll = [Unroller(basis_gates)] elif translation_method == "translator": from qiskit.circuit.equivalence_library import SessionEquivalenceLibrary as sel _unroll = [ # Use unitary synthesis for basis aware decomposition of UnitaryGates before # custom unrolling UnitarySynthesis( basis_gates, approximation_degree=approximation_degree, coupling_map=coupling_map, backend_props=backend_properties, method=unitary_synthesis_method, plugin_config=unitary_synthesis_plugin_config, ), UnrollCustomDefinitions(sel, basis_gates), BasisTranslator(sel, basis_gates, target), ] elif translation_method == "synthesis": _unroll = [ # Use unitary synthesis for basis aware decomposition of UnitaryGates before # collection UnitarySynthesis( basis_gates, approximation_degree=approximation_degree, coupling_map=coupling_map, backend_props=backend_properties, method=unitary_synthesis_method, plugin_config=unitary_synthesis_plugin_config, min_qubits=3, ), Unroll3qOrMore(), Collect2qBlocks(), ConsolidateBlocks(basis_gates=basis_gates, target=target), UnitarySynthesis( basis_gates, approximation_degree=approximation_degree, coupling_map=coupling_map, backend_props=backend_properties, method=unitary_synthesis_method, plugin_config=unitary_synthesis_plugin_config, ), ] else: raise TranspilerError("Invalid translation method %s." % translation_method) # 6. Fix any bad CX directions _direction_check = [CheckGateDirection(coupling_map, target)] def _direction_condition(property_set): return not property_set["is_direction_mapped"] _direction = [GateDirection(coupling_map, target)] # 7. Remove zero-state reset _reset = RemoveResetInZeroState() # 8. 1q rotation merge and commutative cancellation iteratively until no more change in depth _depth_check = [Depth(), FixedPoint("depth")] def _opt_control(property_set): return not property_set["depth_fixed_point"] _opt = [ Optimize1qGatesDecomposition(basis_gates), CommutativeCancellation(basis_gates=basis_gates), ] # 9. Unify all durations (either SI, or convert to dt if known) # Schedule the circuit only when scheduling_method is supplied _time_unit_setup = [ContainsInstruction("delay")] _time_unit_conversion = [TimeUnitConversion(instruction_durations)] def _contains_delay(property_set): return property_set["contains_delay"] _scheduling = [] if scheduling_method: _scheduling += _time_unit_conversion if scheduling_method in {"alap", "as_late_as_possible"}: _scheduling += [ALAPSchedule(instruction_durations), PadDelay()] elif scheduling_method in {"asap", "as_soon_as_possible"}: _scheduling += [ASAPSchedule(instruction_durations), PadDelay()] else: raise TranspilerError("Invalid scheduling method %s." % scheduling_method) # 10. Call measure alignment. Should come after scheduling. if (timing_constraints.granularity != 1 or timing_constraints.min_length != 1 or timing_constraints.acquire_alignment != 1): _alignments = [ ValidatePulseGates(granularity=timing_constraints.granularity, min_length=timing_constraints.min_length), AlignMeasures(alignment=timing_constraints.acquire_alignment), ] else: _alignments = [] # Build pass manager pm2 = PassManager() if coupling_map or initial_layout: pm2.append(_given_layout) pm2.append(_unroll3q) pm2.append(_choose_layout_0, condition=_choose_layout_condition) pm2.append(_choose_layout_1, condition=_vf2_match_not_found) pm2.append(_embed) pm2.append(_swap_check) pm2.append(_swap, condition=_swap_condition) pm2.append(_unroll) if (coupling_map and not coupling_map.is_symmetric) or ( target is not None and target.get_non_global_operation_names(strict_direction=True)): pm2.append(_direction_check) pm2.append(_direction, condition=_direction_condition) pm2.append(_reset) pm2.append(_depth_check + _opt + _unroll, do_while=_opt_control) if inst_map and inst_map.has_custom_gate(): pm2.append(PulseGates(inst_map=inst_map)) if scheduling_method: pm2.append(_scheduling) elif instruction_durations: pm2.append(_time_unit_setup) pm2.append(_time_unit_conversion, condition=_contains_delay) pm2.append(_alignments) return pm2
def level_2_pass_manager( pass_manager_config: PassManagerConfig) -> StagedPassManager: """Level 2 pass manager: medium optimization by initial layout selection and gate cancellation using commutativity rules. This pass manager applies the user-given initial layout. If none is given, a search for a perfect layout (i.e. one that satisfies all 2-qubit interactions) is conducted. If no such layout is found, qubits are laid out on the most densely connected subset which also exhibits the best gate fidelities. The pass manager then transforms the circuit to match the coupling constraints. It is then unrolled to the basis, and any flipped cx directions are fixed. Finally, optimizations in the form of commutative gate cancellation and redundant reset removal are performed. Args: pass_manager_config: configuration of the pass manager. Returns: a level 2 pass manager. Raises: TranspilerError: if the passmanager config is invalid. """ basis_gates = pass_manager_config.basis_gates inst_map = pass_manager_config.inst_map coupling_map = pass_manager_config.coupling_map initial_layout = pass_manager_config.initial_layout layout_method = pass_manager_config.layout_method or "dense" routing_method = pass_manager_config.routing_method or "stochastic" translation_method = pass_manager_config.translation_method or "translator" scheduling_method = pass_manager_config.scheduling_method instruction_durations = pass_manager_config.instruction_durations seed_transpiler = pass_manager_config.seed_transpiler backend_properties = pass_manager_config.backend_properties approximation_degree = pass_manager_config.approximation_degree unitary_synthesis_method = pass_manager_config.unitary_synthesis_method timing_constraints = pass_manager_config.timing_constraints or TimingConstraints( ) unitary_synthesis_plugin_config = pass_manager_config.unitary_synthesis_plugin_config target = pass_manager_config.target # Search for a perfect layout, or choose a dense layout, if no layout given _given_layout = SetLayout(initial_layout) def _choose_layout_condition(property_set): # layout hasn't been set yet return not property_set["layout"] def _vf2_match_not_found(property_set): # If a layout hasn't been set by the time we run vf2 layout we need to # run layout if property_set["layout"] is None: return True # if VF2 layout stopped for any reason other than solution found we need # to run layout since VF2 didn't converge. if (property_set["VF2Layout_stop_reason"] is not None and property_set["VF2Layout_stop_reason"] is not VF2LayoutStopReason.SOLUTION_FOUND): return True return False # Try using VF2 layout to find a perfect layout _choose_layout_0 = ([] if pass_manager_config.layout_method else VF2Layout( coupling_map, seed=seed_transpiler, call_limit=int(5e6), # Set call limit to ~10 sec with retworkx 0.10.2 properties=backend_properties, target=target, )) if layout_method == "trivial": _choose_layout_1 = TrivialLayout(coupling_map) elif layout_method == "dense": _choose_layout_1 = DenseLayout(coupling_map, backend_properties, target=target) elif layout_method == "noise_adaptive": _choose_layout_1 = NoiseAdaptiveLayout(backend_properties) elif layout_method == "sabre": _choose_layout_1 = SabreLayout(coupling_map, max_iterations=2, seed=seed_transpiler) else: raise TranspilerError("Invalid layout method %s." % layout_method) toqm_pass = False if routing_method == "basic": routing_pass = BasicSwap(coupling_map) elif routing_method == "stochastic": routing_pass = StochasticSwap(coupling_map, trials=20, seed=seed_transpiler) elif routing_method == "lookahead": routing_pass = LookaheadSwap(coupling_map, search_depth=5, search_width=5) elif routing_method == "sabre": routing_pass = SabreSwap(coupling_map, heuristic="decay", seed=seed_transpiler) elif routing_method == "toqm": HAS_TOQM.require_now("TOQM-based routing") from qiskit_toqm import ToqmSwap, ToqmStrategyO2, latencies_from_target if initial_layout: raise TranspilerError( "Initial layouts are not supported with TOQM-based routing.") toqm_pass = True # Note: BarrierBeforeFinalMeasurements is skipped intentionally since ToqmSwap # does not yet support barriers. routing_pass = ToqmSwap( coupling_map, strategy=ToqmStrategyO2( latencies_from_target(coupling_map, instruction_durations, basis_gates, backend_properties, target)), ) elif routing_method == "none": routing_pass = Error( msg= "No routing method selected, but circuit is not routed to device. " "CheckMap Error: {check_map_msg}", action="raise", ) else: raise TranspilerError("Invalid routing method %s." % routing_method) # Build optimization loop: 1q rotation merge and commutative cancellation iteratively until # no more change in depth _depth_check = [Depth(), FixedPoint("depth")] _size_check = [Size(), FixedPoint("size")] def _opt_control(property_set): return (not property_set["depth_fixed_point"]) or ( not property_set["size_fixed_point"]) _opt = [ Optimize1qGatesDecomposition(basis_gates), CommutativeCancellation(basis_gates=basis_gates), ] unroll_3q = None # Build pass manager if coupling_map or initial_layout: unroll_3q = common.generate_unroll_3q( target, basis_gates, approximation_degree, unitary_synthesis_method, unitary_synthesis_plugin_config, ) layout = PassManager() layout.append(_given_layout) layout.append(_choose_layout_0, condition=_choose_layout_condition) layout.append(_choose_layout_1, condition=_vf2_match_not_found) layout += common.generate_embed_passmanager(coupling_map) vf2_call_limit = None if pass_manager_config.layout_method is None and pass_manager_config.initial_layout is None: vf2_call_limit = int( 5e6) # Set call limit to ~10 sec with retworkx 0.10.2 routing = common.generate_routing_passmanager( routing_pass, target, coupling_map=coupling_map, vf2_call_limit=vf2_call_limit, backend_properties=backend_properties, seed_transpiler=seed_transpiler, use_barrier_before_measurement=not toqm_pass, ) else: layout = None routing = None translation = common.generate_translation_passmanager( target, basis_gates, translation_method, approximation_degree, coupling_map, backend_properties, unitary_synthesis_method, unitary_synthesis_plugin_config, ) pre_routing = None if toqm_pass: pre_routing = translation if (coupling_map and not coupling_map.is_symmetric) or ( target is not None and target.get_non_global_operation_names(strict_direction=True)): pre_optimization = common.generate_pre_op_passmanager( target, coupling_map, True) else: pre_optimization = common.generate_pre_op_passmanager( remove_reset_in_zero=True) optimization = PassManager() unroll = [pass_ for x in translation.passes() for pass_ in x["passes"]] optimization.append(_depth_check + _size_check) opt_loop = _opt + unroll + _depth_check + _size_check optimization.append(opt_loop, do_while=_opt_control) sched = common.generate_scheduling(instruction_durations, scheduling_method, timing_constraints, inst_map) return StagedPassManager( init=unroll_3q, layout=layout, pre_routing=pre_routing, routing=routing, translation=translation, pre_optimization=pre_optimization, optimization=optimization, scheduling=sched, )
def level_0_pass_manager( pass_manager_config: PassManagerConfig) -> PassManager: """Level 0 pass manager: no explicit optimization other than mapping to backend. This pass manager applies the user-given initial layout. If none is given, a trivial layout consisting of mapping the i-th virtual qubit to the i-th physical qubit is used. Any unused physical qubit is allocated as ancilla space. The pass manager then unrolls the circuit to the desired basis, and transforms the circuit to match the coupling map. Note: In simulators where ``coupling_map=None``, only the unrolling and optimization stages are done. Args: pass_manager_config: configuration of the pass manager. Returns: a level 0 pass manager. Raises: TranspilerError: if the passmanager config is invalid. """ basis_gates = pass_manager_config.basis_gates coupling_map = pass_manager_config.coupling_map initial_layout = pass_manager_config.initial_layout layout_method = pass_manager_config.layout_method or 'trivial' routing_method = pass_manager_config.routing_method or 'stochastic' seed_transpiler = pass_manager_config.seed_transpiler backend_properties = pass_manager_config.backend_properties # 1. Choose an initial layout if not set by user (default: trivial layout) _given_layout = SetLayout(initial_layout) def _choose_layout_condition(property_set): return not property_set['layout'] if layout_method == 'trivial': _choose_layout = TrivialLayout(coupling_map) elif layout_method == 'dense': _choose_layout = DenseLayout(coupling_map, backend_properties) elif layout_method == 'noise_adaptive': _choose_layout = NoiseAdaptiveLayout(backend_properties) else: raise TranspilerError("Invalid layout method %s." % layout_method) # 2. Extend dag/layout with ancillas using the full coupling map _embed = [ FullAncillaAllocation(coupling_map), EnlargeWithAncilla(), ApplyLayout() ] # 3. Decompose so only 1-qubit and 2-qubit gates remain _unroll3q = Unroll3qOrMore() # 4. Swap to fit the coupling map _swap_check = CheckMap(coupling_map) def _swap_condition(property_set): return not property_set['is_swap_mapped'] _swap = [BarrierBeforeFinalMeasurements()] if routing_method == 'basic': _swap += [BasicSwap(coupling_map)] elif routing_method == 'stochastic': _swap += [ StochasticSwap(coupling_map, trials=20, seed=seed_transpiler) ] elif routing_method == 'lookahead': _swap += [LookaheadSwap(coupling_map, search_depth=2, search_width=2)] else: raise TranspilerError("Invalid routing method %s." % routing_method) # 5. Unroll to the basis _unroll = Unroller(basis_gates) # 6. Fix any bad CX directions _direction_check = [CheckCXDirection(coupling_map)] def _direction_condition(property_set): return not property_set['is_direction_mapped'] _direction = [CXDirection(coupling_map)] # Build pass manager pm0 = PassManager() if coupling_map: pm0.append(_given_layout) pm0.append(_choose_layout, condition=_choose_layout_condition) pm0.append(_embed) pm0.append(_unroll3q) pm0.append(_swap_check) pm0.append(_swap, condition=_swap_condition) pm0.append(_unroll) if coupling_map and not coupling_map.is_symmetric: pm0.append(_direction_check) pm0.append(_direction, condition=_direction_condition) return pm0
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.get_qubits(): identity_wire_map[qubit] = qubit for bit in circuit_graph.get_bits(): 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("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("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
def multi_pass_manager(pass_manager_config: PassManagerConfig, crosstalk_prop=None) -> PassManager: basis_gates = pass_manager_config.basis_gates coupling_map = pass_manager_config.coupling_map initial_layout = pass_manager_config.initial_layout layout_method = pass_manager_config.layout_method or 'dense' routing_method = pass_manager_config.routing_method or 'stochastic' translation_method = pass_manager_config.translation_method or 'translator' scheduling_method = pass_manager_config.scheduling_method instruction_durations = pass_manager_config.instruction_durations seed_transpiler = pass_manager_config.seed_transpiler backend_properties = pass_manager_config.backend_properties # 1. Unroll to 1q or 2q gates _unroll3q = Unroll3qOrMore() # 2. Layout on good qubits if calibration info available, otherwise on dense links _given_layout = SetLayout(initial_layout) def _choose_layout_condition(property_set): return not property_set['layout'] _choose_layout_1 = CSPLayout(coupling_map, call_limit=10000, time_limit=60) if layout_method == 'trivial': _choose_layout_2 = TrivialLayout(coupling_map) elif layout_method == 'dense': _choose_layout_2 = DenseLayout(coupling_map, backend_properties) elif layout_method == 'noise_adaptive': _choose_layout_2 = NoiseAdaptiveLayout(backend_properties) elif layout_method == 'sabre': _choose_layout_2 = SabreLayout(coupling_map, max_iterations=4, seed=seed_transpiler) elif layout_method == 'xtalk_adaptive': _choose_layout_2 = CrosstalkAdaptiveMultiLayout( backend_properties, crosstalk_prop=crosstalk_prop) # elif layout_method == 'xtalk_sabre': # _choose_layout_2 = CrosstalkSabreLayout(coupling_map, max_iterations=4, seed=seed_transpiler, crosstalk_prop=crosstalk_prop) else: raise TranspilerError("Invalid layout method %s." % layout_method) # 3. Extend dag/layout with ancillas using the full coupling map _embed = [ FullAncillaAllocation(coupling_map), EnlargeWithAncilla(), ApplyLayout() ] # 4. Swap to fit the coupling map _swap_check = CheckMap(coupling_map) def _swap_condition(property_set): return not property_set['is_swap_mapped'] _swap = [BarrierBeforeFinalMeasurements()] if routing_method == 'basic': _swap += [BasicSwap(coupling_map)] elif routing_method == 'stochastic': _swap += [ StochasticSwap(coupling_map, trials=200, seed=seed_transpiler) ] elif routing_method == 'lookahead': _swap += [LookaheadSwap(coupling_map, search_depth=5, search_width=6)] elif routing_method == 'sabre': _swap += [ SabreSwap(coupling_map, heuristic='decay', seed=seed_transpiler) ] else: raise TranspilerError("Invalid routing method %s." % routing_method) # 5. Unroll to the basis if translation_method == 'unroller': _unroll = [Unroller(basis_gates)] elif translation_method == 'translator': from qiskit.circuit.equivalence_library import SessionEquivalenceLibrary as sel _unroll = [ UnrollCustomDefinitions(sel, basis_gates), BasisTranslator(sel, basis_gates) ] elif translation_method == 'synthesis': _unroll = [ Unroll3qOrMore(), Collect2qBlocks(), ConsolidateBlocks(basis_gates=basis_gates), UnitarySynthesis(basis_gates), ] else: raise TranspilerError("Invalid translation method %s." % translation_method) # 6. Fix any CX direction mismatch _direction_check = [CheckCXDirection(coupling_map)] def _direction_condition(property_set): return not property_set['is_direction_mapped'] _direction = [CXDirection(coupling_map)] # 8. Optimize iteratively until no more change in depth. Removes useless gates # after reset and before measure, commutes gates and optimizes continguous blocks. _depth_check = [Depth(), FixedPoint('depth')] def _opt_control(property_set): return not property_set['depth_fixed_point'] _reset = [RemoveResetInZeroState()] _meas = [OptimizeSwapBeforeMeasure(), RemoveDiagonalGatesBeforeMeasure()] if basis_gates and ('u1' in basis_gates or 'u2' in basis_gates or 'u3' in basis_gates): _opt = [ Collect2qBlocks(), ConsolidateBlocks(basis_gates=basis_gates), UnitarySynthesis(basis_gates), Optimize1qGates(basis_gates), CommutativeCancellation(), ] else: _opt = [ Collect2qBlocks(), ConsolidateBlocks(basis_gates=basis_gates), UnitarySynthesis(basis_gates), Optimize1qGatesDecomposition(basis_gates), CommutativeCancellation(), ] # Schedule the circuit only when scheduling_method is supplied if scheduling_method: _scheduling = [TimeUnitAnalysis(instruction_durations)] if scheduling_method in {'alap', 'as_late_as_possible'}: _scheduling += [MultiALAPSchedule(instruction_durations)] elif scheduling_method in {'asap', 'as_soon_as_possible'}: # _scheduling += [ASAPSchedule(instruction_durations)] """FIXME""" raise TranspilerError( "Sorry now this method is not available: %s." % scheduling_method) else: raise TranspilerError("Invalid scheduling method %s." % scheduling_method) # Build pass manager multi_pm = PassManager() multi_pm.append(_unroll3q) multi_pm.append(_reset + _meas) if coupling_map or initial_layout: multi_pm.append(_given_layout) multi_pm.append(_choose_layout_1, condition=_choose_layout_condition) multi_pm.append(_choose_layout_2, condition=_choose_layout_condition) multi_pm.append(_embed) multi_pm.append(_swap_check) multi_pm.append(_swap, condition=_swap_condition) multi_pm.append(_unroll) multi_pm.append(_depth_check + _opt + _unroll, do_while=_opt_control) if coupling_map and not coupling_map.is_symmetric: multi_pm.append(_direction_check) multi_pm.append(_direction, condition=_direction_condition) multi_pm.append(_reset) if scheduling_method: multi_pm.append(_scheduling) return multi_pm
def level_1_pass_manager( pass_manager_config: PassManagerConfig) -> PassManager: """Level 1 pass manager: light optimization by simple adjacent gate collapsing. This pass manager applies the user-given initial layout. If none is given, and a trivial layout (i-th virtual -> i-th physical) makes the circuit fit the coupling map, that is used. Otherwise, the circuit is mapped to the most densely connected coupling subgraph, and swaps are inserted to map. Any unused physical qubit is allocated as ancilla space. The pass manager then unrolls the circuit to the desired basis, and transforms the circuit to match the coupling map. Finally, optimizations in the form of adjacent gate collapse and redundant reset removal are performed. Note: In simulators where ``coupling_map=None``, only the unrolling and optimization stages are done. Args: pass_manager_config: configuration of the pass manager. Returns: a level 1 pass manager. Raises: TranspilerError: if the passmanager config is invalid. """ basis_gates = pass_manager_config.basis_gates coupling_map = pass_manager_config.coupling_map initial_layout = pass_manager_config.initial_layout layout_method = pass_manager_config.layout_method or 'dense' routing_method = pass_manager_config.routing_method or 'stochastic' seed_transpiler = pass_manager_config.seed_transpiler backend_properties = pass_manager_config.backend_properties # 1. Use trivial layout if no layout given _given_layout = SetLayout(initial_layout) _choose_layout_and_score = [ TrivialLayout(coupling_map), Layout2qDistance(coupling_map, property_name='trivial_layout_score') ] def _choose_layout_condition(property_set): return not property_set['layout'] # 2. Use a better layout on densely connected qubits, if circuit needs swaps if layout_method == 'trivial': _improve_layout = TrivialLayout(coupling_map) elif layout_method == 'dense': _improve_layout = DenseLayout(coupling_map, backend_properties) elif layout_method == 'noise_adaptive': _improve_layout = NoiseAdaptiveLayout(backend_properties) else: raise TranspilerError("Invalid layout method %s." % layout_method) def _not_perfect_yet(property_set): return property_set['trivial_layout_score'] is not None and \ property_set['trivial_layout_score'] != 0 # 3. Extend dag/layout with ancillas using the full coupling map _embed = [ FullAncillaAllocation(coupling_map), EnlargeWithAncilla(), ApplyLayout() ] # 4. Decompose so only 1-qubit and 2-qubit gates remain _unroll3q = Unroll3qOrMore() # 5. Swap to fit the coupling map _swap_check = CheckMap(coupling_map) def _swap_condition(property_set): return not property_set['is_swap_mapped'] _swap = [BarrierBeforeFinalMeasurements()] if routing_method == 'basic': _swap += [BasicSwap(coupling_map)] elif routing_method == 'stochastic': _swap += [ StochasticSwap(coupling_map, trials=20, seed=seed_transpiler) ] elif routing_method == 'lookahead': _swap += [LookaheadSwap(coupling_map, search_depth=4, search_width=4)] else: raise TranspilerError("Invalid routing method %s." % routing_method) # 6. Unroll to the basis _unroll = Unroller(basis_gates) # 7. Fix any bad CX directions _direction_check = [CheckCXDirection(coupling_map)] def _direction_condition(property_set): return not property_set['is_direction_mapped'] _direction = [CXDirection(coupling_map)] # 8. Remove zero-state reset _reset = RemoveResetInZeroState() # 9. Merge 1q rotations and cancel CNOT gates iteratively until no more change in depth _depth_check = [Depth(), FixedPoint('depth')] def _opt_control(property_set): return not property_set['depth_fixed_point'] _opt = [Optimize1qGates(basis_gates), CXCancellation()] # Build pass manager pm1 = PassManager() if coupling_map: pm1.append(_given_layout) pm1.append(_choose_layout_and_score, condition=_choose_layout_condition) pm1.append(_improve_layout, condition=_not_perfect_yet) pm1.append(_embed) pm1.append(_unroll3q) pm1.append(_swap_check) pm1.append(_swap, condition=_swap_condition) pm1.append(_unroll) if coupling_map and not coupling_map.is_symmetric: pm1.append(_direction_check) pm1.append(_direction, condition=_direction_condition) pm1.append(_reset) pm1.append(_depth_check + _opt, do_while=_opt_control) return pm1
def chain(self, num_qubits=None): """Finds a chain of qubits such that qubit *i* has a connection with qubits *(i-1)* and *(i+1)* in the coupling chain. Relies on best_subset() to select a subset of qubits with high cx reliability. Sometimes not all qubits in a device can be arranged in a chain. If necessary, such outliers will be inserted in the chain after one of their neighbors. Args: num_qubits (int): number of virtual qubits, defaults to the number of qubits of the coupling chain. Raises: TranspilerError: if invalid options """ if num_qubits is None: num_qubits = self.coupling_map.size() if num_qubits > self.coupling_map.size(): raise TranspilerError('Number of qubits greater than device.') current = 0 full_map = [current] isolated = [] isolated_with_data = [] explored = set() explored.add(current) last_back_step = None # loop over the coupling map until all qubits no more qubits # can be connected to the chain while True: neighbors = [] no_neighbors = True for n in self.coupling_graph[current].keys(): if n not in explored: if self.check_isolated_not_last(n, explored): if self.backend_prop is None: isolated_with_data.append((current, n)) else: isolated_with_data.append( (current, n, self.cx_reliab[(current, n)])) isolated.append(n) explored.add(n) no_neighbors = False neighbors.append(n) logger.debug('Neighbors: %s' % str(neighbors)) # try to select next qubit from neighbors of last connected qubit if no_neighbors is False: if current + 1 in neighbors: next = current + 1 else: next = min(neighbors) else: # if no neighbors are found, go back the chain until a new neighbor is found # and restart the loop from there if self.backend_prop is None: isolated_with_data.append((full_map[-2], current)) else: isolated_with_data.append( (full_map[-2], current, self.cx_reliab[(full_map[-2], current)])) isolated.append(current) full_map.remove(current) current = full_map[-1] logger.debug('last back step: %s' % str(last_back_step)) if current != last_back_step: last_back_step = current else: break continue explored.add(next) current = next full_map.append(next) logger.debug('Full chain: %s' % str(full_map)) logger.debug('Explored: %s' % str(explored)) logger.debug('Isolated: %s' % str(isolated_with_data)) # check that there are still qubits to explore if len(explored) < self.coupling_map.size() - 1: neighbors1 = [] for n1 in self.coupling_graph[next].keys(): if n1 not in explored: neighbors1.append(n1) # check that the selected qubit does not lead to a dead end for n1 in neighbors1: to_remove = True for n2 in self.coupling_graph[n1].keys(): if n2 not in explored or n2 == next: to_remove = False if to_remove is True: explored.add(n1) if self.backend_prop is None: isolated_with_data.append((next, n1)) else: isolated_with_data.append( (next, n1, self.cx_reliab[(next, n1)])) isolated.append(n1) # break the loop when all qubits have been explored if len(explored) == self.coupling_map.size(): # if enough qubits have been connected, # return a subset with high cx reliability if len(full_map) >= num_qubits: return self.best_subset(full_map, num_qubits) break # check for isolated qubits for q in range(self.coupling_map.size()): if q not in explored and q not in isolated: for i in isolated: if q in self.coupling_graph[i].keys(): if self.backend_prop is None: isolated_with_data.append((i, q)) else: isolated_with_data.append( (i, q, self.cx_reliab[(i, q)])) isolated.append(q) explored.add(q) break if q not in isolated: for n in self.coupling_graph[q].keys(): if n in full_map: if self.backend_prop is None: isolated_with_data.append((n, q)) else: isolated_with_data.append( (n, q, self.cx_reliab[(n, q)])) isolated.append(q) explored.add(q) break # if the chain is not long enough, add the isolated qubits logger.debug('Searching for isolated') remaining = num_qubits - len(full_map) if remaining > 0: if self.backend_prop is not None: isolated_with_data = sorted(isolated_with_data, key=lambda x: x[2], reverse=True) while remaining > 0: for next in isolated_with_data: if next[0] in full_map: logger.debug(next) full_map.insert(full_map.index(next[0]) + 1, next[1]) isolated_with_data.remove(next) isolated.remove(next[1]) remaining -= 1 break return full_map
def level_3_pass_manager( pass_manager_config: PassManagerConfig) -> PassManager: """Level 3 pass manager: heavy optimization by noise adaptive qubit mapping and gate cancellation using commutativity rules and unitary synthesis. This pass manager applies the user-given initial layout. If none is given, a search for a perfect layout (i.e. one that satisfies all 2-qubit interactions) is conducted. If no such layout is found, and device calibration information is available, the circuit is mapped to the qubits with best readouts and to CX gates with highest fidelity. The pass manager then transforms the circuit to match the coupling constraints. It is then unrolled to the basis, and any flipped cx directions are fixed. Finally, optimizations in the form of commutative gate cancellation, resynthesis of two-qubit unitary blocks, and redundant reset removal are performed. Note: In simulators where ``coupling_map=None``, only the unrolling and optimization stages are done. Args: pass_manager_config: configuration of the pass manager. Returns: a level 3 pass manager. Raises: TranspilerError: if the passmanager config is invalid. """ basis_gates = pass_manager_config.basis_gates coupling_map = pass_manager_config.coupling_map initial_layout = pass_manager_config.initial_layout layout_method = pass_manager_config.layout_method or 'dense' routing_method = pass_manager_config.routing_method or 'stochastic' translation_method = pass_manager_config.translation_method or 'translator' scheduling_method = pass_manager_config.scheduling_method instruction_durations = pass_manager_config.instruction_durations seed_transpiler = pass_manager_config.seed_transpiler backend_properties = pass_manager_config.backend_properties # 1. Unroll to 1q or 2q gates _unroll3q = Unroll3qOrMore() # 2. Layout on good qubits if calibration info available, otherwise on dense links _given_layout = SetLayout(initial_layout) def _choose_layout_condition(property_set): return not property_set['layout'] _choose_layout_1 = [] if pass_manager_config.layout_method \ else CSPLayout(coupling_map, call_limit=10000, time_limit=60, seed=seed_transpiler) if layout_method == 'trivial': _choose_layout_2 = TrivialLayout(coupling_map) elif layout_method == 'dense': _choose_layout_2 = DenseLayout(coupling_map, backend_properties) elif layout_method == 'noise_adaptive': _choose_layout_2 = NoiseAdaptiveLayout(backend_properties) elif layout_method == 'sabre': _choose_layout_2 = SabreLayout(coupling_map, max_iterations=4, seed=seed_transpiler) else: raise TranspilerError("Invalid layout method %s." % layout_method) # 3. Extend dag/layout with ancillas using the full coupling map _embed = [ FullAncillaAllocation(coupling_map), EnlargeWithAncilla(), ApplyLayout() ] # 4. Swap to fit the coupling map _swap_check = CheckMap(coupling_map) def _swap_condition(property_set): return not property_set['is_swap_mapped'] _swap = [BarrierBeforeFinalMeasurements()] if routing_method == 'basic': _swap += [BasicSwap(coupling_map)] elif routing_method == 'stochastic': _swap += [ StochasticSwap(coupling_map, trials=200, seed=seed_transpiler) ] elif routing_method == 'lookahead': _swap += [LookaheadSwap(coupling_map, search_depth=5, search_width=6)] elif routing_method == 'sabre': _swap += [ SabreSwap(coupling_map, heuristic='decay', seed=seed_transpiler) ] elif routing_method == 'none': _swap += [ Error( msg= 'No routing method selected, but circuit is not routed to device. ' 'CheckMap Error: {check_map_msg}', action='raise') ] else: raise TranspilerError("Invalid routing method %s." % routing_method) # 5. Unroll to the basis if translation_method == 'unroller': _unroll = [Unroller(basis_gates)] elif translation_method == 'translator': from qiskit.circuit.equivalence_library import SessionEquivalenceLibrary as sel _unroll = [ UnrollCustomDefinitions(sel, basis_gates), BasisTranslator(sel, basis_gates) ] elif translation_method == 'synthesis': _unroll = [ Unroll3qOrMore(), Collect2qBlocks(), ConsolidateBlocks(basis_gates=basis_gates), UnitarySynthesis(basis_gates), ] else: raise TranspilerError("Invalid translation method %s." % translation_method) # 6. Fix any CX direction mismatch _direction_check = [CheckCXDirection(coupling_map)] def _direction_condition(property_set): return not property_set['is_direction_mapped'] _direction = [CXDirection(coupling_map)] # 8. Optimize iteratively until no more change in depth. Removes useless gates # after reset and before measure, commutes gates and optimizes contiguous blocks. _depth_check = [Depth(), FixedPoint('depth')] def _opt_control(property_set): return not property_set['depth_fixed_point'] _reset = [RemoveResetInZeroState()] _meas = [OptimizeSwapBeforeMeasure(), RemoveDiagonalGatesBeforeMeasure()] _opt = [ Collect2qBlocks(), ConsolidateBlocks(basis_gates=basis_gates), UnitarySynthesis(basis_gates), Optimize1qGatesDecomposition(basis_gates), CommutativeCancellation(), ] # Schedule the circuit only when scheduling_method is supplied if scheduling_method: _scheduling = [TimeUnitAnalysis(instruction_durations)] if scheduling_method in {'alap', 'as_late_as_possible'}: _scheduling += [ALAPSchedule(instruction_durations)] elif scheduling_method in {'asap', 'as_soon_as_possible'}: _scheduling += [ASAPSchedule(instruction_durations)] else: raise TranspilerError("Invalid scheduling method %s." % scheduling_method) # Build pass manager pm3 = PassManager() pm3.append(_unroll3q) pm3.append(_reset + _meas) if coupling_map or initial_layout: pm3.append(_given_layout) pm3.append(_choose_layout_1, condition=_choose_layout_condition) pm3.append(_choose_layout_2, condition=_choose_layout_condition) pm3.append(_embed) pm3.append(_swap_check) pm3.append(_swap, condition=_swap_condition) pm3.append(_unroll) pm3.append(_depth_check + _opt + _unroll, do_while=_opt_control) if coupling_map and not coupling_map.is_symmetric: pm3.append(_direction_check) pm3.append(_direction, condition=_direction_condition) pm3.append(_reset) if scheduling_method: pm3.append(_scheduling) return pm3
def level_0_pass_manager( pass_manager_config: PassManagerConfig) -> PassManager: """Level 0 pass manager: no explicit optimization other than mapping to backend. This pass manager applies the user-given initial layout. If none is given, a trivial layout consisting of mapping the i-th virtual qubit to the i-th physical qubit is used. Any unused physical qubit is allocated as ancilla space. The pass manager then unrolls the circuit to the desired basis, and transforms the circuit to match the coupling map. Note: In simulators where ``coupling_map=None``, only the unrolling and optimization stages are done. Args: pass_manager_config: configuration of the pass manager. Returns: a level 0 pass manager. Raises: TranspilerError: if the passmanager config is invalid. """ basis_gates = pass_manager_config.basis_gates inst_map = pass_manager_config.inst_map coupling_map = pass_manager_config.coupling_map initial_layout = pass_manager_config.initial_layout layout_method = pass_manager_config.layout_method or "trivial" routing_method = pass_manager_config.routing_method or "stochastic" translation_method = pass_manager_config.translation_method or "translator" scheduling_method = pass_manager_config.scheduling_method instruction_durations = pass_manager_config.instruction_durations seed_transpiler = pass_manager_config.seed_transpiler backend_properties = pass_manager_config.backend_properties approximation_degree = pass_manager_config.approximation_degree timing_constraints = pass_manager_config.timing_constraints or TimingConstraints( ) unitary_synthesis_method = pass_manager_config.unitary_synthesis_method unitary_synthesis_plugin_config = pass_manager_config.unitary_synthesis_plugin_config target = pass_manager_config.target # 1. Decompose so only 1-qubit and 2-qubit gates remain _unroll3q = [ # Use unitary synthesis for basis aware decomposition of UnitaryGates UnitarySynthesis( basis_gates, approximation_degree=approximation_degree, method=unitary_synthesis_method, min_qubits=3, plugin_config=unitary_synthesis_plugin_config, ), Unroll3qOrMore(), ] # 2. Choose an initial layout if not set by user (default: trivial layout) _given_layout = SetLayout(initial_layout) def _choose_layout_condition(property_set): return not property_set["layout"] if layout_method == "trivial": _choose_layout = TrivialLayout(coupling_map) elif layout_method == "dense": _choose_layout = DenseLayout(coupling_map, backend_properties) elif layout_method == "noise_adaptive": _choose_layout = NoiseAdaptiveLayout(backend_properties) elif layout_method == "sabre": _choose_layout = SabreLayout(coupling_map, max_iterations=1, seed=seed_transpiler) else: raise TranspilerError("Invalid layout method %s." % layout_method) # 3. Extend dag/layout with ancillas using the full coupling map _embed = [ FullAncillaAllocation(coupling_map), EnlargeWithAncilla(), ApplyLayout() ] # 4. Swap to fit the coupling map _swap_check = CheckMap(coupling_map) def _swap_condition(property_set): return not property_set["is_swap_mapped"] _swap = [BarrierBeforeFinalMeasurements()] if routing_method == "basic": _swap += [BasicSwap(coupling_map)] elif routing_method == "stochastic": _swap += [ StochasticSwap(coupling_map, trials=20, seed=seed_transpiler) ] elif routing_method == "lookahead": _swap += [LookaheadSwap(coupling_map, search_depth=2, search_width=2)] elif routing_method == "sabre": _swap += [ SabreSwap(coupling_map, heuristic="basic", seed=seed_transpiler) ] elif routing_method == "none": _swap += [ Error( msg= ("No routing method selected, but circuit is not routed to device. " "CheckMap Error: {check_map_msg}"), action="raise", ) ] else: raise TranspilerError("Invalid routing method %s." % routing_method) # 5. Unroll to the basis if translation_method == "unroller": _unroll = [Unroller(basis_gates)] elif translation_method == "translator": from qiskit.circuit.equivalence_library import SessionEquivalenceLibrary as sel _unroll = [ UnitarySynthesis( basis_gates, approximation_degree=approximation_degree, coupling_map=coupling_map, backend_props=backend_properties, method=unitary_synthesis_method, plugin_config=unitary_synthesis_plugin_config, ), UnrollCustomDefinitions(sel, basis_gates), BasisTranslator(sel, basis_gates, target), ] elif translation_method == "synthesis": _unroll = [ UnitarySynthesis( basis_gates, approximation_degree=approximation_degree, coupling_map=coupling_map, backend_props=backend_properties, method=unitary_synthesis_method, min_qubits=3, plugin_config=unitary_synthesis_plugin_config, ), Unroll3qOrMore(), Collect2qBlocks(), Collect1qRuns(), ConsolidateBlocks(basis_gates=basis_gates), UnitarySynthesis( basis_gates, approximation_degree=approximation_degree, coupling_map=coupling_map, backend_props=backend_properties, method=unitary_synthesis_method, plugin_config=unitary_synthesis_plugin_config, ), ] else: raise TranspilerError("Invalid translation method %s." % translation_method) # 6. Fix any bad CX directions _direction_check = [CheckGateDirection(coupling_map, target)] def _direction_condition(property_set): return not property_set["is_direction_mapped"] _direction = [GateDirection(coupling_map, target)] # 7. Unify all durations (either SI, or convert to dt if known) # Schedule the circuit only when scheduling_method is supplied _time_unit_setup = [ContainsInstruction("delay")] _time_unit_conversion = [TimeUnitConversion(instruction_durations)] def _contains_delay(property_set): return property_set["contains_delay"] _scheduling = [] if scheduling_method: _scheduling += _time_unit_conversion if scheduling_method in {"alap", "as_late_as_possible"}: _scheduling += [ALAPSchedule(instruction_durations)] elif scheduling_method in {"asap", "as_soon_as_possible"}: _scheduling += [ASAPSchedule(instruction_durations)] else: raise TranspilerError("Invalid scheduling method %s." % scheduling_method) # 8. Call measure alignment. Should come after scheduling. if (timing_constraints.granularity != 1 or timing_constraints.min_length != 1 or timing_constraints.acquire_alignment != 1): _alignments = [ ValidatePulseGates(granularity=timing_constraints.granularity, min_length=timing_constraints.min_length), AlignMeasures(alignment=timing_constraints.acquire_alignment), ] else: _alignments = [] # Build pass manager pm0 = PassManager() if coupling_map or initial_layout: pm0.append(_given_layout) pm0.append(_unroll3q) pm0.append(_choose_layout, condition=_choose_layout_condition) pm0.append(_embed) pm0.append(_swap_check) pm0.append(_swap, condition=_swap_condition) pm0.append(_unroll) if (coupling_map and not coupling_map.is_symmetric) or ( target is not None and target.get_non_global_operation_names(strict_direction=True)): pm0.append(_direction_check) pm0.append(_direction, condition=_direction_condition) pm0.append(_unroll) if inst_map and inst_map.has_custom_gate(): pm0.append(PulseGates(inst_map=inst_map)) if scheduling_method: pm0.append(_scheduling) elif instruction_durations: pm0.append(_time_unit_setup) pm0.append(_time_unit_conversion, condition=_contains_delay) pm0.append(_alignments) return pm0
def level_3_pass_manager( pass_manager_config: PassManagerConfig) -> StagedPassManager: """Level 3 pass manager: heavy optimization by noise adaptive qubit mapping and gate cancellation using commutativity rules and unitary synthesis. This pass manager applies the user-given initial layout. If none is given, a search for a perfect layout (i.e. one that satisfies all 2-qubit interactions) is conducted. If no such layout is found, and device calibration information is available, the circuit is mapped to the qubits with best readouts and to CX gates with highest fidelity. The pass manager then transforms the circuit to match the coupling constraints. It is then unrolled to the basis, and any flipped cx directions are fixed. Finally, optimizations in the form of commutative gate cancellation, resynthesis of two-qubit unitary blocks, and redundant reset removal are performed. Args: pass_manager_config: configuration of the pass manager. Returns: a level 3 pass manager. Raises: TranspilerError: if the passmanager config is invalid. """ basis_gates = pass_manager_config.basis_gates inst_map = pass_manager_config.inst_map coupling_map = pass_manager_config.coupling_map initial_layout = pass_manager_config.initial_layout layout_method = pass_manager_config.layout_method or "sabre" routing_method = pass_manager_config.routing_method or "sabre" translation_method = pass_manager_config.translation_method or "translator" scheduling_method = pass_manager_config.scheduling_method instruction_durations = pass_manager_config.instruction_durations seed_transpiler = pass_manager_config.seed_transpiler backend_properties = pass_manager_config.backend_properties approximation_degree = pass_manager_config.approximation_degree unitary_synthesis_method = pass_manager_config.unitary_synthesis_method timing_constraints = pass_manager_config.timing_constraints or TimingConstraints( ) unitary_synthesis_plugin_config = pass_manager_config.unitary_synthesis_plugin_config target = pass_manager_config.target # Layout on good qubits if calibration info available, otherwise on dense links _given_layout = SetLayout(initial_layout) def _choose_layout_condition(property_set): # layout hasn't been set yet return not property_set["layout"] def _vf2_match_not_found(property_set): # If a layout hasn't been set by the time we run vf2 layout we need to # run layout if property_set["layout"] is None: return True # if VF2 layout stopped for any reason other than solution found we need # to run layout since VF2 didn't converge. if (property_set["VF2Layout_stop_reason"] is not None and property_set["VF2Layout_stop_reason"] is not VF2LayoutStopReason.SOLUTION_FOUND): return True return False # 2a. If layout method is not set, first try VF2Layout _choose_layout_0 = ([] if pass_manager_config.layout_method else VF2Layout( coupling_map, seed=seed_transpiler, call_limit=int(3e7), # Set call limit to ~60 sec with retworkx 0.10.2 properties=backend_properties, target=target, )) # 2b. if VF2 didn't converge on a solution use layout_method (dense). if layout_method == "trivial": _choose_layout_1 = TrivialLayout(coupling_map) elif layout_method == "dense": _choose_layout_1 = DenseLayout(coupling_map, backend_properties, target=target) elif layout_method == "noise_adaptive": _choose_layout_1 = NoiseAdaptiveLayout(backend_properties) elif layout_method == "sabre": _choose_layout_1 = SabreLayout(coupling_map, max_iterations=4, seed=seed_transpiler) else: raise TranspilerError("Invalid layout method %s." % layout_method) toqm_pass = False if routing_method == "basic": routing_pass = BasicSwap(coupling_map) elif routing_method == "stochastic": routing_pass = StochasticSwap(coupling_map, trials=200, seed=seed_transpiler) elif routing_method == "lookahead": routing_pass = LookaheadSwap(coupling_map, search_depth=5, search_width=6) elif routing_method == "sabre": routing_pass = SabreSwap(coupling_map, heuristic="decay", seed=seed_transpiler) elif routing_method == "toqm": HAS_TOQM.require_now("TOQM-based routing") from qiskit_toqm import ToqmSwap, ToqmStrategyO3, latencies_from_target if initial_layout: raise TranspilerError( "Initial layouts are not supported with TOQM-based routing.") toqm_pass = True # Note: BarrierBeforeFinalMeasurements is skipped intentionally since ToqmSwap # does not yet support barriers. routing_pass = ToqmSwap( coupling_map, strategy=ToqmStrategyO3( latencies_from_target(coupling_map, instruction_durations, basis_gates, backend_properties, target)), ) elif routing_method == "none": routing_pass = Error( msg= "No routing method selected, but circuit is not routed to device. " "CheckMap Error: {check_map_msg}", action="raise", ) else: raise TranspilerError("Invalid routing method %s." % routing_method) # 8. Optimize iteratively until no more change in depth. Removes useless gates # after reset and before measure, commutes gates and optimizes contiguous blocks. _depth_check = [Depth(), FixedPoint("depth")] _size_check = [Size(), FixedPoint("size")] def _opt_control(property_set): return (not property_set["depth_fixed_point"]) or ( not property_set["size_fixed_point"]) _opt = [ Collect2qBlocks(), ConsolidateBlocks(basis_gates=basis_gates, target=target), UnitarySynthesis( basis_gates, approximation_degree=approximation_degree, coupling_map=coupling_map, backend_props=backend_properties, method=unitary_synthesis_method, plugin_config=unitary_synthesis_plugin_config, target=target, ), Optimize1qGatesDecomposition(basis_gates), CommutativeCancellation(), ] # Build pass manager init = common.generate_unroll_3q( target, basis_gates, approximation_degree, unitary_synthesis_method, unitary_synthesis_plugin_config, ) init.append(RemoveResetInZeroState()) init.append(OptimizeSwapBeforeMeasure()) init.append(RemoveDiagonalGatesBeforeMeasure()) if coupling_map or initial_layout: layout = PassManager() layout.append(_given_layout) layout.append(_choose_layout_0, condition=_choose_layout_condition) layout.append(_choose_layout_1, condition=_vf2_match_not_found) layout += common.generate_embed_passmanager(coupling_map) vf2_call_limit = None if pass_manager_config.layout_method is None and pass_manager_config.initial_layout is None: vf2_call_limit = int( 3e7) # Set call limit to ~60 sec with retworkx 0.10.2 routing = common.generate_routing_passmanager( routing_pass, target, coupling_map=coupling_map, vf2_call_limit=vf2_call_limit, backend_properties=backend_properties, seed_transpiler=seed_transpiler, use_barrier_before_measurement=not toqm_pass, ) else: layout = None routing = None translation = common.generate_translation_passmanager( target, basis_gates, translation_method, approximation_degree, coupling_map, backend_properties, unitary_synthesis_method, unitary_synthesis_plugin_config, ) pre_routing = None if toqm_pass: pre_routing = translation optimization = PassManager() unroll = [pass_ for x in translation.passes() for pass_ in x["passes"]] optimization.append(_depth_check + _size_check) if (coupling_map and not coupling_map.is_symmetric) or ( target is not None and target.get_non_global_operation_names(strict_direction=True)): pre_optimization = common.generate_pre_op_passmanager( target, coupling_map, True) _direction = [ pass_ for x in common.generate_pre_op_passmanager( target, coupling_map).passes() for pass_ in x["passes"] ] # For transpiling to a target we need to run GateDirection in the # optimization loop to correct for incorrect directions that might be # inserted by UnitarySynthesis which is direction aware but only via # the coupling map which with a target doesn't give a full picture if target is not None: optimization.append(_opt + unroll + _depth_check + _size_check + _direction, do_while=_opt_control) else: optimization.append(_opt + unroll + _depth_check + _size_check, do_while=_opt_control) else: pre_optimization = common.generate_pre_op_passmanager( remove_reset_in_zero=True) optimization.append(_opt + unroll + _depth_check + _size_check, do_while=_opt_control) opt_loop = _depth_check + _opt + unroll optimization.append(opt_loop, do_while=_opt_control) sched = common.generate_scheduling(instruction_durations, scheduling_method, timing_constraints, inst_map) return StagedPassManager( init=init, layout=layout, pre_routing=pre_routing, routing=routing, translation=translation, pre_optimization=pre_optimization, optimization=optimization, scheduling=sched, )
def level_1_pass_manager( pass_manager_config: PassManagerConfig) -> PassManager: """Level 1 pass manager: light optimization by simple adjacent gate collapsing. This pass manager applies the user-given initial layout. If none is given, and a trivial layout (i-th virtual -> i-th physical) makes the circuit fit the coupling map, that is used. Otherwise, the circuit is mapped to the most densely connected coupling subgraph, and swaps are inserted to map. Any unused physical qubit is allocated as ancilla space. The pass manager then unrolls the circuit to the desired basis, and transforms the circuit to match the coupling map. Finally, optimizations in the form of adjacent gate collapse and redundant reset removal are performed. Note: In simulators where ``coupling_map=None``, only the unrolling and optimization stages are done. Args: pass_manager_config: configuration of the pass manager. Returns: a level 1 pass manager. Raises: TranspilerError: if the passmanager config is invalid. """ basis_gates = pass_manager_config.basis_gates coupling_map = pass_manager_config.coupling_map initial_layout = pass_manager_config.initial_layout layout_method = pass_manager_config.layout_method or 'dense' routing_method = pass_manager_config.routing_method or 'stochastic' translation_method = pass_manager_config.translation_method or 'translator' scheduling_method = pass_manager_config.scheduling_method instruction_durations = pass_manager_config.instruction_durations seed_transpiler = pass_manager_config.seed_transpiler backend_properties = pass_manager_config.backend_properties approximation_degree = pass_manager_config.approximation_degree # 1. Use trivial layout if no layout given _given_layout = SetLayout(initial_layout) _choose_layout_and_score = [ TrivialLayout(coupling_map), Layout2qDistance(coupling_map, property_name='trivial_layout_score') ] def _choose_layout_condition(property_set): return not property_set['layout'] # 2. Use a better layout on densely connected qubits, if circuit needs swaps if layout_method == 'trivial': _improve_layout = TrivialLayout(coupling_map) elif layout_method == 'dense': _improve_layout = DenseLayout(coupling_map, backend_properties) elif layout_method == 'noise_adaptive': _improve_layout = NoiseAdaptiveLayout(backend_properties) elif layout_method == 'sabre': _improve_layout = SabreLayout(coupling_map, max_iterations=2, seed=seed_transpiler) else: raise TranspilerError("Invalid layout method %s." % layout_method) def _not_perfect_yet(property_set): return property_set['trivial_layout_score'] is not None and \ property_set['trivial_layout_score'] != 0 # 3. Extend dag/layout with ancillas using the full coupling map _embed = [ FullAncillaAllocation(coupling_map), EnlargeWithAncilla(), ApplyLayout() ] # 4. Decompose so only 1-qubit and 2-qubit gates remain _unroll3q = Unroll3qOrMore() # 5. Swap to fit the coupling map _swap_check = CheckMap(coupling_map) def _swap_condition(property_set): return not property_set['is_swap_mapped'] _swap = [BarrierBeforeFinalMeasurements()] if routing_method == 'basic': _swap += [BasicSwap(coupling_map)] elif routing_method == 'stochastic': _swap += [ StochasticSwap(coupling_map, trials=20, seed=seed_transpiler) ] elif routing_method == 'lookahead': _swap += [LookaheadSwap(coupling_map, search_depth=4, search_width=4)] elif routing_method == 'sabre': _swap += [ SabreSwap(coupling_map, heuristic='lookahead', seed=seed_transpiler) ] elif routing_method == 'none': _swap += [ Error( msg= 'No routing method selected, but circuit is not routed to device. ' 'CheckMap Error: {check_map_msg}', action='raise') ] else: raise TranspilerError("Invalid routing method %s." % routing_method) # 6. Unroll to the basis if translation_method == 'unroller': _unroll = [Unroller(basis_gates)] elif translation_method == 'translator': from qiskit.circuit.equivalence_library import SessionEquivalenceLibrary as sel _unroll = [ UnrollCustomDefinitions(sel, basis_gates), BasisTranslator(sel, basis_gates) ] elif translation_method == 'synthesis': _unroll = [ Unroll3qOrMore(), Collect2qBlocks(), ConsolidateBlocks(basis_gates=basis_gates), UnitarySynthesis(basis_gates, approximation_degree=approximation_degree), ] else: raise TranspilerError("Invalid translation method %s." % translation_method) # 7. Fix any bad CX directions _direction_check = [CheckGateDirection(coupling_map)] def _direction_condition(property_set): return not property_set['is_direction_mapped'] _direction = [GateDirection(coupling_map)] # 8. Remove zero-state reset _reset = RemoveResetInZeroState() # 9. Merge 1q rotations and cancel CNOT gates iteratively until no more change in depth _depth_check = [Depth(), FixedPoint('depth')] def _opt_control(property_set): return not property_set['depth_fixed_point'] _opt = [Optimize1qGatesDecomposition(basis_gates), CXCancellation()] # 10. Unify all durations (either SI, or convert to dt if known) # Schedule the circuit only when scheduling_method is supplied _scheduling = [TimeUnitConversion(instruction_durations)] if scheduling_method: if scheduling_method in {'alap', 'as_late_as_possible'}: _scheduling += [ALAPSchedule(instruction_durations)] elif scheduling_method in {'asap', 'as_soon_as_possible'}: _scheduling += [ASAPSchedule(instruction_durations)] else: raise TranspilerError("Invalid scheduling method %s." % scheduling_method) # Build pass manager pm1 = PassManager() if coupling_map or initial_layout: pm1.append(_given_layout) pm1.append(_choose_layout_and_score, condition=_choose_layout_condition) pm1.append(_improve_layout, condition=_not_perfect_yet) pm1.append(_embed) pm1.append(_unroll3q) pm1.append(_swap_check) pm1.append(_swap, condition=_swap_condition) pm1.append(_unroll) if coupling_map and not coupling_map.is_symmetric: pm1.append(_direction_check) pm1.append(_direction, condition=_direction_condition) pm1.append(_unroll) pm1.append(_reset) pm1.append(_depth_check + _opt, do_while=_opt_control) pm1.append(_scheduling) return pm1
def level_3_pass_manager(pass_manager_config: PassManagerConfig) -> PassManager: """Level 3 pass manager: heavy optimization by noise adaptive qubit mapping and gate cancellation using commutativity rules and unitary synthesis. This pass manager applies the user-given initial layout. If none is given, a search for a perfect layout (i.e. one that satisfies all 2-qubit interactions) is conducted. If no such layout is found, and device calibration information is available, the circuit is mapped to the qubits with best readouts and to CX gates with highest fidelity. The pass manager then transforms the circuit to match the coupling constraints. It is then unrolled to the basis, and any flipped cx directions are fixed. Finally, optimizations in the form of commutative gate cancellation, resynthesis of two-qubit unitary blocks, and redundant reset removal are performed. Note: In simulators where ``coupling_map=None``, only the unrolling and optimization stages are done. Args: pass_manager_config: configuration of the pass manager. Returns: a level 3 pass manager. Raises: TranspilerError: if the passmanager config is invalid. """ basis_gates = pass_manager_config.basis_gates inst_map = pass_manager_config.inst_map coupling_map = pass_manager_config.coupling_map initial_layout = pass_manager_config.initial_layout layout_method = pass_manager_config.layout_method or "sabre" routing_method = pass_manager_config.routing_method or "sabre" translation_method = pass_manager_config.translation_method or "translator" scheduling_method = pass_manager_config.scheduling_method instruction_durations = pass_manager_config.instruction_durations seed_transpiler = pass_manager_config.seed_transpiler backend_properties = pass_manager_config.backend_properties approximation_degree = pass_manager_config.approximation_degree unitary_synthesis_method = pass_manager_config.unitary_synthesis_method timing_constraints = pass_manager_config.timing_constraints or TimingConstraints() unitary_synthesis_plugin_config = pass_manager_config.unitary_synthesis_plugin_config target = pass_manager_config.target # 1. Unroll to 1q or 2q gates _unroll3q = [ # Use unitary synthesis for basis aware decomposition of UnitaryGates UnitarySynthesis( basis_gates, approximation_degree=approximation_degree, method=unitary_synthesis_method, plugin_config=unitary_synthesis_plugin_config, min_qubits=3, ), Unroll3qOrMore(), ] # 2. Layout on good qubits if calibration info available, otherwise on dense links _given_layout = SetLayout(initial_layout) def _choose_layout_condition(property_set): # layout hasn't been set yet return not property_set["layout"] def _csp_not_found_match(property_set): # If a layout hasn't been set by the time we run csp we need to run layout if property_set["layout"] is None: return True # if CSP layout stopped for any reason other than solution found we need # to run layout since CSP didn't converge. if ( property_set["CSPLayout_stop_reason"] is not None and property_set["CSPLayout_stop_reason"] != "solution found" ): return True return False # 2a. If layout method is not set, first try a trivial layout _choose_layout_0 = ( [] if pass_manager_config.layout_method else [ TrivialLayout(coupling_map), Layout2qDistance(coupling_map, property_name="trivial_layout_score"), ] ) # 2b. If trivial layout wasn't perfect (ie no swaps are needed) then try # using CSP layout to find a perfect layout _choose_layout_1 = ( [] if pass_manager_config.layout_method else CSPLayout(coupling_map, call_limit=10000, time_limit=60, seed=seed_transpiler) ) def _trivial_not_perfect(property_set): # Verify that a trivial layout is perfect. If trivial_layout_score > 0 # the layout is not perfect. The layout property set is unconditionally # set by trivial layout so we clear that before running CSP if property_set["trivial_layout_score"] is not None: if property_set["trivial_layout_score"] != 0: return True return False # 2c. if CSP didn't converge on a solution use layout_method (dense). if layout_method == "trivial": _choose_layout_2 = TrivialLayout(coupling_map) elif layout_method == "dense": _choose_layout_2 = DenseLayout(coupling_map, backend_properties) elif layout_method == "noise_adaptive": _choose_layout_2 = NoiseAdaptiveLayout(backend_properties) elif layout_method == "sabre": _choose_layout_2 = SabreLayout(coupling_map, max_iterations=4, seed=seed_transpiler) else: raise TranspilerError("Invalid layout method %s." % layout_method) # 3. Extend dag/layout with ancillas using the full coupling map _embed = [FullAncillaAllocation(coupling_map), EnlargeWithAncilla(), ApplyLayout()] # 4. Swap to fit the coupling map _swap_check = CheckMap(coupling_map) def _swap_condition(property_set): return not property_set["is_swap_mapped"] _swap = [BarrierBeforeFinalMeasurements()] if routing_method == "basic": _swap += [BasicSwap(coupling_map)] elif routing_method == "stochastic": _swap += [StochasticSwap(coupling_map, trials=200, seed=seed_transpiler)] elif routing_method == "lookahead": _swap += [LookaheadSwap(coupling_map, search_depth=5, search_width=6)] elif routing_method == "sabre": _swap += [SabreSwap(coupling_map, heuristic="decay", seed=seed_transpiler)] elif routing_method == "none": _swap += [ Error( msg=( "No routing method selected, but circuit is not routed to device. " "CheckMap Error: {check_map_msg}" ), action="raise", ) ] else: raise TranspilerError("Invalid routing method %s." % routing_method) # 5. Unroll to the basis if translation_method == "unroller": _unroll = [Unroller(basis_gates)] elif translation_method == "translator": from qiskit.circuit.equivalence_library import SessionEquivalenceLibrary as sel _unroll = [ UnitarySynthesis( basis_gates, approximation_degree=approximation_degree, coupling_map=coupling_map, backend_props=backend_properties, plugin_config=unitary_synthesis_plugin_config, method=unitary_synthesis_method, ), UnrollCustomDefinitions(sel, basis_gates), BasisTranslator(sel, basis_gates, target), ] elif translation_method == "synthesis": _unroll = [ UnitarySynthesis( basis_gates, approximation_degree=approximation_degree, coupling_map=coupling_map, backend_props=backend_properties, method=unitary_synthesis_method, plugin_config=unitary_synthesis_plugin_config, min_qubits=3, ), Unroll3qOrMore(), Collect2qBlocks(), ConsolidateBlocks(basis_gates=basis_gates), UnitarySynthesis( basis_gates, approximation_degree=approximation_degree, coupling_map=coupling_map, backend_props=backend_properties, method=unitary_synthesis_method, plugin_config=unitary_synthesis_plugin_config, ), ] else: raise TranspilerError("Invalid translation method %s." % translation_method) # 6. Fix any CX direction mismatch _direction_check = [CheckGateDirection(coupling_map, target)] def _direction_condition(property_set): return not property_set["is_direction_mapped"] _direction = [GateDirection(coupling_map, target)] # 8. Optimize iteratively until no more change in depth. Removes useless gates # after reset and before measure, commutes gates and optimizes contiguous blocks. _depth_check = [Depth(), FixedPoint("depth")] def _opt_control(property_set): return not property_set["depth_fixed_point"] _reset = [RemoveResetInZeroState()] _meas = [OptimizeSwapBeforeMeasure(), RemoveDiagonalGatesBeforeMeasure()] _opt = [ Collect2qBlocks(), ConsolidateBlocks(basis_gates=basis_gates), UnitarySynthesis( basis_gates, approximation_degree=approximation_degree, coupling_map=coupling_map, backend_props=backend_properties, method=unitary_synthesis_method, plugin_config=unitary_synthesis_plugin_config, ), Optimize1qGatesDecomposition(basis_gates), CommutativeCancellation(), ] # 9. Unify all durations (either SI, or convert to dt if known) # Schedule the circuit only when scheduling_method is supplied _time_unit_setup = [ContainsInstruction("delay")] _time_unit_conversion = [TimeUnitConversion(instruction_durations)] def _contains_delay(property_set): return property_set["contains_delay"] _scheduling = [] if scheduling_method: _scheduling += _time_unit_conversion if scheduling_method in {"alap", "as_late_as_possible"}: _scheduling += [ALAPSchedule(instruction_durations)] elif scheduling_method in {"asap", "as_soon_as_possible"}: _scheduling += [ASAPSchedule(instruction_durations)] else: raise TranspilerError("Invalid scheduling method %s." % scheduling_method) # 10. Call measure alignment. Should come after scheduling. if ( timing_constraints.granularity != 1 or timing_constraints.min_length != 1 or timing_constraints.acquire_alignment != 1 ): _alignments = [ ValidatePulseGates( granularity=timing_constraints.granularity, min_length=timing_constraints.min_length ), AlignMeasures(alignment=timing_constraints.acquire_alignment), ] else: _alignments = [] # Build pass manager pm3 = PassManager() pm3.append(_unroll3q) pm3.append(_reset + _meas) if coupling_map or initial_layout: pm3.append(_given_layout) pm3.append(_choose_layout_0, condition=_choose_layout_condition) pm3.append(_choose_layout_1, condition=_trivial_not_perfect) pm3.append(_choose_layout_2, condition=_csp_not_found_match) pm3.append(_embed) pm3.append(_swap_check) pm3.append(_swap, condition=_swap_condition) pm3.append(_unroll) if (coupling_map and not coupling_map.is_symmetric) or ( target is not None and target.get_non_global_operation_names(strict_direction=True) ): pm3.append(_direction_check) pm3.append(_direction, condition=_direction_condition) pm3.append(_reset) # For transpiling to a target we need to run GateDirection in the # optimization loop to correct for incorrect directions that might be # inserted by UnitarySynthesis which is direction aware but only via # the coupling map which with a target doesn't give a full picture if target is not None: pm3.append(_depth_check + _opt + _unroll + _direction, do_while=_opt_control) else: pm3.append(_depth_check + _opt + _unroll, do_while=_opt_control) else: pm3.append(_reset) pm3.append(_depth_check + _opt + _unroll, do_while=_opt_control) if inst_map and inst_map.has_custom_gate(): pm3.append(PulseGates(inst_map=inst_map)) if scheduling_method: pm3.append(_scheduling) elif instruction_durations: pm3.append(_time_unit_setup) pm3.append(_time_unit_conversion, condition=_contains_delay) pm3.append(_alignments) return pm3
def level_0_pass_manager( pass_manager_config: PassManagerConfig) -> StagedPassManager: """Level 0 pass manager: no explicit optimization other than mapping to backend. This pass manager applies the user-given initial layout. If none is given, a trivial layout consisting of mapping the i-th virtual qubit to the i-th physical qubit is used. Any unused physical qubit is allocated as ancilla space. The pass manager then unrolls the circuit to the desired basis, and transforms the circuit to match the coupling map. Args: pass_manager_config: configuration of the pass manager. Returns: a level 0 pass manager. Raises: TranspilerError: if the passmanager config is invalid. """ basis_gates = pass_manager_config.basis_gates inst_map = pass_manager_config.inst_map coupling_map = pass_manager_config.coupling_map initial_layout = pass_manager_config.initial_layout layout_method = pass_manager_config.layout_method or "trivial" routing_method = pass_manager_config.routing_method or "stochastic" translation_method = pass_manager_config.translation_method or "translator" scheduling_method = pass_manager_config.scheduling_method instruction_durations = pass_manager_config.instruction_durations seed_transpiler = pass_manager_config.seed_transpiler backend_properties = pass_manager_config.backend_properties approximation_degree = pass_manager_config.approximation_degree timing_constraints = pass_manager_config.timing_constraints or TimingConstraints( ) unitary_synthesis_method = pass_manager_config.unitary_synthesis_method unitary_synthesis_plugin_config = pass_manager_config.unitary_synthesis_plugin_config target = pass_manager_config.target # Choose an initial layout if not set by user (default: trivial layout) _given_layout = SetLayout(initial_layout) def _choose_layout_condition(property_set): return not property_set["layout"] if layout_method == "trivial": _choose_layout = TrivialLayout(coupling_map) elif layout_method == "dense": _choose_layout = DenseLayout(coupling_map, backend_properties, target=target) elif layout_method == "noise_adaptive": _choose_layout = NoiseAdaptiveLayout(backend_properties) elif layout_method == "sabre": _choose_layout = SabreLayout(coupling_map, max_iterations=1, seed=seed_transpiler) else: raise TranspilerError("Invalid layout method %s." % layout_method) toqm_pass = False # Choose routing pass if routing_method == "basic": routing_pass = BasicSwap(coupling_map) elif routing_method == "stochastic": routing_pass = StochasticSwap(coupling_map, trials=20, seed=seed_transpiler) elif routing_method == "lookahead": routing_pass = LookaheadSwap(coupling_map, search_depth=2, search_width=2) elif routing_method == "sabre": routing_pass = SabreSwap(coupling_map, heuristic="basic", seed=seed_transpiler) elif routing_method == "toqm": HAS_TOQM.require_now("TOQM-based routing") from qiskit_toqm import ToqmSwap, ToqmStrategyO0, latencies_from_target if initial_layout: raise TranspilerError( "Initial layouts are not supported with TOQM-based routing.") toqm_pass = True # Note: BarrierBeforeFinalMeasurements is skipped intentionally since ToqmSwap # does not yet support barriers. routing_pass = ToqmSwap( coupling_map, strategy=ToqmStrategyO0( latencies_from_target(coupling_map, instruction_durations, basis_gates, backend_properties, target)), ) elif routing_method == "none": routing_pass = Error( msg= "No routing method selected, but circuit is not routed to device. " "CheckMap Error: {check_map_msg}", action="raise", ) else: raise TranspilerError("Invalid routing method %s." % routing_method) unroll_3q = None # Build pass manager if coupling_map or initial_layout: unroll_3q = common.generate_unroll_3q( target, basis_gates, approximation_degree, unitary_synthesis_method, unitary_synthesis_plugin_config, ) layout = PassManager() layout.append(_given_layout) layout.append(_choose_layout, condition=_choose_layout_condition) layout += common.generate_embed_passmanager(coupling_map) routing = common.generate_routing_passmanager( routing_pass, target, coupling_map=coupling_map, seed_transpiler=seed_transpiler, use_barrier_before_measurement=not toqm_pass, ) else: layout = None routing = None translation = common.generate_translation_passmanager( target, basis_gates, translation_method, approximation_degree, coupling_map, backend_properties, unitary_synthesis_method, unitary_synthesis_plugin_config, ) pre_routing = None if toqm_pass: pre_routing = translation if (coupling_map and not coupling_map.is_symmetric) or ( target is not None and target.get_non_global_operation_names(strict_direction=True)): pre_opt = common.generate_pre_op_passmanager(target, coupling_map) pre_opt += translation else: pre_opt = None sched = common.generate_scheduling(instruction_durations, scheduling_method, timing_constraints, inst_map) return StagedPassManager( init=unroll_3q, layout=layout, pre_routing=pre_routing, routing=routing, translation=translation, pre_optimization=pre_opt, scheduling=sched, )