예제 #1
0
    def __pow__(self, power):
        if power == 1:
            return self

        if power == -1:
            # HACK: break cycle
            from cirq.devices import line_qubit

            decomposed = protocols.decompose_once_with_qubits(
                self, qubits=line_qubit.LineQid.for_gate(self), default=None)
            if decomposed is None:
                return NotImplemented

            inverse_decomposed = protocols.inverse(decomposed, None)
            if inverse_decomposed is None:
                return NotImplemented

            return _InverseCompositeGate(self)

        return NotImplemented
예제 #2
0
    def __pow__(self, power: float) -> 'GateOperation':
        """Raise gate to a power, then reapply to the same qubits.

        Only works if the gate implements cirq.ExtrapolatableEffect.
        For extrapolatable gate G this means the following two are equivalent:

            (G ** 1.5)(qubit)  or  G(qubit) ** 1.5

        Args:
            power: The amount to scale the gate's effect by.

        Returns:
            A new operation on the same qubits with the scaled gate.
        """
        if power == -1:
            inv_gate = protocols.inverse(self.gate, None)
            if inv_gate is None:
                return NotImplemented
            return self.with_gate(inv_gate)
        return self.extrapolate_effect(power)
예제 #3
0
def _parity_interaction(
    q0: 'cirq.Qid', q1: 'cirq.Qid', rads: float, atol: float, gate: Optional[ops.Gate] = None
):
    """Yields a ZZ interaction framed by the given operation."""
    if abs(rads) < atol:
        return

    h = rads * -2 / np.pi
    if gate is not None:
        g = cast(ops.Gate, gate)
        yield g.on(q0), g.on(q1)

    # If rads is ±pi/4 radians within tolerance, single full-CZ suffices.
    if _is_trivial_angle(rads, atol):
        yield ops.CZ.on(q0, q1)
    else:
        yield ops.CZ(q0, q1) ** (-2 * h)

    yield ops.Z(q0) ** h
    yield ops.Z(q1) ** h
    if gate is not None:
        g = protocols.inverse(gate)
        yield g.on(q0), g.on(q1)
예제 #4
0
def controlled_op_to_native_gates(
        control: ops.QubitId,
        target: ops.QubitId,
        operation: np.ndarray,
        tolerance: float = 0.0) -> List[ops.Operation]:
    """Decomposes a controlled single-qubit operation into Z/XY/CZ gates.

    Args:
        control: The control qubit.
        target: The qubit to apply an operation to, when the control is on.
        operation: The single-qubit operation being controlled.
        tolerance: A limit on the amount of error introduced by the
            construction.

    Returns:
        A list of Operations that apply the controlled operation.
    """
    u, z_phase, global_phase = single_qubit_op_to_framed_phase_form(operation)
    if abs(z_phase - 1) <= tolerance:
        return []

    u_gates = single_qubit_matrix_to_native_gates(u, tolerance)
    if u_gates and isinstance(u_gates[-1], ops.ZPowGate):
        # Don't keep border operations that commute with CZ.
        del u_gates[-1]

    ops_before = [gate(target) for gate in u_gates]
    ops_after = protocols.inverse(ops_before)
    effect = ops.CZ(control, target)**(cmath.phase(z_phase) / math.pi)
    kickback = ops.Z(control)**(cmath.phase(global_phase) / math.pi)

    return list(
        ops.flatten_op_tree(
            (ops_before, effect,
             kickback if abs(global_phase - 1) > tolerance else [],
             ops_after)))
예제 #5
0
 def _decompose_(self, qubits):
     return protocols.inverse(protocols.decompose_once_with_qubits(self._original, qubits))
