def _swap_ops_from_edge(edge, layout): """Generate list of ops to implement a SWAP gate along a coupling edge.""" device_qreg = QuantumRegister(len(layout.get_physical_bits()), 'q') qreg_edge = [device_qreg[i] for i in edge] # TODO shouldn't be making other nodes not by the DAG!! return [DAGNode(op=SwapGate(), qargs=qreg_edge, cargs=[], type='op')]
def test_definition_specification(self): """Test instantiation with explicit definition""" swap = SwapGate() cswap = ControlledGate('cswap', 3, [], num_ctrl_qubits=1, definition=swap.definition) self.assertEqual(swap.definition, cswap.definition)
def _swap_ops_from_edge(edge, layout): """Generate list of ops to implement a SWAP gate along a coupling edge.""" device_qreg = QuantumRegister(len(layout.get_physical_bits()), 'q') qreg_edge = [(device_qreg, i) for i in edge] return [ {'op': SwapGate(*qreg_edge), 'qargs': qreg_edge}, ]
def _swap_ops_from_edge(edge, layout): """Generate list of ops to implement a SWAP gate along a coupling edge.""" device_qreg = QuantumRegister(len(layout.get_physical_bits()), 'q') qreg_edge = [(device_qreg, i) for i in edge] # TODO shouldn't be making other nodes not by the DAG!! return [ DAGNode({'op': SwapGate(*qreg_edge), 'qargs': qreg_edge, 'type': 'op'}) ]
class TestParameterCtrlState(QiskitTestCase): """Test gate equality with ctrl_state parameter.""" @data((RXGate(0.5), CRXGate(0.5)), (RYGate(0.5), CRYGate(0.5)), (RZGate(0.5), CRZGate(0.5)), (XGate(), CXGate()), (YGate(), CYGate()), (ZGate(), CZGate()), (U1Gate(0.5), CU1Gate(0.5)), (SwapGate(), CSwapGate()), (HGate(), CHGate()), (U3Gate(0.1, 0.2, 0.3), CU3Gate(0.1, 0.2, 0.3))) @unpack def test_ctrl_state_one(self, gate, controlled_gate): """Test controlled gates with ctrl_state See https://github.com/Qiskit/qiskit-terra/pull/4025 """ self.assertEqual(gate.control(1, ctrl_state='1'), controlled_gate)
def test_is_identity(self): """ The is_identity function determines whether a pair of gates forms the identity, when ignoring control qubits. """ seq = [ DAGNode({ 'type': 'op', 'op': XGate().control() }), DAGNode({ 'type': 'op', 'op': XGate().control(2) }) ] self.assertTrue(HoareOptimizer()._is_identity(seq)) seq = [ DAGNode({ 'type': 'op', 'op': RZGate(-pi / 2).control() }), DAGNode({ 'type': 'op', 'op': RZGate(pi / 2).control(2) }) ] self.assertTrue(HoareOptimizer()._is_identity(seq)) seq = [ DAGNode({ 'type': 'op', 'op': CSwapGate() }), DAGNode({ 'type': 'op', 'op': SwapGate() }) ] self.assertTrue(HoareOptimizer()._is_identity(seq)) seq = [ DAGNode({ 'type': 'op', 'op': UnitaryGate([[1, 0], [0, 1j]]).control() }), DAGNode({ 'type': 'op', 'op': UnitaryGate([[1, 0], [0, -1j]]) }) ]
def random_clifford_circuit(num_qubits, num_gates, gates='all', seed=None): """Generate a pseudo random Clifford circuit.""" if gates == 'all': if num_qubits == 1: gates = ['i', 'x', 'y', 'z', 'h', 's', 'sdg', 'v', 'w'] else: gates = [ 'i', 'x', 'y', 'z', 'h', 's', 'sdg', 'v', 'w', 'cx', 'cz', 'swap' ] instructions = { 'i': (IGate(), 1), 'x': (XGate(), 1), 'y': (YGate(), 1), 'z': (ZGate(), 1), 'h': (HGate(), 1), 's': (SGate(), 1), 'sdg': (SdgGate(), 1), 'v': (VGate(), 1), 'w': (WGate(), 1), 'cx': (CXGate(), 2), 'cz': (CZGate(), 2), 'swap': (SwapGate(), 2) } if isinstance(seed, np.random.RandomState): rng = seed else: rng = np.random.RandomState(seed=seed) samples = rng.choice(gates, num_gates) circ = QuantumCircuit(num_qubits) for name in samples: gate, nqargs = instructions[name] qargs = rng.choice(range(num_qubits), nqargs, replace=False).tolist() circ.append(gate, qargs) return circ
def _layer_permutation(layer_partition, initial_layout, layout, qubit_subset, coupling, trials, qregs, rng): """Find a swap circuit that implements a permutation for this layer. Args: layer_partition (list): The layer_partition is a list of (qu)bit lists and each qubit is a tuple (qreg, index). initial_layout (Layout): The initial layout passed. 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. qregs (OrderedDict): Ordered dict of registers from input DAG. rng (RandomState): Random number generator. Returns: Tuple: success_flag, best_circuit, best_depth, best_layout, trivial_flag Raises: TranspilerError: if anything went wrong. """ 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]) 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_edges = None # best edges found best_circuit = None # initialize best swap circuit best_layout = None # initialize best final layout cdist2 = coupling._dist_matrix**2 # Scaling matrix scale = np.zeros((num_qubits, num_qubits)) int_qubit_subset = regtuple_to_numeric(qubit_subset, qregs) int_gates = gates_to_idx(gates, qregs) int_layout = nlayout_from_layout(layout, qregs, coupling.size()) trial_circuit = DAGCircuit() # SWAP circuit for this trial for register in layout.get_virtual_bits().keys(): if register[0] not in trial_circuit.qregs.values(): trial_circuit.add_qreg(register[0]) slice_circuit = DAGCircuit() # circuit for this swap slice for register in layout.get_virtual_bits().keys(): if register[0] not in slice_circuit.qregs.values(): slice_circuit.add_qreg(register[0]) edges = np.asarray(coupling.get_edges(), dtype=np.int32).ravel() cdist = coupling._dist_matrix for trial in range(trials): logger.debug("layer_permutation: trial %s", trial) # This is one Trial -------------------------------------- dist, optim_edges, trial_layout, depth_step = swap_trial( num_qubits, int_layout, int_qubit_subset, int_gates, cdist2, cdist, edges, scale, rng) logger.debug("layer_permutation: final distance for this trial = %s", dist) if dist == len(gates) and depth_step < best_depth: logger.debug( "layer_permutation: got circuit with improved depth %s", depth_step) best_edges = optim_edges 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_layout is None: logger.debug("layer_permutation: failed!") return False, None, None, None, False edgs = best_edges.edges() for idx in range(best_edges.size // 2): slice_circuit.apply_operation_back( SwapGate(), [initial_layout[edgs[2 * idx]], initial_layout[edgs[2 * idx + 1]]], []) trial_circuit.extend_back(slice_circuit) best_circuit = trial_circuit # Otherwise, we return our result for this layer logger.debug("layer_permutation: success!") best_lay = best_layout.to_layout(qregs) return True, best_circuit, best_depth, best_lay, False
def test_controlled_swap(self): """Test creation of controlled swap gate""" self.assertEqual(SwapGate().control(), FredkinGate())
def run(self, dag): """Run 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 len(dag.qregs) != 1 or dag.qregs.get('q', None) is None: raise TranspilerError('Basic swap runs on physical circuits only') if len(dag.qubits()) > len(self.coupling_map.physical_qubits): raise TranspilerError('The layout does not match the amount of qubits in the DAG') canonical_register = dag.qregs['q'] trivial_layout = Layout.generate_trivial_layout(canonical_register) current_layout = trivial_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() swap_layer.add_qreg(canonical_register) 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 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(trivial_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(trivial_layout) new_dag.extend_back(subdag, edge_map) 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
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]) 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 cdist2 = coupling._dist_matrix**2 # Scaling matrix scale = np.zeros((num_qubits, num_qubits)) utri_idx = np.triu_indices(num_qubits) 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 data = 1 + np.random.normal(0, 1/num_qubits, size=num_qubits*(num_qubits+1)//2) scale[utri_idx] = data xi = (scale+scale.T)*cdist2 # pylint: disable=invalid-name 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]) # 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 need_copy = True for edge in coupling.get_edges(): qubits = (trial_layout[edge[0]], trial_layout[edge[1]]) # Are the qubits available? if qubits[0] in qubit_set and qubits[1] in qubit_set: # Try this edge to reduce the cost if need_copy: new_layout = trial_layout.copy() need_copy = False 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 = (self.initial_layout[edge[0]], self.initial_layout[edge[1]]) optimal_qubits = qubits need_copy = True else: new_layout.swap(edge[0], edge[1]) # Were there any good swap choices? if cost_reduced: qubit_set.remove(optimal_qubits[0]) qubit_set.remove(optimal_qubits[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 _layer_permutation(self, layer_partition, layout, qubit_subset, coupling, trials): """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. Args: 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. Returns: Tuple: success_flag, best_circuit, best_depth, best_layout 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. Raises: TranspilerError: if anything went wrong. """ logger.debug("layer_permutation: layer_partition = %s", layer_partition) logger.debug("layer_permutation: layout = %s", layout.get_virtual_bits()) logger.debug("layer_permutation: qubit_subset = %s", qubit_subset) logger.debug("layer_permutation: trials = %s", trials) # The input dag is on a flat canonical register # TODO: cleanup the code that is general for multiple qregs below canonical_register = QuantumRegister(len(layout), 'q') qregs = OrderedDict({canonical_register.name: canonical_register}) 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") if len(gate_args) == 2: gates.append(tuple(gate_args)) logger.debug("layer_permutation: gates = %s", 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() circ.add_qreg(canonical_register) return True, circ, 0, layout # Begin loop over trials of randomized algorithm num_qubits = len(layout) best_depth = inf # initialize best depth best_edges = None # best edges found best_circuit = None # initialize best swap circuit best_layout = None # initialize best final layout cdist2 = coupling._dist_matrix**2 # Scaling matrix scale = np.zeros((num_qubits, num_qubits)) int_qubit_subset = _regtuple_to_numeric(qubit_subset, qregs) int_gates = _gates_to_idx(gates, qregs) int_layout = nlayout_from_layout(layout, qregs, coupling.size()) trial_circuit = DAGCircuit( ) # SWAP circuit for slice of swaps in this trial for qubit in layout.get_virtual_bits().keys(): if qubit.register not in trial_circuit.qregs.values(): trial_circuit.add_qreg(qubit.register) edges = np.asarray(coupling.get_edges(), dtype=np.int32).ravel() cdist = coupling._dist_matrix for trial in range(trials): logger.debug("layer_permutation: trial %s", trial) # This is one Trial -------------------------------------- dist, optim_edges, trial_layout, depth_step = swap_trial( num_qubits, int_layout, int_qubit_subset, int_gates, cdist2, cdist, edges, scale, self.rng) logger.debug( "layer_permutation: final distance for this trial = %s", dist) if dist == len(gates) and depth_step < best_depth: logger.debug( "layer_permutation: got circuit with improved depth %s", depth_step) best_edges = optim_edges 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_layout is None: logger.debug("layer_permutation: failed!") return False, None, None, None edges = best_edges.edges() for idx in range(best_edges.size // 2): swap_src = self.trivial_layout[edges[2 * idx]] swap_tgt = self.trivial_layout[edges[2 * idx + 1]] trial_circuit.apply_operation_back(SwapGate(), [swap_src, swap_tgt], []) best_circuit = trial_circuit # Otherwise, we return our result for this layer logger.debug("layer_permutation: success!") best_lay = best_layout.to_layout(qregs) return True, best_circuit, best_depth, best_lay
def layer_permutation(self, layer_partition, layout, qubit_subset): """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 Sergey Bravyi's algorithm. The layer_partition is a list of (qu)bit lists and each qubit is a tuple (qreg, index). The layout is a dict mapping qubits in the circuit to qubits in the coupling graph and represents the current positions of the data. The qubit_subset is the subset of qubits in the coupling graph that we have chosen to map into. The coupling is a CouplingGraph. TRIALS is the number of attempts the randomized algorithm makes. Returns: success_flag, best_circ, best_d, best_layout, trivial_flag If success_flag is True, then best_circ contains a DAGCircuit with the swap circuit, best_d 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. """ if self.seed is None: self.seed = np.random.randint(0, np.iinfo(np.int32).max) rng = np.random.RandomState(self.seed) rev_layout = {b: a for a, b in layout.items()} gates = [] for layer in layer_partition: if len(layer) > 2: raise TranspilerError("Layer contains >2 qubit gates") elif len(layer) == 2: gates.append(tuple(layer)) # Can we already apply the gates? dist = sum([ self.coupling_map.distance(layout[g[0]][1], layout[g[1]][1]) for g in gates ]) if dist == len(gates): circ = DAGCircuit() circ.add_qreg(QuantumRegister(self.coupling_map.size(), "q")) return True, circ, 0, layout, bool(gates) # Begin loop over trials of randomized algorithm n = self.coupling_map.size() best_d = sys.maxsize # initialize best depth best_circ = None # initialize best swap circuit best_layout = None # initialize best final layout QR = QuantumRegister(self.coupling_map.size(), "q") for _ in range(self.trials): trial_layout = layout.copy() rev_trial_layout = rev_layout.copy() # SWAP circuit constructed this trial trial_circ = DAGCircuit() trial_circ.add_qreg(QR) # Compute Sergey's randomized distance xi = {} for i in self.coupling_map.physical_qubits: xi[(QR, i)] = {} for i in self.coupling_map.physical_qubits: i = (QR, i) for j in self.coupling_map.physical_qubits: j = (QR, j) scale = 1 + rng.normal(0, 1 / n) xi[i][j] = scale * self.coupling_map.distance(i[1], j[1])**2 xi[j][i] = xi[i][j] # Loop over depths d up to a max depth of 2n+1 d = 1 # Circuit for this swap slice circ = DAGCircuit() circ.add_qreg(QR) # Identity wire-map for composing the circuits identity_wire_map = {(QR, j): (QR, j) for j in range(n)} while d < 2 * n + 1: # Set of available qubits 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 progress_made = False # Loop over edges of coupling graph for e in self.coupling_map.get_edges(): e = [(QR, edge) for edge in e] # Are the qubits available? if e[0] in qubit_set and e[1] in qubit_set: # Try this edge to reduce the cost new_layout = trial_layout.copy() new_layout[rev_trial_layout[e[0]]] = e[1] new_layout[rev_trial_layout[e[1]]] = e[0] rev_new_layout = rev_trial_layout.copy() rev_new_layout[e[0]] = rev_trial_layout[e[1]] rev_new_layout[e[1]] = rev_trial_layout[e[0]] # 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: progress_made = True min_cost = new_cost opt_layout = new_layout rev_opt_layout = rev_new_layout opt_edge = e # Were there any good choices? if progress_made: qubit_set.remove(opt_edge[0]) qubit_set.remove(opt_edge[1]) trial_layout = opt_layout rev_trial_layout = rev_opt_layout circ.apply_operation_back( SwapGate(), [(opt_edge[0][0], opt_edge[0][1]), (opt_edge[1][0], opt_edge[1][1])], []) else: break # We have either run out of qubits or failed to improve # Compute the coupling graph distance_qubits dist = sum([ self.coupling_map.distance(trial_layout[g[0]][1], trial_layout[g[1]][1]) for g in gates ]) # If all gates can be applied now, we are finished # Otherwise we need to consider a deeper swap circuit if dist == len(gates): trial_circ.compose_back(circ, identity_wire_map) break # Increment the depth d += 1 # Either we have succeeded at some depth d < dmax or failed dist = sum([ self.coupling_map.distance(trial_layout[g[0]][1], trial_layout[g[1]][1]) for g in gates ]) if dist == len(gates): if d < best_d: best_circ = trial_circ best_layout = trial_layout best_d = min(best_d, d) if best_circ is None: return False, None, None, None, False return True, best_circ, best_d, best_layout, False