Ejemplo n.º 1
0
    def run(self, circuit: Circuit, data: dict[str, Any]) -> None:
        """Perform the pass's operation, see BasePass for more info."""
        start = 'left' if self.start_from_left else 'right'
        _logger.debug(f'Starting scan gate removal from {start}.')

        target = circuit.get_unitary()

        circuit_copy = circuit.copy()
        reverse_iter = not self.start_from_left
        for cycle, op in circuit.operations_with_cycles(reverse=reverse_iter):

            if not self.collection_filter(op):
                _logger.debug(f'Skipping operation {op} at cycle {cycle}.')
                continue

            _logger.info(f'Attempting removal of operation at cycle {cycle}.')
            _logger.debug(f'Operation: {op}')

            working_copy = circuit_copy.copy()

            # If removing gates from the left, we need to track index changes.
            if self.start_from_left:
                idx_shift = circuit.get_num_cycles()
                idx_shift -= working_copy.get_num_cycles()
                cycle -= idx_shift

            working_copy.pop((cycle, op.location[0]))
            working_copy.instantiate(target, **self.instantiate_options)

            if self.cost(working_copy, target) < self.success_threshold:
                _logger.info('Successfully removed operation.')
                circuit_copy = working_copy

        circuit.become(circuit_copy)
Ejemplo n.º 2
0
    def run(self, circuit: Circuit, data: dict[str, Any]) -> None:
        """
        Partition gates in a circuit into a series of CircuitGates.

        Args:
            circuit (Circuit): Circuit to be partitioned.

            data (dict[str,Any]): Optional data unique to specific run.
        """

        if self.block_size > circuit.get_size():
            _logger.warning(
                'Configured block size is greater than circuit size; '
                'blocking entire circuit.', )
            circuit.fold({
                qudit_index: (0, circuit.get_num_cycles())
                for qudit_index in range(circuit.get_size())
            })
            return

        # If a MachineModel is provided in the data dict, it will be used.
        # Otherwise all-to-all connectivity is assumed.
        model = None

        if 'machine_model' in data:
            model = data['machine_model']

        if (not isinstance(model, MachineModel)
                or model.num_qudits < circuit.get_size()):
            _logger.warning(
                'MachineModel not specified or invalid;'
                ' defaulting to all-to-all.', )
            model = MachineModel(circuit.get_size())

        # Find all connected, `block_size`-sized groups of qudits
        # NOTE: This assumes circuit and topology qudit numbers are equal
        qudit_groups = model.get_locations(self.block_size)
        # Prune unused qudit groups
        used_qudits = [
            q for q in range(circuit.get_size())
            if not circuit.is_qudit_idle(q)
        ]
        for qudit_group in qudit_groups:
            if all([q not in used_qudits for q in qudit_group]):
                qudit_groups.remove(qudit_group)

        # divider splits the circuit into partitioned and unpartitioned spaces.
        active_qudits = circuit.get_active_qudits()
        num_cycles = circuit.get_num_cycles()
        divider = [
            0 if q in active_qudits else num_cycles
            for q in range(circuit.get_size())
        ]

        # Form regions until there are no more gates to partition
        regions: list[CircuitRegion] = []
        while any(cycle < num_cycles for cycle in divider):

            # Move past/skip any gates that are larger than block size
            qudits_to_increment: list[int] = []
            for qudit, cycle in enumerate(divider):
                if qudit in qudits_to_increment or cycle >= num_cycles:
                    continue

                if not circuit.is_point_idle((cycle, qudit)):
                    op = circuit[cycle, qudit]
                    if len(op.location) > self.block_size:
                        if all(divider[q] == cycle for q in op.location):
                            qudits_to_increment.extend(op.location)
                            _logger.warning(
                                'Skipping gate larger than block size.', )

            if len(qudits_to_increment) > 0:
                regions.append(
                    CircuitRegion(
                        {
                            qudit: (divider[qudit], divider[qudit])
                            for qudit in qudits_to_increment
                        }, ), )

            for qudit in qudits_to_increment:
                divider[qudit] += 1

            # Skip any idle qudit-cycles
            amount_to_add_to_each_qudit = [
                0 for _ in range(circuit.get_size())
            ]
            for qudit, cycle in enumerate(divider):
                while (cycle < num_cycles and circuit.is_point_idle(
                    (cycle, qudit))):
                    amount_to_add_to_each_qudit[qudit] += 1
                    cycle += 1

            for qudit, amount in enumerate(amount_to_add_to_each_qudit):
                divider[qudit] += amount

            # Find the scores of the qudit groups.
            best_score = None
            best_region = None

            for qudit_group in qudit_groups:

                ops_and_cycles = circuit.operations_with_cycles(
                    qudits_or_region=CircuitRegion({
                        qudit_index: (divider[qudit_index], num_cycles)
                        for qudit_index in qudit_group
                    }), )

                in_qudits = list(q for q in qudit_group)
                stopped_cycles = {q: num_cycles for q in qudit_group}
                score = 0

                for cycle, op in ops_and_cycles:
                    if len(op.location.union(in_qudits)) != len(in_qudits):
                        for qudit_index in op.location.intersection(in_qudits):
                            stopped_cycles[qudit_index] = cycle
                            in_qudits.remove(qudit_index)
                    else:
                        if len(op.location) > 1:
                            score += self.multi_gate_score
                        else:
                            score += self.single_gate_score

                    if len(in_qudits) == 0:
                        break

                if best_score is None or score > best_score:
                    best_score = score
                    best_region = CircuitRegion({
                        qudit: (
                            divider[qudit],
                            # Might have errors if below is removed
                            stopped_cycles[qudit] - 1,
                        )
                        for qudit in qudit_group
                        # This statement
                        # if stopped_cycles[qudit] - 1 >= divider[qudit]
                    })

            if best_score is None or best_region is None:
                raise RuntimeError('No valid block found.')

            _logger.info('Found block with score: %d.' % (best_score))
            regions.append(best_region)

            # Update divider
            for qudit_index in best_region:
                divider[qudit_index] = best_region[qudit_index].upper + 1

        # Fold the circuit
        folded_circuit = Circuit(circuit.get_size(), circuit.get_radixes())
        # Option to keep a block's idle qudits as part of the CircuitGate
        if 'keep_idle_qudits' in data and data['keep_idle_qudits'] is True:
            for region in regions:
                small_region = circuit.downsize_region(region)
                cgc = circuit.get_slice(small_region.points)
                if len(region.location) > len(small_region.location):
                    for i in range(len(region.location)):
                        if region.location[i] not in small_region.location:
                            cgc.insert_qudit(i)
                folded_circuit.append_gate(
                    CircuitGate(cgc, True),
                    sorted(list(region.keys())),
                    list(cgc.get_params()),
                )
        else:
            for region in regions:
                region = circuit.downsize_region(region)
                if 0 < len(region) <= self.block_size:
                    cgc = circuit.get_slice(region.points)
                    folded_circuit.append_gate(
                        CircuitGate(cgc, True),
                        sorted(list(region.keys())),
                        list(cgc.get_params()),
                    )
                else:
                    folded_circuit.extend(circuit[region])
        circuit.become(folded_circuit)