Beispiel #1
0
def has_mixture(val: Any, *, allow_decompose: bool = True) -> bool:
    """Returns whether the value has a mixture representation.

    Args:
        val: The value to check.
        allow_decompose: Used by internal methods to stop redundant
            decompositions from being performed (e.g. there's no need to
            decompose an object to check if it is unitary as part of determining
            if the object is a quantum channel, when the quantum channel check
            will already be doing a more general decomposition check). Defaults
            to True. When false, the decomposition strategy for determining
            the result is skipped.

    Returns:
        If `val` has a `_has_mixture_` method and its result is not
        NotImplemented, that result is returned. Otherwise, if the value
        has a `_mixture_` method return True if that has a non-default value.
        Returns False if neither function exists.
    """
    mixture_getter = getattr(val, '_has_mixture_', None)
    result = NotImplemented if mixture_getter is None else mixture_getter()
    if result is not NotImplemented:
        return result

    if has_unitary(val, allow_decompose=False):
        return True

    if allow_decompose:
        operations, _, _ = _try_decompose_into_operations_and_qubits(val)
        if operations is not None:
            return all(has_mixture(val) for val in operations)

    # No _has_mixture_ or _has_unitary_ function, use _mixture_ instead.
    return mixture(val, None) is not None
Beispiel #2
0
def measurement_keys(val: Any,
                     *,
                     allow_decompose: bool = True) -> AbstractSet[str]:
    """Gets the measurement keys of measurements within the given value.

    Args:
        val: The value which has the measurement key.
        allow_decompose: Defaults to True. When true, composite operations that
            don't directly specify their measurement keys will be decomposed in
            order to find measurement keys within the decomposed operations. If
            not set, composite operations will appear to have no measurement
            keys. Used by internal methods to stop redundant decompositions from
            being performed.

    Returns:
        The measurement keys of the value. If the value has no measurement,
        the result is the empty tuple.
    """
    getter = getattr(val, '_measurement_keys_', None)
    result = NotImplemented if getter is None else getter()
    if result is not NotImplemented and result is not None:
        return set(result)

    getter = getattr(val, '_measurement_key_', None)
    result = NotImplemented if getter is None else getter()
    if result is not NotImplemented and result is not None:
        return {result}

    if allow_decompose:
        operations, _, _ = _try_decompose_into_operations_and_qubits(val)
        if operations is not None:
            return {key for op in operations for key in measurement_keys(op)}

    return set()
Beispiel #3
0
def measurement_keys(val: Any,
                     *,
                     allow_decompose: bool = True) -> Tuple[str, ...]:
    """Gets the measurement keys of measurements within the given value.

    Args:
        val: The value which has the measurement key.
        allow_decompose: Defaults to True. When true, composite operations that
            don't directly specify their measurement keys will be decomposed in
            order to find measurement keys within the decomposed operations. If
            not set, composite operations will appear to have no measurement
            keys.

    Returns:
        The measurement keys of the value. If the value has no measurement,
        the result is the empty tuple.
    """
    getter = getattr(val, '_measurement_keys_', None)
    result = NotImplemented if getter is None else getter()
    if result is not NotImplemented and result is not None:
        return tuple(result)

    getter = getattr(val, '_measurement_key_', None)
    result = NotImplemented if getter is None else getter()
    if result is not NotImplemented and result is not None:
        return result,

    if allow_decompose:
        operations, _, _ = _try_decompose_into_operations_and_qubits(val)
        if operations is not None:
            return tuple(key for op in operations
                         for key in measurement_keys(op))

    return ()
def _strat_act_on_state_vector_from_apply_decompose(
        val: Any,
        args: ActOnStateVectorArgs,
) -> bool:
    operations, qubits, _ = _try_decompose_into_operations_and_qubits(val)
    if operations is None:
        return NotImplemented
    return _act_all_on_state_vector(operations, qubits, args)
Beispiel #5
0
def strat_act_on_from_apply_decompose(val: Any, args: 'cirq.SimulationState',
                                      qubits: Sequence['cirq.Qid']) -> bool:
    operations, qubits1, _ = _try_decompose_into_operations_and_qubits(val)
    assert len(qubits1) == len(qubits)
    qubit_map = {q: qubits[i] for i, q in enumerate(qubits1)}
    if operations is None:
        return NotImplemented
    for operation in operations:
        operation = operation.with_qubits(
            *[qubit_map[q] for q in operation.qubits])
        protocols.act_on(operation, args)
    return True
