Exemplo n.º 1
0
    def optimization_at(self, circuit, index, op):
        def find_removable_rz():
            """Finds z rotations that can be removed.

            A z rotation is removable iff it is followed by a z-basis measurement.

            Returns:
                list[int]: moment indices of the z rotations to be removed
            """
            remove_indices = []
            for idx, moment in enumerate(circuit[index:], start=index):
                for x in moment.operations:
                    if x.qubits == op.qubits:
                        if isinstance(
                                x.gate, cirq.ZPowGate
                        ):  # add idx to the list, keep looking for more
                            remove_indices.append(idx)
                            break
                        if isinstance(x.gate, cirq.MeasurementGate
                                      ):  # remove the accumulated indices
                            return remove_indices
                        return []  # other operations: do not remove anything
            return []  # circuit ends here: do not remove anything

        indices = find_removable_rz()
        if not indices:
            return None
        return circuits.PointOptimizationSummary(clear_span=max(indices) + 1 -
                                                 index,
                                                 clear_qubits=op.qubits,
                                                 new_operations=[])
Exemplo n.º 2
0
    def optimization_at(
            self, circuit: circuits.Circuit, index: int,
            op: ops.Operation) -> Optional[circuits.PointOptimizationSummary]:
        if len(op.qubits) != 2:
            return None

        old_operations, indices, matrix = (
            self._scan_two_qubit_ops_into_matrix(circuit, index, op.qubits))

        # Find a max-3-cz construction.
        new_operations = decompositions.two_qubit_matrix_to_native_gates(
            op.qubits[0], op.qubits[1], matrix, self.allow_partial_czs,
            self.tolerance)

        old_interaction_count = len(
            [old_op for old_op in old_operations if len(old_op.qubits) == 2])
        new_interaction_count = len(
            [new_op for new_op in new_operations if len(new_op.qubits) == 2])
        switch_to_new = False
        switch_to_new |= new_interaction_count < old_interaction_count
        switch_to_new |= any(not xmon_gates.XmonGate.is_xmon_op(old_op)
                             for old_op in old_operations)
        if not switch_to_new:
            return None

        converter = convert_to_xmon_gates.ConvertToXmonGates()
        new_xmon_operations = [
            converter.convert(new_op) for new_op in new_operations
        ]

        return circuits.PointOptimizationSummary(
            clear_span=max(indices) + 1 - index,
            clear_qubits=op.qubits,
            new_operations=new_xmon_operations)
Exemplo n.º 3
0
    def optimization_at(
        self,
        circuit: cirq.Circuit,
        index: int,
        op: cirq.Operation,
    ) -> Optional[cirq.PointOptimizationSummary]:
        """Describes how to change operations near the given location.

        Args:
            circuit: The circuit to improve.
            index: The index of the moment with the operation to focus on.
            op: The operation to focus improvements upon.

        Returns:
            A description of the optimization to perform, or else None if no
            change should be made.
        """
        if not isinstance(op.gate, self.ONE_PARAMETER_FAMILIES):
            return None

        def is_not_mergable(next_op: cirq.Operation) -> bool:
            """Predicate for finding gates that can be merged with op.

            A gate is mergable with op iff it (1) belongs to the same gate family,
            and (2) is acting on the same qubits.
            """
            if not isinstance(next_op.gate, type(op.gate)):
                return True
            if isinstance(op.gate,
                          ops.gate_features.InterchangeableQubitsGate):
                # same qubits in any order
                return set(op.qubits) != set(next_op.qubits)
            # same qubits in the same order
            return op.qubits != next_op.qubits

        # start searching from op onwards
        start_frontier = {q: index for q in op.qubits}
        op_list = circuit.findall_operations_until_blocked(
            start_frontier, is_blocker=is_not_mergable)

        if len(op_list) == 1:
            return None  # just the one gate found, no changes
        indices, operations = zip(*op_list)

        # all the gates are in the same family so we may simply sum their parameters (mod periodicity)
        par = sum(o.gate.exponent for o in operations)
        # zero parameter (mod period) corresponds to identity
        # due to floating point errors we may be just a little below the period, which should also be
        # considered close to zero so let's shift away from the troublesome point before taking the modulo
        par = self._normalize_par(par)
        if abs(par) <= self.GATE_MERGING_TOLERANCE:
            rewritten = []
        else:
            rewritten = op.gate.__class__(exponent=par).on(*op.qubits)

        return circuits.PointOptimizationSummary(clear_span=max(indices) + 1 -
                                                 index,
                                                 clear_qubits=op.qubits,
                                                 new_operations=rewritten)
