
    ij                        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 d
lmZ d dlmZ  G d de      Z ed      d        Z G d de      Z ed      d[d       Z G d de      Z ed      d\d       Z G d de      Z ed      d        Z G d de      Z ed      d]d       Z G d  d!e      Z ed"      d#        Z G d$ d%e      Z  ed&      d'        Z! G d( d)e      Z" ed*      d+        Z# G d, d-e      Z$ ed.      	 d]d/       Z% G d0 d1e      Z& ed2      d3        Z' G d4 d5e      Z( ed6      d7        Z) G d8 d9e      Z* ed:      d^d;       Z+ ed<      d=        Z, ed>      d?        Z- G d@ dAe      Z. edB      dC        Z/ G dD dEe      Z0 edF      dG        Z1d]dHZ2 G dI dJe      Z3 edK      d_dL       Z4 edM      dN        Z5 G dO dPe      Z6 edQ      dR        Z7 G dS dTe      Z8 edU      dV        Z9 edW      dX        Z: edY      dZ        Z;y)`    N)backend)tree)keras_export)KerasTensor)any_symbolic_tensors)canonicalize_axis)slice_along_axis)	Operation)serialization_lib)traceback_utilsc                       e Zd Zd Zd Zy)Mapc                 B    t         j                  j                  ||      S N)r   coremap)selffxss      g/var/www/html/emotional.easysim.app/public_html/venv/lib/python3.12/site-packages/keras/src/ops/core.pycallzMap.call   s    ||2&&    c                     t        j                  d |      }t        j                  |      d   j                  d   t	        j
                  ||      }fd}t        j                  ||      }|S )Nc                     | d   S Nr    ts    r   <lambda>z)Map.compute_output_spec.<locals>.<lambda>   
    1 r   r   c                 z    t        f| j                  z   | j                  | j                  | j                        S Nshapedtypesparseraggedr   r$   r%   r&   r'   r   ns    r   append_batch_axisz2Map.compute_output_spec.<locals>.append_batch_axis   2    dQWWnggxxxx	 r   )r   map_structureflattenr$   r   compute_output_spec)r   r   r   xyr+   r*   s         @r   r/   zMap.compute_output_spec   sd    ~r2LLQ%%a(''1-	 0!4r   N__name__
__module____qualname__r   r/   r   r   r   r   r      s    'r   r   zkeras.ops.mapc                     t        |f      rt               j                  | |      S t        j                  j                  | |      S )u  Map a function over leading array axes.

    Like Python’s builtin map, except inputs and outputs are in the form of
    stacked arrays. Consider using the `vectorized_map()` transform instead,
    unless you need to apply a function element by element for reduced memory
    usage or heterogeneous computation with other control flow primitives.

    When `xs` is an array type, the semantics of `map()` are given by this
    Python implementation:

    ```python
    def map(f, xs):
        return np.stack([f(x) for x in xs])
    ```

    Args:
        f: Callable defines the function to apply element-wise over the first
            axis or axes of `xs`.
        xs: Values over which to map along the leading axis.

    Returns:
        Mapped values.

    Examples:

    >>> f = lambda x: x**2
    >>> xs = keras.ops.arange(10)
    >>> ys = keras.ops.map(f, xs)
    >>> ys
    [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

    >>> f = lambda x: {"y1": x**2, "y2": x * 10}  # Can have nested outputs
    >>> ys = keras.ops.map(f, xs)
    >>> ys["y1"]
    [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
    >>> ys["y2"]
    [0, 10, 20, 30, 40, 50, 60, 70, 80, 90]
    )r   r   symbolic_callr   r   r   )r   r   s     r   r   r   %   s;    P RE"u""1b))<<Ar""r   c                   6     e Zd Zddd fdZddZddZ xZS )ScanNnamec                P    t         |   |       || _        || _        || _        y Nr:   )super__init__lengthreverseunroll)r   r@   rA   rB   r;   	__class__s        r   r?   zScan.__init__S   s(    d#r   c                     t         j                  j                  |||| j                  | j                  | j
                        S )Nr@   rA   rB   )r   r   scanr@   rA   rB   )r   r   initr   s       r   r   z	Scan.callY   s<    ||  ;;LL;; ! 
 	
r   c                 h   |t        | j                        }d }nK| j                  t        | j                        n$t        j                  |      d   j                  d   }|d   }t        j                  |||      \  }}t        |f|j                  z   |j                  |j                        }||fS Nr   )r$   r%   r&   )
intr@   r   r.   r$   r   r/   r   r%   r&   )r   r   rG   r   r*   r0   carryr1   s           r   r/   zScan.compute_output_specc   s    :DKK AA ;;* DKK \\"%a(..q1 
 1A..q$:qqdQWWnAGGAHHMaxr   )NF   r   r3   r4   r5   r?   r   r/   __classcell__rC   s   @r   r9   r9   R   s    T 
