# 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