a
    .Sicݹ                     @   s  d Z ddlZddlZddlZddlmZ ddlmZ ddlm	Z	 ddlm
Z ddlmZ ddlmZ dd	lmZ ddlm
Z
 d
ZdZg Ze
dg dG dd deZe
dg dG dd deZdd ZG dd deZdd Zdd ZeZe
ddd Zi Zdd Zd d! Zd"d# Ze
d$d%d& Z d'd( Z!d)d* Z"d+d, Z#d-d. Z$i Z%d/d0 Z&d1d2 Z'g Z(g Z)g Z*i Z+i Z,d3Z-e
d4d5d6 Z.e
d7d8d9 Z/e
d:d;d< Z0d=d> Z1d?d@ Z2dAdB Z3dCdD Z4dEdF Z5dGdH Z6dIdJ Z7dKdL Z8dMdN Z9dOdP Z:dQdR Z;e
dSg ddXdTdUZ<dVdW Z=dS )YaS  Type-based dispatch for TensorFlow's Python APIs.

"Python APIs" refers to Python functions that have been exported with
`tf_export`, such as `tf.add` and `tf.linalg.matmul`; they are sometimes also
referred to as "ops".

There are currently two dispatch systems for TensorFlow:

  * The "fallback dispatch" system calls an API's standard implementation first,
    and only tries to perform dispatch if that standard implementation raises a
    TypeError (or ValueError) exception.

  * The "type-based dispatch" system checks the types of the parameters passed
    to an API, and performs dispatch if those types match any signatures that
    have been registered for dispatch.

The fallback dispatch system was the original dispatch system, but it was
somewhat brittle and had limitations, such as an inability to support dispatch
for some operations (like convert_to_tensor).  We plan to remove the fallback
dispatch system in favor of the type-based dispatch system, once all users have
been switched over to use it.

### Fallback Dispatch

The fallback dispatch system is based on "operation dispatchers", which can be
used to override the behavior for TensorFlow ops when they are called with
otherwise unsupported argument types.  In particular, when an operation is
called with arguments that would cause it to raise a TypeError, it falls back on
its registered operation dispatchers.  If any registered dispatchers can handle
the arguments, then its result is returned. Otherwise, the original TypeError is
raised.

### Type-based Dispatch

The main interface for the type-based dispatch system is the `dispatch_for_api`
decorator, which overrides the default implementation for a TensorFlow API.
The decorated function (known as the "dispatch target") will override the
default implementation for the API when the API is called with parameters that
match a specified type signature.

### Dispatch Support

By default, dispatch support is added to the generated op wrappers for any
visible ops by default.  APIs/ops that are implemented in Python can opt in to
dispatch support using the `add_dispatch_support` decorator.
    N)_pywrap_python_api_dispatcher)ops)tf_decorator)	tf_export)
tf_inspect)traceback_utils)type_annotationsZ_tf_fallback_dispatchersZ_tf_type_based_dispatcherz"__internal__.dispatch.OpDispatcher)v1c                   @   s&   e Zd ZdZe Zdd Zdd ZdS )OpDispatchera@  Abstract base class for TensorFlow operator dispatchers.

  Each operation dispatcher acts as an override handler for a single
  TensorFlow operation, and its results are used when the handler indicates
  that it can handle the operation's arguments (by returning any value other
  than `OpDispatcher.NOT_SUPPORTED`).
  c                 C   s   | j S )a  Handle this dispatcher's operation with the specified arguments.

    If this operation dispatcher can handle the given arguments, then
    return an appropriate value (or raise an appropriate exception).

    Args:
      args: The arguments to the operation.
      kwargs: They keyword arguments to the operation.

    Returns:
      The result of the operation, or `OpDispatcher.NOT_SUPPORTED` if this
      dispatcher can not handle the given arguments.
    )NOT_SUPPORTEDselfargskwargs r   [/var/www/html/django/DPS/env/lib/python3.9/site-packages/tensorflow/python/util/dispatch.pyhandleg   s    zOpDispatcher.handlec                 C   s*   t |tstd| t|t|  dS )a:  Register this dispatcher as a handler for `op`.

    Args:
      op: Python function: the TensorFlow operation that should be handled. Must
        have a dispatch list (which is added automatically for generated ops,
        and can be added to Python ops using the `add_dispatch_support`
        decorator).
    zDispatching not enabled for %sN)hasattrFALLBACK_DISPATCH_ATTRAssertionErrorgetattrappend)r   opr   r   r   registerw   s    	
zOpDispatcher.registerN)__name__
__module____qualname____doc__objectr   r   r   r   r   r   r   r
   Y   s   
r
   z(__internal__.dispatch.GlobalOpDispatcherc                   @   s&   e Zd ZdZejZdd Zdd ZdS )GlobalOpDispatcherz?Abstract base class for TensorFlow global operator dispatchers.c                 C   s   dS )z<Handle the specified operation with the specified arguments.Nr   )r   r   r   r   r   r   r   r      s    zGlobalOpDispatcher.handlec                 C   s   t |  dS )z2Register this dispatcher as a handler for all ops.N)_GLOBAL_DISPATCHERSr   r   r   r   r   r      s    zGlobalOpDispatcher.registerN)r   r   r   r   r
   r   r   r   r   r   r   r   r      s   r   c                 C   s^   t | tD ]"}|||}|tjur
