Exemplo n.º 1
0
 def run(self, circuit: Circuit, data: dict[str, Any]) -> None:
     """Perform the pass's operation, see BasePass for more info."""
     _logger.debug('Converting PauliGates to U3Gates.')
     for cycle, op in circuit.operations_with_cycles():
         if isinstance(op.gate, PauliGate) and len(op.location, ) == 1:
             # Convert to SU(2)
             unitary = op.get_unitary().get_numpy()
             mag = np.linalg.det(unitary)**(-1 / 2)
             special_unitary = mag * unitary
             a = np.angle(special_unitary[1, 1])
             b = np.angle(special_unitary[1, 0])
             # Get angles
             theta = float(
                 np.arctan2(
                     np.abs(special_unitary[1, 0]),
                     np.abs(special_unitary[0, 0]),
                 ), ) * 2
             phi = (a + b)
             lamb = (a - b)
             # Replace
             point = CircuitPoint(cycle, op.location[0])
             circuit.replace_gate(
                 point,
                 U3Gate(),
                 op.location,
                 [theta, phi, lamb],
             )
Exemplo n.º 2
0
 def points(self) -> list[CircuitPoint]:
     """Return the points described by this region."""
     return [
         CircuitPoint(cycle_index, qudit_index)
         for qudit_index, bounds in self.items()
         for cycle_index in bounds.indices
     ]
Exemplo n.º 3
0
 def test_type_invalid_2(self, not_an_int: Any) -> None:
     circuit = Circuit(1)
     try:
         circuit.get_operation(CircuitPoint(not_an_int, 0))
     except TypeError:
         return
     except BaseException:
         assert False, 'Unexpected Exception.'
Exemplo n.º 4
0
 def test_type_valid_4(self, an_int: int) -> None:
     circuit = Circuit(4, [2, 2, 3, 3])
     try:
         circuit.get_operation(CircuitPoint(an_int, an_int))
     except TypeError:
         assert False, 'Unexpected TypeError.'
     except BaseException:
         return
Exemplo n.º 5
0
 def test_type_valid_2(self, an_int: int) -> None:
     circuit = Circuit(1)
     try:
         circuit.is_point_in_range(CircuitPoint(an_int, an_int))
     except TypeError:
         assert False, 'Unexpected TypeError.'
     except BaseException:
         return
Exemplo n.º 6
0
    def run(self, circuit: Circuit, data: dict[str, Any]) -> None:
        """Perform the pass's operation, see BasePass for more info."""

        # Collect CircuitGate blocks
        blocks: list[tuple[CircuitPoint, Operation]] = []
        for cycle, op in circuit.operations_with_cycles():
            if isinstance(op.gate, CircuitGate):
                blocks.append((cycle, op))

        # 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())

        subdata = data.copy()

        # Perform work
        points: list[CircuitPoint] = []
        ops: list[Operation] = []
        for cycle, op in blocks:
            gate: CircuitGate = op.gate
            subcircuit = gate._circuit.copy()
            subcircuit.set_params(op.params)

            subnumbering = {op.location[i]: i for i in range(len(op.location))}
            subdata['machine_model'] = MachineModel(
                len(op.location),
                model.get_subgraph(op.location, subnumbering),
            )

            for loop_pass in self.loop_body:
                loop_pass.run(circuit, subdata)

            if self.replace_filter(subcircuit, op):
                points.append(CircuitPoint(cycle, op.location[0]))
                ops.append(
                    Operation(
                        CircuitGate(subcircuit, True),
                        op.location,
                        subcircuit.get_params(),
                    ), )

            # TODO: Load freshly written data from subdata into data

        circuit.batch_replace(points, ops)
Exemplo n.º 7
0
    def __lt__(self, other: object) -> bool:
        if CircuitPoint.is_point(other):  # TODO: TypeGuard
            other = CircuitPoint(*other)  # type: ignore
            if other[0] < self.min_cycle:
                return True

            if other[1] in self.keys():
                return other[0] < self[other[1]].lower

        elif CircuitRegion.is_region(other):  # TODO: TypeGuard
            other = CircuitRegion(other)  # type: ignore

            if len(self.location.intersection(other.location)) != 0:
                lt = None
                for qudit in self.location.intersection(other.location):
                    if lt is None:
                        lt = self[qudit] < other[qudit]
                    elif lt != self[qudit] < other[qudit]:
                        raise ValueError('Both regions depend on each other.')

                assert lt is not None
                return lt

            lower_bounds = tuple(sorted({x.lower for x in self.values()}))
            other_lower_bounds = tuple(
                sorted({x.lower
                        for x in other.values()}), )
            upper_bounds = tuple(
                reversed(sorted({x.upper
                                 for x in self.values()})), )
            other_upper_bounds = tuple(
                reversed(sorted({x.upper
                                 for x in other.values()})), )
            return (lower_bounds, upper_bounds) < (
                other_lower_bounds,
                other_upper_bounds,
            )

        return NotImplemented
