diff --git a/qiskit/transpiler/passes/basis/basis_translator.py b/qiskit/transpiler/passes/basis/basis_translator.py index 30b25b271755..8bf98f38e087 100644 --- a/qiskit/transpiler/passes/basis/basis_translator.py +++ b/qiskit/transpiler/passes/basis/basis_translator.py @@ -249,8 +249,9 @@ def run(self, dag): replace_start_time = time.time() def apply_translation(dag, wire_map): - dag_updated = False - for node in dag.op_nodes(): + is_updated = False + out_dag = dag.copy_empty_like() + for node in dag.topological_op_nodes(): node_qargs = tuple(wire_map[bit] for bit in node.qargs) qubit_set = frozenset(node_qargs) if node.name in target_basis or len(node.qargs) < self._min_qubits: @@ -258,45 +259,48 @@ def apply_translation(dag, wire_map): flow_blocks = [] for block in node.op.blocks: dag_block = circuit_to_dag(block) - dag_updated = apply_translation( + updated_dag, is_updated = apply_translation( dag_block, { inner: wire_map[outer] for inner, outer in zip(block.qubits, node.qargs) }, ) - if dag_updated: - flow_circ_block = dag_to_circuit(dag_block) + if is_updated: + flow_circ_block = dag_to_circuit(updated_dag) else: flow_circ_block = block flow_blocks.append(flow_circ_block) node.op = node.op.replace_blocks(flow_blocks) + out_dag.apply_operation_back(node.op, node.qargs, node.cargs, check=False) continue if ( node_qargs in self._qargs_with_non_global_operation and node.name in self._qargs_with_non_global_operation[node_qargs] ): + out_dag.apply_operation_back(node.op, node.qargs, node.cargs, check=False) continue if dag.has_calibration_for(node): + out_dag.apply_operation_back(node.op, node.qargs, node.cargs, check=False) continue if qubit_set in extra_instr_map: - self._replace_node(dag, node, extra_instr_map[qubit_set]) + self._replace_node(out_dag, node, extra_instr_map[qubit_set]) elif (node.name, node.num_qubits) in instr_map: - self._replace_node(dag, node, instr_map) + self._replace_node(out_dag, node, instr_map) else: raise TranspilerError(f"BasisTranslator did not map {node.name}.") - dag_updated = True - return dag_updated + is_updated = True + return out_dag, is_updated - apply_translation(dag, qarg_indices) + out_dag, _ = apply_translation(dag, qarg_indices) replace_end_time = time.time() logger.info( "Basis translation instructions replaced in %.3fs.", replace_end_time - replace_start_time, ) - return dag + return out_dag def _replace_node(self, dag, node, instr_map): target_params, target_dag = instr_map[node.name, node.num_qubits] @@ -307,12 +311,18 @@ def _replace_node(self, dag, node, instr_map): ) if node.params: parameter_map = dict(zip(target_params, node.params)) - bound_target_dag = target_dag.copy_empty_like() for inner_node in target_dag.topological_op_nodes(): new_node = DAGOpNode.from_instruction( inner_node._to_circuit_instruction(), - dag=bound_target_dag, + dag=target_dag, ) + new_node.qargs = tuple( + node.qargs[target_dag.find_bit(x).index] for x in inner_node.qargs + ) + new_node.cargs = tuple( + node.cargs[target_dag.find_bit(x).index] for x in inner_node.cargs + ) + if not new_node.is_standard_gate: new_node.op = new_node.op.copy() if any(isinstance(x, ParameterExpression) for x in inner_node.params): @@ -334,7 +344,8 @@ def _replace_node(self, dag, node, instr_map): new_node.params = new_params if not new_node.is_standard_gate: new_node.op.params = new_params - bound_target_dag._apply_op_node_back(new_node) + dag._apply_op_node_back(new_node) + if isinstance(target_dag.global_phase, ParameterExpression): old_phase = target_dag.global_phase bind_dict = {x: parameter_map[x] for x in old_phase.parameters} @@ -342,31 +353,37 @@ def _replace_node(self, dag, node, instr_map): new_phase = old_phase for x in bind_dict.items(): new_phase = new_phase.assign(*x) - else: new_phase = old_phase.bind(bind_dict) if not new_phase.parameters: new_phase = new_phase.numeric() if isinstance(new_phase, complex): raise TranspilerError(f"Global phase must be real, but got '{new_phase}'") - bound_target_dag.global_phase = new_phase - else: - bound_target_dag = target_dag - - if len(bound_target_dag.op_nodes()) == 1 and len( - bound_target_dag.op_nodes()[0].qargs - ) == len(node.qargs): - dag_op = bound_target_dag.op_nodes()[0].op - # dag_op may be the same instance as other ops in the dag, - # so if there is a condition, need to copy - if getattr(node, "condition", None): - dag_op = dag_op.copy() - dag.substitute_node(node, dag_op, inplace=True) - - if bound_target_dag.global_phase: - dag.global_phase += bound_target_dag.global_phase + dag.global_phase += new_phase + else: - dag.substitute_node_with_dag(node, bound_target_dag) + for inner_node in target_dag.topological_op_nodes(): + new_node = DAGOpNode.from_instruction( + inner_node._to_circuit_instruction(), + dag=target_dag, + ) + new_node.qargs = tuple( + node.qargs[target_dag.find_bit(x).index] for x in inner_node.qargs + ) + new_node.cargs = tuple( + node.cargs[target_dag.find_bit(x).index] for x in inner_node.cargs + ) + if not new_node.is_standard_gate: + new_node.op = new_node.op.copy() + # dag_op may be the same instance as other ops in the dag, + # so if there is a condition, need to copy + if getattr(node.op, "condition", None): + new_node_op = new_node.op.to_mutable() + new_node_op.condition = node.op.condition + new_node.op = new_node_op + dag._apply_op_node_back(new_node) + if target_dag.global_phase: + dag.global_phase += target_dag.global_phase @singledispatchmethod def _extract_basis(self, circuit):