Example #1
0
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')]
Example #2
0
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'})
    ]
Example #3
0
    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]])
            })
        ]
Example #4
0
    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))
Example #5
0
    def run(self, dag):
        """Run the SabreSwap pass on `dag`.

        Args:
            dag (DAGCircuit): the directed acyclic graph to be mapped.
        Returns:
            DAGCircuit: A dag mapped to be compatible with the coupling_map.
        Raises:
            TranspilerError: if the coupling map or the layout are not
            compatible with the DAG
        """
        if len(dag.qregs) != 1 or dag.qregs.get('q', None) is None:
            raise TranspilerError('Sabre swap runs on physical circuits only.')

        if len(dag.qubits) > self.coupling_map.size():
            raise TranspilerError('More virtual qubits exist than physical.')

        rng = np.random.default_rng(self.seed)

        # Preserve input DAG's name, regs, wire_map, etc. but replace the graph.
        mapped_dag = dag._copy_circuit_metadata()

        # Assume bidirectional couplings, fixing gate direction is easy later.
        self.coupling_map.make_symmetric()

        canonical_register = dag.qregs['q']
        current_layout = Layout.generate_trivial_layout(canonical_register)

        # A decay factor for each qubit used to heuristically penalize recently
        # used qubits (to encourage parallelism).
        self.qubits_decay = {qubit: 1 for qubit in dag.qubits}

        # Start algorithm from the front layer and iterate until all gates done.
        num_search_steps = 0
        front_layer = dag.front_layer()
        self.applied_gates = set()
        while front_layer:
            execute_gate_list = []

            # Remove as many immediately applicable gates as possible
            for node in front_layer:
                if len(node.qargs) == 2:
                    v0, v1 = node.qargs
                    physical_qubits = (current_layout[v0], current_layout[v1])
                    if physical_qubits in self.coupling_map.get_edges():
                        execute_gate_list.append(node)
                else:  # Single-qubit gates as well as barriers are free
                    execute_gate_list.append(node)

            if execute_gate_list:
                for node in execute_gate_list:
                    new_node = _transform_gate_for_layout(node, current_layout)
                    mapped_dag.apply_operation_back(new_node.op,
                                                    new_node.qargs,
                                                    new_node.cargs,
                                                    new_node.condition)
                    front_layer.remove(node)
                    self.applied_gates.add(node)
                    for successor in dag.quantum_successors(node):
                        if successor.type != 'op':
                            continue
                        if self._is_resolved(successor, dag):
                            front_layer.append(successor)

                    if node.qargs:
                        self._reset_qubits_decay()

                # Diagnostics
                logger.debug('free! %s',
                             [(n.name, n.qargs) for n in execute_gate_list])
                logger.debug('front_layer: %s',
                             [(n.name, n.qargs) for n in front_layer])

                continue

            # After all free gates are exhausted, heuristically find
            # the best swap and insert it. When two or more swaps tie
            # for best score, pick one randomly.
            extended_set = self._obtain_extended_set(dag, front_layer)
            swap_candidates = self._obtain_swaps(front_layer, current_layout)
            swap_scores = dict.fromkeys(swap_candidates, 0)
            for swap_qubits in swap_scores:
                trial_layout = current_layout.copy()
                trial_layout.swap(*swap_qubits)
                score = self._score_heuristic(self.heuristic, front_layer,
                                              extended_set, trial_layout,
                                              swap_qubits)
                swap_scores[swap_qubits] = score
            min_score = min(swap_scores.values())
            best_swaps = [k for k, v in swap_scores.items() if v == min_score]
            best_swaps.sort(key=lambda x: (x[0].index, x[1].index))
            best_swap = rng.choice(best_swaps)
            swap_node = DAGNode(op=SwapGate(), qargs=best_swap, type='op')
            swap_node = _transform_gate_for_layout(swap_node, current_layout)
            mapped_dag.apply_operation_back(swap_node.op, swap_node.qargs)
            current_layout.swap(*best_swap)

            num_search_steps += 1
            if num_search_steps % DECAY_RESET_INTERVAL == 0:
                self._reset_qubits_decay()
            else:
                self.qubits_decay[best_swap[0]] += DECAY_RATE
                self.qubits_decay[best_swap[1]] += DECAY_RATE

            # Diagnostics
            logger.debug('SWAP Selection...')
            logger.debug('extended_set: %s',
                         [(n.name, n.qargs) for n in extended_set])
            logger.debug('swap scores: %s', swap_scores)
            logger.debug('best swap: %s', best_swap)
            logger.debug('qubits decay: %s', self.qubits_decay)

        self.property_set['final_layout'] = current_layout

        return mapped_dag