Exemplo n.º 8
0
    def overlaps(self, other: CircuitPointLike | CircuitRegionLike) -> bool:
        """Return true if `other` overlaps this region."""

        if CircuitPoint.is_point(other):
            other = CircuitPoint(*other)

            if other.qudit not in self:
                return False

            bounds = self[other.qudit]
            return bounds.lower <= other.cycle <= bounds.upper

        if CircuitRegion.is_region(other):  # TODO: Typeguard
            other = CircuitRegion(other)  # type: ignore

            if self.min_cycle > other.max_cycle:
                return False

            if self.max_cycle < other.min_cycle:
                return False

            qudit_intersection = self.location.intersection(other.location)

            if len(qudit_intersection) == 0:
                return False

            for qudit in qudit_intersection:
                if (self[qudit].lower < other[qudit].upper
                        and self[qudit].upper > other[qudit].lower):
                    return True

            return False

        raise TypeError(
            'Expected either CircuitPoint or CircuitRegion, got %s.' %
            type(other), )
Exemplo n.º 9
0
    def __contains__(self, other: object) -> bool:
        if is_integer(other):
            return other in self._bounds.keys()

        if CircuitPoint.is_point(other):  # TODO: TypeGuard
            return other[1] in self.keys() and other[0] in self[
                other[1]]  # type: ignore  # noqa

        if CircuitRegion.is_region(other):  # TODO: TypeGuard
            other = CircuitRegion(other)  # type: ignore
            return (
                all(qudit in self.keys() for qudit in other.keys()) and all(
                    self[qudit].lower <= other[qudit][0] <= self[qudit].upper
                    for qudit in self.keys()) and
                all(self[qudit].lower <= other[qudit][1] <= self[qudit].upper
                    for qudit in self.keys()))

        return NotImplemented
