o
    + ic                    @  sb  d dl mZ d dlZd dlmZmZmZ d dlm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mZmZmZmZ d d	lmZmZmZmZ d d
lmZmZmZmZmZ d dl m!Z! d dl"m#Z# d dl$m%Z%m&Z&m'Z'm(Z(m)Z)m*Z*m+Z+m,Z, dUddZ-G dd dZ.G dd de.Z/G dd dZ0G dd dZ1G dd de.Z2dd Z3G dd dZ4e5e6e7fZ8dd  Z9d!d" Z:G d#d$ d$Z;dVd&d'Z<d(d) Z=d*d+ Z>dWd,d-Z?dWd.d/Z@d0d1 ZAd2d3 ZBd4d5 ZCd6d7 ZDG d8d9 d9ZEdXd:d;ZFd<d= ZGd>d? ZHd@dA ZIdBdC ZJdDdE ZKdFdG ZLdHdI ZMdJdK ZNdLdM ZOe	N			O	O	O	O	O	PdYdQdRZPG dSdT dTZQdS )Z    )annotationsN)cached_propertypartialreduce)Any)_C_ops)core)_infer_var_data_type_shape_)OperatorProgramVariablein_pir_modestatic_only)build_assert_opbuild_if_opbuild_while_opcf_yield)LayerHelper
check_typecheck_variable_and_dtypeconvert_dtypein_dygraph_mode)use_pir_api)_PADDLE_PIR_DTYPE_2_NUMPY_DTYPE)assert_same_structurecopy_mutable_varsflattenhold_mutable_varsis_sequencemap_structurepack_sequence_asto_sequence   c           	      C  s   t | ddgd t|dtttdfd t|dtd t|dttdfd t r;|du r/g nt|}t| ||}dS |r?|nd| j	 }t
|fi t }|jd	| |du rWg nt|d
d|id}|S )a  
    This API creates an op that asserts the given condition is true. If the
    condition is false, prints the tensors in data. ``summarize`` specifies the
    number of the elements in the tensors to print.

    Args:
        cond (Tensor): The boolean condition tensor whose numel should be 1.
        data (list|tuple, optional): list or tuple of tensors to print when
            condition is not true. If it's ``None``, no tensor will be printed.
            The default value is ``None``.
        summarize (int, optional): Number of elements in the tensor to be
            printed. If its value is -1, then all elements in the tensor will
            be printed. The default value is 20.
        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:
        Operator: the created operation.

    Examples:
        .. code-block:: python

            >>> import paddle
            >>> from paddle.static.nn.control_flow import Assert

            >>> paddle.enable_static()
            >>> x = paddle.full([2, 3], 2.0, 'float32')
            >>> condition = paddle.max(x) < 1.0 # False
            >>> Assert(condition, [x], 10, "example_assert_layer")

            >>> exe = paddle.static.Executor()
            >>> try:
            ...     exe.run(paddle.static.default_main_program())
            ...     # Print x and throws ValueError
            ...     # Example printed message for x:
            ...     #
            ...     # Variable: fill_constant_0.tmp_0
            ...     #   - lod: {}
            ...     #   - place: CPUPlace()
            ...     #   - shape: [2, 3]
            ...     #   - layout: NCHW
            ...     #   - dtype: float
            ...     #   - data: [2 2 2 2 2 2]
            ... except ValueError as e:
            ...     print("Assert Exception Example")

    condboolzstatic.nn.control_flow.AssertdataN	summarizenameZassert_assert)CondZData)typeinputsattrs)r   r   listtupler*   intstrr   r   r'   r   locals	append_op)	r#   r%   r&   r'   Z
input_dataZ	assert_opZ
layer_namehelperop r5   i/home/app/PaddleOCR-VL-test/.venv_paddleocr/lib/python3.10/site-packages/paddle/static/nn/control_flow.pyAssert;   s(   1
r7   c                   @  (   e Zd ZdZdd Zdd Zdd ZdS )	
BlockGuardz
    BlockGuard class.

    BlockGuard class is used to create a sub-block in a program by
    using the Python `with` keyword.
    c                 C  s   t |ts	td|| _d S )NzBlockGuard takes a program)
isinstancer   	TypeErrormain_program)selfr<   r5   r5   r6   __init__   s   

zBlockGuard.__init__c                 C  s   | j   d S N)r<   Z_create_blockr=   r5   r5   r6   	__enter__   s   zBlockGuard.__enter__c                 C  s   | j   |d urdS dS NFT)r<   Z	_rollbackr=   exc_typeexc_valexc_tbr5   r5   r6   __exit__   s   
zBlockGuard.__exit__N)__name__
__module____qualname____doc__r>   rA   rG   r5   r5   r5   r6   r9      s
    r9   c                      s4   e Zd Z fddZ fddZ fddZ  ZS )
WhileGuardc                   s2   t |ts	tdt st |jj || _d S )NzWhileGuard takes a while op)	r:   Whiler;   r   superr>   r3   r<   while_opr=   rO   	__class__r5   r6   r>      s
   

zWhileGuard.__init__c                   s8   t  rt| jjg  | _| j S tj| j_	t
  S r?   )r   r   rO   cond_varbodyblockrA   rM   IN_WHILE_BLOCKstatusrN   r@   rQ   r5   r6   rA      s
   


zWhileGuard.__enter__c                   sT   t  rt| jjg | j|||S |d urdS tj| j_| j	  t
 |||S NF)r   r   rO   rS   rU   rG   rM   AFTER_WHILE_BLOCKrW   	_completerN   rC   rQ   r5   r6   rG      s   