예제 #6
0
    def __post_init__(self):
        if not isinstance(self.circuit, circuits.FrozenCircuit):
            raise TypeError(
                f'Expected circuit of type FrozenCircuit, got: {type(self.circuit)!r}'
            )

        # Ensure that the circuit is invertible if the repetitions are negative.
        if isinstance(self.repetitions, float):
            if math.isclose(self.repetitions, round(self.repetitions)):
                object.__setattr__(self, 'repetitions',
                                   round(self.repetitions))
        if isinstance(self.repetitions, INT_CLASSES):
            if self.repetitions < 0:
                try:
                    protocols.inverse(self.circuit.unfreeze())
                except TypeError:
                    raise ValueError(
                        'repetitions are negative but the circuit is not invertible'
                    )

            # Initialize repetition_ids to default, if unspecified. Else, validate their length.
            loop_size = abs(self.repetitions)
            if not self.repetition_ids:
                object.__setattr__(self, 'repetition_ids',
                                   self._default_repetition_ids())
            elif len(self.repetition_ids) != loop_size:
                raise ValueError(
                    f'Expected repetition_ids to be a list of length {loop_size}, '
                    f'got: {self.repetition_ids}')
        elif isinstance(self.repetitions, sympy.Basic):
            if self.repetition_ids is not None:
                raise ValueError(
                    'Cannot use repetition ids with parameterized repetitions')
        else:
            raise TypeError(f'Only integer or sympy repetitions are allowed.\n'
                            f'User provided: {self.repetitions}')

        # Disallow mapping to keys containing the `MEASUREMENT_KEY_SEPARATOR`
        for mapped_key in self.measurement_key_map.values():
            if value.MEASUREMENT_KEY_SEPARATOR in mapped_key:
                raise ValueError(
                    f'Mapping to invalid key: {mapped_key}. "{value.MEASUREMENT_KEY_SEPARATOR}" '
                    'is not allowed for measurement keys in a CircuitOperation'
                )

        # Disallow qid mapping dimension conflicts.
        for q, q_new in self.qubit_map.items():
            if q_new.dimension != q.dimension:
                raise ValueError(
                    f'Qid dimension conflict.\nFrom qid: {q}\nTo qid: {q_new}')

        if self.repeat_until:
            if self.use_repetition_ids or self.repetitions != 1:
                raise ValueError('Cannot use repetitions with repeat_until')
            if protocols.measurement_key_objs(
                    self._mapped_single_loop()).isdisjoint(
                        self.repeat_until.keys):
                raise ValueError(
                    'Infinite loop: condition is not modified in subcircuit.')

        # Ensure that param_resolver is converted to an actual ParamResolver.
        object.__setattr__(self, 'param_resolver',
                           study.ParamResolver(self.param_resolver))