Exemplo n.º 10
0
class TestFold:
    """This tests `circuit.fold`."""
    @pytest.mark.parametrize(
        'points',
        [
            [(0, 0)],
            [(0, 0), (1, 2)],
            [CircuitPoint(0, 0), (1, 2)],
            [(0, 0), CircuitPoint(1, 2)],
            [CircuitPoint(0, 0), CircuitPoint(1, 2)],
        ],
    )
    def test_type_valid(self, points: Sequence[CircuitPointLike]) -> None:
        circuit = Circuit(4, [2, 2, 3, 3])
        try:
            circuit.fold(circuit.get_region(points))
        except TypeError:
            assert False, 'Unexpected TypeError.'
        except BaseException:
            return

    @pytest.mark.parametrize(
        'not_points',
        [
            5,
            [1, 2],
            [1, 'a'],
            'abc',
        ],
    )
    def test_type_invalid(self, not_points: Any) -> None:
        circuit = Circuit(4, [2, 2, 3, 3])
        with pytest.raises(TypeError):
            circuit.fold(not_points)

    @pytest.mark.parametrize(
        'points',
        [
            [(0, 0)],
            [(0, 0), (1, 2)],
            [CircuitPoint(0, 0), (1, 2)],
            [(0, 0), CircuitPoint(1, 2)],
            [CircuitPoint(0, 0), CircuitPoint(1, 2)],
        ],
    )
    def test_invalid_points(self, points: Sequence[CircuitPointLike]) -> None:
        circuit = Circuit(4, [2, 2, 3, 3])
        with pytest.raises(IndexError):
            circuit.fold(circuit.get_region(points))

    # def test_empty(self, r6_qudit_circuit: Circuit) -> None:
    #     num_ops = r6_qudit_circuit.get_num_operations()
    #     gate_set = r6_qudit_circuit.get_gate_set()
    #     r6_qudit_circuit.fold(r6_qudit_circuit.get_region([]))
    #     assert num_ops == r6_qudit_circuit.get_num_operations()
    #     assert gate_set == r6_qudit_circuit.get_gate_set()

    @pytest.mark.parametrize(
        'points',
        [
            [(0, 0), (3, 0)],
            [(3, 0), (0, 0)],
            [(0, 0), (2, 0)],
            [(2, 0), (0, 0)],
        ],
    )
    def test_invalid_fold(self, points: Sequence[CircuitPointLike]) -> None:
        circuit = Circuit(4)
        wide_gate = IdentityGate(4)
        circuit.append_gate(wide_gate, [0, 1, 2, 3])
        circuit.append_gate(wide_gate, [0, 1, 2, 3])
        circuit.append_gate(wide_gate, [0, 1, 2, 3])
        circuit.append_gate(wide_gate, [0, 1, 2, 3])
        with pytest.raises(ValueError):
            circuit.fold(circuit.get_region(points))

    def test_correctness_1(self) -> None:
        circuit = Circuit(4)
        wide_gate = IdentityGate(4)
        circuit.append_gate(wide_gate, [0, 1, 2, 3])
        circuit.append_gate(wide_gate, [0, 1, 2, 3])
        circuit.append_gate(wide_gate, [0, 1, 2, 3])
        circuit.append_gate(wide_gate, [0, 1, 2, 3])
        assert circuit.get_num_operations() == 4
        assert circuit.get_depth() == 4
        utry = circuit.get_unitary()

        circuit.fold(circuit.get_region([(0, 0), (1, 0)]))
        assert circuit.get_num_operations() == 3
        assert circuit.get_depth() == 3
        check_no_idle_cycles(circuit)
        for q in range(4):
            assert isinstance(circuit[0, q].gate, CircuitGate)
        for c in range(1, 3, 1):
            for q in range(4):
                assert isinstance(circuit[c, q].gate, IdentityGate)
                assert isinstance(circuit[c, q].gate, IdentityGate)
        test_gate: CircuitGate = circuit[0, 0].gate  # type: ignore
        assert test_gate._circuit.get_num_operations() == 2
        assert test_gate._circuit.get_num_cycles() == 2
        for q in range(4):
            assert isinstance(test_gate._circuit[0, q].gate, IdentityGate)
            assert isinstance(test_gate._circuit[1, q].gate, IdentityGate)

        circuit.fold(circuit.get_region([(1, 0), (2, 0)]))
        assert circuit.get_num_operations() == 2
        assert circuit.get_depth() == 2
        check_no_idle_cycles(circuit)
        for c in range(2):
            for q in range(4):
                assert isinstance(circuit[c, q].gate, CircuitGate)
        test_gate: CircuitGate = circuit[0, 0].gate  # type: ignore
        assert test_gate._circuit.get_num_operations() == 2
        assert test_gate._circuit.get_num_cycles() == 2
        for q in range(4):
            assert isinstance(test_gate._circuit[0, q].gate, IdentityGate)
            assert isinstance(test_gate._circuit[1, q].gate, IdentityGate)
        test_gate: CircuitGate = circuit[1, 0].gate  # type: ignore
        assert test_gate._circuit.get_num_operations() == 2
        assert test_gate._circuit.get_num_cycles() == 2
        for q in range(4):
            assert isinstance(test_gate._circuit[0, q].gate, IdentityGate)
            assert isinstance(test_gate._circuit[1, q].gate, IdentityGate)

        circuit.fold(circuit.get_region([(0, 0), (1, 0)]))
        assert circuit.get_num_operations() == 1
        assert circuit.get_depth() == 1
        check_no_idle_cycles(circuit)
        for q in range(4):
            assert isinstance(circuit[0, q].gate, CircuitGate)
        test_gate: CircuitGate = circuit[0, 0].gate  # type: ignore
        assert test_gate._circuit.get_num_operations() == 2
        assert test_gate._circuit.get_num_cycles() == 2
        for q in range(4):
            assert isinstance(test_gate._circuit[0, q].gate, CircuitGate)
            assert isinstance(test_gate._circuit[1, q].gate, CircuitGate)
        inner_gate1: CircuitGate = test_gate._circuit[0,
                                                      0].gate  # type: ignore
        inner_gate2: CircuitGate = test_gate._circuit[1,
                                                      0].gate  # type: ignore
        assert inner_gate1._circuit.get_num_operations() == 2
        assert inner_gate1._circuit.get_num_cycles() == 2
        for q in range(4):
            assert isinstance(inner_gate1._circuit[0, q].gate, IdentityGate)
            assert isinstance(inner_gate1._circuit[1, q].gate, IdentityGate)
            assert isinstance(inner_gate2._circuit[0, q].gate, IdentityGate)
            assert isinstance(inner_gate2._circuit[1, q].gate, IdentityGate)

        check_no_idle_cycles(circuit)
        assert np.allclose(utry.get_numpy(), circuit.get_unitary().get_numpy())

    def test_correctness_2(self) -> None:
        circuit = Circuit(3)
        wide_gate = IdentityGate(3)
        circuit.append_gate(HGate(), [0])
        circuit.append_gate(CNOTGate(), [1, 2])
        circuit.append_gate(wide_gate, [0, 1, 2])
        utry = circuit.get_unitary()

        circuit.fold(circuit.get_region([(0, 1), (1, 0)]))
        assert circuit.get_num_operations() == 2
        assert circuit.get_depth() == 2
        assert circuit[0, 0].gate is HGate()
        assert isinstance(circuit[1, 0].gate, CircuitGate)
        test_gate: CircuitGate = circuit[1, 0].gate
        assert test_gate._circuit[0, 1].gate is CNOTGate()
        assert test_gate._circuit[0, 2].gate is CNOTGate()
        assert test_gate._circuit[1, 0].gate is wide_gate
        assert test_gate._circuit[1, 1].gate is wide_gate
        assert test_gate._circuit[1, 2].gate is wide_gate
        check_no_idle_cycles(circuit)
        assert np.allclose(utry.get_numpy(), circuit.get_unitary().get_numpy())

    def test_correctness_3(self) -> None:
        circuit = Circuit(5)
        wide_gate = IdentityGate(3)
        circuit.append_gate(HGate(), [1])
        circuit.append_gate(CNOTGate(), [2, 3])
        circuit.append_gate(wide_gate, [1, 2, 3])
        circuit.append_gate(CNOTGate(), [1, 2])
        circuit.append_gate(HGate(), [3])
        circuit.append_gate(XGate(), [0])
        circuit.append_gate(XGate(), [0])
        circuit.append_gate(XGate(), [0])
        circuit.append_gate(XGate(), [4])
        circuit.append_gate(XGate(), [4])
        circuit.append_gate(XGate(), [4])
        utry = circuit.get_unitary()

        circuit.fold(circuit.get_region([(0, 2), (1, 1), (2, 1)]))
        assert circuit.get_num_operations() == 9
        assert circuit.get_depth() == 3
        assert circuit.count(HGate()) == 2
        assert circuit.count(XGate()) == 6
        assert isinstance(circuit[1, 1].gate, CircuitGate)
        test_gate: CircuitGate = circuit[1, 1].gate
        assert test_gate._circuit[0, 1].gate is CNOTGate()
        assert test_gate._circuit[0, 2].gate is CNOTGate()
        assert test_gate._circuit[1, 0].gate is wide_gate
        assert test_gate._circuit[1, 1].gate is wide_gate
        assert test_gate._circuit[1, 2].gate is wide_gate
        check_no_idle_cycles(circuit)
        assert np.allclose(utry.get_numpy(), circuit.get_unitary().get_numpy())

    def test_parameters(self) -> None:
        circ = Circuit(2)
        circ.append_gate(CNOTGate(), [1, 0])
        circ.append_gate(U3Gate(), [0], [0, 0, 0.23])
        circ.append_gate(CNOTGate(), [1, 0])

        before_fold = circ.get_unitary()

        circ.fold(circ.get_region([(0, 0), (1, 0), (2, 0)]))

        after_fold = circ.get_unitary()

        assert after_fold == before_fold