Example #6
0
    def run(self, dag):
        """Run the SabreSwap pass on `dag`.

        Args:
            dag (DAGCircuit): the directed acyclic graph to be mapped.
        Returns:
            DAGCircuit: A dag mapped to be compatible with the coupling_map.
        Raises:
            TranspilerError: if the coupling map or the layout are not
            compatible with the DAG
        """
        if len(dag.qregs) != 1 or dag.qregs.get("q", None) is None:
            raise TranspilerError("Sabre swap runs on physical circuits only.")

        if len(dag.qubits) > self.coupling_map.size():
            raise TranspilerError("More virtual qubits exist than physical.")

        rng = np.random.default_rng(self.seed)

        # Preserve input DAG's name, regs, wire_map, etc. but replace the graph.
        mapped_dag = None
        if not self.fake_run:
            mapped_dag = dag._copy_circuit_metadata()

        canonical_register = dag.qregs["q"]
        current_layout = Layout.generate_trivial_layout(canonical_register)

        self._bit_indices = {
            bit: idx
            for idx, bit in enumerate(canonical_register)
        }

        # A decay factor for each qubit used to heuristically penalize recently
        # used qubits (to encourage parallelism).
        self.qubits_decay = {qubit: 1 for qubit in dag.qubits}

        # Start algorithm from the front layer and iterate until all gates done.
        num_search_steps = 0
        front_layer = dag.front_layer()
        self.applied_predecessors = defaultdict(int)
        for _, input_node in dag.input_map.items():
            for successor in self._successors(input_node, dag):
                self.applied_predecessors[successor] += 1
        while front_layer:
            execute_gate_list = []

            # Remove as many immediately applicable gates as possible
            for node in front_layer:
                if len(node.qargs) == 2:
                    v0, v1 = node.qargs
                    if self.coupling_map.graph.has_edge(
                            current_layout[v0], current_layout[v1]):
                        execute_gate_list.append(node)
                else:  # Single-qubit gates as well as barriers are free
                    execute_gate_list.append(node)

            if execute_gate_list:
                for node in execute_gate_list:
                    self._apply_gate(mapped_dag, node, current_layout,
                                     canonical_register)
                    front_layer.remove(node)
                    for successor in self._successors(node, dag):
                        self.applied_predecessors[successor] += 1
                        if self._is_resolved(successor):
                            front_layer.append(successor)

                    if node.qargs:
                        self._reset_qubits_decay()

                # Diagnostics
                logger.debug("free! %s",
                             [(n.name, n.qargs) for n in execute_gate_list])
                logger.debug("front_layer: %s",
                             [(n.name, n.qargs) for n in front_layer])

                continue

            # After all free gates are exhausted, heuristically find
            # the best swap and insert it. When two or more swaps tie
            # for best score, pick one randomly.
            extended_set = self._obtain_extended_set(dag, front_layer)
            swap_candidates = self._obtain_swaps(front_layer, current_layout)
            swap_scores = dict.fromkeys(swap_candidates, 0)
            for swap_qubits in swap_scores:
                trial_layout = current_layout.copy()
                trial_layout.swap(*swap_qubits)
                score = self._score_heuristic(self.heuristic, front_layer,
                                              extended_set, trial_layout,
                                              swap_qubits)
                swap_scores[swap_qubits] = score
            min_score = min(swap_scores.values())
            best_swaps = [k for k, v in swap_scores.items() if v == min_score]
            best_swaps.sort(key=lambda x:
                            (self._bit_indices[x[0]], self._bit_indices[x[1]]))
            best_swap = rng.choice(best_swaps)
            swap_node = DAGNode(op=SwapGate(), qargs=best_swap, type="op")
            self._apply_gate(mapped_dag, swap_node, current_layout,
                             canonical_register)
            current_layout.swap(*best_swap)

            num_search_steps += 1
            if num_search_steps % DECAY_RESET_INTERVAL == 0:
                self._reset_qubits_decay()
            else:
                self.qubits_decay[best_swap[0]] += DECAY_RATE
                self.qubits_decay[best_swap[1]] += DECAY_RATE

            # Diagnostics
            logger.debug("SWAP Selection...")
            logger.debug("extended_set: %s",
                         [(n.name, n.qargs) for n in extended_set])
            logger.debug("swap scores: %s", swap_scores)
            logger.debug("best swap: %s", best_swap)
            logger.debug("qubits decay: %s", self.qubits_decay)

        self.property_set["final_layout"] = current_layout

        if not self.fake_run:
            return mapped_dag
        return dag