|  S q
tD ]$}|| ||}|tjur2|  S q2tjS )a  Returns the result from the first successful dispatcher for a given op.

  Calls the `handle` method of each `OpDispatcher` that has been registered
  to handle `op`, and returns the value from the first successful handler.

  Args:
    op: Python function: the operation to dispatch for.
    args: The arguments to the operation.
    kwargs: They keyword arguments to the operation.

  Returns:
    The result of the operation, or `NOT_SUPPORTED` if no registered
    dispatcher can handle the given arguments.
  )r   r   r   r
   r   r    )r   r   r   
dispatcherresultr   r   r   dispatch   s    



r$   c                   @   s(   e Zd ZdZdd Zdd Zdd ZdS )	_TypeBasedDispatchera  Dispatcher that handles op if any arguments have a specified type.

  Checks the types of the arguments and keyword arguments (including elements
  of lists or tuples), and if any argument values have the indicated type(s),
  then delegates to an override function.
  c                 C   s   || _ || _d S N)_types_override_func)r   Zoverride_functypesr   r   r   __init__   s    z_TypeBasedDispatcher.__init__c                    sP   t || D ]:}t| jsDt|ttfrt fdd|D r dS qdS )Nc                 3   s   | ]}t | jV  qd S r&   )
isinstancer'   ).0eltr!   r   r   	<genexpr>       z0_TypeBasedDispatcher._handles.<locals>.<genexpr>TF)	itertoolschainvaluesr+   r'   listtupleany)r   r   r   argr   r!   r   _handles   s    z_TypeBasedDispatcher._handlesc                 C   s&   |  ||r| j|i |S | jS d S r&   )r7   r(   r   r   r   r   r   r      s    z_TypeBasedDispatcher.handleN)r   r   r   r   r*   r7   r   r   r   r   r   r%      s   r%   c                    s    fdd}|S )a  Decorator to declare that a Python function overrides an op for a type.

  The decorated function is used to override `op` if any of the arguments or
  keyword arguments (including elements of lists or tuples) have one of the
  specified types.

  Example:

  ```python
  @dispatch_for_types(math_ops.add, RaggedTensor, RaggedTensorValue)
  def ragged_add(x, y, name=None): ...
  ```

  Args:
    op: Python function: the operation that should be overridden.
    *types: The argument types for which this function should be used.
  c                    s0   t | t  krtdt|   | S )NzYThe decorated function's signature must exactly match the signature of the overridden op.)r   
getargspecr   r%   r   )funcr   r)   r   r   	decorator   s    z%dispatch_for_types.<locals>.decoratorr   )r   r)   r;   r   r:   r   dispatch_for_types   s    r<   c                 C   s&   t | trtd|  t| tg  | S )z7Decorator that adds a dispatch_list attribute to an op.z%s already has a dispatch list)r   r   r   setattr)targetr   r   r   add_fallback_dispatch_list   s    
r?   zexperimental.dispatch_for_apic                    sV   t  tddu r"t  dt fddD  fdd}|S )a}  Decorator that overrides the default implementation for a TensorFlow API.

  The decorated function (known as the "dispatch target") will override the
  default implementation for the API when the API is called with parameters that
  match a specified type signature.  Signatures are specified using dictionaries
  that map parameter names to type annotations.  E.g., in the following example,
  `masked_add` will be called for `tf.add` if both `x` and `y` are
  `MaskedTensor`s:

  >>> class MaskedTensor(tf.experimental.ExtensionType):
  ...   values: tf.Tensor
  ...   mask: tf.Tensor

  >>> @dispatch_for_api(tf.math.add, {'x': MaskedTensor, 'y': MaskedTensor})
  ... def masked_add(x, y, name=None):
  ...   return MaskedTensor(x.values + y.values, x.mask & y.mask)

  >>> mt = tf.add(MaskedTensor([1, 2], [True, False]), MaskedTensor(10, True))
  >>> print(f"values={mt.values.numpy()}, mask={mt.mask.numpy()}")
  values=[11 12], mask=[ True False]

  If multiple type signatures are specified, then the dispatch target will be
  called if any of the signatures match.  For example, the following code
  registers `masked_add` to be called if `x` is a `MaskedTensor` *or* `y` is
  a `MaskedTensor`.

  >>> @dispatch_for_api(tf.math.add, {'x': MaskedTensor}, {'y':MaskedTensor})
  ... def masked_add(x, y):
  ...   x_values = x.values if isinstance(x, MaskedTensor) else x
  ...   x_mask = x.mask if isinstance(x, MaskedTensor) else True
  ...   y_values = y.values if isinstance(y, MaskedTensor) else y
  ...   y_mask = y.mask if isinstance(y, MaskedTensor) else True
  ...   return MaskedTensor(x_values + y_values, x_mask & y_mask)

  The type annotations in type signatures may be type objects (e.g.,
  `MaskedTensor`), `typing.List` values, or `typing.Union` values.   For
  example, the following will register `masked_concat` to be called if `values`
  is a list of `MaskedTensor` values:

  >>> @dispatch_for_api(tf.concat, {'values': typing.List[MaskedTensor]})
  ... def masked_concat(values, axis):
  ...   return MaskedTensor(tf.concat([v.values for v in values], axis),
  ...                       tf.concat([v.mask for v in values], axis))

  Each type signature must contain at least one subclass of `tf.CompositeTensor`
  (which includes subclasses of `tf.ExtensionType`), and dispatch will only be
  triggered if at least one type-annotated parameter contains a
  `CompositeTensor` value.  This rule avoids invoking dispatch in degenerate
  cases, such as the following examples:

  * `@dispatch_for_api(tf.concat, {'values': List[MaskedTensor]})`: Will not
    dispatch to the decorated dispatch target when the user calls
    `tf.concat([])`.

  * `@dispatch_for_api(tf.add, {'x': Union[MaskedTensor, Tensor], 'y':
    Union[MaskedTensor, Tensor]})`: Will not dispatch to the decorated dispatch
    target when the user calls `tf.add(tf.constant(1), tf.constant(2))`.

  The dispatch target's signature must match the signature of the API that is
  being overridden.  In particular, parameters must have the same names, and
  must occur in the same order.  The dispatch target may optionally elide the
  "name" parameter, in which case it will be wrapped with a call to
  `tf.name_scope` when appropraite.

  Args:
    api: The TensorFlow API to override.
    *signatures: Dictionaries mapping parameter names or indices to type
      annotations, specifying when the dispatch target should be called.  In
      particular, the dispatch target will be called if any signature matches;
      and a signature matches if all of the specified parameters have types that
      match with the indicated type annotations.  If no signatures are
      specified, then a signature will be read from the dispatch target
      function's type annotations.

  Returns:
    A decorator that overrides the default implementation for `api`.

  #### Registered APIs

  The TensorFlow APIs that may be overridden by `@dispatch_for_api` are:

  <<API_LIST>>
  Nz does not support dispatch.c                    s   g | ]}t  |qS r   )_make_signature_checker)r,   	signature)api_signaturer   r   