Exemplo n.º 11
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()
Exemplo n.º 12
0
    def run(self, circuit: Circuit, data: dict[str, Any]) -> None:
        """Perform the pass's operation, see BasePass for more info."""

        # Collect synthesizable operations
        ops_to_syn: list[tuple[int, Operation]] = []
        for cycle, op in circuit.operations_with_cycles():
            if self.collection_filter(op):
                ops_to_syn.append((cycle, op))

        # 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())

        sub_data = data.copy()
        structure_list: list[Sequence[int]] = []

        # Synthesize operations
        errors: list[float] = []
        points: list[CircuitPoint] = []
        new_ops: list[Operation] = []
        num_blocks = len(ops_to_syn)

        for block_num, (cycle, op) in enumerate(ops_to_syn):
            sub_numbering = {op.location[i]: i for i in range(op.size)}
            sub_data['machine_model'] = MachineModel(
                len(op.location),
                model.get_subgraph(op.location, sub_numbering),
            )
            structure_list.append([op.location[i] for i in range(op.size)])
            syn_circuit = self.synthesize(op.get_unitary(), sub_data)
            if self.checkpoint_dir is not None:
                save_checkpoint(syn_circuit, self.checkpoint_dir, block_num)
            if self.replace_filter(syn_circuit, op):
                # Calculate errors
                new_utry = syn_circuit.get_unitary()
                old_utry = op.get_unitary()
                error = new_utry.get_distance_from(old_utry)
                errors.append(error)
                points.append(CircuitPoint(cycle, op.location[0]))
                new_ops.append(
                    Operation(
                        CircuitGate(syn_circuit, True),
                        op.location,
                        list(syn_circuit.get_params()),  # TODO: RealVector
                    ),
                )
                _logger.info(
                    f'Error in synthesized CircuitGate {block_num+1} of '
                    f'{num_blocks}: {error}',
                )
        data['synthesispass_error_sum'] = sum(errors)  # TODO: Might be replaced
        _logger.info(
            'Synthesis pass completed. Upper bound on '
            f"circuit error is {data['synthesispass_error_sum']}",
        )
        if self.checkpoint_dir is not None:
            with open(f'{self.checkpoint_dir}/structure.pickle', 'wb') as f:
                dump(structure_list, f)

        circuit.batch_replace(points, new_ops)