예제 #7
0
    def __init__(
        self,
        circuit: 'cirq.FrozenCircuit',
        repetitions: int = 1,
        qubit_map: Optional[Dict['cirq.Qid', 'cirq.Qid']] = None,
        measurement_key_map: Optional[Dict[str, str]] = None,
        param_resolver: Optional[study.ParamResolverOrSimilarType] = None,
        repetition_ids: Optional[Sequence[str]] = None,
        parent_path: Tuple[str, ...] = (),
        extern_keys: FrozenSet['cirq.MeasurementKey'] = frozenset(),
        use_repetition_ids: bool = True,
        repeat_until: Optional['cirq.Condition'] = None,
    ):
        """Initializes a CircuitOperation.

        Args:
            circuit: The FrozenCircuit wrapped by this operation.
            repetitions: How many times the circuit should be repeated. This can be
                integer, or a sympy expression. If sympy, the expression must
                resolve to an integer, or float within 0.001 of integer, at
                runtime.
            qubit_map: Remappings for qubits in the circuit.
            measurement_key_map: Remappings for measurement keys in the circuit.
                The keys and values should be unindexed (i.e. without repetition_ids).
                The values cannot contain the `MEASUREMENT_KEY_SEPARATOR`.
            param_resolver: Resolved values for parameters in the circuit.
            repetition_ids: List of identifiers for each repetition of the
                CircuitOperation. If populated, the length should be equal to the
                repetitions. If not populated and abs(`repetitions`) > 1, it is
                initialized to strings for numbers in `range(repetitions)`.
            parent_path: A tuple of identifiers for any parent CircuitOperations
                containing this one.
            extern_keys: The set of measurement keys defined at extern scope. The
                values here are used by decomposition and simulation routines to
                cache which external measurement keys exist as possible binding
                targets for unbound `ClassicallyControlledOperation` keys. This
                field is not intended to be set or changed manually, and should be
                empty in circuits that aren't in the middle of decomposition.
            use_repetition_ids: When True, any measurement key in the subcircuit
                will have its path prepended with the repetition id for each
                repetition. When False, this will not happen and the measurement
                key will be repeated.
            repeat_until: A condition that will be tested after each iteration of
                the subcircuit. The subcircuit will repeat until condition returns
                True, but will always run at least once, and the measurement key
                need not be defined prior to the subcircuit (but must be defined in
                a measurement within the subcircuit). This field is incompatible
                with repetitions or repetition_ids.

        Raises:
            TypeError: if repetitions is not an integer or sympy expression, or if
                the provided circuit is not a FrozenCircuit.
            ValueError: if any of the following conditions is met.
                - Negative repetitions on non-invertible circuit
                - Number of repetition IDs does not match repetitions
                - Repetition IDs used with parameterized repetitions
                - Conflicting qubit dimensions in qubit_map
                - Measurement key map has invalid key names
                - repeat_until used with other repetition controls
                - Key(s) in repeat_until are not modified by circuit
        """
        # This fields is exclusively for use in decomposition. It should not be
        # referenced outside this class.
        self._extern_keys = extern_keys

        # All other fields are pseudo-private: read access is allowed via the
        # @property methods, but mutation is prohibited.
        self._param_resolver = study.ParamResolver(param_resolver)
        self._parent_path = parent_path

        self._circuit = circuit
        if not isinstance(self._circuit, circuits.FrozenCircuit):
            raise TypeError(
                f'Expected circuit of type FrozenCircuit, got: {type(self._circuit)!r}'
            )

        # Ensure that the circuit is invertible if the repetitions are negative.
        self._repetitions = repetitions
        self._repetition_ids = None if repetition_ids is None else list(
            repetition_ids)
        self._use_repetition_ids = use_repetition_ids
        if isinstance(self._repetitions, float):
            if math.isclose(self._repetitions, round(self._repetitions)):
                self._repetitions = round(self._repetitions)
        if isinstance(self._repetitions, INT_CLASSES):
            if self._repetitions < 0:
                try:
                    protocols.inverse(self._circuit.unfreeze())
                except TypeError:
                    raise ValueError(
                        'repetitions are negative but the circuit is not invertible'
                    )

            # Initialize repetition_ids to default, if unspecified. Else, validate their length.
            loop_size = abs(self._repetitions)
            if not self._repetition_ids:
                self._repetition_ids = self._default_repetition_ids()
            elif len(self._repetition_ids) != loop_size:
                raise ValueError(
                    f'Expected repetition_ids to be a list of length {loop_size}, '
                    f'got: {self._repetition_ids}')
        elif isinstance(self._repetitions, sympy.Expr):
            if self._repetition_ids is not None:
                raise ValueError(
                    'Cannot use repetition ids with parameterized repetitions')
        else:
            raise TypeError(f'Only integer or sympy repetitions are allowed.\n'
                            f'User provided: {self._repetitions}')

        # Disallow qid mapping dimension conflicts.
        self._qubit_map = dict(qubit_map or {})
        for q, q_new in self._qubit_map.items():
            if q_new.dimension != q.dimension:
                raise ValueError(
                    f'Qid dimension conflict.\nFrom qid: {q}\nTo qid: {q_new}')

        self._measurement_key_map = dict(measurement_key_map or {})
        # Disallow mapping to keys containing the `MEASUREMENT_KEY_SEPARATOR`
        for mapped_key in self._measurement_key_map.values():
            if value.MEASUREMENT_KEY_SEPARATOR in mapped_key:
                raise ValueError(
                    f'Mapping to invalid key: {mapped_key}. "{value.MEASUREMENT_KEY_SEPARATOR}" '
                    'is not allowed for measurement keys in a CircuitOperation'
                )

        self._repeat_until = repeat_until
        if self._repeat_until:
            if self._use_repetition_ids or self._repetitions != 1:
                raise ValueError('Cannot use repetitions with repeat_until')
            if protocols.measurement_key_objs(
                    self._mapped_single_loop()).isdisjoint(
                        self._repeat_until.keys):
                raise ValueError(
                    'Infinite loop: condition is not modified in subcircuit.')