<listcomp>T  s   z$dispatch_for_api.<locals>.<listcomp>c                    s   t | std| t| } t|  D ]}||  q.t  |   st| }t|}||  t  |  	| | S )z3Decorator that registers the given dispatch target.z-Expected dispatch_target to be callable; got )
callable	TypeError_add_name_scope_wrapper_check_signatureZRegister_TYPE_BASED_DISPATCH_SIGNATURESextend_signature_from_annotationsr@   r   )dispatch_targetZsignature_checkerrA   checkerapirB   r"   Zsignature_checkers
signaturesr   r   r;   Y  s    


z#dispatch_for_api.<locals>.decorator)r   TYPE_BASED_DISPATCH_ATTR
ValueErrorr   rA   )rN   rO   r;   r   rM   r   dispatch_for_api   s    U

rR   c                   C   s   t tdd dS )zCReturns a list of TensorFlow APIs that support type-based dispatch.c                 S   s   | j  d| j S )N.)r   r   )rN   r   r   r   <lambda>z  r/   z/apis_with_type_based_dispatch.<locals>.<lambda>key)sortedrH   r   r   r   r   apis_with_type_based_dispatchv  s    rX   c                    s^    fddi }t  D ]>\}}| D ],\}}tt|}|r*||g | q*q|S )a  Returns dispatch signatures that have been registered for a given class.

  This function is intended for documentation-generation purposes.

  Args:
    cls: The class to search for.  Type signatures are searched recursively, so
      e.g., if `cls=RaggedTensor`, then information will be returned for all
      dispatch targets that have `RaggedTensor` anywhere in their type
      annotations (including nested in `typing.Union` or `typing.List`.)

  Returns:
    A `dict` mapping `api` -> `signatures`, where `api` is a TensorFlow API
    function; and `signatures` is a list of dispatch signatures for `api`
    that include `cls`.  (Each signature is a dict mapping argument names to
    type annotations; see `dispatch_for_api` for more info.)
  c                    sl   t | tr$tfdd|  D S |  u r0dS t| sDt| rdt| }tfdd|D S dS dS )z#Returns true if `x` contains `cls`.c                 3   s   | ]} |V  qd S r&   r   )r,   vcontains_clsr   r   r.     r/   zKtype_based_dispatch_signatures_for.<locals>.contains_cls.<locals>.<genexpr>Tc                 3   s   | ]} |V  qd S r&   r   )r,   r6   rZ   r   r   r.     r/   FN)r+   dictr5   r2   r   is_generic_listis_generic_unionget_generic_type_args)x	type_argsclsr[   r   r   r[     s    