zWhileGuard.__exit__)rH   rI   rJ   r>   rA   rG   __classcell__r5   r5   rQ   r6   rL      s    rL   c                   @  r8   )	Ifa  
    **If**

    If is an operator that bind two blocks (true_block and false_block) to a specific condition,
    According to the condition, the corresponding block will be executed.

    Args:
        cond (Value): A value whose data type is bool controlling which block is executed.

    Examples:
        .. code-block:: python

            >>> import paddle
            >>> from paddle.static.nn.control_flow import ConditionalBlock

            >>> label = paddle.rand([1])
            >>> limit = paddle.ones([1]) * 0.5
            >>> cond = paddle.less_than(x=label, y=limit)
            >>> if_op = If(cond)
            >>> with if_op.true_block():
            ...     pass
            >>> with if_op.false_block():
            ...     pass
    c                 C  s`   t |ts#t|ddgd tdd |jddkr#tdt|j dt|| _| j | _	d S )	Nr#   r$   zstatic.nn.Ifc                 S     | | S r?   r5   abr5   r5   r6   <lambda>       zIf.__init__.<locals>.<lambda>   4condition expected shape as [1], but given shape as .)
r:   r-   r   r   shaper;   r   if_opr#   rS   )r=   r#   r5   r5   r6   r>      s   

zIf.__init__c                 C  
   | j  S r?   )rg   
true_blockr@   r5   r5   r6   ri         
zIf.true_blockc                 C  rh   r?   )rg   false_blockr@   r5   r5   r6   rk      rj   zIf.false_blockN)rH   rI   rJ   rK   r>   ri   rk   r5   r5   r5   r6   r\      s
    
r\   c                   @  s:   e Zd ZdZdddZdd Zdd	 Zd
d Zdd ZdS )ConditionalBlocka  
    **ConditionalBlock**

    ConditionalBlock is an operator that bind a block to a specific condition,
    if the condition matches, the corresponding block will be executed.

    Args:
        inputs (Variable): bool conditions.
        is_scalar_condition (bool): whether the branch is controlled by a scalar.
        name(str): name of this ConditionalBlock.

    Examples:
        .. code-block:: python

            >>> import paddle
            >>> from paddle.static.nn.control_flow import ConditionalBlock

            >>> label = paddle.rand([1])
            >>> limit = paddle.ones([1]) * 0.5
            >>> cond = paddle.less_than(x=label, y=limit)
            >>> image = paddle.ones([1])

            >>> true_image = image[cond]
            >>> true_cond = ConditionalBlock([true_image])

            >>> with true_cond.block():
            ...     pass
            >>> with false_cond.block():
            ...     pass
    FNc                 C  sX   || _ t r|rt|dkrtdd S |D ]	}t|dtd q|| _td|d| _d S )Nrc   zSFor ConditionalBlock Api,  Only support one input while is_scalar_condition is Trueinputrl   conditional_blockr'   )	r+   r   lenr;   r   r   is_scalar_conditionr   r3   )r=   r+   rq   r'   Z
each_inputr5   r5   r6   r>     s   zConditionalBlock.__init__c                 C  s   t  r
t| j S t| S r?   )r   r\   r+   ri   ConditionalBlockGuardr@   r5   r5   r6   rU     s   zConditionalBlock.blockc           
        s   | j j }| j j|j t }t }t|||| j d\}} fdd|D }g }|D ]} |}|r:|| q, j	t
jjjd} jd| j|d||gd|| jdd	}	| |rg|  ||	 d S d S )
Nr3   c                      g | ]}  |qS r5   Z_var_recursive).0Z	each_nameparent_blockr5   r6   
<listcomp>#  s    
z-ConditionalBlock.complete.<locals>.<listcomp>r*   rn   )r)   Input)OutZScope)	sub_blockrq   r*   r+   outputsr,   )r3   r<   current_blockrU   
parent_idxsetget_inputs_outputs_in_block_find_var_recursiveappend
create_varr   VarDescVarTypeSTEP_SCOPESr2   r+   rq   "need_append_conditional_block_gradappend_conditional_block_grad)
r=   inside_blockintermediateparams
param_listZout_listinner_out_name	inner_var
step_scopeconditional_block_opr5   rw   r6   complete  sD   






zConditionalBlock.completec                 C  s   |j }|j}|dko||kS )N)backward_block_idxidx)r=   r   grad_sub_block_idxZinside_block_idxr5   r5   r6   r   B  s   z3ConditionalBlock.need_append_conditional_block_gradc                 C  s  |j }| jj|}t }t }|jD ]3}t|tsJ |jD ]}	|	|	D ]}
|
|vr1|
|
 q&q|jD ]}||D ]}|
| q=q6qg }|D ]}||}|rZ||j qKt|jt |jg\}}tj }tjjj}|j }||d  ||| |d| |ddd |D  t }| D ]#}|j| s|t krq|j |  |
| ||vrqq|!|j |"|j | D ]}||v rt#|| q| jj$  dS )a  
        Append op `conditional_block_grad` manually.
        When `optimizer.minimize/append_backward` is called in Paddle control flow,
        grad ops will be appended before appending op `conditional_block` so that
        op `conditional_block_grad` can't be appended when calling
        `optimizer.minimize/append_backward`. After appending op `conditional_block`,
        `conditional_block_grad` is appended manually.

        Args:
            parent_block (Block): The block that `conditional_block_op` belongs to.
            inside_block (Block): The sub block of `conditional_block_op`.
            conditional_block_op (Operator): The forward op conditional_block.
        r   r{   z
Input@GRADc                 S  s   g | ]}|d  qS )z@GRADr5   )rv   paramr5   r5   r6   ry         zBConditionalBlock.append_conditional_block_grad.<locals>.<listcomp>N)%r   r3   r<   rU   r   opsr:   r
   input_namesrm   addoutput_namesoutputr   r   r'   r   Zget_grad_op_descZdescZop_proto_and_checker_makerZkOpRoleAttrNameZOpRoleZBackwardr2   Z	copy_fromZ	_set_attrZ	set_inputZ
set_outputZoutput_arg_namesZhas_var_recursiveencodeZempty_var_namevarZinfer_var_typeZinfer_shaper	   Z_sync_with_cpp)r=   rx   r   r   r   Zgrad_sub_blockr   r   Zeach_opinamein_var_nameonameout_var_namer   Zinner_input_namer   Zgrad_op_descZop_grad_to_varZop_role_attr_nameZbackwardZnew_op_descZnew_varsZgrad_var_nameargr5   r5   r6   r   L  sj   









z.ConditionalBlock.append_conditional_block_gradFN)	rH   rI   rJ   rK   r>   rU   r   r   r   r5   r5   r5   r6   rl      s    
,
rl   c                      s8   e Zd ZdZ fddZ fddZ fddZ  ZS )rr   a?  
    ConditionalBlockGuard is derived from BlockGuard. It is dedicated for
    holding a ConditionalBlock, and helping users entering and exiting the
    ConditionalBlock via Python's 'with' keyword. However, ConditionalBlockGuard
    is generally an internal component of IfElse, users should not use it directly.
    c                   s(   t |dtd t |jj || _d S )NrU   rr   )r   rl   rN   r>   r3   r<   rU   )r=   rU   rQ   r5   r6   r>     s   
zConditionalBlockGuard.__init__c                   s
   t   S r?   )rN   rA   r@   rQ   r5   r6   rA     rj   zConditionalBlockGuard.__enter__c                   s   | j   t |||S r?   )rU   r   rN   rG   rC   rQ   r5   r6   rG     s   
zConditionalBlockGuard.__exit__)rH   rI   rJ   rK   r>   rA   rG   r[   r5   r5   rQ   r6   rr     s
    rr   c                 C  s   dd }| j D ]8}t|tsJ |jD ]}||D ]}||vr*|||s*|| qq|jD ]}||D ]}	||	 q6q/qt }
|j	
| j}|D ]$}||}d}| |r_| |}|sp|rp|jtjjjkrp|
| qL||
 }||fS )a#  
    Find inputs and outputs in current control flow block.
    :param current_block: Current control flow block.
    :param inner_inputs: Input var name of ops in current block.
    :param inner_outputs: Output var name of ops in current block.
    :return: inner_inputs, inner_outputs
    c                 S  s:   ddgi}| j |v r|| j  }|D ]	}||v r dS qdS )NZshuffle_batchZshuffle_batch_seedTFrz   )r4   var_nameZIGNORE_VAR_NAMESZ	var_namesr'   r5   r5   r6   is_ignore_vars  s   


z3get_inputs_outputs_in_block.<locals>.is_ignore_varsN)r   r:   r
   r   rm   r   r   r   r   r<   rU   r   r   Zhas_varr   r*   r   r   r   DENSE_TENSOR_ARRAY)r   Zinner_inputsinner_outputsr3   r   r4   r   r   r   r   Zremove_inner_inputsrx   parent_block_varZcurrent_block_varr5   r5   r6   r     sD   








r   c                   @  s6   e Zd ZdZdZdZdZdddZd	d
 Zdd Z	dS )rM   a  
    :api_attr: Static Graph

    while loop control flow. Repeat while body until cond is False.

    Note:
        A new OP :ref:`api_paddle_static_nn_while_loop` is highly recommended instead of ``While`` if the shape of parameter ``cond`` is [1].
        OP :ref:`api_paddle_static_nn_while_loop` is easier to use and is called with less code but does the same thing as ``While`` .

    Notice:
        Local variables created in ``While`` are similar to that created in while of C++, and cannot be referenced externally.
        As a result, they cannot be obtained through ``fetch_list`` of ``Executor``. If you would like to access the variable
        out of ``while`` , PaddlePaddle provides ``assign`` API to assign local variables to external. Please refer to example
        code 2 or refer to `issue#22724 <https://github.com/PaddlePaddle/Paddle/issues/22724>`_.

    Args:
        cond(Variable): A Tensor whose data type is bool controlling whether to continue looping.
        is_test(bool, optional): A flag indicating whether execution is in test phase. Default value is False.
        name(str, optional): The default value is None.  Normally there is no need for user to set this property.  For more information, please refer to :ref:`api_guide_Name` .

    Examples:
        .. code-block:: python
            :name: example-1

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

            >>> paddle.enable_static()

            >>> i = paddle.full(shape=[1], dtype='int64', fill_value=0)           # loop counter

            >>> loop_len = paddle.full(shape=[1],dtype='int64', fill_value=10)    # loop length

            >>> cond = paddle.less_than(x=i, y=loop_len)
            >>> while_op = paddle.static.nn.control_flow.While(cond=cond)
            >>> with while_op.block():
            ...     i = paddle.increment(x=i, value=1)
            ...     paddle.assign(paddle.less_than(x=i, y=loop_len), output=cond)

            >>> exe = paddle.static.Executor(paddle.CPUPlace())
            >>> exe.run(paddle.static.default_startup_program())

            >>> res = exe.run(paddle.static.default_main_program(), feed={}, fetch_list=[i])
            >>> print(res)
            [array([10], dtype=int64)]

        .. code-block:: python
            :name: example-2

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

            >>> paddle.enable_static()

            >>> i = paddle.full(shape=[1], dtype='int64', fill_value=0)
            >>> loop_len = paddle.full(shape=[1], dtype='int64', fill_value=10)
            >>> one = paddle.full(shape=[1], dtype='float32', fill_value=1)
            >>> data = paddle.static.data(name='data', shape=[1], dtype='float32')
            >>> sums = paddle.full(shape=[1], dtype='float32', fill_value=0)  # Define the variable to be obtained outside of While, which name should be different from the variable inside the While to be obtained

            >>> cond = paddle.less_than(x=i, y=loop_len)
            >>> while_op = paddle.static.nn.control_flow.While(cond=cond)
            >>> with while_op.block():
            ...     sums_tensor = paddle.add(x=data, y=data)
            ...     paddle.assign(sums_tensor, sums)  # Update the value of sums_tensor defined in While to the sums which defined outside of While through layers.assign
            ...     i = paddle.increment(x=i, value=1)
            ...     data = paddle.add(x=data, y=one)
            ...     paddle.assign(paddle.less_than(x=i, y=loop_len), output=cond)

            >>> feed_data = np.ones(1).astype('float32')
            >>> exe = paddle.static.Executor(paddle.CPUPlace())
            >>> exe.run(paddle.static.default_startup_program())
            >>> res = exe.run(paddle.static.default_main_program(), feed={'data': feed_data}, fetch_list=sums)
            >>> print(res[0]) # Because the data in While does not update the value outside the While, the value of sums is [2.] after the loop
            [2.]
    r   rc      FNc                 C  sl   || _ t|ddgd tdd |jddkr!tdt|j dt r&d S tj| _	t
