o
    2hE                     @   s   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 edG d	d
 d
eZdd Zdd Zdd Zdd ZdS )    N)tree)keras_exportKerasTensor)backend)is_nnx_enabled)	Operationzkeras.Functionc                       s   e Zd ZdZd fdd	Zedd Zedd Zed	d
 Zdd Z	dd Z
dd Zdd Zdd ZdddZdd Z  ZS )Functiona  Class that encapsulates a computation graph of Keras operations.

    You can use a `Function` to capture the computation graph linking
    some input tensors to some output tensors, and reapply the same
    computation on new inputs.

    A `Function` is similar to a Functional Model, with the difference
    that it is stateless (it does not track state variables)
    and does not implement the `Layer` API.

    Example:

    ```python
    input_1 = keras.KerasTensor(shape=(None, 2, 3))
    input_2 = keras.KerasTensor(shape=(None, 2, 3))
    x = input_1 + input_2
    output = keras.ops.sigmoid(x)
    fn = keras.Function(inputs=[input_1, input_2], outputs=output)

    input_1_val = np.random.random((4, 2, 3))
    input_2_val = np.random.random((4, 2, 3))
    output_val = fn([input_1_val, input_2_val])
    ```

    Args:
        inputs: `KerasTensor` instance or nested structured of
            `KerasTensor` instances.
        outputs: `KerasTensor` instance or nested structured of
            `KerasTensor` instances. They should be computable
            given only the values of `inputs`.
        name: String. The name of the function.
    Nc           
         s  t  j|d t dkrt| dd}d| _tdd || _tdd || _t	|| _
t	|| _| j
s@td	| d
| | jsMtd| d
| t dkrU|| _t| j
| j\}}}}|| _|| _|| _|| _| j
D ]}	|	jjr~|	jjjs~tdqot r|   d S d S )Nname
tensorflow_self_setattr_trackingTFc                 S      | S N xr   r   Q/var/www/html/chatgem/venv/lib/python3.10/site-packages/keras/src/ops/function.py<lambda>:       z#Function.__init__.<locals>.<lambda>c                 S   r   r   r   r   r   r   r   r   ;   r   z4`inputs` argument cannot be empty. Received:
inputs=z	
outputs=z5`outputs` argument cannot be empty. Received:
inputs=z#`inputs` not connected to `outputs`)super__init__r   getattrr   r   map_structure_inputs_struct_outputs_structflatten_inputs_outputs
ValueError	map_graph_nodes_nodes_by_depth_operations_operations_by_depth_keras_history	operation_outbound_nodesr   _setup_nnx_op_mapping)
selfinputsoutputsr   r   nodesnodes_by_depth
operationsoperations_by_depthinput	__class__r   r   r   .   sZ   


zFunction.__init__c                 C   s   | j d d  S r   )r#   r)   r   r   r   r.   `   s   zFunction.operationsc                 C      | j S )z1Flat list of the symbolic inputs of the Function.)r   r3   r   r   r   r*   d      zFunction.inputsc                 C   r4   )z2Flat list of the symbolic outputs of the Function.)r   r3   r   r   r   r+   i   r5   zFunction.outputsc                 C   s4   i | _ | jdd | _| jD ]	}|| j t|< qdS )zSetup operation mapping for NNXN)_nnx_op_mappingr#   nnx_operationsid)r)   r&   r   r   r   r(   n   s
   
zFunction._setup_nnx_op_mappingc                 C   s6   |j }t| drt|t| di v r| jt| S |S )z;Get the operation for a node, using NNX mapping if enabled.r6   )r&   hasattrr8   r   r6   )r)   noder&   r   r   r   _get_operation_for_nodey   s   z Function._get_operation_for_nodec                 C   sf   |  | d}tt|| jD ]\}}|j|jkrd} nq|r*tdd | jS | j|dd dS )NTFc                 S   s   t | j| jdS )N)shapedtype)r   r<   r=   r   r   r   r   r      s    z.Function.compute_output_spec.<locals>.<lambda>c                 S   r4   r   )compute_output_specopr   r   r   r          operation_fn)	_assert_input_compatibilityzipr   r   r   r<   r   r   _run_through_graph)r)   r*   shortcutr   x_refr   r   r   r>      s   
zFunction.compute_output_specc                 C   sV   t dd |}tt || jD ]\}}|j|_|j|_q| 	|}t 
dd |S )Nc                 S   s
   t | dS )Nr<   r   r   r   r   r   r      s   
 z/Function.compute_output_shape.<locals>.<lambda>c                 S   r4   r   rI   r   r   r   r   r      rA   )r   map_shape_structurerE   r   r   r=   _dtypesparse_sparser>   r   )r)   input_shapeinput_shape_structr   rH   output_specr   r   r   compute_output_shape   s   