z8type_based_dispatch_signatures_for.<locals>.contains_cls)rH   itemsr3   filter
setdefaultrI   )rc   r#   rN   Zapi_signatures_rO   filteredr   rb   r   "type_based_dispatch_signatures_for}  s    ri   c                    s   d|j vr S t }t }d|j v s6|jdur: S t|j d fdd}t |}|j	t|j 
 |j d g d|_|`|S )a  Wraps `func` to expect a "name" arg, and use it to call `ops.name_scope`.

  If `func` already expects a "name" arg, or if `api_signature` does not
  expect a "name" arg, then returns `func` as-is.

  Args:
    func: The function to wrap.  Signature must match `api_signature` (except
      the "name" parameter may be missing.
    api_signature: The signature of the original API (used to find the index for
      the "name" parameter).

  Returns:
    The wrapped function (or the original function if no wrapping is needed).
  nameNc                     s   t | k r2|  }| d  | d d   } n|dd }|d u rT | i |S t|  | i |W  d    S 1 s0    Y  d S )N   rj   )lenpopr   
name_scope)r   r   rj   r9   
name_indexr   r   wrapped_func  s    z-_add_name_scope_wrapper.<locals>.wrapped_func)
parameters)rr   r   rA   r8   keywordsr3   indexr   make_decoratorreplacer2   __signature___tf_decorator)r9   rB   func_signaturefunc_argspecrq   r   ro   r   rF     s     



rF   z$experimental.unregister_dispatch_forc           	         s   d}t  D ].\}} |v rt|t}|  | = d}q fddt D }t|D ].}t| D ]\}}t| qft|= t|= d}qZ|st	d  ddS )a  Unregisters a function that was registered with `@dispatch_for_*`.

  This is primarily intended for testing purposes.

  Example:

  >>> # Define a type and register a dispatcher to override `tf.abs`:
  >>> class MyTensor(tf.experimental.ExtensionType):
  ...   value: tf.Tensor
  >>> @tf.experimental.dispatch_for_api(tf.abs)
  ... def my_abs(x: MyTensor):
  ...   return MyTensor(tf.abs(x.value))
  >>> tf.abs(MyTensor(5))
  MyTensor(value=<tf.Tensor: shape=(), dtype=int32, numpy=5>)

  >>> # Unregister the dispatcher, so `tf.abs` no longer calls `my_abs`.
  >>> unregister_dispatch_for(my_abs)
  >>> tf.abs(MyTensor(5))
  Traceback (most recent call last):
  ...
  ValueError: Attempt to convert a value ... to a Tensor.

  Args:
    dispatch_target: The function to unregister.

  Raises:
    ValueError: If `dispatch_target` was not registered using `@dispatch_for`,
      `@dispatch_for_unary_elementwise_apis`, or
      `@dispatch_for_binary_elementwise_apis`.
  FTc                    s   g | ]\}}| u r|qS r   r   )r,   rV   handlerrK   r   r   rC      s   z+unregister_dispatch_for.<locals>.<listcomp>z	Function z8 was not registered using a `@dispatch_for_*` decorator.N)
rH   rd   r   rP   Z
Unregister_ELEMENTWISE_API_HANDLERSset_ELEMENTWISE_API_TARGETSunregister_dispatch_forrQ   )	rK   foundrN   rO   r"   Zelementwise_keys_to_deleterV   rg   r>   r   r|   r   r     s$     



r   c                 C   s   t |  | S )a  Class decorator that registers a type for use with type-based dispatch.

  Should *not* be used with subclasses of `CompositeTensor` or `ExtensionType`
  (which are automatically registered).

  Note: this function is intended to support internal legacy use cases (such
  as RaggedTensorValue), and will probably not be exposed as a public API.

  Args:
    cls: The class to register.

  Returns:
    `cls`.
  )_api_dispatcherregister_dispatchable_type)rc   r   r   r   r     s    
r   c                 C   sn   t | trt|  dt| \}}t|}|js<|jr@| S t	| tt
|j|j|j ttt| < | S )z@Adds a PythonAPIDispatcher to the given TensorFlow API function.z) already has a type-based API dispatcher.)r   rP   rQ   r   unwrapr   r8   varargsrs   r=   r   ZPythonAPIDispatcherr   r   defaultscollectionsdefaultdictr3   rH   )r>   rg   Z	unwrappedZtarget_argspecr   r   r   add_type_based_api_dispatcher#  s    

r   c                 C   s   t |}|jdur(|jdur(|js(dS t |}t| jt|jk}|rt| j	 |j	 D ]$\}}|j
|j
ks|j|jkr`d}q`|std| d|  ddS )a  Checks that a dispatch target's signature is compatible with an API.

  Args:
    api_signature: The signature of the TensorFlow API.
    func: The dispatch target.

  Raises:
    ValueError: if the signatures are incompatible.  Two signatures are
      considered compatible if they have the same number of parameters, and all
      corresponding parameters have the same `name` and `kind`.  (Parameters
      are not required to have the same default value or the same annotation.)
  NFzDispatch function's signature z  does not match API's signature rS   )r   r8   r   rs   r   rA   rl   rr   zipr2   rj   kindrQ   )rB   r9   rz   ry   okZparam_1Zparam_2r   r   r   rG   9  s"    



rG   c           	      C   s   t |trtdd |D s$tdg }t| j}| D ]\}}t |trn|t| jk rnt| j	 | j
}| j|d}|du rtd|d|jtjjtjjfvrtd|jd| dt|}||}|||f q:t|S )	a  Builds a PySignatureChecker for the given type signature.

  Args:
    api_signature: The `inspect.Signature` of the API whose signature is
      being checked.
    signature: Dictionary mapping parameter names to type annotations.

  Returns:
    A `PySignatureChecker`.
  c                 s   s   | ]}t |ttfV  qd S r&   )r+   strint)r,   kr   r   r   r.   d  r/   z*_make_signature_checker.<locals>.<genexpr>zLsignatures must be dictionaries mapping parameter names to type annotations.Nz4signature includes annotation for unknown parameter rS   ziDispatch currently only supports type annotations for positional parameters; can't handle annotation for z parameter )r+   r\   allrE   r3   rr   rd   r   rl   r2   rj   getrQ   r   r   	ParameterPOSITIONAL_ONLYPOSITIONAL_OR_KEYWORDmake_type_checkerrt   r   r   ZPySignatureChecker)	rB   rA   Zcheckersparam_names
param_nameZ
param_typeparamrL   rt   r   r   r   r@   X  s8    






r@   c                 C   s(  t | rt | }dd |D }tt|td}t|dkrz|tvrXtj	| }|t|< t| gdd |D  }t
|S dd |D }t
|S t | rt | }t|dkrtdt|d }t|S t| t r| tvrt	| }|t| < t|  S | d	u rttd	S td
|  dd	S )z5Builds a PyTypeChecker for the given type annotation.c                 S   s   g | ]}t |tr|qS r   )r+   typer,   tr   r   r   rC     r/   z%make_type_checker.<locals>.<listcomp>rU   rk   c                 S   s   g | ]}t |tst|qS r   )r+   r   r   r   r   r   r   rC     s   
c                 S   s   g | ]}t |qS r   )r   r   r   r   r   rC     r/   z2Expected List[...] to have a single type parameterr   NzType annotation zi is not currently supported by dispatch.  Supported annotations: type objects,  List[...], and Union[...])r   r^   r_   r4   rW   idrl   _is_instance_checker_cacher   ZMakeInstanceCheckerZMakeUnionCheckerr]   r   r   ZMakeListCheckerr+   r   rQ   )
annotationra   simple_typesrL   optionsZelt_typer   r   r   r     s8    









r   c                 C   s2   t | }tdd |j D }|s.td|S )z?Builds a dict mapping from parameter names to type annotations.c                 S   s(   g | ] \}}|j tjjkr||j fqS r   )r   r   r   empty)r,   rj   r   r   r   r   rC     s   z/_signature_from_annotations.<locals>.<listcomp>zThe dispatch_for_api decorator must be called with at least one signature, or applied to a function that has type annotations on its parameters.)r   rA   r\   rr   rd   rQ   )r9   ry   rA   r   r   r   rJ     s    
rJ   ZASSERT_API_TAGz0experimental.dispatch_for_unary_elementwise_apisc                    s    fdd}|S )a  Decorator to override default implementation for unary elementwise APIs.

  The decorated function (known as the "elementwise api handler") overrides
  the default implementation for any unary elementwise API whenever the value
  for the first argument (typically named `x`) matches the type annotation
  `x_type`. The elementwise api handler is called with two arguments:

    `elementwise_api_handler(api_func, x)`

  Where `api_func` is a function that takes a single parameter and performs the
  elementwise operation (e.g., `tf.abs`), and `x` is the first argument to the
  elementwise api.

  The following example shows how this decorator can be used to update all
  unary elementwise operations to handle a `MaskedTensor` type:

  >>> class MaskedTensor(tf.experimental.ExtensionType):
  ...   values: tf.Tensor
  ...   mask: tf.Tensor
  >>> @dispatch_for_unary_elementwise_apis(MaskedTensor)
  ... def unary_elementwise_api_handler(api_func, x):
  ...   return MaskedTensor(api_func(x.values), x.mask)
  >>> mt = MaskedTensor([1, -2, -3], [True, False, True])
  >>> abs_mt = tf.abs(mt)
  >>> print(f"values={abs_mt.values.numpy()}, mask={abs_mt.mask.numpy()}")
  values=[1 2 3], mask=[ True False True]

  For unary elementwise operations that take extra arguments beyond `x`, those
  arguments are *not* passed to the elementwise api handler, but are
  automatically added when `api_func` is called.  E.g., in the following
  example, the `dtype` parameter is not passed to
  `unary_elementwise_api_handler`, but is added by `api_func`.

  >>> ones_mt = tf.ones_like(mt, dtype=tf.float32)
  >>> print(f"values={ones_mt.values.numpy()}, mask={ones_mt.mask.numpy()}")
  values=[1.0 1.0 1.0], mask=[ True False True]

  Args:
    x_type: A type annotation indicating when the api handler should be called.
      See `dispatch_for_api` for a list of supported annotation types.

  Returns:
    A decorator.

  #### Registered APIs

  The unary elementwise APIs are:

  <<API_LIST>>
  c                    sJ    ft v r&tdt  f  d  d| t  f< tD ]}t| |  q4| S )Nz&A unary elementwise dispatch handler (z") has already been registered for rS   )r}   rQ   _UNARY_ELEMENTWISE_APIS'_add_dispatch_for_unary_elementwise_apir{   rN   x_typer   r   r;     s    


z6dispatch_for_unary_elementwise_apis.<locals>.decoratorr   )r   r;   r   r   r   #dispatch_for_unary_elementwise_apis  s    5r   z1experimental.dispatch_for_binary_elementwise_apisc                    s    fdd}|S )a{  Decorator to override default implementation for binary elementwise APIs.

  The decorated function (known as the "elementwise api handler") overrides
  the default implementation for any binary elementwise API whenever the value
  for the first two arguments (typically named `x` and `y`) match the specified
  type annotations.  The elementwise api handler is called with two arguments:

    `elementwise_api_handler(api_func, x, y)`

  Where `x` and `y` are the first two arguments to the elementwise api, and
  `api_func` is a TensorFlow function that takes two parameters and performs the
  elementwise operation (e.g., `tf.add`).

  The following example shows how this decorator can be used to update all
  binary elementwise operations to handle a `MaskedTensor` type:

  >>> class MaskedTensor(tf.experimental.ExtensionType):
  ...   values: tf.Tensor
  ...   mask: tf.Tensor
  >>> @dispatch_for_binary_elementwise_apis(MaskedTensor, MaskedTensor)
  ... def binary_elementwise_api_handler(api_func, x, y):
  ...   return MaskedTensor(api_func(x.values, y.values), x.mask & y.mask)
  >>> a = MaskedTensor([1, 2, 3, 4, 5], [True, True, True, True, False])
  >>> b = MaskedTensor([2, 4, 6, 8, 0], [True, True, True, False, True])
  >>> c = tf.add(a, b)
  >>> print(f"values={c.values.numpy()}, mask={c.mask.numpy()}")
  values=[ 3 6 9 12 5], mask=[ True True True False False]

  Args:
    x_type: A type annotation indicating when the api handler should be called.
    y_type: A type annotation indicating when the api handler should be called.

  Returns:
    A decorator.

  #### Registered APIs

  The binary elementwise APIs are:

  <<API_LIST>>
  c                    sX    ft v r0tdt  f  d  d d| t  f< tD ]}t| |  q@| S )Nz'A binary elementwise dispatch handler (#) has already been registered for (, ).)r}   rQ   _BINARY_ELEMENTWISE_APIS(_add_dispatch_for_binary_elementwise_apir   r   y_typer   r   r;   F  s    

z7dispatch_for_binary_elementwise_apis.<locals>.decoratorr   r   r   r;   r   r   r   $dispatch_for_binary_elementwise_apis  s    ,r   z8experimental.dispatch_for_binary_elementwise_assert_apisc                    s    fdd}|S )aX  Decorator to override default implementation for binary elementwise assert APIs.

  The decorated function (known as the "elementwise assert handler")
  overrides the default implementation for any binary elementwise assert API
  whenever the value for the first two arguments (typically named `x` and `y`)
  match the specified type annotations.  The handler is called with two
  arguments:

    `elementwise_assert_handler(assert_func, x, y)`

  Where `x` and `y` are the first two arguments to the binary elementwise assert
  operation, and `assert_func` is a TensorFlow function that takes two
  parameters and performs the elementwise assert operation (e.g.,
  `tf.debugging.assert_equal`).

  The following example shows how this decorator can be used to update all
  binary elementwise assert operations to handle a `MaskedTensor` type:

  >>> class MaskedTensor(tf.experimental.ExtensionType):
  ...   values: tf.Tensor
  ...   mask: tf.Tensor
  >>> @dispatch_for_binary_elementwise_assert_apis(MaskedTensor, MaskedTensor)
  ... def binary_elementwise_assert_api_handler(assert_func, x, y):
  ...   merged_mask = tf.logical_and(x.mask, y.mask)
  ...   selected_x_values = tf.boolean_mask(x.values, merged_mask)
  ...   selected_y_values = tf.boolean_mask(y.values, merged_mask)
  ...   assert_func(selected_x_values, selected_y_values)
  >>> a = MaskedTensor([1, 1, 0, 1, 1], [False, False, True, True, True])
  >>> b = MaskedTensor([2, 2, 0, 2, 2], [True, True, True, False, False])
  >>> tf.debugging.assert_equal(a, b) # assert passed; no exception was thrown

  >>> a = MaskedTensor([1, 1, 1, 1, 1], [True, True, True, True, True])
  >>> b = MaskedTensor([0, 0, 0, 0, 2], [True, True, True, True, True])
  >>> tf.debugging.assert_greater(a, b)
  Traceback (most recent call last):
  ...
  InvalidArgumentError: Condition x > y did not hold.

  Args:
    x_type: A type annotation indicating when the api handler should be called.
    y_type: A type annotation indicating when the api handler should be called.

  Returns:
    A decorator.

  #### Registered APIs

  The binary elementwise assert APIs are:

  <<API_LIST>>
  c                    sV    t f}|tv r2tdt|  d  d d| t|< tD ]}t| |  q>| S )Nz.A binary elementwise assert dispatch handler (r   r   r   )_ASSERT_API_TAGr}   rQ   _BINARY_ELEMENTWISE_ASSERT_APISr   )r{   Zapi_handler_keyrN   r   r   r   r;     s    

z>dispatch_for_binary_elementwise_assert_apis.<locals>.decoratorr   r   r   r   r   +dispatch_for_binary_elementwise_assert_apisT  s    6r   c                 C   s<   t |  t D ]$\}}t|dkrt| |d | q| S )zDDecorator that registers a TensorFlow op as a unary elementwise API.rk   r   )r   r   r}   rd   rl   r   r9   r   r{   r   r   r   register_unary_elementwise_api  s
    
r   c                 C   sB   t |  t D ]*\}}t|dkrt| |d |d | q| S )zEDecorator that registers a TensorFlow op as a binary elementwise API.   r   rk   )r   r   r}   rd   rl   r   r   r   r   r   register_binary_elementwise_api  s
    
r   c                 C   sN   t |  t D ]6\}}t|dkr|d tu rt| |d |d | q| S )a  Decorator that registers a TensorFlow op as a binary elementwise assert API.

  Different from `dispatch_for_binary_elementwise_apis`, this decorator is used
  for assert apis, such as assert_equal, assert_none_equal, etc, which return
  None in eager mode and an op in graph mode.

  Args:
    func: The function that implements the binary elementwise assert API.

  Returns:
    `func`
     r   r   rk   )r   r   r}   rd   rl   r   r   r   r   r   r   &register_binary_elementwise_assert_api  s
    
r   c                   C   s   t tS )zFReturns a list of APIs that have been registered as unary elementwise.)r4   r   r   r   r   r   unary_elementwise_apis  s    r   c                   C   s   t tS )zGReturns a list of APIs that have been registered as binary elementwise.)r4   r   r   r   r   r   binary_elementwise_apis  s    r   c                    s   t  }t|jd t|t|jdkp6d|jvt |i fdd}d j |_|j|_t	
|fg }| |f dS )zFRegisters a unary elementwise handler as a dispatcher for a given API.r   r   rj   c                     s   t  \ } r. d  dd   } n
}rN fdd}n}|d u rd||S t|d |g ||W  d    S 1 s0    Y  d S )Nr   rk   c                    s    | gR i S r&   r   )rY   rN   r   r   r   r   rT     r/   zR_add_dispatch_for_unary_elementwise_api.<locals>.dispatch_target.<locals>.<lambda>)_extract_name_argrm   r   rn   )r   r   rj   r`   
tensor_apirN   elementwise_api_handlerrp   need_to_bind_api_argsx_namer   r   r   rK     s    