d	|d
| _|| _d S )Nr#   r$   zstatic.nn.Whilec                 S  r]   r?   r5   r^   r5   r5   r6   ra   D  rb   z While.__init__.<locals>.<lambda>rc   rd   re   whilero   )rS   r   r   rf   r;   r-   r   rM   BEFORE_WHILE_BLOCKrW   r   r3   is_test)r=   r#   r   r'   r5   r5   r6   r>   A  s   
zWhile.__init__c                 C  s   t | S r?   )rL   r@   r5   r5   r6   rU   N  s   zWhile.blockc           	        s   | j j}| }|| j | jjh}t }t|||| j \}}g }|D ]} 	|}|r4|
| q&|dd |D O }|| jjh8 } jtjjjd} jd fdd|D | jgd||gd|| jd	d
 d S )Nc                 S  s   h | ]}|j qS r5   ro   )rv   xr5   r5   r6   	<setcomp>d      z"While._complete.<locals>.<setcomp>rz   r   c                   rt   r5   ru   )rv   Zx_namerw   r5   r6   ry   p  s    z#While._complete.<locals>.<listcomp>)X	Condition)r|   Z
StepScopes)r}   r   r~   )r3   r<   r   rU   r   rS   r'   r   r   r   r   r   r   r   r   r   r2   r   )	r=   r<   Zwhile_blockr   Zx_name_listZout_varsr   r   r   r5   rw   r6   rZ   Q  s>   







zWhile._completer   )
rH   rI   rJ   rK   r   rV   rY   r>   rU   rZ   r5   r5   r5   r6   rM     s    M
rM   c                 C  s  dd }t | ttjjfs#t |trt | trt| | dS | }dS | jtj	j
jkrK| jj}|| j}|rG|| jsIt| | dS dS dS t |trht | trh|| |rhtd| j d|j d tjj  t| | W d   dS 1 sw   Y  dS )zv
    Assign input to output, but skip the process of copying DenseTensorArray unless it's created in while_block.
    c                 S  sN   t | jt |jkrdS t| j|jD ]\}}||kr$d||fvr$ dS qdS )NTr   F)rp   rf   zip)Zx_varZy_varZx_dimZy_dimr5   r5   r6   has_shape_diff  s   z4assign_skip_lod_tensor_array.<locals>.has_shape_diffNz>In dy2static mode, we attempt to assign a variable with shape z into a variable with shapez, which is not always right.)r:   r   r   eagerZTensorsupport_ret_buildin_typepaddleassignr*   r   r   r   rU   programr   r   r   r'   warningswarnrf   baseZ	frameworkZ_stride_in_no_check_dy2st_diff)rm   r   r   r<   rx   r5   r5   r6   assign_skip_lod_tensor_array~  s:   "r   c                 C  sl   |j }tjg |jdd}| }||   ||  ||_ | 	| | 
 | }||_ ||fS )Nr   rf   dtypeZ
fill_value)stop_gradientr   fullr   get_defining_opZmove_beforeZas_operationset_typer*   Zadd_extra_inputrT   Zadd_arg)rO   valuer   
fake_valueZfake_value_op	block_argr5   r5   r6   #create_fake_value_for_undefined_var  s   
r   c                   @  sf   e Zd ZdddZe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S )LoopVarNc                 C  s   || _ || _|| _d| _d S rX   )curr_varnext_varr   _is_fake)r=   r   r   r   r5   r5   r6   r>     s   
zLoopVar.__init__c                 C     t | jtjjS r?   )r:   r   r   pirValuer@   r5   r5   r6   is_variable_curr_var     zLoopVar.is_variable_curr_varc                 C  s   t | jtjjjjS r?   )r:   r   r   jit	dy2staticutilsUndefinedVarr@   r5   r5   r6   is_undefined_curr_var  s   zLoopVar.is_undefined_curr_varc                 C  r   r?   )r:   r   r   r   r   r@   r5   r5   r6   is_variable_next_var  r   zLoopVar.is_variable_next_varc                 C     | j S r?   )r   r@   r5   r5   r6   is_fake  s   zLoopVar.is_fakec                 C  
   || _ d S r?   )r   )r=   r   r5   r5   r6   bind_block_arg  rj   zLoopVar.bind_block_argc                 C  r   r?   r   )r=   r   r5   r5   r6   bind_next_var  rj   zLoopVar.bind_next_varc                   sV    j sJ dd }t|tjjr||S t|r#t fdd|S t j| j	S )Nc                 S  s$   t | |\}}t|||}d|_|S )NT)r   r   r   )rO   r   r   r   loop_varr5   r5   r6   create_loop_var_like  s   z>LoopVar.infer_type_with_next_var.<locals>.create_loop_var_likec                   s     | S r?   )infer_type_with_next_varr   rP   r5   r6   ra         z2LoopVar.infer_type_with_next_var.<locals>.<lambda>)
r   r:   r   r   r   r   r   r   r   r   )r=   r   rO   r   r5   rP   r6   r     s   

z LoopVar.infer_type_with_next_varc                 C  s   d| j  d| j d| j dS )NzLoopVar(curr_var=z, next_var=z, block_arg=))r   r   r   r@   r5   r5   r6   __repr__  s   zLoopVar.__repr__NN)rH   rI   rJ   r>   propertyr   r   r   r   r   r   r   r   r5   r5   r5   r6   r     s    




r   Fc                   s  t | stdt |stdt|dttfd t|dkr#td| | }t|ddgd td	d
 |j	ddkrEtdt|j	 dt
 radd }tt|}dd t|D }t|dd |D  }t| t|ksvJ t|| D ]\}	}
|	|
  q}tdd
 |}|| }t|ttfs|g}fdd}zt||ddd
 d W n ty } ztd| d}~ww |||}ddlm   fdd}tj|| | tdd
 | }d|_t|| ttd d
 t|ttd!d
 t| }t|gd"d# |D  W d   n	1 sw   Y   }t|t|ks.J t||D ]\}	}||	_q3t|D ]}	|	jrX|	j ! rX|	j " }|# $| q@td$d
 |S t% r|& }|r|| }t|ttfs{|g}t|t|krtd%| | & }tt'|| |sl|S t(|||}t)|}|* W |rt+|}|| }n|| }t|ttfs|g}zt,||}t||dd& W n ty } ztd| d}~ww | | }tt'|| t-|| W d   |S 1 sw   Y  |S )'a	  
    :api_attr: Static Graph

    while_loop is one of the control flows. Repeats while_loop `body` until `cond` returns False.

    Notice:
        Local variables defined in ``body`` cannot be obtained through ``fetch_list`` of ``Executor`` , variables should
        be defined outside ``body`` and placed in ``loop_vars`` for looping, then these variables can be fetched by ``fetch_list`` .

    Args:
        cond(Callable): A callable returning a boolean tensor controlling whether to continue looping. And ``cond`` takes
            as many arguments as ``loop_vars`` .
        body(Callable): A callable returning a tuple or list of tensors or DenseTensorArrays of the same arity
            (length and structure) and types as ``loops_vars`` . And ``body`` takes as many arguments as ``loop_vars`` .
        loop_vars(list|tuple): A list or tuple of tensors or DenseTensorArrays that is passed to both ``cond`` and ``body`` .
        is_test(bool, optional): A flag indicating whether execution is in test phase. Default value is False.
        name(str, optional): Normally there is no need for users to set this property. For more information, please
            refer to :ref:`api_guide_Name`. Default is None.

    Returns:
        A list or tuple of Tensors or DenseTensorArrays which returned by ``body`` .

    Examples:
        .. code-block:: python

            >>> import paddle
            >>> paddle.enable_static()

            >>> def cond(i, ten):
            ...     return i < ten

            >>> def body(i, ten):
            ...     i = i + 1
            ...     return [i, ten]

            >>> main_program = paddle.static.default_main_program()
            >>> startup_program = paddle.static.default_startup_program()
            >>> with paddle.static.program_guard(main_program, startup_program):
            ...     i = paddle.full(shape=[1], fill_value=0, dtype='int64')     # loop counter
            ...     ten = paddle.full(shape=[1], fill_value=10, dtype='int64')  # loop length
            ...     i, ten = paddle.static.nn.while_loop(cond, body, [i, ten])

            ...     exe = paddle.static.Executor(paddle.CPUPlace())
            ...     res = exe.run(main_program, feed={}, fetch_list=[i])
            ...     print(res)
            [array([10], dtype=int64)]
    z%cond in while_loop should be callablez%body in while_loop should be callable	loop_varszstatic.nn.while_loopr   z+loop_vars in while_loop should not be emptyzvar of cond returnedr$   c                 S  r]   r?   r5   r^   r5   r5   r6   ra   0  rb   zwhile_loop.<locals>.<lambda>rc   zLthe shape of the variable returned by cond should be [1],but given shape as re   c                 S  sd   t  }|j}t jjt jjg}||vrd S | jsd S | jj| j	jkr0t