zFunction.compute_output_shapec                 C   s   |  | | j|dd dS )z'Computes output tensors for new inputs.c                 S   r   r   r   r?   r   r   r   r      r   zFunction.call.<locals>.<lambda>rB   )rD   rF   )r)   r*   r   r   r   call   s   
zFunction.callc                    sD  t |}i  t| j|D ]
\}}| t|< q| j}t| }|jdd |D ]`}|| }	|	D ]W}
|
j	r9|
j
r:q1t fdd|
jD rGq1|
j \}}|durd||
j	}||g|R i |}n| |
}||}||i |}t|
jt |D ]
\}}| t|< q}q1q)g }| jD ]}| t|  qt | j|S )zExecute the graph.

        At each node we compute outputs via
        `operation_fn(node.operation)(*args, **kwargs)`.
        Treversec                 3   s    | ]	}t | vV  qd S r   )r8   ).0r   tensor_dictr   r   	<genexpr>   s    z.Function._run_through_graph.<locals>.<genexpr>N)r   r   rE   r*   r8   r"   listkeyssortr&   is_inputanyinput_tensors	argumentsfill_inr;   r+   appendpack_sequence_asr   )r)   r*   rC   call_fnr   yr-   
depth_keysdepthr,   r:   argskwargsr@   r+   r&   output_tensorsr   rV   r   rF      s:   



zFunction._run_through_graphc                 C   s   z	t || j W n ty   td| j d| w tt || jD ]P\}}t|jt|jkrHt| j	j
 d|j d|j d|j dt|j|jD ]%\}}|d urt|d urt||krtt| j	j
 d|j d|j d|j dqOq%d S )NzOFunction was called with an invalid input structure. Expected input structure: z
Received input structure: z, was passed incompatible inputs. For input 'z', expected shape z+, but received instead a tensor with shape .)r   assert_same_structurer   r   rE   r   r   lenr<   r2   __name__r   )r)   r*   r   rH   dimref_dimr   r   r   rD      sJ   

z$Function._assert_input_compatibilityr   )rm   
__module____qualname____doc__r   propertyr.   r*   r+   r(   r;   r>   rQ   rR   rF   rD   __classcell__r   r   r1   r   r	      s     !2


	
/r	   c                 C   s   t t| d t | S )Nz_ib-)strr8   )r@   
node_indexr   r   r   make_node_key   s   rw   c              
      s  t | |\} dd |D }i }i }t|D ]2}||d}||jd}t||}|||j< |||< |jD ]}	||	d}t|d |||	< q6q| D ]$}
|
jd }|ro||vrod||< d |< d||jd < |	t
|d qKtt}| D ]\}}|| | qytt}| D ]\}}|| | qt| }|jdd g }|D ]}|| }|j fdd	d
 || qt| }|jdd t }| D ]}|	| qg }|D ]=}|| D ]6}t|jD ]}||vr|j}td| d| d| ||jj qt|jD ]}|	| qqqdd |D }|D ]}||dkr<td| d|| dq$||||fS )a  Validates a graph's topology and gather its operations and nodes.

    Args:
        inputs: List of input tensors.
        outputs: List of outputs tensors.

    Returns:
        A tuple `(nodes, nodes_by_depth, operations, operations_by_depth)`.
        - nodes: set of Node instances
        - nodes_by_depth: dict mapping ints (depth) to lists of node instances.
        - operations: list of Operation instances.
        - operations_by_depth: dict mapping ints (depth) to lists of Operation
            instances.
    c                 S   s"   h | ]}t |j|jj|qS r   )rw   r&   _inbound_nodesindex)rU   r:   r   r   r   	<setcomp>  s    zmap_graph.<locals>.<setcomp>r      TrS   c                    s    |  S r   r   r   operation_indicesr   r   r   H  s    zmap_graph.<locals>.<lambda>)keyz2Graph disconnected: cannot find parent for tensor z at operation 'zB'. The following previous operations were accessed without issue: c                 S   s   g | ]}|j qS r   r
   )rU   r&   r   r   r   
<listcomp>i  s    zmap_graph.<locals>.<listcomp>z
The name "z
" is used z: times in the model. All operation names should be unique.)
_build_mapreversed
setdefaultgetr&   maxparent_nodesr%   rx   addrw   collectionsdefaultdictrY   itemsra   rZ   r[   extendsetr   r   r^   r   r   r+   count)r*   r+   nodes_in_decreasing_depthnetwork_nodesnodes_depthsoperations_depthsr:   rf   previous_depthnode_depinput_tinput_operationr-   r/   r&   re   r.   operations_for_depthcomputable_tensorsr   operations_with_complete_input	all_namesr   r   r}   r   r       s   






r    c                 C   s>   t  }t  }g }i }t|D ]}t| ||||| q||fS )a  Topologically sort nodes in order from inputs to outputs.

    It uses a depth-first search to topologically sort nodes that appear in the
    _keras_history connectivity metadata of `outputs`.

    Args:
        outputs: the output tensors whose _keras_history metadata should be
                walked. This may be an arbitrary nested structure.

    Returns:
        A tuple like (ordered_nodes, operation_to_first_traversal_index)
        ordered_nodes: list of nodes appearing in the keras history,
            topologically sorted from original inputs to the `outputs`.
            (If outputs have different sets of ancestors, the inputs to one
            output may appear after a different output).
        operation_to_first_traversal_index:
            A dict mapping operation to the traversal index in the DFS where it
            is seen. Note: if a operation is shared by several nodes, the dict
            will onlystore the index corresponding to the *first* time the
            operation seen.
    )r   r   r   _build_map_helper)r*   r+   finished_nodesnodes_in_progressr   r~   outputr   r   r   r   s  s   r   c           
      C   s   |j \}}}|s
dS |j| }	|	|v rdS |	|v r%td| d|j d||vr/t|||< ||	 |	jsM|t| vrM|	j	D ]}t
| ||||| qA||	 ||	 ||	 dS )z"Recursive helper for `_build_map`.NzTensor z from operation 'z' is part of a cycle.)r%   rx   r   r   rl   r   r\   r   r   r^   r   removera   )
r*   tensorr   r   r   r~   r&   rv   _r:   r   r   r   r     s<   



	
r   )r   	keras.srcr   keras.src.api_exportr   keras.src.backendr   keras.src.backend.configr   r   keras.src.ops.operationr   r	   rw   r    r   r   r   r   r   r   <module>   s     ky&