z@_add_dispatch_for_unary_elementwise_api.<locals>.dispatch_target elementwise_dispatch_target_for_Nr   rA   r3   rr   _find_name_indexrl   rR   r   r   r   rf   r   )rN   r   r   rB   rK   target_listr   r   r   r     s    
r   c                    s   t  }t|jdd \t|t|jdkp>d|jvt ||i fdd}d j |_|j|_t	
||fg }| |f dS )zGRegisters a binary elementwise handler as a dispatcher for a given API.Nr   r   rj   c                     s   t  \ }t dkr@ d  d  dd    }} n@ rh d  dd   } d }nd }d }r fdd}n}|d u r|||S t|d ||g |||W  d    S 1 s0    Y  d S )Nrk   r   r   c                    s    | |gR i S r&   r   )r	   v2r   r   r   rT     r/   zS_add_dispatch_for_binary_elementwise_api.<locals>.dispatch_target.<locals>.<lambda>)r   rl   rm   r   rn   )r   r   rj   r`   yr   rN   r   rp   r   r   Zy_namer   r   rK     s    "zA_add_dispatch_for_binary_elementwise_api.<locals>.dispatch_targetr   r   )rN   r   r   r   rB   rK   r   r   r   r   r     s    
r   c                 C   s,   zt | jdW S  ty&   Y dS 0 dS )zEReturns the index of the `name` parameter, or -1 if it's not present.rj   N)r3   rr   rt   rQ   )rA   r   r   r   r     s    r   c                 C   sV   |dk rd}n>|t | k r@| | }| d| | |d d  } n|dd}| ||fS )zGExtracts the parameter `name` and returns `(args, kwargs, name_value)`.r   Nrk   rj   )rl   rm   )r   r   rp   
name_valuer   r   r   r     s    r   c                   C   s,   t tt t tt t tt t tt dS )a0  Updates the docstrings of dispatch decorators with API lists.

  Updates docstrings for `dispatch_for_api`,
  `dispatch_for_unary_elementwise_apis`, and
  `dispatch_for_binary_elementwise_apis`, by replacing the string '<<API_LIST>>'
  with a list of APIs that have been registered for that decorator.
  N)	_update_docstring_with_api_listr   r   r   r   r   r   rR   rH   r   r   r   r    update_docstrings_with_api_lists*  s    r   c              	   C   st   g }|D ]H}t j|dd}|durt|j }|d| dd| d q|  | j	
dd	|| _	dS )
zFReplaces `<<API_LIST>>` in target.__doc__ with the given list of APIs.T)add_prefix_to_v1_namesNz  * `tf.(r   z)`z  <<API_LIST>>
)tf_export_libget_canonical_name_for_symbolr   rA   rr   keysr   joinsortr   rv   )r>   Zapi_listlinesr9   rj   paramsr   r   r   r   <  s     r   z*__internal__.dispatch.add_dispatch_supportc                    sT    du s0t  ttfr(tdd  D s0td fdd}| du rH|S || S dS )a  Decorator that adds a dispatch handling wrapper to a TensorFlow Python API.

  This wrapper adds the decorated function as an API that can be overridden
  using the `@dispatch_for_api` decorator.  In the following example, we first
  define a new API (`double`) that supports dispatch, then define a custom type
  (`MaskedTensor`) and finally use `dispatch_for_api` to override the default
  implementation of `double` when called with `MaskedTensor` values:

  >>> @add_dispatch_support
  ... def double(x):
  ...   return x * 2
  >>> class MaskedTensor(tf.experimental.ExtensionType):
  ...   values: tf.Tensor
  ...   mask: tf.Tensor
  >>> @dispatch_for_api(double, {'x': MaskedTensor})
  ... def masked_double(x):
  ...   return MaskedTensor(x.values * 2, y.mask)

  The optional `iterable_parameter` argument can be used to mark parameters that
  can take arbitrary iterable values (such as generator expressions).  These
  need to be handled specially during dispatch, since just iterating over an
  iterable uses up its values.  In the following example, we define a new API
  whose second argument can be an iterable value; and then override the default
  implementatio of that API when the iterable contains MaskedTensors:

  >>> @add_dispatch_support(iterable_parameters=['ys'])
  ... def add_tensor_to_list_of_tensors(x, ys):
  ...   return [x + y for y in ys]
  >>> @dispatch_for_api(add_tensor_to_list_of_tensors,
  ...               {'ys': typing.List[MaskedTensor]})
  ... def masked_add_tensor_to_list_of_tensors(x, ys):
  ...   return [MaskedTensor(x+y.values, y.mask) for y in ys]

  (Note: the only TensorFlow API that currently supports iterables is `add_n`.)

  Args:
    target: The TensorFlow API that should support dispatch.
    iterable_parameters: Optional list of parameter names that may be called
      with iterables (such as the `inputs` parameter for `tf.add_n`).

  Returns:
    A decorator.
  Nc                 s   s   | ]}t |tV  qd S r&   )r+   r   )r,   pr   r   r   r.   |  r/   z'add_dispatch_support.<locals>.<genexpr>z8iterable_parameters should be a list or tuple of string.c                    sp   d u rd nt jfddD tj fddt tt t	t
d  S )Nc                    s   g | ]}|  |fqS r   )rt   )r,   rj   )	arg_namesr   r   rC     s   z;add_dispatch_support.<locals>.decorator.<locals>.<listcomp>c               	      s    dur8dur t | |\} } | |}|tur8|S z| i |W S  ttfy~   t| |}|tjurx| Y S  Y n0 dS )z<Call `dispatch_target`, peforming dispatch when appropriate.N)replace_iterable_paramsZDispatchNotImplementedrE   rQ   r$   r
   r   )r   r   r#   )api_dispatcherrK   iterable_paramsop_dispatch_handlerr   r   r     s    
zDadd_dispatch_support.<locals>.decorator.<locals>.op_dispatch_handler)r   r8   r   r   filter_tracebackr?   r   ru   r   r   rP   r|   iterable_parameters)r   r   rK   r   r   r   r;     s"    
z'add_dispatch_support.<locals>.decorator)r+   r3   r4   r   rE   )r>   r   r;   r   r   r   add_dispatch_supportL  s    .+r   c                 C   sX   t | } |D ]>\}}|t| k r2t | | | |< q||v rt || ||< qt| |fS )a  Returns (args, kwargs) with any iterable parameters converted to lists.

  Args:
    args: Positional rguments to a function
    kwargs: Keyword arguments to a function.
    iterable_params: A list of (name, index) tuples for iterable parameters.

  Returns:
    A tuple (args, kwargs), where any positional or keyword parameters in
    `iterable_params` have their value converted to a `list`.
  )r3   rl   r4   )r   r   r   rj   rt   r   r   r   r     s    r   )NN)>r   r   r0   typingtensorflow.python.frameworkr   r   r   tensorflow.python.utilr   r   r   r   r   r    tensorflow.python.util.tf_exportr   rP   r    r   r
   r   r$   r%   r<   r?   Zadd_dispatch_listrR   rH   rX   ri   rF   r   r   r   rG   r@   r   r   rJ   r   r   r   r}   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   <module>   s   /
+
 	
x+-
:-+
B
9
D		%(
c