| j	| jj}|| _	d S d S r?   )r   _get_amp_attrs
_amp_levelAmpLevelO1O2r   r   r   r   r   cast)r   	amp_attrs	amp_levelapply_amp_level_listZcast_out_varr5   r5   r6   cast_value_in_amp8  s   

z%while_loop.<locals>.cast_value_in_ampc                 S  s   g | ]}|j r|qS r5   )r   )rv   r   r5   r5   r6   ry   J  s    zwhile_loop.<locals>.<listcomp>c                 S  s   g | ]}|j qS r5   )r   rv   r   r5   r5   r6   ry   P  r   c                 S  s   | j r| jS | jS r?   )r   r   r   r   r5   r5   r6   ra   Y     c                   s:    fdd g }t || D ]\}}| || q|S )Nc                   s<   t | r
t | |S | jr| |}|S | | | }|S r?   )r   r   r   r   r   )r   r   Znew_loop_var)!infer_loop_var_type_with_next_varrO   r5   r6   r   d  s   
zbwhile_loop.<locals>.infer_loop_vars_type_with_next_vars.<locals>.infer_loop_var_type_with_next_var)r   r   )r   	next_varsnew_loop_varsr   r   )rO   )r   r6   #infer_loop_vars_type_with_next_varsc  s   z7while_loop.<locals>.infer_loop_vars_type_with_next_varsFc                 S  s0   t | tot | jtjjjjpt | tjjjjS r?   )r:   r   r   r   r   r   r   r   r   r5   r5   r6   ra     s   
)check_typesZskip_ifzUbody in while_loop should return the same arity (length and structure) as loop_vars: Nto_static_variablec                   sD   | j sd S t| jtjjst| jtttfst	d | j| _d S )NzThe loop var in the while op is variable, but the corresponding yielded var is not variable, and it is not a constant of type bool, int, or float.)
r   r:   r   r   r   r   r$   floatr/   
ValueError)r   r   r5   r6   check_next_var  s   
z"while_loop.<locals>.check_next_varc                 S  r   r?   r   r   r5   r5   r6   ra         Tc                 S  s   | j o| j S r?   r   r   r   r5   r5   r6   ra         c                 S  s   | j o| jS r?   r  r   r5   r5   r6   ra     r   c                 s  s    | ]}|j V  qd S r?   r   r   r5   r5   r6   	<genexpr>  s    zwhile_loop.<locals>.<genexpr>c                 S  r   r?   r   r   r5   r5   r6   ra     r  z]body in while_loop should return the same arity (length and structure) and types as loop_varsr   ).callabler;   r   r-   r.   rp   r   r   r   rf   r   r   r   r   r   rT   argsr   r   Z_cloner:   r   &paddle.jit.dy2static.convert_operatorsr   r   r   r   filterr   Zoptimize_updater   r   r   Z	use_emptyr   Zget_parent_blockZ	remove_opr   itemr   rM   r   rU   r   _deal_with_undefined_varr   )r#   rT   r   r   r'   Zpre_condr   Zvariable_loop_varsZ	cur_blockr   r   r  r   r   er   Z	next_condZoptimized_resultsresultZfake_value_def_opZnow_condoutput_varsZwhile_loop_blockZhas_mutable_vars_in_loopr   r5   )r   rO   r6   
while_loop  s  0




h





r  c                   s|   ddl m}m   fdd}t| t|krtdg }t| |D ]\}}t||s.|du r6||| q!|| q!|S )a  Deal with undefined var cases, We create undefined variable based on the results of body().
    In Dy2Static, we use undefined var to represent the var created in control flow. This function
    expand the loop_vars and replace original loop_vars.
    1. UndefinedVar = Variable      # create a variable
    2. UndefinedVar = None          # create a undefined var with RETURN_NO_VALUE_MAGIC_NUM
    3. UndefinedVar = List(int)     # create a list of variable
    4. UndefinedVar = value         # create a variable
    r   )r   create_undefined_variablec                   s@   t | tgtR s| d u r  S t| r	 t fdd| S d S )Nc                   s     S r?   r5   r   r  r5   r6   ra     r  zC_deal_with_undefined_var.<locals>.create_var_like.<locals>.<lambda>)r:   r   r   r   r   )o_varr  r5   r6   create_var_like  s   z1_deal_with_undefined_var.<locals>.create_var_likez+The length of loop_vars should be the same.N)paddle.jit.dy2static.utilsr   r  rp   r   r   r:   r   )r  r   r   r  resultsr  Zl_varr5   r  r6   r    s   	r  c              
   C  s&   |  d| d| d| d| d
}|S )Nz of 'z' in z	 must be z, but received: re   r5   )whatZarg_nameZop_nameZright_valueZerror_valueerror_messager5   r5   r6   _error_message  s   r  c                 C  sF   dd }|| |\} }|}t | D ]\}}tt|||d}q|}| S )a  
    :api_attr: Static Graph

    This operator works like an if-elif-elif-else chain.

    Args:
        pred_fn_pairs(list|tuple): A list or tuple of (pred, fn) pairs. ``pred`` is a boolean Tensor whose numel should be 1 (shape [] or shape [1]), ``fn`` is a callable. All callables return the same structure of Tensors.
        default(callable, optional): Callable that returns a structure of Tensors.
        name(str, optional): The default value is None. Normally there is no need for user to set this property. For more information, please refer to :ref:`api_guide_Name`.

    Returns:
        Tensor|list(Tensor): Tensors returned by the callable from the first pair whose pred is True,
        or Tensors returned by ``default`` if no pred in ``pred_fn_pairs`` is True and ``default`` is not None,
        or Tensors returned by the last callable in ``pred_fn_pairs``  if no pred in ``pred_fn_pairs`` is True and ``default`` is None.

    Raises:
        TypeError: If the type of ``pred_fn_pairs`` is not list or tuple.
        TypeError: If the type of elements in ``pred_fn_pairs`` is not tuple.
        TypeError: If the size of tuples in ``pred_fn_pairs`` is not 2.
        TypeError: If the first element of 2-tuple in ``pred_fn_pairs`` is not a Tensor.
        TypeError: If the second element of 2-tuple in ``pred_fn_pairs`` is not callable.
        TypeError: If ``default`` is not None but it is not callable.

    Examples:
        .. code-block:: python

            >>> import paddle
            >>> paddle.enable_static()

            >>> def fn_1():
            ...     return paddle.full(shape=[1, 2], dtype='float32', fill_value=1)

            >>> def fn_2():
            ...     return paddle.full(shape=[2, 2], dtype='int32', fill_value=2)

            >>> def fn_3():
            ...     return paddle.full(shape=[3], dtype='int32', fill_value=3)

            >>> main_program = paddle.static.default_startup_program()
            >>> startup_program = paddle.static.default_main_program()

            >>> with paddle.static.program_guard(main_program, startup_program):
            ...     x = paddle.full(shape=[1], dtype='float32', fill_value=0.3)
            ...     y = paddle.full(shape=[1], dtype='float32', fill_value=0.1)
            ...     z = paddle.full(shape=[1], dtype='float32', fill_value=0.2)

            ...     pred_1 = paddle.less_than(z, x)  # true: 0.2 < 0.3
            ...     pred_2 = paddle.less_than(x, y)  # false: 0.3 < 0.1
            ...     pred_3 = paddle.equal(x, y)      # false: 0.3 == 0.1

            ...     # Call fn_1 because pred_1 is True
            ...     out_1 = paddle.static.nn.case(
            ...         pred_fn_pairs=[(pred_1, fn_1), (pred_2, fn_2)], default=fn_3)

            ...     # Argument default is None and no pred in pred_fn_pairs is True. fn_3 will be called.
            ...     # because fn_3 is the last callable in pred_fn_pairs.
            ...     out_2 = paddle.static.nn.case(pred_fn_pairs=[(pred_2, fn_2), (pred_3, fn_3)])

            ...     exe = paddle.static.Executor(paddle.CPUPlace())
            ...     res_1, res_2 = exe.run(main_program, fetch_list=[out_1, out_2])
            ...     print(res_1, res_2)
            [[1. 1.]] [3 3 3]
    c              
   S  s   t | dttfd | D ]=}t|tsttdddtt|t|dkr4ttddddtt|d |\}}t	|dd	gd
 t
