コード例 #1
0
ファイル: test_region_methods.py プロジェクト: BQSKit/bqskit
 def test_through_middle_of_outside(self) -> None:
     circuit = Circuit(3)
     circuit.append_gate(CNOTGate(), (0, 1))
     circuit.append_gate(HGate(), 0)
     circuit.append_gate(HGate(), 1)
     circuit.append_gate(CNOTGate(), (0, 2))
     circuit.append_gate(CNOTGate(), (0, 1))
     region = circuit.surround((1, 0), 2)
     assert region == CircuitRegion({0: (0, 1), 1: (0, 1)})
コード例 #2
0
ファイル: test_region_methods.py プロジェクト: BQSKit/bqskit
 def test_small_circuit_1(self) -> None:
     circuit = Circuit(2)
     circuit.append_gate(HGate(), 0)
     circuit.append_gate(HGate(), 1)
     circuit.append_gate(CNOTGate(), (0, 1))
     circuit.append_gate(HGate(), 0)
     circuit.append_gate(HGate(), 1)
     region = circuit.surround((0, 1), 2)
     assert region == CircuitRegion({0: (0, 2), 1: (0, 2)})
コード例 #3
0
ファイル: scan.py プロジェクト: BQSKit/bqskit
    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)
コード例 #4
0
    def __init__(
        self,
        circuit: Circuit,
        start: CircuitPointLike = CircuitPoint(0, 0),
        end: CircuitPointLike | None = None,
        qudits_or_region: CircuitRegionLike | Sequence[int] | None = None,
        exclude: bool = False,
        reverse: bool = False,
        and_cycles: bool = False,
    ) -> None:
        """
        Construct a CircuitIterator.

        Args:
            circuit (Circuit): The circuit to iterate through.

            start (CircuitPointLike): Only iterate through points greater
                than or equal to `start`. Defaults to start at the beginning
                of the circuit. (Default: (0, 0))

            end (CircuitPointLike | None): Only iterate through points
                less than or equal to this. If left as None, iterates
                until the end of the circuit. (Default: None)

            qudits_or_region (CircuitRegionLike | Sequence[int] | None):
                Determines the way the circuit is iterated. If a region
                is given, then iterate through operations in the region.
                If a sequence of qudit indices is given, then only iterate
                the operations touching those qudits. If left as None,
                then iterate through the entire circuit in simulation order.
                (Default: None)

            exclude (bool): If iterating through a region or only some
                qudits and `exclude` is true, then do not yield operations
                that are only partially in the region or on the desired
                qudits. This may result in a sequence of operations that
                does not occur in simulation order in the circuit.
                (Default: False)

            reverse (bool): Reverse the ordering. If true, then end acts
                as start and vice versa. (Default: False)

            and_cycles (bool): If true, in addition to the operation,
                return the cycle index where it was found. (Default: False)
        """
        if not CircuitPoint.is_point(start):
            raise TypeError(f'Expected point for start, got {type(start)}.')

        if end is not None and not CircuitPoint.is_point(end):
            raise TypeError(f'Expected point for end, got {type(end)}.')

        if end is None:
            end = CircuitPoint(
                circuit.get_num_cycles() - 1,
                circuit.get_size() - 1,
            )

        self.circuit = circuit
        self.start = CircuitPoint(*start)
        self.end = CircuitPoint(*end)
        self.exclude = exclude
        self.reverse = reverse
        self.and_cycles = and_cycles

        # Set mode of iteration:
        if qudits_or_region is None:
            # iterate through the entire circuit normally
            self.qudits = list(range(self.circuit.get_size()))
            self.region = CircuitRegion({
                qudit: (0, self.circuit.get_num_cycles())
                for qudit in self.qudits
            })

        elif CircuitRegion.is_region(qudits_or_region):  # TODO: Typeguard
            # iterate through the region in the circuit
            self.qudits = list(qudits_or_region.keys())  # type: ignore
            self.region = CircuitRegion(qudits_or_region)  # type: ignore

        elif is_sequence(qudits_or_region):
            # iterate through the circuit but only on the specified qudits
            if not all(is_integer(qudit) for qudit in qudits_or_region):
                raise TypeError('Expected region or sequence of indices.')

            if not all(0 <= qudit < self.circuit.get_size()
                       for qudit in qudits_or_region):
                raise ValueError('Invalid sequence of qudit indices.')

            self.qudits = list(qudits_or_region)
            self.region = CircuitRegion({
                qudit: (0, self.circuit.get_num_cycles())
                for qudit in self.qudits
            })

        self.max_qudit = max(self.qudits)
        self.min_qudit = min(self.qudits)
        self.min_cycle = self.region.min_cycle
        self.max_cycle = self.region.max_cycle

        if start < (self.min_cycle, self.min_qudit):
            start = CircuitPoint(self.min_cycle, self.min_qudit)

        if end > (self.max_cycle, self.max_qudit):
            end = CircuitPoint(self.max_cycle, self.max_qudit)

        assert isinstance(start, CircuitPoint)  # TODO: Typeguard
        assert isinstance(end, CircuitPoint)  # TODO: Typeguard

        # Pointer into the circuit structure
        self.cycle = start.cycle if not self.reverse else end.cycle
        self.qudit = start.qudit if not self.reverse else end.qudit

        # Used to track changes to circuit structure
        self.num_ops = self.circuit.get_num_operations()
        self.num_cycles = self.circuit.get_num_cycles()
        self.num_qudits = self.circuit.get_size()

        # Ensure operations are only returned once
        self.qudits_to_skip: set[int] = set()
