o
    + if                     @   s   d dl Z d dlmZ d dlmZ d dlmZmZ d dlm	Z	m
Z
 d dlmZmZmZ d dlmZmZ dd	lmZmZ G d
d deZG dd dZdd Zdd Zdd ZG dd dZdddZdS )    N)core)_append_grad_suffix_)Variablein_pir_mode)build_pylayer_opcf_yield)LayerHelper
check_typein_dygraph_mode)flattenmap_structure   )
BlockGuardcopy_var_to_parent_blockc                       s4   e Zd Z fddZ fddZ fddZ  ZS )StaticPyLayerBlockGuardc                    s(   t |dtd t |jj || _d S )Nblockr   )r	   StaticPyLayerBlocksuper__init__helpermain_programblock_manager)selfr   	__class__ k/home/app/PaddleOCR-VL-test/.venv_paddleocr/lib/python3.10/site-packages/paddle/static/nn/static_pylayer.pyr      s   
z StaticPyLayerBlockGuard.__init__c                    s   t    | jS N)r   	__enter__r   r   r   r   r   r   &   s   
z!StaticPyLayerBlockGuard.__enter__c                    s   | j   t |||S r   )r   completer   __exit__)r   exc_typeexc_valexc_tbr   r   r   r!   *   s   
z StaticPyLayerBlockGuard.__exit__)__name__
__module____qualname__r   r   r!   __classcell__r   r   r   r   r      s    
r   c                   @   s\   e Zd ZdddZd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S )r   Nc                 C   sF   dd |D | _ g | _|| _td|d| _d | _d | _d | _i | _d S )Nc                 S      g | ]	}t |tr|qS r   
isinstancer   ).0Z
each_inputr   r   r   
<listcomp>2   s    z/StaticPyLayerBlock.__init__.<locals>.<listcomp>Zstatic_pylayer_block)name)	
fwd_inputsfwd_outputscontextr   r   	fwd_op_id_forward_block_id_backward_block_idvar_old_to_new)r   inputsr.   pylayer_contextr   r   r   r   0   s   
zStaticPyLayerBlock.__init__Fc                 C   s   || _ t| S r   )is_backward_blockr   )r   r8   r   r   r   r   C   s   zStaticPyLayerBlock.blockc                 C      | j S r   )r3   r   r   r   r   forward_block_indexG      z&StaticPyLayerBlock.forward_block_indexc                 C   r9   r   )r4   r   r   r   r   backward_block_indexK   r;   z'StaticPyLayerBlock.backward_block_indexc                 C   r9   r   )r2   r   r   r   r   fwd_op_indexO   r;   zStaticPyLayerBlock.fwd_op_indexc                 C   sv   | j j }| j j|j}|j| _|jtj	j
jd}|jdd| ji| j|gdd|gid}|j| _| j j  d S )N)typeZpylayerZInput)ZOutZScopeblocks)r>   r6   outputsattrs)r   r   current_blockr   
parent_idxidxr3   
create_varr   VarDescVarTypeZSTEP_SCOPESZ	append_opr/   r0   r2   _sync_with_cpp)r   inside_blockparent_blockZ
step_scope
pylayer_opr   r   r   complete_forward_blockS   s    z)StaticPyLayerBlock.complete_forward_blockc           	      C   s   | j j }| j j|j}|j| _|jD ]}tj	
 }tj	jj}|j|| q|| j t|| j |j| jj}|j}|j| j jd||g | jro| jjD ]}||jsht|j d|j d||j qT| j j  d S )Nr?   zL was saved in forward block but could not be found in backward block. Maybe z was renamed somewhere.)r   r   rB   r   rC   rD   r4   opsr   Zop_proto_and_checker_makerZkOpRoleAttrNameZOpRoleZBackwarddescZ	_set_attrZ_set_forward_block_idxr:   _rename_var_recursively_r5   programr=   Zset_blocks_attrr1   Z