|sHtdq|du rct| d }| | d }| d| } | |fS t
|sktd| |fS )zg
        Check arguments pred_fn_pairs and default. Return canonical pre_fn_pairs and default.
        pred_fn_pairscaseThe elements' typer   The tuple's size2-tuplepredr$   zpaddle.static.nn.casez5The fn of pred_fn_pairs in Op(case) must be callable.Nrc   )The default in Op(case) must be callable.)r   r-   r.   r:   r;   r  r*   rp   r0   r   r  )r  defaultZpred_fnr  fnZdefault_indexr5   r5   r6   _case_check_argsg  sN   
		
zcase.<locals>._case_check_argsr  true_fnfalse_fn)reversedr   r#   )r  r!  r'   r#  r&  r  r%  final_fnr5   r5   r6   r  &  s   A/r  c                 C  sT   t di t }dd }|| ||\}}|}|D ]\}}	tt||	|d}q|}
|
 S )a)  
    :api_attr: Static Graph

    This operator is like a C++ switch/case statement.

    Args:
        branch_index(Tensor): A Tensor whose numel should be 1 (shape [] or shape [1]) to specify which branch to execute. The data type is ``int32``, ``int64`` or ``uint8``.
        branch_fns(dict|list|tuple): If it's a list or tuple, the elements in it could be pairs of (int, callable) or simple callables whose actual index will be used as the index of callable. If it's a dict, its key is a python integer and the value is a callable. All callables return the same structure of Tensors.
        default(callable, optional): Callable that returns a structure of Tensors.
        name(str, optional): The default value is None. Normally there is no need for user to set this property. For more information, please refer to :ref:`api_guide_Name`.

    Returns:
        Tensor|list(Tensor): Tensors returned by the callable specified by ``branch_index`` in ``branch_fns``,
        or Tensors returned by ``default`` if ``default`` is not None and no index matches in ``branch_fns``,
        or Tensors returned by the callable with the max index in ``branch_fns`` if ``default`` is None and no index matches in ``branch_fns``.

    Raises:
        TypeError: If the type of ``branch_index`` is not Tensor.
        TypeError: If the data type of ``branch_index`` is not ``int32``, ``int64`` or ``uint8``.
        TypeError: If the type of ``branch_fns`` is not dict, list or tuple.
        TypeError: If the elements of ``branch_fns`` is not 2-tuple.
        TypeError: If the first element of 2-tuple in ``branch_fns`` is not integer.
        ValueError: If the first element of 2-tuple in ``branch_fns`` is not unique.
        TypeError: If the second element of 2-tuple in ``branch_fns`` is not callable.
        TypeError: If ``default`` is not None but it is not callable.

    Examples:
        .. code-block:: python

            >>> import paddle
            >>> paddle.enable_static()

            >>> def fn_1():
            ...    return paddle.full(shape=[1, 2], dtype='float32', fill_value=1)

            >>> def fn_2():
            ...    return paddle.full(shape=[2, 2], dtype='int32', fill_value=2)

            >>> def fn_3():
            ...    return paddle.full(shape=[3], dtype='int32', fill_value=3)

            >>> startup_program = paddle.static.default_startup_program()
            >>> main_program = paddle.static.default_main_program()
            >>> with paddle.static.program_guard(main_program, startup_program):
            ...    index_1 = paddle.full(shape=[1], dtype='int32', fill_value=1)
            ...    index_2 = paddle.full(shape=[1], dtype='int32', fill_value=2)
            ...
            ...    out_1 = paddle.static.nn.switch_case(
            ...        branch_index=index_1,
            ...        branch_fns={1: fn_1, 2: fn_2},
            ...        default=fn_3)
            ...
            ...    out_2 = paddle.static.nn.switch_case(
            ...        branch_index=index_2,
            ...        branch_fns=[(1, fn_1), (2, fn_2)],
            ...        default=fn_3)
            ...
            ...    # Argument default is None and no index matches. fn_3 will be called because of the max index 7.
            ...    out_3 = paddle.static.nn.switch_case(
            ...        branch_index=index_2,
            ...        branch_fns=[(0, fn_1), (4, fn_2), (7, fn_3)])
            ...
            ...    exe = paddle.static.Executor(paddle.CPUPlace())
            ...    res_1, res_2, res_3 = exe.run(main_program, fetch_list=[out_1, out_2, out_3])
            ...    # Variable: fill_constant_1.tmp_0
            ...    #   - message: The content of input layer:
            ...    #   - lod: {}
            ...    #   - place: Place(cpu)
            ...    #   - shape: [2, 3]
            ...    #   - layout: NCHW
            ...    #   - dtype: int64
            ...    #   - data: [3 3 3 3 3 3]

            >>> print(res_1)
            [[1. 1.]]

            >>> print(res_2)
            [[2 2]
             [2 2]]

            >>> print(res_3)
            [3 3 3]
    switch_casec              
   S  s  t | dg dd t| jdkrt| d} t|dtttfd t	|tr)|
 n|}tdd |D r:tt|n|}g }|D ]b}t	|tsSttd	ddtt|t|d
krittddddtt|d |\}}t	|ts~ttdddtt|||v rtd| d|| t|sttd| dddt|q@|d u rt|d d }t|d d }nt|stdg }|D ]\}}tjdgd|d}	t| |	}
||
|f q||fS )Nbranch_index)Zuint8int32int64zstatic.nn.switch_caser,  
branch_fnsr)  c                 s  s    | ]}t |V  qd S r?   )r  )rv   r"  r5   r5   r6   r  
      z3switch_case.<locals>._check_args.<locals>.<genexpr>r  r   r  r  r  zThe key's typez-The key in 'branch_fns' must be unique, but 'z' appears more than once.zThe type of function for key r  r   rc   r   r   )r   r   r   r   r   r   r-   r.   dictr:   itemsall	enumerater;   r  r*   rp   r0   r/   r   r   r  sortedr   equal)r*  r-  r!  Zkeys_of_fnsZindex_fn_pairkeyr"  r  indexZ	new_indexr  r5   r5   r6   _check_args  s   






z switch_case.<locals>._check_argsr$  N)r)  )r   r1   r   r#   )r*  r-  r!  r'   r3   r7  r  r&  r  r%  r(  r5   r5   r6   r)    s   T\r)  c                 G  sj   dd t t|d D }t| D ]!\}}t|D ]\}}||r*|| |  nq|d | q|S )Nc                 S     g | ]}g qS r5   r5   rv   _r5   r5   r6   ry   ]      z0get_indices_by_discriminator.<locals>.<listcomp>rc   r   rangerp   r2  r   )	containerZdiscriminatorsbucketsr   r
  ir#   r5   r5   r6   get_indices_by_discriminator\  s   rA  c                 G  sX   dd t t|D }t| D ]\}}t|D ]\}}||v r(|| |  nqq|S )Nc                 S  r8  r5   r5   r9  r5   r5   r6   ry   i  r;  z%select_by_indices.<locals>.<listcomp>r<  )r>  Zindex_groupsr?  r   r
  r@  indicesr5   r5   r6   select_by_indicesh  s   rC  c                  G  sd   t dd | d}dd t|D }| D ]\}}t|t|ks!J t||D ]\}}|||< q&q|S )Nc                 S  s   | t |d  S Nr   )rp   )accpairr5   r5   r6   ra   t  r;  z7create_container_by_items_and_indices.<locals>.<lambda>r   c                 S     g | ]}d qS r?   r5   r9  r5   r5   r6   ry   v  r;  z9create_container_by_items_and_indices.<locals>.<listcomp>)r   r=  rp   r   )Zitems_indices_pairstotal_lengthr>  Zpartial_containerrB  r   r
  r5   r5   r6   %create_container_by_items_and_indicesr  s   

rI  c                   s    fdd}|S )Nc                    s8      | i |W  d    S 1 sw   Y  d S r?   r5   )r  kwargsrU   r"  r5   r6   new_fn  s   $zrun_with_block.<locals>.new_fnr5   )r"  rU   rL  r5   rK  r6   run_with_block~  s   rM  c                   @  sx   e Zd Zdd Zedd Zedd Zedd Zed	d
 Z	edd Z
dd Zdd Zedd Zedd ZdS )OutputSelectorc                 C  sJ   || _ || _|| _|| _t|| _t|| jksJ t|| jks#J d S r?   )rg   true_outputfalse_outputnamesrp   Z
num_output)r=   rg   flattened_true_outputflattened_false_outputrQ  r5   r5   r6   r>     s   
zOutputSelector.__init__c                 C  s   g }g }t | j| j| jD ]3\}}}t|| jjf|| jjfg|\}}t	|| jjf|| jjfg|\}}|
| |
| q||fS r?   )r   rO  rP  rQ  rN  constant_to_variable_promotionrg   ri   rk   precision_promotionr   )r=   unified_true_outputunified_false_outputtrue_out	false_outr'   r5   r5   r6   unified_output  s.   