コード例 #5
0
class CircuitIterator(
        Iterator[Union[Operation, Tuple[int,
                                        Operation],  # if and_cycles == True
                       ]], ):
    """
    The CircuitIterator Class.

    A CircuitIterator can iterate through a circuit in a few different ways. By
    default it can iterate through all operations in the circuit in simulation
    order. Additionally, it can iterate all the operations on a qudit or set of
    qudits or iterate through a specified CircuitRegion. If iterating all
    operations in a region or on some qudits, you can choose to exclude
    operations that only are partially in the specified area.
    """
    def __init__(
        self,
        circuit: Circuit,
        start: CircuitPointLike = CircuitPoint(0, 0),
        end: CircuitPointLike | None = None,
        qudits_or_region: CircuitRegionLike | Sequence[int] | None = None,
        exclude: bool = False,
        reverse: bool = False,
        and_cycles: bool = False,
    ) -> None:
        """
        Construct a CircuitIterator.

        Args:
            circuit (Circuit): The circuit to iterate through.

            start (CircuitPointLike): Only iterate through points greater
                than or equal to `start`. Defaults to start at the beginning
                of the circuit. (Default: (0, 0))

            end (CircuitPointLike | None): Only iterate through points
                less than or equal to this. If left as None, iterates
                until the end of the circuit. (Default: None)

            qudits_or_region (CircuitRegionLike | Sequence[int] | None):
                Determines the way the circuit is iterated. If a region
                is given, then iterate through operations in the region.
                If a sequence of qudit indices is given, then only iterate
                the operations touching those qudits. If left as None,
                then iterate through the entire circuit in simulation order.
                (Default: None)

            exclude (bool): If iterating through a region or only some
                qudits and `exclude` is true, then do not yield operations
                that are only partially in the region or on the desired
                qudits. This may result in a sequence of operations that
                does not occur in simulation order in the circuit.
                (Default: False)

            reverse (bool): Reverse the ordering. If true, then end acts
                as start and vice versa. (Default: False)

            and_cycles (bool): If true, in addition to the operation,
                return the cycle index where it was found. (Default: False)
        """
        if not CircuitPoint.is_point(start):
            raise TypeError(f'Expected point for start, got {type(start)}.')

        if end is not None and not CircuitPoint.is_point(end):
            raise TypeError(f'Expected point for end, got {type(end)}.')

        if end is None:
            end = CircuitPoint(
                circuit.get_num_cycles() - 1,
                circuit.get_size() - 1,
            )

        self.circuit = circuit
        self.start = CircuitPoint(*start)
        self.end = CircuitPoint(*end)
        self.exclude = exclude
        self.reverse = reverse
        self.and_cycles = and_cycles

        # Set mode of iteration:
        if qudits_or_region is None:
            # iterate through the entire circuit normally
            self.qudits = list(range(self.circuit.get_size()))
            self.region = CircuitRegion({
                qudit: (0, self.circuit.get_num_cycles())
                for qudit in self.qudits
            })

        elif CircuitRegion.is_region(qudits_or_region):  # TODO: Typeguard
            # iterate through the region in the circuit
            self.qudits = list(qudits_or_region.keys())  # type: ignore
            self.region = CircuitRegion(qudits_or_region)  # type: ignore

        elif is_sequence(qudits_or_region):
            # iterate through the circuit but only on the specified qudits
            if not all(is_integer(qudit) for qudit in qudits_or_region):
                raise TypeError('Expected region or sequence of indices.')

            if not all(0 <= qudit < self.circuit.get_size()
                       for qudit in qudits_or_region):
                raise ValueError('Invalid sequence of qudit indices.')

            self.qudits = list(qudits_or_region)
            self.region = CircuitRegion({
                qudit: (0, self.circuit.get_num_cycles())
                for qudit in self.qudits
            })

        self.max_qudit = max(self.qudits)
        self.min_qudit = min(self.qudits)
        self.min_cycle = self.region.min_cycle
        self.max_cycle = self.region.max_cycle

        if start < (self.min_cycle, self.min_qudit):
            start = CircuitPoint(self.min_cycle, self.min_qudit)

        if end > (self.max_cycle, self.max_qudit):
            end = CircuitPoint(self.max_cycle, self.max_qudit)

        assert isinstance(start, CircuitPoint)  # TODO: Typeguard
        assert isinstance(end, CircuitPoint)  # TODO: Typeguard

        # Pointer into the circuit structure
        self.cycle = start.cycle if not self.reverse else end.cycle
        self.qudit = start.qudit if not self.reverse else end.qudit

        # Used to track changes to circuit structure
        self.num_ops = self.circuit.get_num_operations()
        self.num_cycles = self.circuit.get_num_cycles()
        self.num_qudits = self.circuit.get_size()

        # Ensure operations are only returned once
        self.qudits_to_skip: set[int] = set()

    def increment_iter(self) -> None:
        """Increment the iterator to the next valid circuit point."""
        while (self.qudit in self.qudits_to_skip
               or self.qudit not in self.qudits
               or (self.cycle not in self.region[self.qudit]
                   and self.cycle <= self.max_cycle)):
            self.qudit += 1

            if self.qudit > self.max_qudit:
                self.qudit = self.min_qudit
                self.cycle += 1
                self.qudits_to_skip.clear()

    def decrement_iter(self) -> None:
        """Decrement the iterator to the next valid circuit point."""
        while (self.qudit in self.qudits_to_skip
               or self.qudit not in self.qudits
               or (self.cycle not in self.region[self.qudit]
                   and self.cycle >= self.min_cycle)):
            self.qudit -= 1

            if self.qudit < self.min_qudit:
                self.qudit = self.max_qudit
                self.cycle -= 1
                self.qudits_to_skip.clear()

    def step(self) -> None:
        """Move the iterator one step."""
        if (self.num_ops != self.circuit.get_num_operations()
                or self.num_cycles != self.circuit.get_num_cycles()
                or self.num_qudits != self.circuit.get_size()):
            raise RuntimeError('Circuit changed under iteration.')

        if not self.reverse:
            self.increment_iter()
        else:
            self.decrement_iter()

        point = (self.cycle, self.qudit)
        if point < self.start or point > self.end:
            raise StopIteration

    def __next__(self) -> Operation | tuple[int, Operation]:
        while True:
            self.step()
            op = self.circuit._circuit[self.cycle][self.qudit]

            if op is None:
                self.qudits_to_skip.add(self.qudit)
                continue

            if self.exclude:
                if not all(qudit in self.qudits for qudit in op.location):
                    continue

                if not all(
                        self.region.overlaps((self.cycle, qudit))
                        for qudit in op.location):
                    continue

            self.qudits_to_skip.update(op.location)

            if self.and_cycles:
                return self.cycle, op

            return op

    def __iter__(self) -> CircuitIterator:
        return self