Beispiel #6
0
def has_channel(val: Any, *, allow_decompose: bool = True) -> bool:
    """Returns whether the value has a channel representation.

    Args:
        val: The value to check.
        allow_decompose: Used by internal methods to stop redundant
            decompositions from being performed (e.g. there's no need to
            decompose an object to check if it is unitary as part of determining
            if the object is a quantum channel, when the quantum channel check
            will already be doing a more general decomposition check). Defaults
            to True. When False, the decomposition strategy for determining
            the result is skipped.

    Returns:
        If `val` has a `_has_kraus_` method and its result is not
        NotImplemented, that result is returned. Otherwise, if `val` has a
        `_has_mixture_` method and its result is not NotImplemented, that
        result is returned. Otherwise if `val` has a `_has_unitary_` method
        and its results is not NotImplemented, that result is returned.
        Otherwise, if the value has a _kraus_ method return if that
        has a non-default value. Returns False if none of these functions
        exists.
    """
    channel_getter = getattr(val, '_has_channel_', None)
    if channel_getter is not None:
        warnings.warn(
            '_has_channel_ is deprecated and will be removed in cirq 0.13, rename to _has_kraus_',
            DeprecationWarning,
        )

    kraus_getter = getattr(val, '_has_kraus_', None)
    result = NotImplemented if kraus_getter is None else kraus_getter()
    if result is not NotImplemented:
        return result

    result = has_mixture(val, allow_decompose=False)
    if result is not NotImplemented and result:
        return result

    result = NotImplemented if channel_getter is None else channel_getter()
    if result is not NotImplemented:
        return result

    if allow_decompose:
        operations, _, _ = _try_decompose_into_operations_and_qubits(val)
        if operations is not None:
            return all(has_channel(val) for val in operations)

    # No has methods, use `_kraus_` or delegates instead.
    return channel(val, None) is not None
Beispiel #7
0
def strat_act_on_from_apply_decompose(
    val: Any,
    args: ActOnArgs,
) -> bool:
    operations, qubits, _ = _try_decompose_into_operations_and_qubits(val)
    if operations is None:
        return NotImplemented
    assert len(qubits) == len(args.axes)
    qubit_map = {q: args.axes[i] for i, q in enumerate(qubits)}

    old_axes = args.axes
    try:
        for operation in operations:
            args.axes = tuple(qubit_map[q] for q in operation.qubits)
            protocols.act_on(operation, args)
    finally:
        args.axes = old_axes
    return True
Beispiel #8
0
def _strat_unitary_from_decompose(val: Any) -> Optional[np.ndarray]:
    """Attempts to compute a value's unitary via its _decompose_ method."""
    # Check if there's a decomposition.
    operations, qubits, val_qid_shape = _try_decompose_into_operations_and_qubits(
        val)
    if operations is None:
        return NotImplemented

    # Apply sub-operations' unitary effects to an identity matrix.
    state = qis.eye_tensor(val_qid_shape, dtype=np.complex128)
    buffer = np.empty_like(state)
    result = apply_unitaries(
        operations, qubits,
        ApplyUnitaryArgs(state, buffer, range(len(val_qid_shape))), None)

    # Package result.
    if result is None:
        return None
    state_len = np.prod(val_qid_shape, dtype=np.int64)
    return result.reshape((state_len, state_len))
def _is_any_measurement(vals: List[Any], allow_decompose: bool) -> bool:
    """Given a list of objects, returns True if any of them is a measurement.

    If `allow_decompose` is True, decomposes the objects and runs the measurement checks on the
    constituent decomposed operations. But a decompose operation is only called if all cheaper
    checks are done. A BFS for searching measurements, where "depth" is each level of decompose.
    """
    vals_to_decompose = []  # type: List[Any]
    while vals:
        val = vals.pop(0)
        result = _is_measurement_from_magic_method(val)
        if result is not NotImplemented:
            if result is True:
                return True
            if result is False:
                # Do not try any other strategies if `val` was explicitly marked as
                # "not measurement".
                continue

        keys = _measurement_key_names_from_magic_methods(val)
        if keys is not NotImplemented and bool(keys) is True:
            return True

        if allow_decompose:
            vals_to_decompose.append(val)

        # If vals has finished iterating over, keep decomposing from vals_to_decompose until vals
        # is populated with something.
        while not vals:
            if not vals_to_decompose:
                # Nothing left to process, this is not a measurement.
                return False
            operations, _, _ = _try_decompose_into_operations_and_qubits(
                vals_to_decompose.pop(0))
            if operations:
                # Reverse the decomposed operations because measurements are typically at later
                # moments.
                vals = operations[::-1]

    return False
Beispiel #10
0
def _strat_apply_unitary_from_decompose(
        val: Any, args: ApplyUnitaryArgs) -> Optional[np.ndarray]:
    operations, qubits, _ = _try_decompose_into_operations_and_qubits(val)
    if operations is None:
        return NotImplemented
    return apply_unitaries(operations, qubits, args, None)
def _strat_has_unitary_from_decompose(val: Any) -> Optional[bool]:
    """Attempts to infer a value's unitary-ness via its _decompose_ method."""
    operations, _, _ = _try_decompose_into_operations_and_qubits(val)
    if operations is None:
        return None
    return all(has_unitary(op) for op in operations)