zOutputSelector.unified_outputc                 C  
   | j d S rD  rZ  r@   r5   r5   r6   rV       
z"OutputSelector.unified_true_outputc                 C  r[  )Nrc   r\  r@   r5   r5   r6   rW    r]  z#OutputSelector.unified_false_outputc                 C  s<   t | jdd \}}t | jdd \}}||ksJ d|S )Nc                 S     t | tjjS r?   r:   r   r   r   r   r5   r5   r6   ra     r  z1OutputSelector.variable_indices.<locals>.<lambda>c                 S  r^  r?   r_  r   r5   r5   r6   ra     r  z?true_variable_indices and false_variable_indices should be same)rA  rV  rW  )r=   Ztrue_variable_indicesr:  Zfalse_variable_indicesr5   r5   r6   variable_indices  s   
zOutputSelector.variable_indicesc                   s    fddt t jD S )Nc                   s   g | ]	}| j vr|qS r5   )r`  rv   r@  r@   r5   r6   ry     s
    
z3OutputSelector.constant_indices.<locals>.<listcomp>)r=  rp   rO  r@   r5   r@   r6   constant_indices  s   
zOutputSelector.constant_indicesc                 C  s(   t | j| j\}t | j| j\}||fS r?   )rC  rV  r`  rW  )r=   variable_true_outputvariable_false_outputr5   r5   r6   get_variable_outputs  s   z#OutputSelector.get_variable_outputsc                 C  s*   t | j| j\}t|| jf|| jf}|S r?   )rC  rV  rb  rI  r`  )r=   variable_resultsZconstant_outputrestored_outputr5   r5   r6   #restore_outputs_by_variable_results  s   z2OutputSelector.restore_outputs_by_variable_resultsc              	     s  ddl m ddlm  tttft|  \}dd }dd }dd	 td
d D r.S tdd D r9S tfddD rn|rn|rNS t	
dtd  dd  dd  d fdd| D S tdd D rtfddD rt	
ddtd  d dtd  d  fdd| D S t fddD rt	
d d dd    fd!d| D S td" d#d  d$d  d)%Nr   r   r   c                   .   t  dkrdS t fdd dd  D S )Nrc   Tc                 3  s    | ]	}| d  kV  qdS r   Nr5   rv   outoutsr5   r6   r    s    z\OutputSelector.constant_to_variable_promotion.<locals>.all_has_same_value.<locals>.<genexpr>rp   r1  rn  r5   rn  r6   all_has_same_value     zIOutputSelector.constant_to_variable_promotion.<locals>.all_has_same_valuec                   rj  )Nrc   Tc                 3  s$    | ]}t |t  d  u V  qdS rk  rz   rl  rn  r5   r6   r    s   " z[OutputSelector.constant_to_variable_promotion.<locals>.all_has_same_type.<locals>.<genexpr>rp  rn  r5   rn  r6   all_has_same_type  rr  zHOutputSelector.constant_to_variable_promotion.<locals>.all_has_same_typec                 S  s&   | D ]}t |tjjr|j  S qd S r?   )r:   r   r   r   r   )ro  rm  r5   r5   r6   get_first_value_dtype  s
   
zLOutputSelector.constant_to_variable_promotion.<locals>.get_first_value_dtypec                 s      | ]
}t |tjjV  qd S r?   r_  rl  r5   r5   r6   r        z@OutputSelector.constant_to_variable_promotion.<locals>.<genexpr>c                 s  s    | ]}|d u V  qd S r?   r5   rl  r5   r5   r6   r    r.  c                 3      | ]}t | V  qd S r?   r:   rl  promotion_builtin_typesr5   r6   r    s    

z>Return results from different branches in cond has same type: z*, but has different value: true value is 'z' and false value is 'rc   z/', so we will promote the constant to variable.c                   s"   g | ]\}}t  ||d dqS )Nr   rM  rv   rm  rU   r   r5   r6   ry     s    zAOutputSelector.constant_to_variable_promotion.<locals>.<listcomp>c                 s  ru  r?   r_  rl  r5   r5   r6   r    rv  c                 3  s&    | ]}t |tjjg R V  qd S r?   r_  rl  ry  r5   r6   r    s
    
zBReturn results from different branches in cond are not same type: z#false_var returned by false_fn is 'z' and true_var of true_fn is 'c                   s&   g | ]\}}t || d qS )r{  r|  r}  )rt  ro  r   r5   r6   ry     s    c                 3  rw  r?   rx  rl  ri  r5   r6   r  $  s    z1Return results has maybe unbound local variable `z`, please ensure do not use ``zafter cond.c                   s   g | ]} qS r5   r5   r9  )r   r'   r5   r6   ry   )  r   zMUnsupported return type of true_fn and false_fn in cond: false_var returned `z` by false_fn is `z` and true_var of true_fn is `)r  r   r  r   r$   r/   r   r   r1  r   r   r*   anyr;   )out_with_blocksr'   r:  rq  rs  r5   )r   rt  r'   ro  rz  r   r6   rT    sr   

z-OutputSelector.constant_to_variable_promotionc                   s   t |  \ }t }|j}tjjtjjg}||vr S dd } fdd}tdd  D rJ| sJt	d d j
 d	 d
 j
 d ||   S  S )Nc                   rj  )Nrc   Tc                 3  s     | ]}|j  d  j kV  qdS rk  r{  rl  rn  r5   r6   r  A  s    zQOutputSelector.precision_promotion.<locals>.all_has_same_dtype.<locals>.<genexpr>rp  rn  r5   rn  r6   all_has_same_dtype>  rr  z>OutputSelector.precision_promotion.<locals>.all_has_same_dtypec                   sT    fdd}g }|| }| D ]\}}||j kr"ttj||t| }|| q|S )Nc                   sN   t  dkr d jS tdd | D r tdd | D r tjS | d d jS )Nrc   r   c                 s       | ]\}}|j tjkV  qd S r?   )r   r   float16rv   rm  r:  r5   r5   r6   r  H      
zpOutputSelector.precision_promotion.<locals>.promote_precision.<locals>.get_expected_precision.<locals>.<genexpr>c                 s  r  r?   )r   r   float32r  r5   r5   r6   r  J  r  )rp   r   r  r   r  )r  rn  r5   r6   get_expected_precisionD  s   
z]OutputSelector.precision_promotion.<locals>.promote_precision.<locals>.get_expected_precision)r   rM  r   r   r   r   )r  r  Znew_outsZexpected_dtyperm  rU   rn  r5   r6   promote_precisionC  s   

z=OutputSelector.precision_promotion.<locals>.promote_precisionc                 s  ru  r?   r_  rl  r5   r5   r6   r  [  s    
z5OutputSelector.precision_promotion.<locals>.<genexpr>zYReturn results from different branches in cond has different dtype: true value dtype is 'r   z' and false value dtype is 'rc   z<', so we will promote the lower precision to the higher one.)r   r   r   r   r   r   r   r1  r   r   r   )r  r'   r:  r   r   r   r  r  r5   rn  r6   rU  0  s*   z"OutputSelector.precision_promotionN)rH   rI   rJ   r>   r   rZ  r   rV  rW  r`  rb  re  rh  staticmethodrT  rU  r5   r5   r5   r6   rN    s$    





