Source code for cirq.protocols.inverse

# Copyright 2018 The Cirq Developers
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

from typing import Any, overload, TYPE_CHECKING, TypeVar, Union

import collections

if TYPE_CHECKING:
    # pylint: disable=unused-import
    import cirq
    from typing import Tuple, List

# This is a special indicator value used by the inverse method to determine
# whether or not the caller provided a 'default' argument.
RaiseTypeErrorIfNotProvided = ([],)  # type: Tuple[List[Any]]


TDefault = TypeVar('TDefault')


# pylint: disable=function-redefined
@overload
def inverse(val: 'cirq.Gate') -> 'cirq.Gate':
    pass


@overload
def inverse(val: 'cirq.Operation') -> 'cirq.Operation':
    pass


@overload
def inverse(val: 'cirq.OP_TREE') -> 'cirq.OP_TREE':
    pass


@overload
def inverse(val: 'cirq.Gate',
            default: TDefault
            ) -> Union[TDefault, 'cirq.Gate']:
    pass


@overload
def inverse(val: 'cirq.Operation',
            default: TDefault
            ) -> Union[TDefault, 'cirq.Operation']:
    pass


@overload
def inverse(val: 'cirq.OP_TREE',
            default: TDefault
            ) -> Union[TDefault, 'cirq.OP_TREE']:
    pass


[docs]def inverse(val: Any, default: Any = RaiseTypeErrorIfNotProvided) -> Any: """Returns the inverse `val**-1` of the given value, if defined. An object can define an inverse by defining a __pow__(self, exponent) method that returns something besides NotImplemented when given the exponent -1. The inverse of iterables is by default defined to be the iterable's items, each inverted, in reverse order. Args: val: The value (or iterable of invertible values) to invert. default: Determines the fallback behavior when `val` doesn't have an inverse defined. If `default` is not set, a TypeError is raised. If `default` is set to a value, that value is returned. Returns: If `val` has a __pow__ method that returns something besides NotImplemented when given an exponent of -1, that result is returned. Otherwise, if `val` is iterable, the result is a tuple with the same items as `val` but in reverse order and with each item inverted. Otherwise, if a `default` argument was specified, it is returned. Raises: TypeError: `val` doesn't have a __pow__ method, or that method returned NotImplemented when given -1. Furthermore `val` isn't an iterable containing invertible items. Also, no `default` argument was specified. """ # Check if object defines an inverse via __pow__. raiser = getattr(val, '__pow__', None) result = NotImplemented if raiser is None else raiser(-1) if result is not NotImplemented: return result # Maybe it's an iterable of invertible items? # Note: we avoid str because 'a'[0] == 'a', which creates an infinite loop. if isinstance(val, collections.Iterable) and not isinstance(val, str): unique_indicator = [] # type: List[Any] results = tuple(inverse(e, unique_indicator) for e in val) if all(e is not unique_indicator for e in results): return results[::-1] # Can't invert. if default is not RaiseTypeErrorIfNotProvided: return default raise TypeError( "object of type '{}' isn't invertible. " "It has no __pow__ method (or the method returned NotImplemented) " "and it isn't an iterable of invertible objects.".format(type(val)))
# pylint: enable=function-redefined