r   r9   zkeras.ops.scanc                     t        ||f      rt        |||      j                  | ||      S t        j                  j                  | |||||      S )a  Scan a function over leading array axes while carrying along state.

    When the type of `xs` is an array type or `None`, and the type of `ys` is an
    array type, the semantics of `scan()` are given roughly by this Python
    implementation:

    ```python
    def scan(f, init, xs, length=None):
        if xs is None:
            xs = [None] * length
        carry = init
        ys = []
        for x in xs:
            carry, y = f(carry, x)
            ys.append(y)
        return carry, np.stack(ys)
    ```

    The loop-carried value `carry` (`init`) must hold a fixed shape and dtype
    across all iterations.

    In TensorFlow, `y` must match `carry` in shape and dtype. This is not
    required in other backends.

    Args:
        f: Callable defines the logic for each loop iteration. This accepts two
            arguments where the first is a value of the loop carry and the
            second is a slice of `xs` along its leading axis.
            This callable returns a pair where the first represents a new value
            for the loop carry and the second represents a slice of the output.
        init: The initial loop carry value. This can be a scalar, tensor, or any
            nested structure. It must match the structure of the first element
            returned by `f`.
        xs: Optional value to scan along its leading axis. This can be a tensor
            or any nested structure. If `xs` is not provided, you must specify
            `length` to define the number of loop iterations.
            Defaults to `None`.
        length: Optional integer specifying the number of loop iterations.
            If `length` is not provided, it defaults to the sizes of leading
            axis of the arrays in `xs`. Defaults to `None`.
        reverse: Optional boolean specifying whether to run the scan iteration
            forward or in reverse, equivalent to reversing the leading axes of
            the arrays in both `xs` and in `ys`.
        unroll: Optional positive integer or boolean specifying how many scan
            iterations to unroll within a single iteration of a loop. If an
            integer is provided, it determines how many unrolled loop iterations
            to run within a single rolled iteration of the loop. If a boolean is
            provided, it will determine if the loop is completely unrolled
            (`unroll=True`) or left completely unrolled (`unroll=False`).
            Note that unrolling is only supported by JAX and TensorFlow
            backends.

    Returns:
        A pair where the first element represents the final loop carry value and
        the second element represents the stacked outputs of `f` when scanned
        over the leading axis of the inputs.

    Examples:

    >>> sum_fn = lambda c, x: (c + x, c + x)
    >>> init = keras.ops.array(0)
    >>> xs = keras.ops.array([1, 2, 3, 4, 5])
    >>> carry, result = keras.ops.scan(sum_fn, init, xs)
    >>> carry
    15
    >>> result
    [1, 3, 6, 10, 15]
    rE   )rA   rB   )r   r9   r7   r   r   rF   )r   rG   r   r@   rA   rB   s         r   rF   rF   t   s^    L T2J'76

-4
$	% <<	4VWV   r   c                   2     e Zd Zddd fdZd Zd Z xZS )AssociativeScanNr:   c                B    t         |   |       || _        || _        y r=   )r>   r?   rA   axis)r   rA   rT   r;   rC   s       r   r?   zAssociativeScan.__init__   s!    d#	r   c                 p    t         j                  j                  ||| j                  | j                        S )NrA   rT   )r   r   associative_scanrA   rT   )r   r   elemss      r   r   zAssociativeScan.call   s/    ||,,udll - 
 	
r   c                    t        j                  |      D cg c]  }|j                  | j                      }}t	        t        |            dk7  r2t        dj                  D cg c]  }|j                   c}            t        j                  |D cg c]  }t        |dd| j                         c}      }t        j                  |||      }fd}t        j                  ||      }|S c c}w c c}w c c}w )NrL   zNArray inputs to associative_scan must have the same first dimension. (saw: {})r   )rT   c                 b    t        d   j                  | j                  | j                        S rI   )r   r$   r%   r&   )r0   
elems_flats    r   _restore_shapez;AssociativeScan.compute_output_spec.<locals>._restore_shape   s)     m)) r   )r   r.   r$   rT   lenset
ValueErrorformatpack_sequence_asr	   r   r/   r-   )	r   r   rX   elemlensr0   y_specr\   r[   s	           @r   r/   z#AssociativeScan.compute_output_spec   s    \\%(
2<=$

499%==s4y>Q--3V,67DTZZ7.  !!@JK1aADII6K
 ,,Q15	
 ##NF;+ >
 8 Ls    C5(C: C?
Fr   rM   rO   s   @r   rR   rR      s    d 


r   rR   zkeras.ops.associative_scanc                     t        |f      rt        ||      j                  | |      S t        j                  j                  | |||      S )aJ	  Performs a scan with an associative binary operation, in parallel.

    This operation his similar to `scan`, with the key difference that
    `associative_scan` is a parallel implementation with
    potentially significant performance benefits, especially when jit compiled.
    The catch is that it can only be used when `f` is a binary associative
    operation (i.e. it must verify `f(a, f(b, c)) == f(f(a, b), c)`).

    For an introduction to associative scans, refer to this paper:
    Blelloch, Guy E. 1990.
    [Prefix Sums and Their Applications](
        https://www.cs.cmu.edu/~guyb/papers/Ble93.pdf).

    Args:
        f: A Python callable implementing an associative binary operation with
            signature `r = f(a, b)`. Function `f` must be associative, i.e.,
            it must satisfy the equation
            `f(a, f(b, c)) == f(f(a, b), c)`.
            The inputs and result are (possibly nested Python tree structures
            of) array(s) matching `elems`. Each array has a dimension in place
            of the `axis` dimension. `f` should be applied elementwise over
            the `axis` dimension.
            The result `r` has the same shape (and structure) as the
            two inputs `a` and `b`.
        elems: A (possibly nested Python tree structure of) array(s), each with
            an `axis` dimension of size `num_elems`.
        reverse: A boolean stating if the scan should be reversed with respect
            to the `axis` dimension.
        axis: an integer identifying the axis over which the scan should occur.

    Returns:
        A (possibly nested Python tree structure of) array(s) of the same shape
        and structure as `elems`, in which the `k`'th element of `axis` is
        the result of recursively applying `f` to combine the first `k`
        elements of `elems` along `axis`. For example, given
        `elems = [a, b, c, ...]`, the result would be
        `[a, f(a, b), f(f(a, b), c), ...]`.

    Examples:

    >>> sum_fn = lambda x, y: x + y
    >>> xs = keras.ops.arange(5)
    >>> ys = keras.ops.associative_scan(sum_fn, xs, axis=0)
    >>> ys
    [0, 1, 3, 6, 10]

    >>> sum_fn = lambda x, y: [x[0] + y[0], x[1] + y[1], x[2] + y[2]]
    >>> xs = [keras.ops.array([1, 2]) for _ in range(3)]
    >>> ys = keras.ops.associative_scan(sum_fn, xs, axis=0)
    >>> ys
    [[1, 3], [1, 3], [1, 3]]
    rV   )r   rR   r7   r   r   rW   )r   rX   rA   rT   s       r   rW   rW      sN    l UH%wT:HHu
 	
 <<((E7(NNr   c                   0     e Zd Zdd fd
Zd Zd Z xZS )ScatterNr:   c                4    t         |   |       || _        y r=   r>   r?   r$   r   r$   r;   rC   s      r   r?   zScatter.__init__&      d#
r   c                 X    t         j                  j                  ||| j                        S r   )r   r   scatterr$   r   indicesvaluess      r   r   zScatter.call*  s    ||##GVTZZ@@r   c                 D    t        | j                  |j                        S Nr%   r   r$   r%   ro   s      r   r/   zScatter.compute_output_spec-  s    4::V\\::r   rM   rO   s   @r   rh   rh   %  s    &* A;r   rh   zkeras.ops.scatterc                     t        | |f      rt        |      j                  | |      S t        j                  j                  | ||      S )a  Returns a tensor of shape `shape` where `indices` are set to `values`.

    At a high level, this operation does `zeros[indices] = updates` and
    returns the output. It is equivalent to:

    ```python
    zeros = keras.ops.zeros(shape)
    output = keras.ops.scatter_update(zeros, indices, values)
    ```

    Args:
        indices: A tensor or list/tuple specifying
            indices for the values in `values`.
        values: A tensor, the values to be set at `indices`.
        shape: Shape of the output tensor.

    Example:

    >>> indices = [[0, 1], [1, 1]]
    >>> values = np.array([1., 1.])
    >>> keras.ops.scatter(indices, values, shape=(2, 2))
    array([[0., 1.],
           [0., 1.]])
    r$   )r   rh   r7   r   r   rn   )rp   rq   r$   s      r   rn   rn   1  sB    4 Wf-.U#11'6BB<<77r   c                   2     e Zd Zddd fdZd Zd Z xZS )ScatterUpdateNr:   c                4    t         |   |       || _        y r=   )r>   r?   	reduction)r   r{   r;   rC   s      r   r?   zScatterUpdate.__init__Q  s    d#"r   c                 \    t         j                  j                  |||| j                        S )Nr{   )r   r   scatter_updater{   r   inputsrp   updatess       r   r   zScatterUpdate.callU  s+    ||**GW + 
 	