saved_varshas_varr.   
ValueError_remove_varrH   )	r   rI   rJ   opZop_role_attr_nameZbackwardZforward_block_descZbackward_block_descvarr   r   r   complete_backward_blockj   s6   

z*StaticPyLayerBlock.complete_backward_blockc                 C   s   | j s|  S |  S r   )r8   rL   rV   r   r   r   r   r       s   zStaticPyLayerBlock.completeNN)F)r%   r&   r'   r   r   propertyr:   r<   r=   rL   rV   r    r   r   r   r   r   /   s    




'r   c                 C   sZ   | d u rd S t | dd }|d u rd S ddlm} d }t|dkr+t|d |r+|d }|S )Nargsr   )StaticPyLayerContext)getattrZpaddle.jit.dy2static.py_layerrZ   lenr+   )funcZfn_bind_argsrZ   Zfn_ctxr   r   r   _get_ctx_from_func_   s   r^   c           
      C   s   |  D ]'\}}| |r| j| |  q| jD ]}||| ||| qqddg}| jD ]F}| D ]?}||vr@q9|	|t
jjkrZ||}| j|}t|| q9|	|t
jjkrx||}	|	D ]}| j|}t|| qjq9q3dS )aD  
    Rename the var both the Variable instances and all ops' input and output arg names
    in `cur_block` based on dict `var_old_to_new`.
    Dict `var_old_to_new` should be the following format:
    {
        old_name_0 : new_name_0,
        old_name_1 : new_name_1,
        ...
        old_name_n : new_name_n,
    }
    r?   	sub_blockN)itemsrQ   rN   Z_rename_varencoderM   Z_rename_inputZ_rename_outputZ	all_attrsZ	attr_typer   ZAttrTypeZBLOCKZ_block_attr_idrP   r   rO   ZBLOCKSZ_blocks_attr_ids)
Z	cur_blockr5   Zold_var_nameZnew_var_namerT   Zblock_attr_names	attr_nameZsub_block_idr_   Zsub_blocks_idsr   r   r   rO      s4   




rO   c                 C   sf   t | ts| S |j}| }| jtjjjkr |	| j
r | }|S |j| j| j| jd}t| | |S )N)dtypeshaper>   )r+   r   r   rB   r>   r   rF   rG   ZDENSE_TENSOR_ARRAYZ_find_var_recursiver.   rE   rc   rd   paddleZassign)Zparent_block_varZlayer_helperprogrB   Zcurrent_block_varr   r   r   copy_var_from_parent_block   s    

rg   c                   @   s    e Zd Zg Zdd Zdd ZdS )PyLayerBackwardFunctionc                 C   s6   |d u st |std|| _|| _	 tj|  d S )Nzfunc must be a Python function)callable	TypeError_func_hook_check_funcrh   _register_backward_funcsappend)r   Zbackward_functionhook_check_funcr   r   r   r      s   z PyLayerBackwardFunction.__init__c                 G   sJ   | j sJ | j| }t|ttfs|f}|  || dd t|D }|S )Nc                 S   &   g | ]}t |tjjtd fr|qS r   r+   re   pirValuer>   )r,   Z
input_gradr   r   r   r-         z4PyLayerBackwardFunction.__call__.<locals>.<listcomp>)rl   rk   r+   listtupler   )r   output_gradsinput_gradsr   r   r   __call__  s   