예제 #8
0
 def default_decompose(self, qubits):
     return protocols.inverse(self.forward_form.default_decompose(qubits))
예제 #9
0
def _translate_two_qubit_braket_instruction_to_cirq_operation(
    instr: Instruction,
) -> List[cirq_ops.Operation]:
    """Converts the two-qubit braket instruction to Cirq.

    Args:
        instr: Two-qubit Braket instruction to convert.

    Raises:
        ValueError: If the instruction cannot be converted to Cirq.
    """
    qubits = [LineQubit(int(qubit)) for qubit in instr.target]
    gate = instr.operator

    # Two-qubit non-parameterized gates.
    if isinstance(gate, braket_gates.CNot):
        return [cirq_ops.CNOT.on(*qubits)]

    elif isinstance(gate, braket_gates.Swap):
        return [cirq_ops.SWAP.on(*qubits)]
    elif isinstance(gate, braket_gates.ISwap):
        return [cirq_ops.ISWAP.on(*qubits)]
    elif isinstance(gate, braket_gates.CZ):
        return [cirq_ops.CZ.on(*qubits)]
    elif isinstance(gate, braket_gates.CY):
        return [
            protocols.inverse(cirq_ops.S.on(qubits[1])),
            cirq_ops.CNOT.on(*qubits),
            cirq_ops.S.on(qubits[1]),
        ]

    # Two-qubit parameterized gates.
    elif isinstance(gate, braket_gates.CPhaseShift):
        return [cirq_ops.CZ.on(*qubits) ** (gate.angle / np.pi)]
    elif isinstance(gate, braket_gates.CPhaseShift00):
        return [
            cirq_ops.XX(*qubits),
            cirq_ops.CZ.on(*qubits) ** (gate.angle / np.pi),
            cirq_ops.XX(*qubits),
        ]
    elif isinstance(gate, braket_gates.CPhaseShift01):
        return [
            cirq_ops.X(qubits[0]),
            cirq_ops.CZ.on(*qubits) ** (gate.angle / np.pi),
            cirq_ops.X(qubits[0]),
        ]
    elif isinstance(gate, braket_gates.CPhaseShift10):
        return [
            cirq_ops.X(qubits[1]),
            cirq_ops.CZ.on(*qubits) ** (gate.angle / np.pi),
            cirq_ops.X(qubits[1]),
        ]
    elif isinstance(gate, braket_gates.PSwap):
        return [
            cirq_ops.SWAP.on(*qubits),
            cirq_ops.CNOT.on(*qubits),
            cirq_ops.Z.on(qubits[1]) ** (gate.angle / np.pi),
            cirq_ops.CNOT.on(*qubits),
        ]
    elif isinstance(gate, braket_gates.XX):
        return [
            cirq_ops.XXPowGate(
                exponent=gate.angle / np.pi, global_shift=-0.5
            ).on(*qubits)
        ]
    elif isinstance(gate, braket_gates.YY):
        return [
            cirq_ops.YYPowGate(
                exponent=gate.angle / np.pi, global_shift=-0.5
            ).on(*qubits)
        ]
    elif isinstance(gate, braket_gates.ZZ):
        return [
            cirq_ops.ZZPowGate(
                exponent=gate.angle / np.pi, global_shift=-0.5
            ).on(*qubits)
        ]
    elif isinstance(gate, braket_gates.XY):
        return [cirq_ops.ISwapPowGate(exponent=gate.angle / np.pi).on(*qubits)]

    else:
        _raise_braket_to_cirq_error(instr)

    return None  # type: ignore[return-value]  # pragma: no cover
예제 #10
0
 def _decompose_(self, qubits):
     return protocols.inverse(
         protocols.decompose_once_with_qubits(self.forward_form, qubits))