r   c                 D    t        |j                  |j                        S rs   ru   r   s       r   r/   z!ScatterUpdate.compute_output_specZ      6<<v||<<r   r   rM   rO   s   @r   ry   ry   P  s    #t #

=r   ry   zkeras.ops.scatter_updatec                     |#|j                         }|dvrt        d| d      t        | ||f      rt        |      j	                  | ||      S t
        j                  j                  | |||      S )a
  Update inputs via updates at scattered (sparse) indices.

    At a high level, this operation does `inputs[indices] = updates`.
    Assume `inputs` is a tensor of shape `(D0, D1, ..., Dn)`, there are 2 main
    usages of `scatter_update`.

    1. `indices` is a 2D tensor of shape `(num_updates, n)`, where `num_updates`
        is the number of updates to perform, and `updates` is a 1D tensor of
        shape `(num_updates,)`. For example, if `inputs` is `zeros((4, 4, 4))`,
        and we want to update `inputs[1, 2, 3]` and `inputs[0, 1, 3]` as 1, then
        we can use:

    ```python
    inputs = np.zeros((4, 4, 4))
    indices = [[1, 2, 3], [0, 1, 3]]
    updates = np.array([1., 1.])
    inputs = keras.ops.scatter_update(inputs, indices, updates)
    ```

    2 `indices` is a 2D tensor of shape `(num_updates, k)`, where `num_updates`
        is the number of updates to perform, and `k` (`k < n`) is the size of
        each index in `indices`. `updates` is a `n - k`-D tensor of shape
        `(num_updates, inputs.shape[k:])`. For example, if
        `inputs = np.zeros((4, 4, 4))`, and we want to update `inputs[1, 2, :]`
        and `inputs[2, 3, :]` as `[1, 1, 1, 1]`, then `indices` would have shape
        `(num_updates, 2)` (`k = 2`), and `updates` would have shape
        `(num_updates, 4)` (`inputs.shape[2:] = 4`). See the code below:

    ```python
    inputs = np.zeros((4, 4, 4))
    indices = [[1, 2], [2, 3]]
    updates = np.array([[1., 1., 1, 1,], [1., 1., 1, 1,])
    inputs = keras.ops.scatter_update(inputs, indices, updates)
    ```

    Args:
        inputs: A tensor, the tensor to be updated.
        indices: A tensor or list/tuple of shape `(N, inputs.ndim)`, specifying
            indices to update. `N` is the number of indices to update, must be
            equal to the first dimension of `updates`.
        updates: A tensor, the new values to be put to `inputs` at `indices`.
        reduction: A string specifying the reduction operation to apply when
            multiple updates target the same index. Supported values are:
            `None` (default): Updates replace existing values (last write wins).
            `"add"`: Updates are added to existing values.
            `"max"`: The maximum of updates and existing values is kept.
            `"min"`: The minimum of updates and existing values is kept.
            `"mul"`: Updates are multiplied with existing values.

    Returns:
        A tensor, has the same shape and dtype as `inputs`.

    Example:

    Using `reduction="add"` to accumulate values at the same index:

    >>> inputs = np.zeros((4,))
    >>> indices = [[0], [0], [1]]
    >>> updates = np.array([1., 1., 1.])
    >>> keras.ops.scatter_update(inputs, indices, updates, reduction="add")
    array([2., 1., 0., 0.])
    )addmaxminmulzInvalid reduction: z9. Supported values are: None, 'add', 'max', 'min', 'mul'.r}   )lowerr_   r   ry   r7   r   r   r~   )r   rp   r   r{   s       r   r~   r~   ^  s    @ OO%	88%i[ 1J J  VWg67y1??GW
 	
 <<&&I '  r   c                   0     e Zd Zdd fd
Zd Zd Z xZS )SliceNr:   c                4    t         |   |       || _        y r=   rj   rk   s      r   r?   zSlice.__init__  rl   r   c                 X    t         j                  j                  ||| j                        S r   )r   r   slicer$   )r   r   start_indicess      r   r   z
Slice.call  s    ||!!&-DDr   c                 p   t        | j                        t        |j                        k7  r%t        d|j                   d| j                         t        |d      r<t        |      t        |j                        k7  rt        d| d|j                         g }t	        t        |j                  | j                              D ]Z  \  }\  }}|dk7  r|j                  |        t        |t              s||j                  d        D|j                  |||   z
         \ t        ||j                        S )NzkThe number of dimensions in `inputs` must match the number of dimensions in `shape`. Received inputs.shape=z and shape=__len__ztThe number of dimensions in `start_indices` must match the number of dimensions in `inputs`. Received start_indices=z and inputs.shape=rt   )