KrN  c                   s  t  rIt| tsJ d| jdksJ d|  } | r2|dur0t|s-tdt|j | S dS |durGt|sDtdt|j | S dS d}d}t	| ddgd	 t
|d
ttdfd	 t rt| }|durt|sytdt|j |  | }W d   n1 sw   Y  |durt|stdt|j |  | }W d   n1 sw   Y  ntd%i t   fdd}|durt|stdt|j t| gdd}	|	  | }
|
durt||
}W d   n1 sw   Y  |durBt|stdt|j tt| gdd}|  | }|dur2t||}W d   n	1 s=w   Y  |du rN|du rNdS |du rWtd|du r`td|du rqd}dgtt| }n	 d}t|||\}}tt|tt|krtdtt| dtt| dtt|t|t|D ]&\}}}z
t||dd W q ty } z
td| d| d}~ww dd }|t|t|t| |rt st||\}}t r\t|t|}}dd tt|t|D }t ||||d}|! \}}|  t"| W d   n	1 s*w   Y  |  t"| W d   n	1 sCw   Y  |#  t|$ }|%|}t&||S tj'| dd fd!dfd"d#}t(t)|t|t|t|}td$d |}t&|t|}|S )&a  
    This API returns ``true_fn()`` if the predicate ``pred`` is true else
    ``false_fn()`` . Users could also set ``true_fn`` or ``false_fn`` to
    ``None`` if do nothing and this API will treat the callable simply returns
    ``None`` in this case.

    ``true_fn`` and ``false_fn`` should return same nest structure of tensors
    or both return ``None`` if user doesn't like to return anything. A nest
    structure of tensors in PaddlePaddle is tensor(s), or tuple of tensors, or
    list of tensors.

    Note:
        1. The tuples or lists returned by ``true_fn`` and ``false_fn`` must have
        the same shape because of dataflow model of PaddlePaddle while the
        tensors in the tuples or the lists can have different shapes.

        2. This API could be used under both static graph mode or dygraph mode. If it
        is in dygraph mode, the API only runs one branch based on condition.

        3. If it is in static graph mode, any tensors or operations created outside
        or inside of ``true_fn`` and ``false_fn`` will be in net building
        regardless of which branch is selected at runtime. This has frequently
        surprised users who expected a lazy semantics.

        Examples:
            .. code-block:: python
                :name: code-example-1

                >>> import paddle

                >>> a = paddle.zeros((1, 1))
                >>> b = paddle.zeros((1, 1))
                >>> c = a * b
                >>> out = paddle.static.nn.cond(a < b, lambda: a + c, lambda: b * b)

        No matter whether ``a < b`` , ``c = a * b`` will be in net building and
        run. ``a + c`` and ``b * b`` will be in net building, but only one
        branch will be executed during runtime.

    Args:
        pred(Tensor): A boolean tensor whose numel should be 1 (shape []
            or shape [1]). The boolean value determines whether to return the
            result of ``true_fn`` or ``false_fn`` .
        true_fn(callable, optional): A callable to be performed if ``pred`` is
            true. The default value is ``None`` .
        false_fn(callable, optional): A callable to be performed if ``pred`` is
            false. The default value is ``None`` .
        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` .
        return_names(sequence of string, optional): The default value is ``None`` .
             Normally users don't have to set this parameters.  A sequence of strings
             to represents the name of returned vars.  The structure of sequence must
             be same with return values of true_fn and false_fn.

    Returns:
        Tensor|list(Tensor)|tuple(Tensor): returns ``true_fn()`` if the
        predicate ``pred`` is true else ``false_fn()`` .

    Examples:
        .. code-block:: python
            :name: code-example-2

            >>> import paddle

            >>> # pseudocode:
            >>> # if 0.1 < 0.23:
            >>> #     return 1, True
            >>> # else:
            >>> #     return 3, 2

            >>> def true_func():
            ...     return paddle.full(shape=[1, 2],
            ...                        dtype='int32',
            ...                        fill_value=1
            ...         ), paddle.full(shape=[2, 3],
            ...                        dtype='bool',
            ...                        fill_value=True
            ...         )


            >>> def false_func():
            ...     return paddle.full(shape=[3, 4],
            ...                        dtype='float32',
            ...                        fill_value=3
            ...         ), paddle.full(shape=[4, 5],
            ...                        dtype='int64',
            ...                        fill_value=2
            ...         )


            >>> x = paddle.full(shape=[1], dtype='float32', fill_value=0.1)
            >>> y = paddle.full(shape=[1], dtype='float32', fill_value=0.23)
            >>> pred = paddle.less_than(x=x, y=y, name=None)
            >>> a, b = paddle.static.nn.cond(pred, true_func, false_func)

            >>> print(a)
            Tensor(shape=[1, 2], dtype=int32, place=Place(cpu), stop_gradient=True,
                   [[1, 1]])
            >>> print(b)
            Tensor(shape=[2, 3], dtype=bool, place=Place(cpu), stop_gradient=True,
                   [[True, True, True],
                    [True, True, True]])
    z!The pred in cond must be Variablerc   z#condition input's numel should be 1Nz3The true_fn in cond must be callable, but received z4The false_fn in cond must be callable, but received r  r$   zpaddle.static.nn.condr'   r#   c                   s
   t |  S r?   )copy_var_to_parent_blockr   rs   r5   r6   ra     s   
 zcond.<locals>.<lambda>Trq   zpIncompatible return values of true_fn and false_fn in cond: true_fn returns None while false_fn returns non-NonezpIncompatible return values of true_fn and false_fn in cond: true_fn returns non-None while false_fn returns NoneFzno nameztrue fn returns z vars, but false fn returns z vars, which is not equalsr  zIncompatible return values of `z#` in true_fn and false_fn in cond: c                 S  s   t | ||D ]O\}}}t|}t|}tt|D ];}|| d u r'|| d us3|| d u rT|| d urTtd| dt||  d||  dt||  d||  d qqd S )NIn cond : Var '8' or part of it is set differently in ifelse branches, <, > in true branch and <I> in false branch. Set var to 'None' in ifelse block might lead to error.)r   r   r=  rp   r   r   r*   )Zseq_trueZ	seq_falseZ	seq_namesZf_trueZf_falsef_namer   r5   r5   r6   check_ret_noneI  s,   

zcond.<locals>.check_ret_nonec                 S  s"   g | ]\}}t |D ]}|q
qS r5   )r   )rv   Zseq_outr'   r:  r5   r5   r6   ry   j  s    zcond.<locals>.<listcomp>)rQ  r+  r{  c                   s   t ||g | S r?   )select_input_with_buildin_type)r'   	false_vartrue_var)maskr5   r6   ra     s    
c                   s   t t || |S r?   )r   r   )Z
false_varsZ	true_varsr'   )
merge_funcr5   r6   merge_every_var_list  r   z"cond.<locals>.merge_every_var_listc                 S  s   |  S r?   r5   )r"  r5   r5   r6   ra     r  )r#   )*r   r:   r   sizer
  r  r;   r*   rH   r   r   r0   r   r   ri   rk   r   r1   rl   rU   r   r   logical_notr   rp   _to_sequence_except_dictexpand_undefined_varr   r   r   change_none_to_undefinedvarr   rN  re  r   Zupdate_outputr  rh  r    r   r-   map)r  r%  r&  r'   Zreturn_namesrO  rP  rg   Zcopy_to_parent_funcZtrue_cond_blockZorigin_true_outputZfalse_cond_blockZorigin_false_outputZis_dy2staticrX  rY  Zreturn_namer  r  rR  rS  Zflattened_return_namesZoutput_selectorrc  rd  rf  rg  r  Zmerged_output_fnsZmerged_outputr5   )r3   r  r  r6   r#   h  sP  i










 	





r#   c                 C  s   t | ts| S |j}| j}|dksJ d||}| jtjj	j
kr.|| jr.| }|S |j| j| j| jd}t| | |S )Nr   zOGot wrong parent block index when assigning var to parent scope in control_flowr   rf   r*   )r:   r   r<   r   r   rU   r*   r   r   r   r   r   r'   r   r   rf   r   r   )r   Zlayer_helperprogr   rx   r   r5   r5   r6   r    s$   