Exemplo n.º 4
0
    def optimization_at(self, circuit, index, op):
        if not isinstance(op.gate, circuit.device.DECOMPOSE_FINALLY):
            return None  # no changes

        rewritten = circuit.device.operation_final_decomposer(op)
        return circuits.PointOptimizationSummary(clear_span=1,
                                                 clear_qubits=op.qubits,
                                                 new_operations=rewritten)
Exemplo n.º 5
0
    def optimization_at(self, circuit, index, op):
        converted = self.convert(op)
        if len(converted) == 1 and converted[0] is op:
            return None

        return circuits.PointOptimizationSummary(clear_span=1,
                                                 new_operations=converted,
                                                 clear_qubits=op.qubits)
Exemplo n.º 6
0
    def optimization_at(
            self, circuit: 'cirq.Circuit', index: int,
            op: 'cirq.Operation') -> Optional['cirq.PointOptimizationSummary']:
        if not isinstance(op, ops.GateOperation):
            return None

        gate = op.gate

        # Check for a SWAP and ZZPowGate together
        if isinstance(gate, ops.ZZPowGate) or gate == ops.SWAP:
            gate2 = None
            rads = None
            next_index = circuit.next_moment_operating_on(op.qubits, index + 1)
            if next_index is not None:
                ops_in_front = list(
                    {circuit.operation_at(q, next_index)
                     for q in op.qubits})
                if len(ops_in_front) == 1 and isinstance(
                        ops_in_front[0], ops.GateOperation):
                    gate2 = ops_in_front[0].gate
            else:
                next_index = 0

            if isinstance(gate, ops.SwapPowGate) and isinstance(
                    gate2, ops.ZZPowGate):
                rads = gate2.exponent * np.pi / 2
            if isinstance(gate, ops.ZZPowGate) and gate2 == ops.SWAP:
                rads = gate.exponent * np.pi / 2
            if rads is not None:
                return circuits.PointOptimizationSummary(
                    clear_span=next_index - index + 1,
                    clear_qubits=op.qubits,
                    new_operations=swap_rzz(rads, op.qubits[0], op.qubits[1]),
                )

        converted = self.convert(op)
        if len(converted) == 1 and converted[0] is op:
            return None

        return circuits.PointOptimizationSummary(clear_span=1,
                                                 new_operations=converted,
                                                 clear_qubits=op.qubits)
Exemplo n.º 7
0
    def optimization_at(self, circuit, index, op):
        if isinstance(op.gate, ops.MatrixGate) and len(op.qubits) == 1:
            return None

        converted = self.convert(op)
        if len(converted) == 1 and converted[0] is op:
            return None

        return circuits.PointOptimizationSummary(clear_span=1,
                                                 new_operations=converted,
                                                 clear_qubits=op.qubits)
Exemplo n.º 8
0
def test_clears_small():
    m = circuits.DropNegligible(0.001)
    q = ops.QubitId()
    c = circuits.Circuit([circuits.Moment([ops.Z(q)**0.000001])])

    assert (m.optimization_at(c, 0, c.operation_at(q, 0)) ==
            circuits.PointOptimizationSummary(clear_span=1,
                                              clear_qubits=[q],
                                              new_operations=[]))

    m.optimize_circuit(c)
    assert c == circuits.Circuit([circuits.Moment()])
Exemplo n.º 9
0
    def optimization_at(
        self,
        circuit: cirq.Circuit,
        index: int,
        op: cirq.Operation,
    ) -> Optional[cirq.PointOptimizationSummary]:
        """Describes how to change operations near the given location.

        Args:
            circuit: The circuit to improve.
            index: The index of the moment with the operation to focus on.
            op: The operation to focus improvements upon.

        Returns:
            A description of the optimization to perform, or else None if no
            change should be made.
        """
        def find_removable_rz() -> list[int]:
            """Finds z rotations that can be removed.

            A z rotation is removable iff it is followed by a z-basis measurement.

            Returns:
                moment indices of the z rotations to be removed
            """
            # op is a ZPowGate
            remove_indices = []
            for idx, moment in enumerate(circuit[index:], start=index):
                for x in moment.operations:
                    if op.qubits[0] in x.qubits:
                        # x acts on the same qubit as op
                        if isinstance(x.gate, cirq.ZPowGate):
                            # add idx to the list, keep looking for more
                            remove_indices.append(idx)
                            break  # to next moment
                        if isinstance(x.gate, cirq.MeasurementGate):
                            # follows the ZPowGates, remove the accumulated indices
                            return remove_indices
                        return []  # other operations: do not remove anything
            # circuit ends here
            if self.drop_final:
                return remove_indices
            return []

        if not isinstance(op.gate, cirq.ZPowGate):
            return None  # shortcut
        indices = find_removable_rz()
        if not indices:
            return None
        return circuits.PointOptimizationSummary(clear_span=max(indices) + 1 -
                                                 index,
                                                 clear_qubits=op.qubits,
                                                 new_operations=[])