r]   r$   r_   hasattr	enumeratezipappend
isinstancer   r%   )r   r   r   final_shapei	input_dim	slice_dims          r   r/   zSlice.compute_output_spec  s4   tzz?c&,,//@@F~ N!ZZL* 
 =),]1CsLLH
 2
 !!./A&,,Q  )2djj)*
 	A%A%	9 B""9-M;79;L""4(""9}Q/?#?@	A ;fll;;r   rM   rO   s   @r   r   r     s    &* E<r   r   zkeras.ops.slicec                     t        | |f      rt        |      j                  | |      S t        j                  j                  | ||      S )aT  Return a slice of an input tensor.

    At a high level, this operation is an explicit replacement for array slicing
    e.g. `inputs[start_indices: start_indices + shape]`.
    Unlike slicing via brackets, this operation will accept tensor start
    indices on all backends, which is useful when indices dynamically computed
    via other tensor operations.

    ```python
    inputs = np.zeros((5, 5))
    start_indices = np.array([3, 3])
    shape = np.array([2, 2])
    inputs = keras.ops.slice(inputs, start_indices, shape)
    ```

    Args:
        inputs: A tensor, the tensor to be updated.
        start_indices: A list/tuple of shape `(inputs.ndim,)`, specifying
            the starting indices for updating.
        shape: The full shape of the returned slice.

    Returns:
        A tensor, has the same shape and dtype as `inputs`.
    rw   )r   r   r7   r   r   r   )r   r   r$   s      r   r   r     sB    4 V]345!//FF<<fmU;;r   c                       e Zd Zd Zd Zy)SliceUpdatec                 D    t         j                  j                  |||      S r   )r   r   slice_updater   r   r   r   s       r   r   zSliceUpdate.call  s    ||((HHr   c                 D    t        |j                  |j                        S rs   ru   r   s       r   r/   zSliceUpdate.compute_output_spec  r   r   Nr2   r   r   r   r   r     s    I=r   r   zkeras.ops.slice_updatec                     t        | ||f      rt               j                  | ||      S t        j                  j                  | ||      S )a  Update an input by slicing in a tensor of updated values.

    At a high level, this operation does
    `inputs[start_indices: start_indices + updates.shape] = updates`.
    Assume inputs is a tensor of shape `(D0, D1, ..., Dn)`,
    `start_indices` must be a list/tuple of n integers, specifying the starting
    indices. `updates` must have the same rank as `inputs`, and the size of each
    dim must not exceed `Di - start_indices[i]`. For example, if we have 2D
    inputs `inputs = np.zeros((5, 5))`, and we want to update the intersection
    of last 2 rows and last 2 columns as 1, i.e.,
    `inputs[3:, 3:] = np.ones((2, 2))`, then we can use the code below:

    ```python
    inputs = np.zeros((5, 5))
    start_indices = [3, 3]
    updates = np.ones((2, 2))
    inputs = keras.ops.slice_update(inputs, start_indices, updates)
    ```

    Args:
        inputs: A tensor, the tensor to be updated.
        start_indices: A list/tuple of shape `(inputs.ndim,)`, specifying
            the starting indices for updating.
        updates: A tensor, the new values to be put to `inputs` at `indices`.
            `updates` must have the same rank as `inputs`.

    Returns:
        A tensor, has the same shape and dtype as `inputs`.
    )r   r   r7   r   r   r   )r   r   r   s      r   r   r     sC    > V]G<=}**6='JJ<<$$V]GDDr   c                       e Zd Zd Zd Zy)Switchc                 D    t        j                  j                  ||g| S r   )r   r   switch)r   indexbranchesoperandss       r   r   zSwitch.call  s    ||""5(>X>>r   c                 8    t        j                  |d   g| }|S r   )r   r/   )r   r   r   r   specs        r   r/   zSwitch.compute_output_spec"  s     **8A;BBr   Nr2   r   r   r   r   r     s    ?r   r   zkeras.ops.switchc                     t        |      r t               j                  | |g| S t        j                  j
                  | |g| S )a  Apply exactly one of the `branches` given by `index`.

    If `index` is out of bounds, it is clamped to within bounds.

    The semantics of `switch` are given roughly by this Python implementation:

    ```python
    def switch(index, branches, *operands):
        index = clamp(0, index, len(branches) - 1)
        return branches[index](*operands)
    ```

    Args:
        index: An integer scalar indicating which branch function to apply.
        branches: A sequence of functions to be applied based on `index`.
        operands: Inputs to whichever branch is applied.

    Returns:
        The outputs of `branch(*operands)` for the branch that was selected
        based on `index`.

    Examples:

    >>> add_fn = lambda x, y: x + y
    >>> subtract_fn = lambda x, y: x - y
    >>> x = keras.ops.array(2.0)
    >>> y = keras.ops.array(0.5)
    >>> branches = [add_fn, subtract_fn]
    >>> keras.ops.switch(0, branches, x, y)
    2.5

    >>> keras.ops.switch(1, branches, x, y)
    1.5
    )r   r   r7   r   r   r   )r   r   r   s      r   r   r   (  sE    H H%%vx%%eXAAA<<uh:::r   c                   2     e Zd Zddd fdZd Zd Z xZS )	WhileLoopNr:   c                P    t         |   |       || _        || _        || _        y r=   )r>   r?   condbodymaximum_iterations)r   r   r   r   r;   rC   s        r   r?   zWhileLoop.__init__R  s)    d#		"4r   c                     t         j                  j                  | j                  | j                  || j
                        S )Nr   )r   r   
while_loopr   r   r   r   	loop_varss     r   r   zWhileLoop.callX  s8    ||&&IIII#66	 ' 
 	
