def test_already_mapped_1(self): """Circuit not remapped if matches topology. See: https://github.com/Qiskit/qiskit-terra/issues/342 """ backend = FakeRueschlikon() coupling_map = backend.configuration().coupling_map basis_gates = backend.configuration().basis_gates qr = QuantumRegister(16, 'qr') cr = ClassicalRegister(16, 'cr') qc = QuantumCircuit(qr, cr) qc.cx(qr[3], qr[14]) qc.cx(qr[5], qr[4]) qc.h(qr[9]) qc.cx(qr[9], qr[8]) qc.x(qr[11]) qc.cx(qr[3], qr[4]) qc.cx(qr[12], qr[11]) qc.cx(qr[13], qr[4]) qc.measure(qr, cr) new_qc = transpile(qc, coupling_map=coupling_map, basis_gates=basis_gates, initial_layout=Layout.generate_trivial_layout(qr)) cx_qubits = [ qargs for (gate, qargs, _) in new_qc.data if gate.name == "cx" ] cx_qubits_physical = [[ctrl.index, tgt.index] for [ctrl, tgt] in cx_qubits] self.assertEqual(sorted(cx_qubits_physical), [[3, 4], [3, 14], [5, 4], [9, 8], [12, 11], [13, 4]])
def run(self, dag): """ If `dag` is mapped and the direction is correct the property `is_direction_mapped` is set to True (or to False otherwise). Args: dag (DAGCircuit): DAG to check. """ if self.layout is None: if self.property_set["layout"]: self.layout = self.property_set["layout"] else: self.layout = Layout.generate_trivial_layout( *dag.qregs.values()) self.property_set['is_direction_mapped'] = True edges = self.coupling_map.get_edges() for gate in dag.twoQ_gates(): physical_q0 = self.layout[gate.qargs[0]] physical_q1 = self.layout[gate.qargs[1]] if isinstance(gate.op, (CXBase, CnotGate)) and (physical_q0, physical_q1) not in edges: self.property_set['is_direction_mapped'] = False return
def test_final_measurement_barrier_for_devices(self, mock_pass): """Verify BarrierBeforeFinalMeasurements pass is called in default pipeline for devices.""" circ = QuantumCircuit.from_qasm_file(self._get_resource_path('example.qasm', Path.QASMS)) layout = Layout.generate_trivial_layout(*circ.qregs) transpile(circ, coupling_map=FakeRueschlikon().configuration().coupling_map, initial_layout=layout) self.assertTrue(mock_pass.called)
def swap_decompose(self, dag: DAGCircuit, node: DAGOpNode, current_layout: Layout, swap_strategy: SwapStrategy) -> DAGCircuit: """Take an instance of :class:`.Commuting2qBlock` and map it to the coupling map. The mapping is done with the swap strategy. Args: dag: The dag which contains the :class:`.Commuting2qBlock` we route. node: A node whose operation is a :class:`.Commuting2qBlock`. current_layout: The layout before the swaps are applied. This function will modify the layout so that subsequent gates can be properly composed on the dag. swap_strategy: The swap strategy used to decompose the node. Returns: A dag that is compatible with the coupling map where swap gates have been added to map the gates in the :class:`.Commuting2qBlock` to the hardware. """ trivial_layout = Layout.generate_trivial_layout(*dag.qregs.values()) gate_layers = self._make_op_layers(dag, node.op, current_layout, swap_strategy) # Iterate over and apply gate layers max_distance = max(gate_layers.keys()) circuit_with_swap = QuantumCircuit(len(dag.qubits)) for i in range(max_distance + 1): # Get current layer and replace the problem indices j,k by the corresponding # positions in the coupling map. The current layer corresponds # to all the gates that can be applied at the ith swap layer. current_layer = {} for (j, k), local_gate in gate_layers.get(i, {}).items(): current_layer[self._position_in_cmap( j, k, current_layout)] = local_gate # Not all gates that are applied at the ith swap layer can be applied at the same # time. We therefore greedily build sub-layers. sub_layers = self._build_sub_layers(current_layer) # Apply sub-layers for sublayer in sub_layers: for edge, local_gate in sublayer.items(): circuit_with_swap.append(local_gate, edge) # Apply SWAP gates if i < max_distance: for swap in swap_strategy.swap_layer(i): (j, k) = [ trivial_layout.get_physical_bits()[vertex] for vertex in swap ] circuit_with_swap.swap(j, k) current_layout.swap(j, k) return circuit_to_dag(circuit_with_swap)
def run(self, dag): """Run one pass of the lookahead mapper on the provided DAG. Args: dag (DAGCircuit): the directed acyclic graph to be mapped Returns: DAGCircuit: A dag mapped to be compatible with the coupling_map in the property_set. Raises: TranspilerError: if the coupling map or the layout are not compatible with the DAG """ coupling_map = self._coupling_map ordered_virtual_gates = list(dag.serial_layers()) if self.initial_layout is None: if self.property_set["layout"]: self.initial_layout = self.property_set["layout"] else: self.initial_layout = Layout.generate_trivial_layout( *dag.qregs.values()) if len(dag.qubits()) != len(self.initial_layout): raise TranspilerError( 'The layout does not match the amount of qubits in the DAG') if len(self._coupling_map.physical_qubits) != len(self.initial_layout): raise TranspilerError( "Mappers require to have the layout to be the same size as the coupling map" ) mapped_gates = [] layout = self.initial_layout.copy() gates_remaining = ordered_virtual_gates.copy() while gates_remaining: best_step = _search_forward_n_swaps(layout, gates_remaining, coupling_map) layout = best_step['layout'] gates_mapped = best_step['gates_mapped'] gates_remaining = best_step['gates_remaining'] mapped_gates.extend(gates_mapped) # Preserve input DAG's name, regs, wire_map, etc. but replace the graph. mapped_dag = _copy_circuit_metadata(dag, coupling_map) for node in mapped_gates: mapped_dag.apply_operation_back(op=node.op, qargs=node.qargs, cargs=node.cargs) return mapped_dag
def run(self, dag): """ Flips the cx nodes to match the directed coupling map. Args: dag (DAGCircuit): DAG to map. Returns: DAGCircuit: The rearranged dag for the coupling map Raises: TranspilerError: If the circuit cannot be mapped just by flipping the cx nodes. """ new_dag = DAGCircuit() if self.layout is None: # LegacySwap renames the register in the DAG and does not match the property set self.layout = Layout.generate_trivial_layout(*dag.qregs.values()) for layer in dag.serial_layers(): subdag = layer['graph'] for cnot_node in subdag.named_nodes('cx', 'CX'): control = cnot_node.qargs[0] target = cnot_node.qargs[1] physical_q0 = self.layout[control] physical_q1 = self.layout[target] if self.coupling_map.distance(physical_q0, physical_q1) != 1: raise TranspilerError( 'The circuit requires a connection between physical ' 'qubits %s and %s' % (physical_q0, physical_q1)) if (physical_q0, physical_q1) not in self.coupling_map.get_edges(): # A flip needs to be done # Create the involved registers if control[0] not in subdag.qregs.values(): subdag.add_qreg(control[0]) if target[0] not in subdag.qregs.values(): subdag.add_qreg(target[0]) # Add H gates around subdag.apply_operation_back(HGate(), [target], []) subdag.apply_operation_back(HGate(), [control], []) subdag.apply_operation_front(HGate(), [target], []) subdag.apply_operation_front(HGate(), [control], []) # Flips the CX cnot_node.qargs[0], cnot_node.qargs[1] = target, control new_dag.extend_back(subdag) return new_dag
def test_final_measurement_barrier_for_devices(self): """Verify BarrierBeforeFinalMeasurements pass is called in default pipeline for devices.""" qasm_dir = os.path.join( os.path.dirname(os.path.dirname(os.path.abspath(__file__))), 'qasm') circ = QuantumCircuit.from_qasm_file( os.path.join(qasm_dir, 'example.qasm')) layout = Layout.generate_trivial_layout(*circ.qregs) orig_pass = BarrierBeforeFinalMeasurements() with patch.object(BarrierBeforeFinalMeasurements, 'run', wraps=orig_pass.run) as mock_pass: transpile(circ, coupling_map=FakeRueschlikon().configuration().coupling_map, initial_layout=layout) self.assertTrue(mock_pass.called)
def run(self, dag): """ Pick a layout by assigning n circuit qubits to device qubits 0, .., n-1. Args: dag (DAGCircuit): DAG to find layout for. Raises: TranspilerError: if dag wider than self.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.') self.property_set['layout'] = Layout.generate_trivial_layout(*dag.qregs.values())
def test_do_not_run_cxdirection_with_symmetric_cm(self): """When the coupling map is symmetric, do not run CXDirection.""" circ = QuantumCircuit.from_qasm_file(self._get_resource_path('example.qasm', Path.QASMS)) layout = Layout.generate_trivial_layout(*circ.qregs) coupling_map = [] for node1, node2 in FakeRueschlikon().configuration().coupling_map: coupling_map.append([node1, node2]) coupling_map.append([node2, node1]) cxdir_pass = CXDirection(CouplingMap(coupling_map)) with unittest.mock.patch.object(CXDirection, 'run', wraps=cxdir_pass.run) as mock_pass: transpile(circ, coupling_map=coupling_map, initial_layout=layout) self.assertFalse(mock_pass.called)
def test_ignore_initial_layout(self): """Ignoring initial layout even when it is supplied""" coupling = CouplingMap([[0, 1], [0, 2]]) circuit = QuantumCircuit(3) circuit.cx(1, 2) property_set = {"layout": Layout.generate_trivial_layout(*circuit.qubits)} actual = BIPMapping(coupling)(circuit, property_set) q = QuantumRegister(3, name="q") expected = QuantumCircuit(q) expected.cx(q[0], q[1]) self.assertEqual(expected, actual)
def test_parameterized_circuit_for_device(self): """Verify that a parameterized circuit can be transpiled for a device backend.""" qr = QuantumRegister(2, name='qr') qc = QuantumCircuit(qr) theta = Parameter('theta') qc.rz(theta, qr[0]) transpiled_qc = transpile(qc, backend=FakeMelbourne(), initial_layout=Layout.generate_trivial_layout(qr)) qr = QuantumRegister(14, 'q') expected_qc = QuantumCircuit(qr, global_phase=-1 * theta / 2.0) expected_qc.append(U1Gate(theta), [qr[0]]) self.assertEqual(expected_qc, transpiled_qc)
def test_do_not_run_gatedirection_with_symmetric_cm(self): """When the coupling map is symmetric, do not run GateDirection.""" qasm_dir = os.path.join( os.path.dirname(os.path.dirname(os.path.abspath(__file__))), 'qasm') circ = QuantumCircuit.from_qasm_file( os.path.join(qasm_dir, 'example.qasm')) layout = Layout.generate_trivial_layout(*circ.qregs) coupling_map = [] for node1, node2 in FakeRueschlikon().configuration().coupling_map: coupling_map.append([node1, node2]) coupling_map.append([node2, node1]) orig_pass = GateDirection(CouplingMap(coupling_map)) with patch.object(GateDirection, 'run', wraps=orig_pass.run) as mock_pass: transpile(circ, coupling_map=coupling_map, initial_layout=layout) self.assertFalse(mock_pass.called)
def test_parameter_expression_circuit_for_device(self): """Verify that a circuit including expressions of parameters can be transpiled for a device backend.""" qr = QuantumRegister(2, name='qr') qc = QuantumCircuit(qr) theta = Parameter('theta') square = theta * theta qc.rz(square, qr[0]) transpiled_qc = transpile(qc, backend=FakeMelbourne(), initial_layout=Layout.generate_trivial_layout(qr)) qr = QuantumRegister(14, 'q') expected_qc = QuantumCircuit(qr, global_phase=-1 * square / 2.0) expected_qc.append(U1Gate(square), [qr[0]]) self.assertEqual(expected_qc, transpiled_qc)
def run(self, dag): """ Run the StochasticSwap pass on `dag`. Args: dag (DAGCircuit): DAG to map. Returns: DAGCircuit: A mapped DAG. Raises: TranspilerError: if the coupling map or the layout are not compatible with the DAG """ if self.initial_layout is None: if self.property_set["layout"]: self.initial_layout = self.property_set["layout"] else: self.initial_layout = Layout.generate_trivial_layout( *dag.qregs.values()) if len(dag.qubits()) != len(self.initial_layout): raise TranspilerError( 'The layout does not match the amount of qubits in the DAG') if len(self.coupling_map.physical_qubits) != len(self.initial_layout): raise TranspilerError( "Mappers require to have the layout to be the same size as the coupling map" ) self.input_layout = self.initial_layout.copy() self.qregs = dag.qregs if self.seed is None: self.seed = np.random.randint(0, np.iinfo(np.int32).max) self.rng = np.random.RandomState(self.seed) logger.debug("StochasticSwap RandomState seeded with seed=%s", self.seed) new_dag = self._mapper(dag, self.coupling_map, trials=self.trials) # self.property_set["layout"] = self.initial_layout return new_dag
def run(self, dag): """ If `dag` is mapped to `coupling_map`, the property `is_swap_mapped` is set to True (or to False otherwise). Args: dag (DAGCircuit): DAG to map. """ if self.layout is None: if self.property_set["layout"]: self.layout = self.property_set["layout"] else: self.layout = Layout.generate_trivial_layout( *dag.qregs.values()) self.property_set['is_swap_mapped'] = True for gate in dag.twoQ_gates(): physical_q0 = self.layout[gate.qargs[0]] physical_q1 = self.layout[gate.qargs[1]] if self.coupling_map.distance(physical_q0, physical_q1) != 1: self.property_set['is_swap_mapped'] = False return
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 run(self, dag): """ Runs the BasicSwap pass on `dag`. Args: dag (DAGCircuit): DAG to map. Returns: DAGCircuit: A mapped DAG. Raises: TranspilerError: if the coupling map or the layout are not compatible with the DAG """ new_dag = DAGCircuit() if self.initial_layout is None: if self.property_set["layout"]: self.initial_layout = self.property_set["layout"] else: self.initial_layout = Layout.generate_trivial_layout( *dag.qregs.values()) if len(dag.qubits()) != len(self.initial_layout): raise TranspilerError( 'The layout does not match the amount of qubits in the DAG') if len(self.coupling_map.physical_qubits) != len(self.initial_layout): raise TranspilerError( "Mappers require to have the layout to be the same size as the coupling map" ) current_layout = self.initial_layout.copy() for layer in dag.serial_layers(): subdag = layer['graph'] for gate in subdag.twoQ_gates(): physical_q0 = current_layout[gate.qargs[0]] physical_q1 = current_layout[gate.qargs[1]] if self.coupling_map.distance(physical_q0, physical_q1) != 1: # Insert a new layer with the SWAP(s). swap_layer = DAGCircuit() path = self.coupling_map.shortest_undirected_path( physical_q0, physical_q1) for swap in range(len(path) - 2): connected_wire_1 = path[swap] connected_wire_2 = path[swap + 1] qubit_1 = current_layout[connected_wire_1] qubit_2 = current_layout[connected_wire_2] # create qregs for qreg in current_layout.get_registers(): if qreg not in swap_layer.qregs.values(): swap_layer.add_qreg(qreg) # create the swap operation swap_layer.apply_operation_back( SwapGate(), qargs=[qubit_1, qubit_2], cargs=[]) # layer insertion edge_map = current_layout.combine_into_edge_map( self.initial_layout) new_dag.compose_back(swap_layer, edge_map) # update current_layout for swap in range(len(path) - 2): current_layout.swap(path[swap], path[swap + 1]) edge_map = current_layout.combine_into_edge_map( self.initial_layout) new_dag.extend_back(subdag, edge_map) return new_dag