Exemplo n.º 10
0
def test_combines_sequence():
    m = MergeRotations(0.000001)
    q = ops.QubitId()
    c = circuits.Circuit([
        circuits.Moment([ops.X(q)**0.5]),
        circuits.Moment([ops.Z(q)**0.5]),
        circuits.Moment([ops.X(q)**-0.5]),
    ])

    assert (m.optimization_at(c, 0, c.operation_at(
        q,
        0)) == circuits.PointOptimizationSummary(clear_span=3,
                                                 clear_qubits=[q],
                                                 new_operations=ops.Y(q)**0.5))
Exemplo n.º 11
0
    def optimization_at(self,
                        circuit: circuits.Circuit,
                        index: int,
                        op: ops.Operation
    ) -> Optional[circuits.PointOptimizationSummary]:
        if len(op.qubits) != 2:
            return None

        old_operations, indices, matrix = (
            self._scan_two_qubit_ops_into_matrix(circuit, index, op.qubits))

        old_interaction_count = len([old_op for old_op in old_operations
                                     if len(old_op.qubits) == 2])

        switch_to_new = False
        switch_to_new |= any(len(old_op.qubits) == 2 and
                             not (isinstance(old_op, ops.GateOperation) and
                                  isinstance(old_op.gate, ops.CZPowGate))
                             for old_op in old_operations)
        if not self.allow_partial_czs:
            switch_to_new |= any(isinstance(old_op, ops.GateOperation) and
                                 isinstance(old_op.gate, ops.CZPowGate)
                                 and old_op.gate.exponent != 1
                                 for old_op in old_operations)

        # This point cannot be optimized using this method
        if not switch_to_new and old_interaction_count <= 1:
            return None

        # Find a max-3-cz construction.
        new_operations = (
            two_qubit_decompositions.two_qubit_matrix_to_operations(
                op.qubits[0],
                op.qubits[1],
                matrix,
                self.allow_partial_czs,
                self.tolerance,
                False))
        new_interaction_count = len([new_op for new_op in new_operations
                                     if len(new_op.qubits) == 2])

        switch_to_new |= new_interaction_count < old_interaction_count

        if not switch_to_new:
            return None

        return circuits.PointOptimizationSummary(
            clear_span=max(indices) + 1 - index,
            clear_qubits=op.qubits,
            new_operations=new_operations)
    def optimization_at(
            self, circuit: circuits.Circuit, index: int,
            op: ops.Operation) -> Optional[circuits.PointOptimizationSummary]:
        converted = protocols.decompose(
            op,
            intercepting_decomposer=self._decompose_two_qubit_unitaries,
            keep=self._keep,
            on_stuck_raise=(None
                            if self.ignore_failures else self._on_stuck_raise))
        if converted == [op]:
            return None

        return circuits.PointOptimizationSummary(clear_span=1,
                                                 new_operations=converted,
                                                 clear_qubits=op.qubits)
Exemplo n.º 13
0
def test_stopped_at_2qubit():
    m = MergeRotations(0.000001)
    q = ops.QubitId()
    q2 = ops.QubitId()
    c = circuits.Circuit([
        circuits.Moment([ops.Z(q)]),
        circuits.Moment([ops.H(q)]),
        circuits.Moment([ops.X(q)]),
        circuits.Moment([ops.H(q)]),
        circuits.Moment([ops.CZ(q, q2)]),
        circuits.Moment([ops.H(q)]),
    ])

    assert (m.optimization_at(c, 0, c.operation_at(
        q, 0)) == circuits.PointOptimizationSummary(clear_span=4,
                                                    clear_qubits=[q],
                                                    new_operations=[]))
Exemplo n.º 14
0
    def optimization_at(self, circuit: 'cirq.Circuit', index: int, op: 'cirq.Operation'):
        if isinstance(op.gate, AcquaintanceOpportunityGate):
            logical_indices = tuple(self.mapping[q] for q in op.qubits)
            logical_operations = self.execution_strategy.get_operations(logical_indices, op.qubits)
            clear_span = int(not self.execution_strategy.keep_acquaintance)

            return circuits.PointOptimizationSummary(
                clear_span=clear_span, clear_qubits=op.qubits, new_operations=logical_operations
            )

        if isinstance(op, ops.GateOperation) and isinstance(op.gate, PermutationGate):
            op.gate.update_mapping(self.mapping, op.qubits)
            return

        raise TypeError(
            'Can only execute a strategy consisting of gates that '
            'are instances of AcquaintanceOpportunityGate or '
            'PermutationGate.'
        )