z PyLayerBackwardFunction.__call__N)r%   r&   r'   rm   r   ry   r   r   r   r   rh      s    rh   c           #         sL  t  du s	J dttsJ |du r*D ]}|jdu r)td|j d|j qt| }t|}|r:||kr:|nd}t rdd tD }t	|}	d}
| durt
| sZtd|	  |  }
W d   n1 smw   Y  |
du rxdS d	d t|
D  |	   durtt  W d   n1 sw   Y  |	  |durt
|std
 fdd}t||d}|	| d}t|
}
tt|
D ]}t|
| tjjr|	 | |
|< |d7 }qt|
dkr|
d S |
S t|dttdfd tdi t fdd}| durt
| sJ t|d}|jdd$}|  }|dur<t||}dd t|D |_ng |_W d   n	1 sJw   Y  j  }|!  |durt
|sdJ |du rkg }g }|jD ].}|j}t"|}|j#$|% std| d| |j&|j'|j(|j|d}|)| qpfdd}t|tsJ |jddU}t||}|| }|durt|}|j*|j+ j#, }t|t|ksJ dt| dt| t-||D ]\}} t|t.rt"| }!|!|j/|j< qW d   n	1 sw   Y  |D ]	}"|0|"j q|du r$dS |S ) a  
    This API returns ``forward_fn(inputs)``, and two sub-block are created based on
    the logic of ``forward_fn`` and ``backward_fn``, with the operator ``pylayer``
    holding information about the two blocks.

    ``forward_fn`` and ``backward_fn`` should return a nest structure of Variables.
    A nest structure of Variables in PaddlePaddle is Variable(s), or tuple of Variables, or
    list of Variables.

    Note:
        1. If ``backward_fn`` is not None, user needs to keep the number of `Variable` inputs to ``forward_fn`` the same as the
        number of `Variable` outputs to ``backward_fn``, and the number of `Variable` outputs to ``forward_fn``
        the same as the number of `Variable` inputs to ``backward_fn``.

        2. If ``backward_fn`` is None, ``stop_gradient`` attr of all Variable in ``inputs`` is expected to be True.
        Otherwise it might get unexpected results in backward propagation.

        3. This API can only be used under static graph mode.

    Args:
        forward_fn (callable): A callable to be performed in forward propagation
        inputs (list[Variable]): The list of input Variable to the ``forward_fn``
        backward_fn (callable, optional): A callable to be performed in backward propagation. Default: None, which means no need to do backward propagation.
        name (str, optional): The default value is ``None`` . Normally users
            don't have to set this parameter. For more information, please
            refer to :ref:`api_guide_Name` .

    Returns:
        Variable|list(Variable)|tuple(Variable): returns the output of ``forward_fn(inputs)``

    Examples:
        .. code-block:: python

            >>> import paddle
            >>> import numpy as np

            >>> paddle.enable_static()

            >>> def forward_fn(x):
            ...     return paddle.exp(x)

            >>> def backward_fn(dy):
            ...     return 2 * paddle.exp(dy)

            >>> main_program = paddle.static.Program()
            >>> start_program = paddle.static.Program()

            >>> place = paddle.CPUPlace()
            >>> exe = paddle.static.Executor(place)
            >>> with paddle.static.program_guard(main_program, start_program):
            ...     data = paddle.static.data(name="X", shape=[None, 5], dtype="float32")
            ...     data.stop_gradient = False
            ...     ret = paddle.static.nn.static_pylayer(forward_fn, [data], backward_fn)
            ...     data_grad = paddle.static.gradients([ret], data)[0]

            >>> exe.run(start_program)
            >>> x = np.array([[1.0, 2.0, 3.0, 4.0, 5.0]], dtype=np.float32)
            >>> x, x_grad, y = exe.run(
            ...     main_program,
            ...     feed={"X": x},
            ...     fetch_list=[data, data_grad, ret],
            ... )

            >>> print(x)
            [[1. 2. 3. 4. 5.]]
            >>> print(x_grad)
            [[5.4365635 5.4365635 5.4365635 5.4365635 5.4365635]]
            >>> print(y)
            [[  2.7182817   7.389056   20.085537   54.59815   148.41316  ]]
    Fz<please use PyLayer instead of static_pylayer in dygraph modeNzr``stop_gradient`` attr of all inputs to ``forward_fn`` are expected to be True, when ``backward_fn == None``, but z.stop_gradient got c                 S      g | ]}t |tjjr|qS r   r+   re   rr   rs   )r,   inpr   r   r   r-   t  s
    z"static_pylayer.<locals>.<listcomp>z`forward_fn` should be callablec                 S   rz   r   r{   )r,   outr   r   r   r-         z `bakcward_fn` should be callablec                    sh  dd t D }dd t |D }t|t|kr)tdt| dt| dt||D ]n\}}|d u r7q.|j|jksJJ d|j d|j d	|j|jks]J d