r   c                 0    t        j                  d |      S )Nc                 D    t        | j                  | j                        S rs   ru   )vs    r   r   z/WhileLoop.compute_output_spec.<locals>.<lambda>b  s    k!''9 r   )r   r-   r   s     r   r/   zWhileLoop.compute_output_spec`  s    !!99
 	
r   r   rM   rO   s   @r   r   r   Q  s    5D 5

r   r   zkeras.ops.while_loopc                     t        |f      rt        | ||      j                  |      S t        j                  j                  | |||      S )a  While loop implementation.

    Args:
        cond: A callable that represents the termination condition of the loop.
            Must accept a `loop_vars` like structure as an argument. If
            `loop_vars` is a tuple or list, each element of `loop_vars` will be
            passed positionally to the callable.
        body: A callable that represents the loop body. Must accept a
            `loop_vars` like structure as an argument, and return update value
            with the same structure. If `loop_vars` is a tuple or list, each
            element of `loop_vars` will be passed positionally to the callable.
        loop_vars: An arbitrary nested structure of tensor state to persist
            across loop iterations.
        maximum_iterations: Optional maximum number of iterations of the while
            loop to run. If provided, the `cond` output is AND-ed with an
            additional condition ensuring the number of iterations executed is
            no greater than `maximum_iterations`.

    Returns:
        A list/tuple of tensors, has the same shape and dtype as `inputs`.

    Examples:

    >>> i = 0
    >>> cond = lambda i: i < 10
    >>> body = lambda i: i + 1
    >>> keras.ops.while_loop(cond, body, i)
    10

    >>> x, y = 0, 1
    >>> cond = lambda x, y: x < 10
    >>> body = lambda x, y: (x + 1, y + 1)
    >>> keras.ops.while_loop(cond, body, (x, y))
    10, 11
    r   )r   r   r7   r   r   r   )r   r   r   r   s       r   r   r   f  sX    T YL)$+=

-	
"	# <<""-	 #  r   c                       e Zd Zd Zd Zy)StopGradientc                 @    t         j                  j                  |      S r   )r   r   stop_gradientr   variables     r   r   zStopGradient.call  s    ||))(33r   c                 D    t        |j                  |j                        S rs   ru   r   s     r   r/   z StopGradient.compute_output_spec      8>>@@r   Nr2   r   r   r   r   r     s    4Ar   r   zkeras.ops.stop_gradientc                     t        | f      rt               j                  |       S t        j                  j                  |       S )a  Stops gradient computation.

    Args:
        variable: A tensor variable for which the gradient
            computation is to be disabled.

    Returns:
        The variable with gradient computation disabled.

    Examples:

    >>> var = keras.backend.convert_to_tensor(
    ...     [1., 2., 3.],
    ...     dtype="float32"
    ... )
    >>> var = keras.ops.stop_gradient(var)
    )r   r   r7   r   r   r   )r   s    r   r   r     s6    & XK(~++H55<<%%h//r   c                   0     e Zd Zdd fd
Zd Zd Z xZS )ForiLoopNr:   c                P    t         |   |       || _        || _        || _        y r=   )r>   r?   r   upperbody_fun)r   r   r   r   r;   rC   s        r   r?   zForiLoop.__init__  s(    d#

 r   c                     t         j                  j                  | j                  | j                  | j
                  |      S r   )r   r   	fori_loopr   r   r   r   init_vals     r   r   zForiLoop.call  s1    ||%%JJJJMM	
 	
r   c                 D    t        |j                  |j                        S rs   ru   r   s     r   r/   zForiLoop.compute_output_spec  r   r   rM   rO   s   @r   r   r     s    7; !
Ar   r   zkeras.ops.fori_loopc                     t        | ||f      rt        | ||      j                  |      S t        j                  j                  | |||      S )a  For loop implementation.

    Args:
        lower: The initial value of the loop variable.
        upper: The upper bound of the loop variable.
        body_fun: A callable that represents the loop body. Must take two
            arguments: the loop variable and the loop state. The loop state
            should be updated and returned by this function.
        init_val: The initial value of the loop state.

    Returns:
        The final state after the loop.

    Example:

    >>> lower = 0
    >>> upper = 10
    >>> body_fun = lambda i, s: (i + 1, s + i)
    >>> init_val = 0
    >>> keras.ops.fori_loop(lower, upper, body_fun, init_val)
    45
    )r   r   r7   r   r   r   )r   r   r   r   s       r   r   r     sH    0 UE845uh/==hGG<<!!%(CCr   c                   2     e Zd Zddd fdZd Zd Z xZS )UnstackNr:   c                B    t         |   |       || _        || _        y r=   )r>   r?   numrT   )r   r   rT   r;   rC   s       r   r?   zUnstack.__init__  s!    d#	r   c                 l    t         j                  j                  || j                  | j                        S r   )r   r   unstackr   rT   r   r0   s     r   r   zUnstack.call  s#    ||##Atxx;;r   c                 p   t        | j                  t        |j                              }|j                  d | |j                  |dz   d  z   }| j                  }||j                  |   }|t        d|j                   d      t        |      D cg c]  }t        ||j                         }}|S c c}w )NrL   z'Cannot infer argument `num` from shape zn. Either provide a tensor with a concrete shape in the `axis` dimension or explicitly pass the `num` argument.r$   r%   )	r   rT   r]   r$   r   r_   ranger   r%   )r   r0   rT   output_shapesr   _outputs          r   r/   zUnstack.compute_output_spec  s     CL9)<<hh;''$-C;977) 66  FK3Z
@AKm177;
 
 
s   B3r   rM   rO   s   @r   r   r     s     
<r   r   zkeras.ops.unstackc                     t        | f      rt        ||      j                  |       S t        j                  j                  | ||      S )a  Unpacks the given dimension of a rank-R tensor into rank-(R-1) tensors.

    Args:
        x: The input tensor.
        num: The length of the dimension axis. Automatically inferred
            if `None`.
        axis: The axis along which to unpack.

    Returns:
        A list of tensors unpacked along the given axis.

    Example:

    >>> x = keras.ops.array([[1, 2], [3, 4]])
    >>> keras.ops.unstack(x, axis=0)
    [array([1, 2]), array([3, 4])]
    )r   rT   )r   r   r7   r   r   r   )r0   r   rT   s      r   r   r     sB    & QD!sD!//22<<s66r   zkeras.ops.shapec                 p    t        | f      r| j                  S t        j                  j                  |       S )aK  Gets the shape of the tensor input.

    Note: On the TensorFlow backend, when `x` is a `tf.Tensor` with dynamic
    shape, dimensions which are dynamic in the context of a compiled function
    will have a `tf.Tensor` value instead of a static integer value.

    Args:
        x: A tensor. This function will try to access the `shape` attribute of
            the input tensor.

    Returns:
        A tuple of integers or None values, indicating the shape of the input
            tensor.

    Example:

    >>> x = keras.ops.zeros((8, 12))
    >>> keras.ops.shape(x)
    (8, 12)
    )r   r$   r   r   r0   s    r   r$   r$      s,    , QD!ww<<a  r   zkeras.ops.dtypec                 @    t        j                  | j                        S )a  Return the dtype of the tensor input as a standardized string.

    Note that due to the standardization, the dtype will not compare equal
    to the backend-specific version of the dtype.

    Args:
        x: A tensor. This function will try to access the `dtype` attribute of
            the input tensor.

    Returns:
        A string indicating the dtype of the input tensor, e.g. `"float32"`.

    Example:

    >>> x = keras.ops.zeros((8, 12))
    >>> keras.ops.dtype(x)
    'float32'

    )r   standardize_dtyper%   r   s    r   r%   r%   ;  s    * $$QWW--r   c                   0     e Zd Zdd fd
Zd Zd Z xZS )CastNr:   c                4    t         |   |       || _        y r=   r>   r?   r%   r   r%   r;   rC   s      r   r?   zCast.__init__T  rl   r   c                 V    t         j                  j                  || j                        S r   )r   r   castr%   r   s     r   r   z	Cast.callX  s    ||  DJJ//r   c                 X    t        j                  |j                  | j                        S Nr   r   r   r$   r%   r   s     r   r/   zCast.compute_output_spec[      ""

CCr   rM   rO   s   @r   r   r   S  s    &* 0Dr   r   zkeras.ops.castc                     t        j                  |      }t        | f      r t        |      |       S t         j                  j                  | |      S )a  Cast a tensor to the desired dtype.

    Args:
        x: A tensor or variable.
        dtype: The target type.

    Returns:
        A tensor of the specified `dtype`.

    Example:

    >>> x = keras.ops.arange(4)
    >>> x = keras.ops.cast(x, dtype="float16")
    rt   )r   r   r   r   r   r   r0   r%   s     r   r   r   _  sG      %%e,EQD! t% ##<<Q&&r   c                   0     e Zd Zdd fd
Zd Zd Z xZS )SaturateCastNr:   c                4    t         |   |       || _        y r=   r   r   s      r   r?   zSaturateCast.__init__v  rl   r   c                 .    t        || j                        S r   )_saturate_castr%   r   s     r   r   zSaturateCast.callz  s    a,,r   c                 X    t        j                  |j                  | j                        S r   r   r   s     r   r/   z SaturateCast.compute_output_spec}  r   r   rM   rO   s   @r   r   r   u  s    &* -Dr   r   zkeras.ops.saturate_castc                     t        j                  |      }t        | f      r t        |      |       S t	        | |      S )a  Performs a safe saturating cast to the desired dtype.

    Saturating cast prevents data type overflow when casting to `dtype` with
    smaller values range. E.g.
    `ops.cast(ops.cast([-1, 256], "float32"), "uint8")` returns `[255, 0]`,
    but `ops.saturate_cast(ops.cast([-1, 256], "float32"), "uint8")` returns
    `[0, 255]`.

    Args:
        x: A tensor or variable.
        dtype: The target type.

    Returns:
        A safely casted tensor of the specified `dtype`.

    Example:

    Image resizing with bicubic interpolation may produce values outside
    original range.
    >>> image2x2 = np.array([0, 1, 254, 255], dtype="uint8").reshape(1, 2, 2, 1)
    >>> image4x4 = tf.image.resize(image2x2, (4, 4), method="bicubic")
    >>> print(image4x4.numpy().squeeze())
    >>> # [[-22.500004 -22.204624 -21.618908 -21.32353 ]
    >>> #  [ 52.526054  52.82143   53.407146  53.70253 ]
    >>> #  [201.29752  201.59288  202.17859  202.47395 ]
    >>> #  [276.32355  276.61893  277.20465  277.50006 ]]

    Casting this resized image back to `uint8` will cause overflow.
    >>> image4x4_casted = ops.cast(image4x4, "uint8")
    >>> print(image4x4_casted.numpy().squeeze())
    >>> # [[234 234 235 235]
    >>> #  [ 52  52  53  53]
    >>> #  [201 201 202 202]
    >>> #  [ 20  20  21  21]]

    Saturate casting to `uint8` will clip values to `uint8` range before
    casting and will not cause overflow.
    >>> image4x4_saturate_casted = ops.saturate_cast(image4x4, "uint8")
    >>> print(image4x4_saturate_casted.numpy().squeeze())
    >>> # [[  0   0   0   0]
    >>> #  [ 52  52  53  53]
    >>> #  [201 201 202 202]
    >>> #  [255 255 255 255]]

    rt   )r   r   r   r   r   r   s     r   saturate_castr     s>    ^ %%e,EQD!(|%(++!U##r   c                    |xs t         }d }t        j                  |      }t        j                  | j                        } ||      \  }} ||      \  }}t        j                  ||      j                  |      }	|	|k  rt        j                  |	d|      }	t        j                  ||      j                  |      }
|
|kD  rt        j                  |
d|      }
|j                  j                  | |	|
      } |j                  | |      S )Nc                 ,   d| k(  rd}d}||fS d| v rBt        j                  |       j                  }t        j                  |       j                  }||fS t        j                  |       j                  }t        j                  |       j                  }||fS )Nboolr   rL   rJ   )	ml_dtypesiinfor   r   finfo)r%   	dtype_min	dtype_maxs      r   get_dtype_min_maxz)_saturate_cast.<locals>.get_dtype_min_max  s    U?II )## e^!.22I!.22I )## ".22I!.22I)##r   r   rt   )r   r   r%   npmaximumastype	nextafterminimumnumpyclipr   )r0   r%   backend_moduler   in_dtypein_minin_maxout_minout_max	min_limit	max_limits              r   r   r     s    #.wN
$ %%e,E((1H&x0NFF(/GW 

67+228<I7LLAX>	

67+228<I7LLAX>	 	!!!Y	:Aq%((r   c                   2     e Zd Zddd fdZd Zd Z xZS )ConvertToTensorNr:   c                P    t         |   |       || _        || _        || _        y r=   )r>   r?   r%   r&   r'   )r   r%   r&   r'   r;   rC   s        r   r?   zConvertToTensor.__init__  s(    d#
r   c                     t         j                  j                  || j                  | j                  | j
                        S )Nr%   r&   r'   )r   r   convert_to_tensorr%   r&   r'   r   s     r   r   zConvertToTensor.call  s3    ||--TZZDKK . 
 	
r   c                 N   | j                   t        j                  |j                         n| j                   }| j                  | j                  sdn|j                  }| j                  | j                  sdn|j                  }t        j
                  |j                  |||      S )NFr#   )r%   r   r   r&   r'   r   r$   )r   r0   r%   r&   r'   s        r   r/   z#ConvertToTensor.compute_output_spec  s     zz! %%agg. 	 [[,T[[Eahh 	 [[,T[[Eahh 	 ""''vf
 	
r   NNNrM   rO   s   @r   r  r    s    T 


r   r  zkeras.ops.convert_to_tensorc                     |dnt        j                  |      }t        | f      r t        |||      |       S t         j                  j                  | |||      S )a  Convert a NumPy array or Python array to a tensor.

    Native tensors for the current backend or left unchanged unless the `dtype`,
    `sparse` or `ragged` arguments are set.

    Args:
        x: A NumPy array, Python array (can be nested) or a backend tensor.
        dtype: The target type. If `None`, the type of `x` is used.
        sparse: Whether to keep sparse tensors. `False` will cause sparse
            tensors to be densified. The default value of `None` means that
            sparse tensors are kept only if the backend supports them.
        ragged: Whether to keep ragged tensors. `False` will cause ragged
            tensors to be densified. The default value of `None` means that
            ragged tensors are kept only if the backend supports them.

    Returns:
        A backend tensor of the specified `dtype` and sparseness.

    Example:

    >>> x = np.array([1, 2, 3])
    >>> y = keras.ops.convert_to_tensor(x)
    Nr  )r   r   r   r  r   r  )r0   r%   r&   r'   s       r   r  r    s_    2 MDw'@'@'GEQD!IU6&I!LL<<))	vf *  r   zkeras.ops.convert_to_numpyc                 n    t        | f      rt        j                  |       S t        j                  |       S )zlConvert a tensor to a NumPy array.

    Args:
        x: A tensor.

    Returns:
        A NumPy array.
    )r   r  arrayr   convert_to_numpyr   s    r   r  r    s/     QD! xx{##A&&r   c                   B    e Zd Zej                  d        Zd Zd Zd Zy)Condc                       fd}t        j                         r6t        j                  | j                  j                   d      } ||i |S  ||i |S )Nc                  d    t        | |      r j                  | i |S  j                  | i |S r   )r   r7   r   )argskwargsr   s     r   call_fnzCond.__call__.<locals>.call_fn0  s;    #D&1)t))4:6:: tyy$1&11r   z.call())object_name)r   is_traceback_filtering_enabled!inject_argument_info_in_tracebackrC   r3   )r   r   r!  r"  s   `   r   __call__zCond.__call__.  sb    	2 99;%GG $ 7 78@G D+F++ '''r   c                 D    t         j                  j                  |||      S r   )r   r   r   )r   predtrue_fnfalse_fns       r   r   z	Cond.callA  s    ||  w99r   c                     t        j                  |      }t        j                  |      }| j                  ||      st        d| d| d      |S )Nz_`true_fn` and `false_fn` should return outputs of the same kind (struct, dtype and shape). Got z and z	 instead.)r   r/   _check_output_specr_   )r   r(  r)  r*  true_fn_specfalse_fn_specs         r   r/   zCond.compute_output_specD  s_    227;33H=&&|]C#nE-	C 
 r   c                     	 t        j                  ||       d }t        j                  |||      }t        t        j                  |            S #  Y yxY w)NFc                     | |
| d u xr |d u S | j                   |j                   k(  xr | j                  |j                  k(  S r   r   )t_specf_specs     r   
check_leafz+Cond._check_output_spec.<locals>.check_leafU  sC    ~~8&D.8<<6<</PFLLFLL4PPr   )r   assert_same_structurer-   allr.   )r   r-  r.  r3  sames        r   r,  zCond._check_output_specO  sQ    	&&|]C	Q
 !!*lMJ4<<%&&	s   A AN)	r3   r4   r5   r   filter_tracebackr&  r   r/   r,  r   r   r   r  r  -  s)    %%( &($:	'r   r  zkeras.ops.condc                 &     t               | ||      S )aP  Conditionally applies `true_fn` or `false_fn`.

    Args:
        pred: Boolean scalar type
        true_fn: Callable returning the output for the `pred == True` case.
        false_fn: Callable returning the output for the `pred == False` case.

    Returns:
        The output of either `true_fn` or `false_fn` depending on pred.
    )r  )r(  r)  r*  s      r   r   r   ^  s     46$**r   c                   J     e Zd Zdd fd
Zd Zd Z fdZed        Z xZ	S )VectorizedMapNr:   c                4    t         |   |       || _        y r=   )r>   r?   function)r   r<  r;   rC   s      r   r?   zVectorizedMap.__init__n  s    d# r   c                 V    t         j                  j                  | j                  |      S r   )r   r   vectorized_mapr<  )r   elementss     r   r   zVectorizedMap.callr  s    ||**4==(CCr   c                     t        j                  d |      }t        j                  |      d   j                  d   t	        j
                  | j                  |      }fd}t        j                  ||      }|S )Nc                     | d   S r   r   r   s    r   r   z3VectorizedMap.compute_output_spec.<locals>.<lambda>v  r    r   r   c                 z    t        f| j                  z   | j                  | j                  | j                        S r"   r(   r)   s    r   r+   z<VectorizedMap.compute_output_spec.<locals>.append_batch_axisz  r,   r   )r   r-   r.   r$   r   r/   r<  )r   r?  r0   r1   r+   r*   s        @r   r/   z!VectorizedMap.compute_output_specu  sh    ~x8LL"1%++A.''q9	 0!4r   c                 ^    t         |          }|j                  d| j                  i       |S )Nr<  )r>   
get_configupdater<  )r   configrC   s     r   rD  zVectorizedMap.get_config  s*    #%z4==12r   c                 h    |j                         }t        j                  |d         |d<    | di |S )Nr<  r   )copyr   deserialize_keras_object)clsrF  s     r   from_configzVectorizedMap.from_config  s8    .GG:
z }V}r   )
r3   r4   r5   r?   r   r/   rD  classmethodrK  rN   rO   s   @r   r:  r:  m  s/    )- !D 
  r   r:  zkeras.ops.vectorized_mapc                 |    t        |f      r t        |       |      S t        j                  j	                  | |      S )a:  Parallel map of `function` on axis 0 of tensor(s) `elements`.

    Schematically, `vectorized_map` implements the following,
    in the case of a single tensor input `elements`:

    ```python
    def vectorized_map(function, elements):
        outputs = []
        for e in elements:
            outputs.append(function(e))
        return np.stack(outputs)
    ```

    In the case of an iterable of tensors `elements`,
    it implements the following:

    ```python
    def vectorized_map(function, elements):
        batch_size = elements[0].shape[0]
        outputs = []
        for index in range(batch_size):
            outputs.append(function([e[index] for e in elements]))
        return np.stack(outputs)
    ```

    In this case, `function` is expected to take as input
    a single list of tensor arguments.
    )r   r:  r   r   r>  )r<  r?  s     r   r>  r>    s7    < XK(&}X&x00<<&&x::r   zkeras.ops.is_tensorc                 @    t         j                  j                  |       S )a%  Check whether the given object is a tensor.

    Note: This checks for backend specific tensors so passing a TensorFlow
    tensor would return `False` if your backend is PyTorch or JAX.

    Args:
        x: A variable.

    Returns:
        `True` if `x` is a tensor, otherwise `False`.
    )r   r   	is_tensorr   s    r   rO  rO    s     <<!!!$$r   zkeras.ops.custom_gradientc                 @    t         j                  j                  |       S )a
  Decorator to define a function with a custom gradient.

    This decorator allows fine grained control over the gradients of a sequence
    for operations. This may be useful for multiple reasons, including providing
    a more efficient or numerically stable gradient for a sequence of
    operations.

    Args:
        f: Function `f(*args)` that returns a tuple
            `(output, grad_fn)`, where:
            - `args` is a sequence of (nested structures of) tensor inputs to
                the function.
            - `output` is a (nested structure of) tensor outputs of applying
                operations in `forward_fn` to `args`.
            - `grad_fn` is a function with the signature `grad_fn(*args,
                upstream)` which returns a tuple of tensors the same size as
                (flattened) `args`: the derivatives of tensors in `output` with
                respect to the tensors in `args`. `upstream` is a tensor or
                sequence of tensors holding the initial value gradients for each
                tensor in `output`.

    Returns:
        A function `h(*args)` which returns the same value as
        `f(*args)[0]` and whose gradient is determined by
        `f(*args)[1]`.


    Examples:

    1. Backend-agnostic example.

    ```python
    @ops.custom_gradient
    def log1pexp(x):
        e = ops.exp(x)

        def grad(*args, upstream=None):
            if upstream is None:
                (upstream,) = args
            return ops.multiply(upstream, 1.0 - 1.0 / ops.add(1, e))

        return ops.log(1 + e), grad
    ```

    Note that the grad function that returns gradient computation
    requires `args` as well as an `upstream` keyword argument, depending
    on the backend being set. With the JAX and TensorFlow backends,
    it requires only one argument, whereas it might use the `upstream`
    argument in the case of the PyTorch backend.

    When working with TensorFlow/JAX backend, `grad(upstream)`
    is sufficient. With PyTorch, the `grad` function requires
    `*args` as well as `upstream`, e.g. `def grad(*args, upstream)`.
    Follow the previous example to use `@ops.custom_gradient` in
    a way that is compatible with all backends.

    2. Here's JAX & TensorFlow-specific example:

    ```python
    @ops.custom_gradient
    def log1pexp(x):
        e = ops.exp(x)
        def grad(upstream):
            return ops.multiply(upstream, 1.0 - 1.0 / ops.add(1, e))
        return ops.log(1 + e), grad
    ```

    3. Lastly, here's a PyTorch-specific example,
    using `*args` & `upstream`:

    ```python
    @ops.custom_gradient
    def log1pexp(x):
        e = ops.exp(x)
        def grad(*args, upstream):
            return ops.multiply(upstream, 1.0 - 1.0 / ops.add(1, e))
        return ops.log(1 + e), grad
    ```
    )r   r   custom_gradient)r   s    r   rQ  rQ    s    b <<''**r   )NNFrL   re   r   r   r  )<r   r  r  	keras.srcr   r   keras.src.api_exportr   keras.src.backendr   r   &keras.src.backend.common.backend_utilsr   r	   keras.src.ops.operationr
   keras.src.savingr   keras.src.utilsr   r   r   r9   rF   rR   rW   rh   rn   ry   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  r   r:  r>  rO  rQ  r   r   r   <module>rY     s^       - ) 2 D C - . +) * o)# )#X9 D K  K\"i "J *+9O ,9Ox	;i 	; !"8 #8<=I = ()L *L^"<I "<J  < !<<=) = &' E ( EFY   !%; "%;P
	 
* $%
 	2 &2jA9 A '(0 )0.Ay A& #$D %D8i 8 !"7 #7.  ! !!4  . !..	D9 	D '  '*	D9 	D '(1$ )1$h%)P
i 
: +, -@ *+' ,' .'9 .'b +  +#I #L (); *;D #$% %% )*P+ +P+r   