Exemplo n.º 15
0
    def optimization_at(self, circuit, index, op):
        if not isinstance(op.gate, self.one_parameter_families):
            return None

        def is_not_mergable(next_op):
            """Predicate for finding gates that can be merged with op.

            A gate is mergable with op iff it (1) belongs to the same gate family,
            and (2) is acting on the same qubits.
            """
            if not isinstance(next_op.gate, type(op.gate)):
                return True
            if isinstance(op.gate,
                          ops.gate_features.InterchangeableQubitsGate):
                # same qubits in any order
                return set(op.qubits) != set(next_op.qubits)
            # same qubits in the same order
            return op.qubits != next_op.qubits

        # start searching from op onwards
        start_frontier = {q: index for q in op.qubits}
        op_list = circuit.findall_operations_until_blocked(
            start_frontier, is_blocker=is_not_mergable)

        if len(op_list) == 1:
            return None  # just the one gate found, no changes
        indices, operations = zip(*op_list)

        # all the gates are in the same family so we may simply sum their parameters (mod periodicity)
        par = sum(o.gate.exponent for o in operations)
        # zero parameter (mod period) corresponds to identity
        # due to floating point errors we may be just a little below the period, which should also be
        # considered close to zero so let's shift away from the troublesome point before taking the modulo
        par = operator.mod(par + 1, 2) - 1
        if abs(par) <= GATE_MERGING_TOLERANCE:
            rewritten = []
        else:
            rewritten = op.gate.__class__(exponent=par).on(*op.qubits)

        return circuits.PointOptimizationSummary(clear_span=max(indices) + 1 -
                                                 index,
                                                 clear_qubits=op.qubits,
                                                 new_operations=rewritten)
Exemplo n.º 16
0
    def optimization_at(
        self, circuit: circuits.Circuit, index: int, op: ops.Operation
    ) -> Optional[circuits.PointOptimizationSummary]:
        if len(op.qubits) != 2:
            return None

        old_operations, indices, matrix = self._scan_two_qubit_ops_into_matrix(
            circuit, index, op.qubits
        )

        old_interaction_count = len(
            [old_op for old_op in old_operations if len(old_op.qubits) == 2]
        )

        switch_to_new = False
        switch_to_new |= any(
            len(old_op.qubits) == 2 and not self._may_keep_old_op(old_op)
            for old_op in old_operations
        )

        # This point cannot be optimized using this method
        if not switch_to_new and old_interaction_count <= 1:
            return None

        # Find a (possibly ideal) decomposition of the merged operations.
        new_operations = self._two_qubit_matrix_to_cz_operations(op.qubits[0], op.qubits[1], matrix)
        new_interaction_count = len(
            [new_op for new_op in new_operations if len(new_op.qubits) == 2]
        )

        switch_to_new |= new_interaction_count < old_interaction_count

        if not switch_to_new:
            return None

        return circuits.PointOptimizationSummary(
            clear_span=max(indices) + 1 - index,
            clear_qubits=op.qubits,
            new_operations=new_operations,
        )
Exemplo n.º 17
0
    def optimization_at(
            self, circuit: circuits.Circuit, index: int,
            op: ops.Operation) -> Optional[circuits.PointOptimizationSummary]:
        if len(op.qubits) != 1:
            return None
        start = {op.qubits[0]: index}

        op_list = circuit.findall_operations_until_blocked(
            start,
            is_blocker=lambda next_op: len(
                next_op.qubits) != 1 or not protocols.has_unitary(next_op))
        operations = [op for idx, op in op_list]
        indices = [idx for idx, op in op_list]

        rewritten = self._rewrite(operations)

        if rewritten is None:
            return None
        return circuits.PointOptimizationSummary(clear_span=max(indices) + 1 -
                                                 index,
                                                 clear_qubits=op.qubits,
                                                 new_operations=rewritten)
Exemplo n.º 18
0
    def optimization_at(
            self, circuit: circuits.Circuit, index: int,
            op: ops.Operation) -> Optional[circuits.PointOptimizationSummary]:
        if len(op.qubits) != 1:
            return None

        start = {op.qubits[0]: index}
        end = circuit.reachable_frontier_from(
            start,
            is_blocker=lambda next_op: len(
                next_op.qubits) != 1 or not protocols.has_unitary(next_op))
        operations_indexed = circuit.findall_operations_between(start, end)
        operations = [e for _, e in operations_indexed]
        indices = [e for e, _ in operations_indexed]
        rewritten = self._rewrite(operations)

        if rewritten is None:
            return None
        return circuits.PointOptimizationSummary(clear_span=max(indices) + 1 -
                                                 index,
                                                 clear_qubits=op.qubits,
                                                 new_operations=rewritten)