|j d|j d	| r| siJ d| j| jksJ d| j d| j d	q.|	 |	 ksJ d|	  d|	  d	q.dd t  D }t| t|krtdt|  dt  dt| |D ]o\}}|d u rq|j|jksJ d|j d|j d	|j|jksJ d|j d|j d	| r| sJ d| j| jksJ d| j d| j d	q|	 |	 ks1J d|j	 d|j	 d	qd S )Nc                 S   rz   r   r{   r,   xr   r   r   r-     r~   zNstatic_pylayer.<locals>.hook_inputs_outputs_check_function.<locals>.<listcomp>c                 S   rp   r   rq   r   r   r   r   r-     rt   zKThe number of input grads should be equal to the number of inputs, but got  and .zdtype of inp_grad(z) and fwd_input(z) should be the samezshape of inp_grad(z1fwd_input and inp_grad should both be distributedzprocess_mesh of fwd_input(z) and inp_grad(ztype of inp_grad(c                 S   rz   r   r{   r   r   r   r   r-     r~   zMThe number of output grads should be equal to the number of outputs, but got zdtype of out_grad(z) and fwd_output(zshape of out_grad(z2fwd_output and out_grad should both be distributedzprocess_mesh of fwd_output(z) and out_grad(ztype of out_grad()
r   r\   rR   ziprc   rd   Zis_distZ	dist_attrZprocess_meshr>   )rw   rx   Zforward_inputsZinp_gradZ	fwd_inputZforward_outputsZout_gradZ
fwd_output)r0   r6   r   r   "hook_inputs_outputs_check_function  sx   


z:static_pylayer.<locals>.hook_inputs_outputs_check_function)ro   r   r   r.   zbase.layers.static_pylayerstatic_pylayerc                    
   t |  S r   )r   rU   r   r   r   <lambda>  s   
 z static_pylayer.<locals>.<lambda>)r7   )r8   c                 S   r)   r   r*   r   r   r   r   r-     s
    
z	Grad var z) , we can't find its related forward var )rc   rd   r>   r.   c                    r   r   )rg   r   r   r   r   r     s    Tzneeds to keep the number of inputs to ``forward_fn`` the same as the number of outputs to ``backward_fn``,                     but got r   )r   )1r
   r+   ru   Zstop_gradientrR   r.   r^   r   r   r   ri   Zforward_blockr   Zupdate_outputrh   Zregister_backward_functionranger\   re   rr   rs   resultsr	   strr>   r   localsr   r   r   r0   r   rB   rH   r   rN   Zhas_var_recursivera   rE   rc   rd   rn   rM   r=   Zinput_arg_namesr   r   r5   rS   )#Z
forward_fnr6   Zbackward_fnr.   Z	input_varZ
fwd_fn_ctxZ
bwd_fn_ctxZstatic_pylayer_contextr/   rK   r@   r   Zbwd_fnZop_result_idxiZcopy_to_parent_funcZpylayer_block_managerZmgrZorigin_outputoutputrB   Zgrad_var_insZfwd_varZfwd_var_nameZbwd_var_namerU   Zcopy_from_parent_funcZinside_block_inputsZgrad_origin_outputZflat_grad_originZforward_input_namesZ
bwd_outputZfwd_input_nameZbwd_out_newZbwd_varr   )r0   r   r6   r   r     s  G



O










8
r   rW   )re   Zpaddle.baser   Zpaddle.base.backwardr   Zpaddle.base.frameworkr   r   Zpaddle.base.libpaddle.pirr   r   Zpaddle.common_ops_importr   r	   r
   Zpaddle.utilsr   r   Zcontrol_flowr   r   r   r   r^   rO   rg   rh   r   r   r   r   r   <module>   s   i0)