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 _define(self): """Define the MCX gate using a V-chain of CX gates.""" # pylint: disable=cyclic-import from qiskit.circuit.quantumcircuit import QuantumCircuit q = QuantumRegister(self.num_qubits, name='q') qc = QuantumCircuit(q, name=self.name) q_controls = q[:self.num_ctrl_qubits] q_target = q[self.num_ctrl_qubits] q_ancillas = q[self.num_ctrl_qubits + 1:] definition = [] if self._dirty_ancillas: i = self.num_ctrl_qubits - 3 ancilla_pre_rule = [ (U2Gate(0, numpy.pi), [q_target], []), (CXGate(), [q_target, q_ancillas[i]], []), (U1Gate(-numpy.pi / 4), [q_ancillas[i]], []), (CXGate(), [q_controls[-1], q_ancillas[i]], []), (U1Gate(numpy.pi / 4), [q_ancillas[i]], []), (CXGate(), [q_target, q_ancillas[i]], []), (U1Gate(-numpy.pi / 4), [q_ancillas[i]], []), (CXGate(), [q_controls[-1], q_ancillas[i]], []), (U1Gate(numpy.pi / 4), [q_ancillas[i]], []), ] for inst in ancilla_pre_rule: definition.append(inst) for j in reversed(range(2, self.num_ctrl_qubits - 1)): definition.append( (RCCXGate(), [q_controls[j], q_ancillas[i - 1], q_ancillas[i]], [])) i -= 1 definition.append( (RCCXGate(), [q_controls[0], q_controls[1], q_ancillas[0]], [])) i = 0 for j in range(2, self.num_ctrl_qubits - 1): definition.append( (RCCXGate(), [q_controls[j], q_ancillas[i], q_ancillas[i + 1]], [])) i += 1 if self._dirty_ancillas: ancilla_post_rule = [ (U1Gate(-numpy.pi / 4), [q_ancillas[i]], []), (CXGate(), [q_controls[-1], q_ancillas[i]], []), (U1Gate(numpy.pi / 4), [q_ancillas[i]], []), (CXGate(), [q_target, q_ancillas[i]], []), (U1Gate(-numpy.pi / 4), [q_ancillas[i]], []), (CXGate(), [q_controls[-1], q_ancillas[i]], []), (U1Gate(numpy.pi / 4), [q_ancillas[i]], []), (CXGate(), [q_target, q_ancillas[i]], []), (U2Gate(0, numpy.pi), [q_target], []), ] for inst in ancilla_post_rule: definition.append(inst) else: definition.append( (CCXGate(), [q_controls[-1], q_ancillas[i], q_target], [])) for j in reversed(range(2, self.num_ctrl_qubits - 1)): definition.append( (RCCXGate(), [q_controls[j], q_ancillas[i - 1], q_ancillas[i]], [])) i -= 1 definition.append( (RCCXGate(), [q_controls[0], q_controls[1], q_ancillas[i]], [])) if self._dirty_ancillas: for i, j in enumerate(list(range(2, self.num_ctrl_qubits - 1))): definition.append( (RCCXGate(), [q_controls[j], q_ancillas[i], q_ancillas[i + 1]], [])) for instr, qargs, cargs in definition: qc._append(instr, qargs, cargs) self.definition = qc