r  c                 C  s^   t d
i t }t| dtd t|ddgd t|dttfd |jd| |dd|id |S )a  
    **select_output**
    This API takes in one input and multiple outputs and an integer mask. It
    selects the output specified by the mask and copy the input to selected
    output. It is useful in control flow.

    Args:
        input(Variable): The input variable
        outputs(tuple|list): The output variables
        mask(Variable): A tensor containing 1 integer number selecting which
            output to be copied with input

    Returns:
        Variable: The outputs variables
    select_outputrm   r  r+  r   r   Maskr|   r*   r+   r   N)r  )r   r1   r   r   r   r-   r.   r2   )rm   r   r  r3   r5   r5   r6   r    s   r  c                 C  sB   t | t |krtd|  d|  |S dd t| |D }|S )a)  
    This function infer the output shape by following algorithm:
    1. if the dims is different, raise a error.
    2. compare axis one by one:
        if a == b: we set axis to a
        if a != b: we set axis to -1
    for compatibility, non declarative mode, we just return second_shape.
    zDthe input shapes of select_input should have the same rank, but get r  c                 S  s    g | ]\}}||kr|nd qS )r   r5   )rv   r_   r`   r5   r5   r6   ry     s     z-_select_input_infer_shape.<locals>.<listcomp>)rp   r   r   r   )Zfirst_shapeZsecond_shapeZ	out_shaper5   r5   r6   _select_input_infer_shape  s   	r  c                 C  s   t di t }t| dttfd t|ddgd t| d j| d j}| d j}| d j	}|j
|||d}|jd| |dd	|id
 |S )ay  
    **select_input**

    This API takes in multiple inputs and uses an integer mask to select one
    input to output. It is useful in control flow.

    Args:
        inputs(tuple|list): The input variables
        mask(Tensor): A tensor containing 1 integer number selecting which
            input to output

    Returns:
        Variable: The selected input variable
    select_inputr+   r  r+  r   rc   r  r  r|   r  N)r  )r   r1   r   r-   r.   r   r  rf   r   r*   Zcreate_variabler2   )r+   r  r3   Zoutput_shapeZoutput_dtypeoutput_typerm  r5   r5   r6   r    s   

r  c                   sv  ddl m} ddlm} \ }fdd}t |r't||r'	 dd S t tr3t|tr3|S t trSt t|rS |krI fddS | ||g|S t tr]t|tsgt|trt tr| ||gt	d	t  d
t| d |S t |rt|tgtR st||rt tgtR r||| }  |g|S t
dt  d
t| d)Nr   r   ri  c               
     s:   zt  W S  ty }  z
td d|  d } ~ ww )Nz.Exceptions thrown while doing select_input on z:
)r  	ExceptionRuntimeError)r  )r+   r  r'   r5   r6   start_select_input  s   z:select_input_with_buildin_type.<locals>.start_select_inputc                   S  s   d S r?   r5   r5   r5   r5   r6   ra         z0select_input_with_buildin_type.<locals>.<lambda>c                     s    S r?   r5   r5   )r  r5   r6   ra   '  r  zeReturn results from different branches in cond are not same type: false_var returned by false_fn is 'z' and true_var of true_fn is 'r~  z\Unsupported return type of true_fn and false_fn in cond: false_var returned by false_fn is ')r  r   r  r   r:   r   r   r*   r   r   r;   )r+   r  r'   r   r   r  r  r5   )r  r+   r  r'   r6   r    sr   #r  c                 C  s   t | trdS t| S );
    In this function, dict is not viewed as sequence.
    F)r:   r/  r   r   r5   r5   r6   _is_sequence_except_dictO  s   
r  c                 C  s   t | tr| gS t| S )r  )r:   r/  r!   r   r5   r5   r6   r  X  s   
r  c              	     s   ddl m  ddlm fdd fdd}tt|t| t|t|dd	 t|D }tt|t|t| t|d
d	 t|D }t| sP|d }t|sX|d }||fS )zTODO: make this function recursively.
    nest1: Var1, (UndefinedVar, [1,2,3])
    nest2: Var2, ([1,2,3,4], UndefinedVar)
    In this case, we should not expand recursively.
    r   )RETURN_VALUE_PREFIXri  c                   s   t |  fddt| D S )Nc                   s   g | ]} d qS )paddingr5   ra  ri  r5   r6   ry   n  r   zGexpand_undefined_var.<locals>.pack_undefined_var_as.<locals>.<listcomp>)r    r   )seqri  r5   r6   pack_undefined_var_asl  s   z3expand_undefined_var.<locals>.pack_undefined_var_asc                   s   |  sTt| s| d u rT| d u rP|d urP|dkr7td| dt|  d|  dt| d| d |S td| dt| d| dt|  d|  d |S | S )Nr   r  r  r  r  r  )
startswithr:   r   r   r*   )Zn1Zn2r'   orderr  r   r  r5   r6   map_fnq  s>   
z$expand_undefined_var.<locals>.map_fnc                 S  rG  )r   r5   ra  r5   r5   r6   ry     r;  z(expand_undefined_var.<locals>.<listcomp>c                 S  rG  )rc   r5   ra  r5   r5   r6   ry     r;  )Z4paddle.jit.dy2static.transformers.return_transformerr  r  r   r-   r  r  r  )nest1nest2rQ  r  	nest1_out	nest2_outr5   r  r6   r  a  s6   		r  c                   sP   ddl m   fdd}t| tt|t| }t|tt|t|}||fS )Nr   ri  c                   s   | d u r dS | S )Nr  r5   r   ri  r5   r6   r    s   z+change_none_to_undefinedvar.<locals>.map_fn)r  r   r    r-   r  r   )r  r  r  r  r  r5   ri  r6   r    s
   r  r   Tbothc
                 C  s   t | dg dd |pd}tdi t }
t r)t| |||||||||	 dS |
| j}|
j	dd| id|i|||p=d||||||	 d		d
 |S )aJ
  
    :api_attr: Static Graph

    **Print operator**

    This creates a print op that will print when a tensor is accessed.

    Wraps the tensor passed in so that whenever that a tensor is accessed,
    the message `message` is printed, along with the current value of the
    tensor `t`.

    Args:
        input (Tensor): A Tensor to print.
        first_n (int, optional): Only log `first_n` number of times. Default: -1.
        message (str, optional): A string message to print as a prefix. Default: None.
        summarize (int, optional): Number of elements in the tensor to be print. If
                it's value is -1, then all elements in the tensor will be print.
        print_tensor_name (bool, optional): Print the tensor name. Default: True.
        print_tensor_type (bool, optional): Print the tensor type. Default: True.
        print_tensor_shape (bool, optional): Print the tensor shape. Default: True.
        print_tensor_layout (bool, optional): Print the tensor layout. Default: True.
        print_tensor_lod (bool, optional): Print the tensor lod. Default: True.
        print_phase (str, optional): Which phase to displace, including 'forward',
                'backward' and 'both'. Default: 'both'. If set to 'backward', will
                only print the gradients of input tensor; If set to 'both', will
                both print the input tensor itself and the gradients of input tensor.

    Returns:
        Tensor: Output tensor.

    NOTES:
        The input and output are two different Tensor, and in the
        following process, you should use the output Tensor but not the input,
        otherwise, the print layer doesn't have backward.

    Examples:
        .. code-block:: python

            >>> import paddle

            >>> paddle.enable_static()

            >>> x = paddle.full(shape=[2, 3], fill_value=3, dtype='int64')
            >>> out = paddle.static.Print(x, message="The content of input layer:")

            >>> main_program = paddle.static.default_main_program()
            >>> exe = paddle.static.Executor(place=paddle.CPUPlace())
            >>> res = exe.run(main_program, fetch_list=[out])
            >>> # doctest: +SKIP('Unable to get output')
            Variable: fill_constant_1.tmp_0
              - message: The content of input layer:
              - lod: {}
              - place: Place(cpu)
              - shape: [2, 3]
              - layout: NCHW
              - dtype: int64
              - data: [3 3 3 3 3 3]
            >>> # doctest: -SKIP
            >>> res
            [array([[3, 3, 3],
                    [3, 3, 3]], dtype=int64)]
    rm   )	Zuint16r  r  Zfloat64r+  r,  r$   Zfloat8_e4m3fnZfloat8_e5m2zpaddle.static.Print printTInr|   )	first_nr&   messageprint_tensor_nameprint_tensor_typeprint_tensor_shapeprint_tensor_layoutprint_tensor_lodprint_phaser~   N)r  )
r   r   r1   r   r   r  upperZ"create_variable_for_type_inferencer   r2   )rm   r  r  r&   r  r  r  r  r  r  r3   r   r5   r5   r6   Print  sN   Kr  c                   @  s6   e Zd ZdddZdd Zdd Zdd	 Zd
d ZdS )SwitchNc                 C  s   t d|d| _d| _g | _d S )Nswitchro   F)r   r3   inside_scopepre_not_conditions)r=   r'   r5   r5   r6   r>   -	  s   
zSwitch.__init__c                 C  s   | j stdt|ddgd t| jdkr-t|gdd}tj|d}| j| t
|S t| j}| j|d	  }tj	|tj|dd
}| j| ttj	||d
gdd}t
|S )Nz!case should be called inside with	conditionr$   z.the member function case of base.layers.Switchr   Tr  r   rc   )r   y)r  r   r   rp   r  rl   r   r  r   logical_andrr   )r=   r  
cond_blockZnot_condpre_cond_numZpre_not_condZnew_not_condr5   r5   r6   r  2	  s0   
zSwitch.casec                 C  s:   t | j}|dkrtdt| j|d  gdd}t|S )Nr   z&there should be at least one conditionrc   Tr  )rp   r  r   rl   rr   )r=   r  r  r5   r5   r6   r!  O	  s   
zSwitch.defaultc                 C  s
   d| _ | S )zN
        set flag that now is inside switch.block {}
        :return:
        Tr  r@   r5   r5   r6   rA   Y	  s   zSwitch.__enter__c                 C  s   d| _ |d ur	dS dS rB   r  rC   r5   r5   r6   rG   a	  s   zSwitch.__exit__r?   )rH   rI   rJ   r>   r  r!  rA   rG   r5   r5   r5   r6   r  ,	  s    

r  )Nr"   Nr   r   )NNNN)	r   Nr"   TTTTTr  )R
__future__r   r   	functoolsr   r   r   typingr   r   r   Zpaddle.baser   Zpaddle.base.backwardr	   Zpaddle.base.frameworkr
   r   r   r   r   Zpaddle.base.libpaddle.pirr   r   r   r   Zpaddle.common_ops_importr   r   r   r   r   Zpaddle.frameworkr   Zpaddle.pir.corer   Zpaddle.utilsr   r   r   r   r   r   r    r!   r7   r9   rL   r\   rl   rr   r   rM   r$   r   r/   r   r   r   r   r  r  r  r  r)  rA  rC  rI  rM  rN  r#   r  r  r  r  r  r  r  r  r  r  r  r5   r5   r5   r6   <module>   s   (
K+ :? 
.
7  &
	
{ <
 
c  ;%C		= 