o
    Ne                    @   s  d dl mZ ddlmZ ddlmZmZ ddlmZm	Z	m
Z
 ddlmZ ddlmZmZmZmZmZ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mZm Z m!Z!m"Z"m#Z#m$Z$m%Z% d dl&Z&d dl'Z'd dl(Z(d dl)m*Z*m+Z+ ddl,m-Z-m.Z.m/Z/m0Z0 ddlm1Z2 ddl3m4Z4 d dl5m6Z6m7Z7 g dZ8dd Z9dd Z:dd Z;dd Z<dddZ=dddZ>e	 		!	"	"	"	"	"	#dd$d%Z?dd&d'Z@G d(d) d)eAZBG d*d+ d+eBZCG d,d- d-eAZDG d.d/ d/eAZEG d0d1 d1eBZFd2d3 ZGG d4d5 d5eAZHeIeJe(jKfZLd6d7 ZMdd9d:ZNd;d< ZOdd=d>ZPe d?d@ ZQdAdB ZRdCdD ZSddFdGZTddHdIZUddJdKZVe ddLdMZWe ddNdOZXe ddPdQZYe ddRdSZZddTdUZ[ddVdWZ\dXdY Z]dZd[ Z^d\d] Z_G d^d_ d_eBZ`G d`da daeAZadbdc ZbddddeZcdfdg Zddhdi Zedjdk Zfdldm Zgdndo ZhddpdqZiG drds dseAZjG dtdu dueAZkG dvdw dweAZlG dxdy dyeAZmddzd{Zne d|d} Zodd~dZpdS )    )print_function   )signature_safe_contextmanager   )autodoctemplatedoc)assigncastfill_constant)core)ProgramVariableOperator_non_static_modestatic_only_in_legacy_dygraphin_dygraph_mode)LayerHelperunique_name)logical_andlogical_not
logical_or)	assert_same_structuremap_structurehold_mutable_varscopy_mutable_varspadding_to_same_structureis_sequencepack_sequence_asflattento_sequenceN)reducepartial)convert_dtypecheck_variable_and_dtype
check_typecheck_dtype   )compat)_infer_var_data_type_shape_)_C_ops_legacy_C_ops)WhileSwitch	incrementarray_writecreate_array	less_than
less_equalgreater_thangreater_equalequal	not_equal
array_readarray_lengthcondIfElse
DynamicRNN	StaticRNNreorder_lod_tensor_by_rankPrintAssertis_emptycaseswitch_case
while_loopc                 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_outputinputmaskint32outputsXMaskOuttypeinputsrH   N)rD   )r   localsr%   r   r$   listtuple	append_op)rE   rH   rF   helper rU   PD:\Projects\ConvertPro\env\Lib\site-packages\paddle/fluid/layers/control_flow.pyrD   U   s   rD   c                 C   sB   t | t |krtd|  d|  |S ttdd | |}|S )u*  
    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 z, c                 S   s   | |kr| S dS NrU   abrU   rU   rV   <lambda>       z+_select_input_infer_shape.<locals>.<lambda>)lenwarningswarnrQ   map)Zfirst_shapeZsecond_shapeZ	out_shaperU   rU   rV   _select_input_infer_shaper   s   	rb   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 )a{  
    **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(Variable): A tensor containing 1 integer number selecting which
            input to output

    Returns:
        Variable: The selected input variable
    select_inputrO   rF   rG   r   r   dtypeshaperN   rI   rL   rM   N)rc   )r   rP   r%   rQ   rR   r$   rb   rf   re   rN   create_variablerS   )rO   rF   rT   Zoutput_shapeZoutput_dtypeoutput_typeoutrU   rU   rV   rc      s   

rc   c              
      s  ddl m ddlm  | \}}t| rt| r	 d S t|trDt|trDzt| |W S  tyC } z
td| d| d }~ww t|t	r_t|t
|r_||krV|S ||g} net|t	rit|tsst|t	rt|tr||g} tdt
|t
| n;t| rt|tft	 st| rt|tft	 r fdd}||}}||g} ntd	t
|t
|zt| |W S  ty } z
td| d| d }~ww )
Nr   )to_static_variableUndefinedVarz/Exceptions throwed while doing select_input on z:
zReturn results from different branches in cond are not same type: false_var returned by fasle_fn is '{}' and true_var of true_fn is '{}'c                    s   t |  r| S | S N)
isinstance)rZ   rl   rj   rU   rV   create_var_if_not_undefined_var   s   
zGselect_input_with_buildin_type.<locals>.create_var_if_not_undefined_varzUnsupported return type of true_fn and false_fn in cond: false_var returned by fasle_fn is '{}' and true_var of true_fn is '{}')Z:paddle.fluid.dygraph.dygraph_to_static.variable_trans_funcrj   ,paddle.fluid.dygraph.dygraph_to_static.utilsrl   rn   r   rc   	ExceptionRuntimeErrorsupport_ret_buildin_typerN   r_   r`   format	TypeError)rO   rF   name	false_vartrue_varerp   rU   ro   rV   select_input_with_buildin_type   s   
r{   c                 C   s   t | dttttdfd t |dttfd t |dtd tdi t }|j| j	d}|j| j	d}|j
d| |d||d	d|id
 ||fS )a  
    This function takes in an input that contains the complete lod information,
    and takes in a mask which is used to mask certain parts of the input.
    The output is the true branch and the false branch with the mask applied to
    the input at a certain level in the tensor. Mainly used in IfElse to split
    data into two parts.

    Args:
        input(Variable|tuple|list|None): The input tensor that contains complete
                                lod information needed to construct the output.
        mask(Variable|list): A bool column vector which masks the input.
        level(int): The specific lod level to split.

    Returns:
        tuple(Variable, Variable):
        The true branch of tensor as per the mask applied to input.

        The false branch of tensor as per the mask applied to input.

    Examples:
        .. code-block:: python

          import paddle.fluid as fluid
          x = fluid.layers.data(name='x', shape=[1])
          x.persistable = True

          y = fluid.layers.data(name='y', shape=[1])
          y.persistable = True

          out_true, out_false = fluid.layers.split_lod_tensor(
                input=x, mask=y, level=level)

    rE   Nzfluid.layers.split_lod_tensorrF   levelsplit_lod_tensorre   rI   ZOutTrueZOutFalserN   rO   rH   attrs)r}   )r%   r   rQ   rR   rN   intr   rP   "create_variable_for_type_inferencere   rS   )rE   rF   r|   rT   out_true	out_falserU   rU   rV   r}      s(   "	r}   c                 C   s   t di t }t|dttttdfd t|dttfd t| dttttdfd t|dttttdfd |j| jd}|j	d||| |d	d
|id|id |S )a  
    **merge_lod_tensor**

    This function takes in an input :math:`x`, the True branch, the False
    branch and a binary :math:`mask`. Using this information, this function
    merges the True and False branches of the tensor into a single tensor as
    output at a certain lod level indicated by :math:`level`. Used in IfElse
    to merge the output if True block and False Block.

    Args:
        in_true(Variable|tuple|list|None): The True branch to be merged.
        in_false(Variable|tuple|list|None): The False branch to be merged.
        x(Variable|tuple|list|None): The input tensor that contains complete
                            lod information needed to construct the output.
        mask(Variable|list): A bool column vector which masks the input.
        level(int): The specific lod level to merge.

    Returns:
        Variable: The merged output tensor.

    Examples:
        .. code-block:: python

          import paddle.fluid as fluid
          x = layers.data(
                      name='x', shape=[1], dtype='float32', stop_gradient=False)
          y = layers.data(
                name='y', shape=[1], dtype='bool', stop_gradient=False)

          level = 0

          out_true, out_false = layers.split_lod_tensor(
                input=x, mask=y, level=level)
          out = layers.merge_lod_tensor(
                in_true=out_true, in_false=out_false, mask=y, x=x, level=level)
    merge_lod_tensorxNzfluid.layers.merge_lod_tensorrF   in_truein_falser~   )rJ   rK   ZInTrueZInFalserL   r|   r   )r   )
r   rP   r%   r   rQ   rR   rN   r   re   rS   )r   r   r   rF   r|   rT   ri   rU   rU   rV   r   1  s8   %r   rX      Tbothc
                 C   sp   t | dg dd td| j fi t }
|
| j}|
jdd| id|i|||p)d||||||	 d		d
 |S )a7	  
    :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 (Variable): A Tensor to print.
        summarize (int): Number of elements in the tensor to be print. If it's
                value is -1, then all elements in the tensor will be print.
        message (str): A string message to print as a prefix.
        first_n (int): Only log `first_n` number of times.
        print_tensor_name (bool, optional): Print the tensor name. Default: True.
        print_tensor_type (bool, optional): Print the tensor type. Defaultt: 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): 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:
        Variable: Output tensor.

    NOTES:
        The input and output are two different variables, and in the
        following process, you should use the output variable 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])
           # Variable: fill_constant_1.tmp_0
           #   - message: The content of input layer:
           #   - lod: {}
           #   - place: CPUPlace
           #   - shape: [2, 3]
           #   - layout: NCHW
           #   - dtype: long
           #   - data: [3 3 3 3 3 3]
    rE   )float32float64rG   int64boolzfluid.layers.Printprint_printInrL    )	first_n	summarizemessageprint_tensor_nameprint_tensor_typeprint_tensor_shapeprint_tensor_layoutprint_tensor_lodprint_phaser   )r$   r   rw   rP   r   re   rS   upper)rE   r   r   r   r   r   r   r   r   r   rT   outputrU   rU   rV   r>   t  s0   Fr>   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 |r*|nd| j }t|fi t	 }|j
d	| |du rBg 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 (Variable): 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.

    Raises:
        TypeError: If ``cond`` is not boolean Variable.
        TypeError: If ``data`` is not a list or tuple or ``None``.
        TypeError: If ``summarize`` is not int.
        TypeError: If ``name`` is not a string or ``None`` .
        fluid.core.EnforceNotMet: If the condition is False in running time.

    Examples:
        .. code-block:: python

            import paddle.fluid as fluid
            import paddle.fluid.layers as layers

            x = layers.fill_constant(shape=[2, 3], dtype='float32', value=2.0)
            condition = layers.reduce_max(x) < 1.0 # False
            layers.Assert(condition, [x], 10, "example_assert_layer")

            exe = fluid.Executor()
            try:
                exe.run(fluid.default_main_program())
                # Print x and throws paddle.fluid.core.EnforceNotMet exception
                # 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 fluid.core.EnforceNotMet as e:
                print("Assert Exception Example")

    r9   r   zfluid.layers.AssertdataNr   rw   Zassert_assert)CondZData)rN   rO   r   )r$   r%   rQ   rR   rN   r   strrw   r   rP   rS   )r9   r   r   rw   Z
layer_namerT   oprU   rU   rV   r?     s   7r?   c                   @   s(   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)rn   r   rv   main_program)selfr   rU   rU   rV   __init__&  s   

zBlockGuard.__init__c                 C   s   | j   d S rm   )r   Z_create_blockr   rU   rU   rV   	__enter__+     zBlockGuard.__enter__c                 C   s   | j   |d urdS dS NFT)r   Z	_rollbackr   exc_typeexc_valexc_tbrU   rU   rV   __exit__.  s   
zBlockGuard.__exit__N)__name__
__module____qualname____doc__r   r   r   rU   rU   rU   rV   r     s
    r   c                       8   e Zd ZdZ fddZ fddZ fddZ  ZS )BlockGuardWithCompletionz
    BlockGuardWithCompletion class.

    BlockGuardWithCompletion class is used to create an op with a block in a program.
    c                    0   t |ts	tdtt| |jj || _d S )Nz*BlockGuardWithCompletion takes a StaticRNN)	rn   r<   rv   superr   r   rT   r   rnn)r   r   	__class__rU   rV   r   <     

z!BlockGuardWithCompletion.__init__c                       t j| j_tt|  S rm   )r<   IN_RNN_BLOCKr   statusr   r   r   r   r   rU   rV   r   B     
z"BlockGuardWithCompletion.__enter__c                    4   |d urdS t j| j_| j  tt| |||S NF)r<   AFTER_RNN_BLOCKr   r   _complete_opr   r   r   r   r   rU   rV   r   F  s   


z!BlockGuardWithCompletion.__exit__r   r   r   r   r   r   r   __classcell__rU   rU   r   rV   r   5  s
    r   c                   @   s   e Zd ZdZdddZdS )StaticRNNMemoryLinka  
    StaticRNNMemoryLink class.

    StaticRNNMemoryLink class is used to create a link between two
    memory cells of a StaticRNN.


    NOTE: This is a internal data structure of a very low-level API.
    Please use StaticRNN instead.

    Args:
        init(Variable): the initial variable for Memory.
        pre_mem(Variable): the memory variable in previous time step.
        mem(Variable): the memory variable in current time step.
    Nc                 C   s   || _ || _|| _d S rm   )initpre_memmem)r   r   r   r   rU   rU   rV   r   a  s   
zStaticRNNMemoryLink.__init__rm   )r   r   r   r   r   rU   rU   rU   rV   r   P  s    r   c                   @   s   e Zd ZdZdZdZdZdddZdd	 Zd
d Z							dddZ
dd Zdd Zdd Zdd Zdd Zdd Zdd ZdS )r<   a  
    :api_attr: Static Graph

    StaticRNN class.

    The StaticRNN can process a batch of sequence data. The first dimension of inputs
    represents sequence length, the length of each input sequence must be equal.
    StaticRNN will unfold sequence into time steps, user needs to define how to process
    each time step during the :code:`with` step.

    Args:
        name (str, optional): Please refer to :ref:`api_guide_Name`, Default None.

    Examples:
        .. code-block:: python

            import paddle.fluid as fluid
            import paddle.fluid.layers as layers

            vocab_size, hidden_size=10000, 200
            x = fluid.data(name="x", shape=[None, 1, 1], dtype='int64')
            # create word sequence
            x_emb = layers.embedding(
                input=x,
                size=[vocab_size, hidden_size],
                dtype='float32',
                is_sparse=False)
            # transform batch size to dim 1
            x_emb = layers.transpose(x_emb, perm=[1, 0, 2])

            rnn = fluid.layers.StaticRNN()
            with rnn.step():
                # mark created x_emb as input, each step process a word
                word = rnn.step_input(x_emb)
                # create prev memory parameter, batch size comes from word
                prev = rnn.memory(shape=[-1, hidden_size], batch_ref = word)
                hidden = fluid.layers.fc(input=[word, prev], size=hidden_size, act='relu')
                # use hidden to update prev
                rnn.update_memory(prev, hidden)
                # mark hidden as output
                rnn.step_output(hidden)
            # get StaticrNN final output
            result = rnn()

    r   r   r   Nc                 C   sH   t |dttd fd td|d| _i | _g | _g | _tj	| _
d | _d S )Nrw   zfluid.layers.StaticRNNZ
static_rnnrw   )r%   r   rN   r   rT   memoriesrO   rH   r<   BEFORE_RNN_BLOCKr   seq_lenr   rw   rU   rU   rV   r     s   
zStaticRNN.__init__c                 C      t | S )z
        Define operators in each step. step is used in :code:`with` block, OP in :code:`with` block
        will be executed sequence_len times (sequence_len is the length of input)
        )r   r   rU   rU   rV   step  s   zStaticRNN.stepc                 C      | j tjkrtd|d S )Nz You must invoke {0} in rnn block)r   r<   r   
ValueErrorru   r   methodrU   rU   rV   _assert_in_rnn_block_  s   zStaticRNN._assert_in_rnn_block_        c              
   C   s  |  d t|dttdfd t|dtttdfd t|dttdfd |du rn|du s3|du r7td|  }t	d
| jjd	g}|j|||jd
d}	|jdd|gid|	gi||	j|	j||dd | j|	dS | jjt	d
| jjdg|j|jd}
t||
d| j|
j< |
S )a  
        Create a memory variable for static rnn.
        If the :code:`init` is not None, :code:`memory` will be initialized by
        this Variable. If the :code:`init` is None, :code:`shape` and :code:`batch_ref`
        must be set, and this function will create a new variable with shape and batch_ref
        to initialize :code:`init` Variable.

        Args:
            init(Variable, optional): Tensor used to init memory. If it is not set,
                :code:`shape` and :code:`batch_ref` must be provided.
                Default: None.
            shape(list|tuple): When :code:`init` is None use this arg to initialize memory shape.
            NOTE the shape does not contain batch_size. Default: None.
            batch_ref(Variable, optional): When :code:`init` is None, memory's batch size will
            be set as batch_ref's ref_batch_dim_idx value. Default: None.
            init_value(float, optional): When :code:`init` is None, used to init memory's value. Default: 0.0.
            init_batch_dim_idx(int, optional): the batch_size axis of the :code:`init` Variable. Default: 0.
            ref_batch_dim_idx(int, optional): the batch_size axis of the :code:`batch_ref` Variable. Default: 1.

        Returns:
            Variable: The memory variable.

        Examples 1:
            .. code-block:: python

                import paddle.fluid as fluid
                import paddle.fluid.layers as layers

                vocab_size, hidden_size=10000, 200
                x = fluid.data(name="x", shape=[None, 1, 1], dtype='int64')
                # create word sequence
                x_emb = layers.embedding(
                        input=x,
                        size=[vocab_size, hidden_size],
                        dtype='float32',
                        is_sparse=False)
                # transform batch size to dim 1
                x_emb = layers.transpose(x_emb, perm=[1, 0, 2])

                rnn = fluid.layers.StaticRNN()
                with rnn.step():
                        # mark created x_emb as input, each step process a word
                        word = rnn.step_input(x_emb)
                        # create prev memory parameter, batch size comes from word
                        prev = rnn.memory(shape=[-1, hidden_size], batch_ref = word)
                        hidden = fluid.layers.fc(input=[word, prev], size=hidden_size, act='relu')
                        # use hidden to update prev
                        rnn.update_memory(prev, hidden)


        Examples 2:
            .. code-block:: python

                import paddle.fluid as fluid
                import paddle.fluid.layers as layers
                vocab_size, hidden_size=10000, 200
                x = fluid.data(name="x", shape=[None, 1, 1], dtype='int64')
                # create word sequence
                x_emb = layers.embedding(
                        input=x,
                        size=[vocab_size, hidden_size],
                        dtype='float32',
                        is_sparse=False)
                # transform batch size to dim 1
                x_emb = layers.transpose(x_emb, perm=[1, 0, 2])
                boot_memory = fluid.layers.data(name='boot', shape=[hidden_size], dtype='float32', lod_level=1)
                rnn = fluid.layers.StaticRNN()
                with rnn.step():
                        # mark created x_emb as input, each step process a word
                        word = rnn.step_input(x_emb)
                        # init memory
                        prev = rnn.memory(init=boot_memory)
                        hidden = fluid.layers.fc(input=[word, prev], size=hidden_size, act='relu')
                        # update hidden with prev
                        rnn.update_memory(prev, hidden)

        memoryr   Nzfluid.layers.StaticRNN.memoryrf   	batch_refz9if init is None, memory at least need shape and batch_ref@Zmemory_bootF)rw   rf   re   Zpersistablefill_constant_batch_size_likeInputrL   )valuerf   re   Zinput_dim_idxZoutput_dim_idxr   r   r   )rw   re   rf   )r   r   )r   r%   r   rN   rQ   rR   r   _parent_blockr   generate_with_ignorable_keyjoinrT   rw   
create_varre   rS   rf   r   rg   r   r   )r   r   rf   r   Z
init_valueZinit_batch_dim_idxZref_batch_dim_idxparent_blockvar_nameZboot_varr   rU   rU   rV   r     sr   
V

zStaticRNN.memoryc                 C   s   |  d t|dtd | jdu r|jd | _n|jd dkr+| j|jd kr+td| jj|j|j	t
|jdd |jd	}| j| |S )
a  
        Mark a sequence as a StaticRNN input.

        Args:
            x(Variable): The input sequence, the shape of x
                should be [seq_len, ...].

        Returns:
            Variable: The current time step data in the input sequence.

        Examples:
            .. code-block:: python

                import paddle.fluid as fluid
                import paddle.fluid.layers as layers

                vocab_size, hidden_size=10000, 200
                x = fluid.data(name="x", shape=[None, 1, 1], dtype='int64')
                # create word sequence
                x_emb = layers.embedding(
                        input=x,
                        size=[vocab_size, hidden_size],
                        dtype='float32',
                        is_sparse=False)
                # transform batch size to dim 1
                x_emb = layers.transpose(x_emb, perm=[1, 0, 2])

                rnn = fluid.layers.StaticRNN()
                with rnn.step():
                        # mark created x_emb as input, each step process a word
                        word = rnn.step_input(x_emb)
                        # create prev memory parameter, batch size comes from word
                        prev = rnn.memory(shape=[-1, hidden_size], batch_ref = word)
                        hidden = fluid.layers.fc(input=[word, prev], size=hidden_size, act='relu')
                        # use hidden to update prev
                        rnn.update_memory(prev, hidden)

        
step_inputr   z!fluid.layers.StaticRNN.step_inputNr   rX   z&Static RNN only take fix seq_len inputr   )rw   re   rf   rN   )r   r%   r   r   rf   r   rT   rg   rw   re   rQ   rN   rO   append)r   r   ZiptrU   rU   rV   r   C  s   
'
zStaticRNN.step_inputc                 C   s   |  d t|dtd | jj|jd}| jjdd|gid|id|jid	 |  j|j	| j
gt|j |jd
}| j| dS )a  
        Mark a sequence as a StaticRNN output.

        Args:
            o(Variable): The output sequence.

        Returns:
            None.

        Examples:
            .. code-block:: python

                import paddle.fluid as fluid
                import paddle.fluid.layers as layers

                vocab_size, hidden_size=10000, 200
                x = fluid.data(name="x", shape=[None, 1, 1], dtype='int64')
                # create word sequence
                x_emb = layers.embedding(
                        input=x,
                        size=[vocab_size, hidden_size],
                        dtype='float32',
                        is_sparse=False)
                # transform batch size to dim 1
                x_emb = layers.transpose(x_emb, perm=[1, 0, 2])

                rnn = fluid.layers.StaticRNN()
                with rnn.step():
                        # mark created x_emb as input, each step process a word
                        word = rnn.step_input(x_emb)
                        # create prev memory parameter, batch size comes from word
                        prev = rnn.memory(shape=[-1, hidden_size], batch_ref = word)
                        hidden = fluid.layers.fc(input=[word, prev], size=hidden_size, act='relu')
                        # use hidden to update prev
                        rnn.update_memory(prev, hidden)
                        rnn.step_output(hidden)

                result = rnn()

        step_outputoz"fluid.layers.StaticRNN.step_outputr~   rnn_memory_helperrJ   rL   re   r   )rw   rf   re   N)r   r%   r   rT   r   re   rS   r   r   rw   r   rQ   rf   rH   r   )r   r   Ztmp_oZout_varrU   rU   rV   r   w  s   
)zStaticRNN.step_outputc                 G   s   |D ]}|  | qdS )a  
        Mark the StaticRNN output variables.

        Args:
            outputs: The output Tensor, can mark multiple variables as output

        Returns:
            None

        Examples:
            .. code-block:: python

                import paddle.fluid as fluid
                import paddle.fluid.layers as layers

                vocab_size, hidden_size=10000, 200
                x = fluid.data(name="x", shape=[None, 1, 1], dtype='int64')
                # create word sequence
                x_emb = layers.embedding(
                        input=x,
                        size=[vocab_size, hidden_size],
                        dtype='float32',
                        is_sparse=False)
                # transform batch size to dim 1
                x_emb = layers.transpose(x_emb, perm=[1, 0, 2])

                rnn = fluid.layers.StaticRNN()
                with rnn.step():
                        # mark created x_emb as input, each step process a word
                        word = rnn.step_input(x_emb)
                        # create prev memory parameter, batch size comes from word
                        prev = rnn.memory(shape=[-1, hidden_size], batch_ref = word)
                        hidden = fluid.layers.fc(input=[word, prev], size=hidden_size, act='relu')
                        # use hidden to update prev
                        rnn.update_memory(prev, hidden)
                        # mark each step's hidden and word as output
                        rnn.output(hidden, word)

                result = rnn()
        N)r   )r   rH   eachrU   rU   rV   r     s   )zStaticRNN.outputc                 C   s.   t |dtd t |dtd || j|j _dS )aP  
        Update the memory from :code:`mem` to :code:`var`.

        Args:
            mem(Variable): the memory variable.
            var(Variable): the plain variable generated in RNN block, used to update memory.
                           var and mem should have same dims and data type.

        Returns:
            None

        r   z$fluid.layers.StaticRNN.update_memoryvarN)r%   r   r   rw   r   )r   r   r   rU   rU   rV   update_memory  s   zStaticRNN.update_memoryc                 C   ,   | j j}| j}|dksJ ||}|S Nr   rT   r   current_block
parent_idxblockr   progr   r   rU   rU   rV   r     s
   

zStaticRNN._parent_blockc                 O   sH   | j tjkr
tdt| jdkrtdt| jdkr!| jd S | jS )Nz0RNN output can only be retrieved after rnn blockr   zRNN has no outputr   )r   r<   r   r   r^   rH   r   argskwargsrU   rU   rV   __call__  s   
zStaticRNN.__call__c              	      s  | j j}| }|   t }|jD ]}t|tsJ |jD ]}|	|D ]}|
| q%qq| jD ]}|
|j q2| jD ]}|
| q>t }	|jD ] }t|tsUJ |jD ]}
||
D ]}||vrj|	| q_qXqL fddt|	D } jtjjjd} fdd| jD }| j}g }g }g }t| jD ]L\}}||j ||jj |jd usJ d|jj ||jj}t|tsJ | j j|j d}|j!dd|gid	|gid
|j id ||j q j!d|||d||gdt"|dk|||dd d S )Nc                       g | ]}  |qS rU   )_find_var_recursive).0rw   r   rU   rV   
<listcomp>      
z*StaticRNN._complete_op.<locals>.<listcomp>rN   c                    s   g | ]}  |jqS rU   )r   rw   r   ir   rU   rV   r   &  s    z#%s should be updated in every step.r~   r   rJ   rL   re   r   Z	recurrent)rO   Zinitial_states
parameters)rH   Zstep_scopesr   )Z
has_statesZ	ex_statesZstates	sub_block)#rT   r   r   r   setopsrn   r   output_namesr   addrO   rw   r   rQ   input_namesrE   r   r   r   VarDescVarTypeSTEP_SCOPESrH   six	iteritemsr   r   r   r   r   r   re   rS   r^   )r   r   Z	rnn_blockZlocal_inputsr   onameout_var_namer   mparamsinamein_var_namer  
step_scopeZinlinksZoutlinksZboot_memoriesZpre_memoriesr   _r   Zmem_varnew_memrU   r   rV   r     s   











zStaticRNN._complete_oprm   )NNNr   r   r   )r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   rU   rU   rU   rV   r<   g  s.    .


 4<,
r<   c                       s4   e Zd Z fddZ fddZ fddZ  ZS )
WhileGuardc                    r   )NzWhileGuard takes a while op)	rn   r,   rv   r   r  r   rT   r   while_op)r   r  r   rU   rV   r   S  r   zWhileGuard.__init__c                    r   rm   )r,   IN_WHILE_BLOCKr  r   r   r  r   r   r   rU   rV   r   Y  r   zWhileGuard.__enter__c                    r   r   )r,   AFTER_WHILE_BLOCKr  r   	_completer   r  r   r   r   rU   rV   r   ]  s
   

zWhileGuard.__exit__)r   r   r   r   r   r   r   rU   rU   r   rV   r  R  s    r  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_seedTFr   )r   r   ZIGNORE_VAR_NAMESZ	var_namesrw   rU   rU   rV   is_ignore_varsp  s   


z3get_inputs_outputs_in_block.<locals>.is_ignore_varsN)r  rn   r   r  rE   r  r  r   r  r   r   r   r   Zhas_varr   rN   r   r	  r
  LOD_TENSOR_ARRAY)r   Zinner_inputsinner_outputsrT   r  r   r  r  r  r  Zremove_inner_inputsr   parent_block_varZcurrent_block_varrU   rU   rV   get_inputs_outputs_in_blocke  s@   








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 )r,   a  
    :api_attr: Static Graph

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

    Note:
        A new OP :ref:`api_fluid_layers_while_loop` is highly recommended instead of ``While`` if the shape of parameter ``cond`` is [1].
        OP :ref:`api_fluid_layers_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 1:
          .. code-block:: python

            import paddle.fluid as fluid
            import numpy as np

            i = fluid.layers.fill_constant(shape=[1], dtype='int64', value=0)           # loop counter

            loop_len = fluid.layers.fill_constant(shape=[1],dtype='int64', value=10)    # loop length

            cond = fluid.layers.less_than(x=i, y=loop_len)
            while_op = fluid.layers.While(cond=cond)
            with while_op.block():
                i = fluid.layers.increment(x=i, value=1, in_place=True)
                fluid.layers.less_than(x=i, y=loop_len, cond=cond)

            exe = fluid.Executor(fluid.CPUPlace())
            exe.run(fluid.default_startup_program())

            res = exe.run(fluid.default_main_program(), feed={}, fetch_list=[i])
            print(res) # [array([10])]


    Examples 2:
          .. code-block:: python

            import paddle.fluid as fluid
            import numpy as np

            i = fluid.layers.fill_constant(shape=[1], dtype='int64', value=0)
            loop_len = fluid.layers.fill_constant(shape=[1], dtype='int64', value=10)
            one = fluid.layers.fill_constant(shape=[1], dtype='float32', value=1)
            data = fluid.data(name='data', shape=[1], dtype='float32')
            sums = fluid.layers.fill_constant(shape=[1], dtype='float32', value=0)  # Define the variable to be obtained ouside of While, which name should be different from the variable inside the While to be obtained

            cond = fluid.layers.less_than(x=i, y=loop_len)
            while_op = fluid.layers.While(cond=cond)
            with while_op.block():
                sums_tensor = fluid.layers.elementwise_add(x=data, y=data)
                fluid.layers.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 = fluid.layers.increment(x=i, value=1, in_place=True)
                data = fluid.layers.elementwise_add(x=data, y=one)
                fluid.layers.less_than(x=i, y=loop_len, cond=cond)

            feed_data = np.ones(1).astype('float32')
            exe = fluid.Executor(fluid.CPUPlace())
            exe.run(fluid.default_startup_program())
            res = exe.run(fluid.default_main_program(), feed={'data': feed_data}, fetch_list=sums)
            print(res[0])  # [2.]    # Because the data in While does not update the value outside the While, the value of sums is [2.] after the loop
    r   r   r   FNc                 C   s`   t d|d| _tj| _t|ddgd tdd |jddkr(td		t
|j|| _|| _d S )
Nwhiler   r9   r   zfluid.layers.Whilec                 S      | | S rm   rU   rY   rU   rU   rV   r\         z While.__init__.<locals>.<lambda>r   z8condition expected shape as [1], but given shape as {0}.)r   rT   r,   BEFORE_WHILE_BLOCKr   r$   r!   rf   rv   ru   rQ   cond_varis_test)r   r9   r&  rw   rU   rU   rV   r     s   
zWhile.__init__c                 C   r   rm   )r  r   rU   rU   rV   r        zWhile.blockc           	         s   | j j}| }|| j | jjh}t }t|||| j \}}g }|D ]} 	|}|r4|
| q&|tt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   | j S rm   r   r   rU   rU   rV   r\         z!While._complete.<locals>.<lambda>r   r!  c                    r   rU   Z_var_recursive)r   Zx_namer   rU   rV   r     s    z#While._complete.<locals>.<listcomp>)rJ   	Condition)rL   Z
StepScopes)r  r&  r   )rT   r   r   r   r   r%  rw   r  r   r   r   ra   r   r   r	  r
  r  rS   r&  )	r   r   Zwhile_blockr  Zx_name_listZout_varsinner_out_name	inner_varr  rU   r   rV   r    s>   







zWhile._completeFN)
r   r   r   r   r$  r  r  r   r   r  rU   rU   rU   rV   r,     s    G
r,   c                 C   s   dd }t | ttjfs!t |trt | trt| | dS | }dS | jtjjj	krH| j
j}|
| j}|rD|| jsFt| | dS dS dS t |trbt | trb|| |rbtd| j|j t| | dS )zt
    Assign input to output, but skip the process of copying LoDTensorArray 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 )NTrX   F)r^   rf   zip)Zx_varZy_varZx_dimZy_dimrU   rU   rV   has_shape_diff1  s   z4assign_skip_lod_tensor_array.<locals>.has_shape_diffNzxIn dy2static mode, we attemp to assign a variable with shape {} into a variable with shape{}, which is not always right.)rn   r   r   ZVarBasert   r   rN   r	  r
  r  r   programr   r   r   rw   r_   r`   ru   rf   )rE   r   r0  r   r   rU   rU   rV   assign_skip_lod_tensor_array,  s:   
r2  Fc                 C   s  t di t }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rLtdt|jt r| d }|r|| }t|ttfse|g}t|t|krqtd| |  d }tt|| |sW|S t|||}	t|}
|	 S |
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 sw   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 LoDTensorArrays 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 LoDTensorArrays 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 LoDTensorArrays 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])]
    rC   z%cond in while_loop should be callablez%body in while_loop should be callable	loop_varszfluid.layers.while_loopr   z+loop_vars in while_loop should not be emptyzvar of cond returnedr   c                 S   r"  rm   rU   rY   rU   rU   rV   r\     r#  zwhile_loop.<locals>.<lambda>r   zPthe shape of the variable returned by cond should be [1],but given shape as {0}.z]body in while_loop should return the same arity (length and structure) and types as loop_varsFZcheck_typeszXbody in while_loop should return the same arity (length and structure) as loop_vars: {0}N)rC   )r   rP   callablerv   r%   rQ   rR   r^   r   r$   r!   rf   ru   r   numpyrn   r   r2  r,   r   r   r   _deal_with_undefined_varr   r   )r9   bodyr3  r&  rw   rT   Zpre_condZnow_condoutput_varsZwhile_loop_blockZhas_mutable_vars_in_loopZnew_loop_varsrz   rU   rU   rV   rC   W  sr   /




rC   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   )rl   create_undefined_variablec                    s>   t | tft s| d u r  S t| r	 t fdd| S d S )Nc                    s     S rm   rU   r(  r:  rU   rV   r\     r)  zC_deal_with_undefined_var.<locals>.create_var_like.<locals>.<lambda>)rn   r   rt   r   r   )o_varr;  rU   rV   create_var_like  s   z1_deal_with_undefined_var.<locals>.create_var_likez+The length of loop_vars should be the same.N)rq   rl   r:  r^   r   r/  rn   r   )r9  r3  rl   r=  resultsr<  Zl_varrU   r;  rV   r7    s   	r7  c                 C   s   t | dttfd t| tr$t| D ]\}}t |dt| d td qtdi t }|jt	j
jjtdd}|jdd| id|id|id	 |S )a  
    LoD Rank Table Operator. Given an input variable **x** and a level number
    of LoD, this layer creates a LodRankTable object. A LoDRankTable object
    contains a list of bi-element tuples. Each tuple consists of an index and
    a length, both of which are int type. Refering to specified level of LoD,
    the index is the sequence index number and the length represents the
    sequence length. Please note that the list is ranked in descending order by
    the length. The following is an example:

        .. code-block:: text

            x is a LoDTensor:
                x.lod = [[2,                1],
                         [5,             1, 1]]
                x.data = [a, b, c, d, e, f, g]

            1. set level to 0:
                Create lod rank table:
                    lod_rank_table_obj = lod_rank_table(x, level=0)

                Get:
                    lod_rank_table_obj.items() = [(0, 2), (1, 1)]

            2. set level to 1:
                Create lod rank table:
                    lod_rank_table_obj = lod_rank_table(x, level=1)

                Get:
                    lod_rank_table_obj.items() = [(0, 5), (1, 1), (2, 1)]

    Args:
        x (Variable): Input variable, a LoDTensor based which to create the lod
            rank table.
        level (int): Specify the LoD level, on which to create the lod rank
            table.

    Returns:
        Variable: The created LoDRankTable object.

    Examples:
        .. code-block:: python

            import paddle.fluid as fluid
            x = fluid.layers.data(name='x', shape=[10],
                                  dtype='float32', lod_level=1)
            out = layers.lod_rank_table(x=x, level=0)
    r   lod_rank_tableinput[])rN   rw   rJ   rL   r|   r   N)r?  )r%   r   rQ   rn   	enumerater   r   rP   rg   r   r	  r
  LOD_RANK_TABLEr   generaterS   )r   r|   r  input_xrT   tablerU   rU   rV   r?    s$   0
r?  c                 C   s8   t d	i t }|jdd}|jdd| id|id |S )
a  
    ${comment}

    >>> import paddle.fluid as fluid
    >>> x = fluid.layers.data(name='x', shape=[10], dtype='float32',
    >>>                       lod_level=1)
    >>> rank_table = layers.lod_rank_table(x=x, level=0)
    >>> max_seq_len = layers.max_sequence_len(rank_table)

    Args:
        rank_table(${rank_table_type}): ${rank_table_comment}.

    Returns:
        ${out_comment}.
    max_seqence_lenr   r~   max_sequence_len	RankTablerL   rM   N)rG  )r   rP   r   rS   )
rank_tablerT   resrU   rU   rV   rH  0  s   rH  c                 C   s   t | dttfd t| tr$t| D ]\}}t |dt| d td qt |dttfd t|trHt|D ]\}}t |dt| d td q6tdi t }|jt	
dtjjj| jd}|jd| |dd	|id
 |S )a  
    Convert a LoDTensor to a LoDTensorArray.

    This function split a LoDTesnor to a LoDTensorArray according to its LoD
    information. LoDTensorArray is an alias of C++ std::vector<LoDTensor> in
    PaddlePaddle. The generated LoDTensorArray of this function can be further read
    or written by `read_from_array()` and `write_to_array()` operators. However,
    this function is generally an internal component of PaddlePaddle `DynamicRNN`.
    Users should not use it directly.

    Args:
        x (Variable|list): The LoDTensor to be converted to a LoDTensorArray.
        table (ParamAttr|list): The variable that stores the level of lod
                                which is ordered by sequence length in
                                descending order. It is generally generated
                                by `layers.lod_rank_table()` API.

    Returns:
        Variable: The LoDTensorArray that has been converted from the input tensor.

    Examples:
        .. code-block:: python

          import paddle.fluid as fluid
          x = fluid.layers.data(name='x', shape=[10])
          table = fluid.layers.lod_rank_table(x, level=0)
          array = fluid.layers.lod_tensor_to_array(x, table)
    r   lod_tensor_to_arrayr@  rA  rF  table[rw   rN   re   rJ   rI  rL   rM   N)rL  )r%   r   rQ   rn   rB  r   r   rP   rg   r   rD  r   r	  r
  r  re   rS   )r   rF  r  rE  table_xrT   arrayrU   rU   rV   rL  K  s<   

rL  c                 C   s   t | dttfd t| tr$t| D ]\}}t |dt| d td qt |dttfd t|trHt|D ]\}}t |dt| d td q6tdi t }|j| j	d}|j
d| |dd	|id
 |S )a	  Convert a LoD_Tensor_Aarry to an LoDTensor.

    Args:
        x (Variable|list): The lod tensor array to be converted to a tensor.
        table (ParamAttr|list): The variable that stores the level of lod
                                which is ordered by sequence length in
                                descending order.

    Returns:
        Variable: The variable of type tensor that has been converted
                  from an array.

    Examples:
        .. code-block:: python

          import paddle.fluid as fluid
          x = fluid.layers.data(name='x', shape=[10])
          table = fluid.layers.lod_rank_table(x, level=0)
          array = fluid.layers.lod_tensor_to_array(x, table)
          lod_tensor = fluid.layers.array_to_lod_tensor(array, table)
    r   array_to_lod_tensorr@  rA  rF  rM  r~   rO  rL   rM   N)rR  )r%   r   rQ   rn   rB  r   r   rP   r   re   rS   )r   rF  r  rE  rP  rT   tmprU   rU   rV   rR    s4   

rR        ?c                 C   sv   t  r	t| |S t| dg dd td
i t }|s$|j| jd}n| }|jdd| gid|gidt	|id |S )aZ  
    The OP is usually used for control flow to increment the data of :attr:`x` by an amount :attr:`value`.
    Notice that the number of elements in :attr:`x` must be equal to 1.

    Parameters:
        x (Variable): A tensor that must always contain only one element, its data type supports
            float32, float64, int32 and int64.
        value (float, optional): The amount to increment the data of :attr:`x`. Default: 1.0.
        in_place (bool, optional): Whether the OP should be performed in-place. Default: True.

    Returns:
        Variable: The elementwise-incremented tensor with the same shape and data type as :attr:`x`.

    Examples:
        .. code-block:: python

          import paddle.fluid as fluid
          counter = fluid.layers.zeros(shape=[1], dtype='float32') # [0.]
          fluid.layers.increment(counter) # [1.]
    r   r   r   rG   r   r.   r~   rJ   rL   r   r   N)r.   )
r   r*   Z
increment_r$   r   rP   r   re   rS   float)r   r   in_placerT   ri   rU   rU   rV   r.     s    
r.   c                 C   sN  t  rUt| tsJ dt|tsJ d|jdgksJ d| d}|du r/t| j}t|ts8J d|t	|ksBJ d|t	|k rN| ||< |S |
|  |S t|d	d
gd t| dtd tdi t }|durt|tr}|jtjjjkrtd|du r|jd|jtjjj| jd}|jd| g|gdd|gid |S )a	  
    This OP writes the input ``x`` into the i-th position of the ``array``
    :ref:`api_fluid_LoDTensorArray` and returns the modified array.
    If ``array`` is none, a new LoDTensorArray will be created and returned.
    This OP is often used together with :ref:`api_fluid_layers_array_read` OP.

    Args:
        x (Variable): The input data to be written into array. It's multi-dimensional
            Tensor or LoDTensor. Data type: float32, float64, int32, int64.
        i (Variable): 1-D Tensor with shape [1], which represents the position into which
            ``x`` is written. Data type: int64.
        array (LoDTensorArray, optional): The LoDTensorArray into which ``x`` is written.
            The default value is None, when a new LoDTensorArray will be created and returned
            as a result.

    Returns:
        Variable: The input ``array`` after ``x`` is written into.

    Examples:
        .. code-block:: python

            import paddle.fluid as fluid
            tmp = fluid.layers.fill_constant(shape=[3, 2], dtype='int64', value=5)
            i = fluid.layers.fill_constant(shape=[1], dtype='int64', value=10)
            # Write tmp into the position of arr with subscript 10 and return arr.
            arr = fluid.layers.array_write(tmp, i=i)

            # Now, arr is a LoDTensorArray with length 11. We can use array_read OP to read
            # the data at subscript 10 and print it out.
            item = fluid.layers.array_read(arr, i=i)
            input = fluid.layers.Print(item, message="The content of i-th LoDTensor:")
            main_program = fluid.default_main_program()
            exe = fluid.Executor(fluid.CPUPlace())
            exe.run(main_program)

            # The printed result is:
            # 1570533133    The content of i-th LoDTensor:  The place is:CPUPlace
            # Tensor[array_read_0.tmp_0]
            #    shape: [3,2,]
            #    dtype: l
            #    data: 5,5,5,5,5,5,

            # the output is 2-D Tensor with shape [3,2], which is tmp above.
            # dtype is the corresponding C++ data type, which may vary in different environments.
            # Eg: if the data type of tensor is int64, then the corresponding C++ data type is int64_t,
            #       so the dtype value is typeid(int64_t).Name(), which is 'x' on MacOS, 'l' on Linux,
            #       and '__int64' on Windows. They both represent 64-bit integer variables.

    zBThe input data 'x' in array_write must be Variable in dygraph modez=The index 'i' in array_write must be Variable in dygraph moder   4The shape of index 'i' should be [1] in dygraph moder   N9The 'array' in array_write must be a list in dygraph modezNThe index 'i' should not be greater than the length of 'array' in dygraph moder  r   r/   r   z7array should be tensor array vairable in array_write Op{0}.outrN  write_to_arrayrJ   IrL   rM   )r/   )r   rn   r   rf   r6  itemr0   re   rQ   r^   r   r$   r%   r   rP   rN   r   r	  r
  r  rv   rg   ru   rw   rS   )r   r  rQ  rT   rU   rU   rV   r/     sp   2


r/   c                 C   s   g }|durt |ttfstdt|t|}|D ]}t |ts,tdt|qt r2|S tdi t	 }|j
d|jtjjj| d}|D ]}t|t||d qK|S )	aQ  
    This OP creates an LOD_TENSOR_ARRAY. It is used as
    the input of :ref:`api_fluid_layers_array_read` and
    :ref:`api_fluid_layers_array_write`. Also it can be used
    with  :ref:`api_fluid_layers_While` to create RNN network.

    Args:
        dtype (str): The data type of the elements in the lod_tensor_array.
                     Support data type: float32, float64, int32, int64.
        initialized_list(list): Used to initialize as default value for created array.
                    All values in initialized list should be a Tensor.

    Returns:
        Variable: The empty lod_tensor_array. The data type of elements in Tensor is ``dtype``.

    Examples:
        .. code-block:: python

          import paddle.fluid as fluid
          data = fluid.layers.create_array(dtype='float32') # Create a float32 LoDTensorArray.

    NzDRequire type(initialized_list) should be list/tuple, but received {}zEAll values in `initialized_list` should be Variable, but recevied {}.rQ  rZ  rN  r   r  rQ  )rQ  )rn   rQ   rR   rv   ru   rN   r   r   r   rP   rg   rw   r   r	  r
  r  r/   r8   )re   Zinitialized_listrQ  valrT   Ztensor_arrayrU   rU   rV   r0   H  s8   

r0   c                 C   s   t | dg dd t |dg dd |durt|dtd |dkr(t|dtd tdi t }|du r=|jdd	}d
|_t }|durH||d< |j	d| g|gdd|gi|d |S )a  

    ${comment}

    Args:
        x(Tensor): ${x_comment}.
        y(Tensor): ${y_comment}.
        force_cpu(${force_cpu_type}): ${force_cpu_comment}.
        cond(Tensor, optional): Optional output which can be any created Tensor
            that meets the requirements to store the result of *less_than*.
            if cond is None, a new Tensor will be created to store the result.
        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:
        ${out_comment}.

    Examples:
        .. code-block:: python

            import paddle

            x = paddle.to_tensor([1, 2, 3, 4], dtype='float32')
            y = paddle.to_tensor([2, 2, 1, 3], dtype='float32')
            result = paddle.less_than(x, y)
            print(result) # [True, False, False, False]

    r   rU  r1   yNr9   	force_cpur   r~   TrJ   YrL   r   )r1   )
r$   r%   r   r   r   rP   r   stop_gradientdictrS   )r   ra  rb  r9   rw   rT   r   rU   rU   rV   r1     s0   r1   c                 C      t | dg dd t |dg dd |durt|dtd tdi t }|du r2|jdd}d	|_t }|jd| g|gd
d|gi|d |S )a  
    :alias_main: paddle.less_equal
        :alias: paddle.less_equal,paddle.tensor.less_equal,paddle.tensor.logic.less_equal
        :old_api: paddle.fluid.layers.less_equal

    This OP returns the truth value of :math:`x <= y` elementwise, which is equivalent function to the overloaded operator `<=`.

    Args:
        x(Variable): First input to compare which is N-D tensor. The input data type should be float32, float64, int32, int64.
        y(Variable): Second input to compare which is N-D tensor. The input data type should be float32, float64, int32, int64.
        cond(Variable, optional): Optional output which can be any created Variable that meets the requirements to store the result of *less_equal*.
            if cond is None, a new Varibale will be created to store the result.
        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:
        Variable, the output data type is bool: The tensor variable storing the output, the output shape is same as input :attr:`x`.

    Examples:
        .. code-block:: python

          import paddle.fluid as fluid
          import numpy as np
          label = fluid.layers.assign(np.array([1, 3], dtype='int32'))
          limit = fluid.layers.assign(np.array([1, 2], dtype='int32'))
          out = fluid.layers.less_equal(x=label, y=limit) #out=[True, False]
          out1 = label<= limit #out1=[True, False]

    r   rU  r2   ra  Nr9   r   r~   Trc  rL   r   )r2   	r$   r%   r   r   rP   r   re  rf  rS   r   ra  r9   rw   rT   r   rU   rU   rV   r2     s(   r2   c                 C   s   t | dg dd t |dg dd |durt|dtd tdi t }|du r2|jdd}d	|_t }t r?t	
| |d
S |jd| g|gdd|gi|d |S )a  
    :alias_main: paddle.greater_than
        :alias: paddle.greater_than,paddle.tensor.greater_than,paddle.tensor.logic.greater_than
        :old_api: paddle.fluid.layers.greater_than

    This OP returns the truth value of :math:`x > y` elementwise, which is equivalent function to the overloaded operator `>`.

    Args:
        x(Variable): First input to compare which is N-D tensor. The input data type should be float32, float64, int32, int64.
        y(Variable): Second input to compare which is N-D tensor. The input data type should be float32, float64, int32, int64.
        cond(Variable, optional): Optional output which can be any created Variable that meets the requirements to store the result of *greater_than*.
            if cond is None, a new Varibale will be created to store the result.
        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:
        Variable, the output data type is bool: The tensor variable storing the output, the output shape is same as input :attr:`x` .

    Examples:
        .. code-block:: python

          import paddle.fluid as fluid
          import numpy as np
          label = fluid.layers.assign(np.array([2, 3], dtype='int32'))
          limit = fluid.layers.assign(np.array([3, 2], dtype='int32'))
          out = fluid.layers.greater_than(x=label, y=limit) #out=[False, True]
          out1 = label > limit #out1=[False, True]
    r   rU  r3   ra  Nr9   r   r~   TrX   rc  rL   r   )r3   )r$   r%   r   r   rP   r   re  rf  r   r*   r3   rS   ri  rU   rU   rV   r3     s,   r3   c                 C   rg  )a  
    :alias_main: paddle.greater_equal
        :alias: paddle.greater_equal,paddle.tensor.greater_equal,paddle.tensor.logic.greater_equal
        :old_api: paddle.fluid.layers.greater_equal

    This OP returns the truth value of :math:`x >= y` elementwise, which is equivalent function to the overloaded operator `>=`.

    Args:
        x(Variable): First input to compare which is N-D tensor. The input data type should be float32, float64, int32, int64.
        y(Variable): Second input to compare which is N-D tensor. The input data type should be float32, float64, int32, int64.
        cond(Variable, optional): Optional output which can be any created Variable that meets the requirements to store the result of *greater_equal*.
            if cond is None, a new Varibale will be created to store the result.
        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:
        Variable, the output data type is bool: The tensor variable storing the output, the output shape is same as input :attr:`x`.

    Examples:
        .. code-block:: python

          import paddle.fluid as fluid
          import numpy as np

          label = fluid.layers.assign(np.array([2, 2], dtype='int32'))
          limit = fluid.layers.assign(np.array([2, 3], dtype='int32'))
          out = fluid.layers.greater_equal(x=label, y=limit) #out=[True, False]
          out_1 = label >= limit #out1=[True, False]

    r   rU  r4   ra  Nr9   r   r~   Trc  rL   r   )r4   rh  ri  rU   rU   rV   r4   .  s(    r4   c                 C   s   t  rd}t| ||S t| dg dd t|dg dd |dur)t|dtd tdi t }|du r>|jdd	}d
|_	|j
d| g|gdd|gid |S )ad  
    This layer returns the truth value of :math:`x == y` elementwise.

    Args:
        x(Variable): Tensor, data type is float32, float64, int32, int64.
        y(Variable): Tensor, data type is float32, float64, int32, int64.
        cond(Variable, optional): Optional output which can be any created
            Variable that meets the requirements to store the result of *equal*.
            if cond is None, a new Varibale will be created to store the result.
        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:
        Variable: output Tensor, it's shape is the same as the input's Tensor,
        and the data type is bool.

    Examples:
        .. code-block:: python

          import paddle.fluid as fluid
          import numpy as np
          out_cond =fluid.data(name="input1", shape=[2], dtype='bool')
          label = fluid.layers.assign(np.array([3, 3], dtype="int32"))
          limit = fluid.layers.assign(np.array([3, 2], dtype="int32"))
          label_cond = fluid.layers.assign(np.array([1, 2], dtype="int32"))
          out1 = fluid.layers.equal(x=label,y=limit) #out1=[True, False]
          out2 = fluid.layers.equal(x=label_cond,y=limit, cond=out_cond) #out2=[False, True] out_cond=[False, True]
    rX   r   rU  r5   ra  Nr9   r   r~   Trc  rL   rM   )r5   )r   r*   r5   r$   r%   r   r   rP   r   re  rS   )r   ra  r9   rw   Zdefault_axisrT   rU   rU   rV   r5   g  s&   r5   c                 C   s   t | dg dd t |dg dd |durt|dtd tdi t }|du r2|jdd}d	|_|jd| g|gd
d|gid |S )a  
    :alias_main: paddle.not_equal
        :alias: paddle.not_equal,paddle.tensor.not_equal,paddle.tensor.logic.not_equal
        :old_api: paddle.fluid.layers.not_equal

    This OP returns the truth value of :math:`x != y` elementwise, which is equivalent function to the overloaded operator `!=`.

    Args:
        x(Variable): First input to compare which is N-D tensor. The input data type should be float32, float64, int32, int64.
        y(Variable): Second input to compare which is N-D tensor. The input data type should be float32, float64, int32, int64.
        cond(Variable, optional): Optional output which can be any created Variable that meets the requirements to store the result of *not_equal*.
            if cond is None, a new Varibale will be created to store the result.
        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:
        Variable, the output data type is bool: The tensor variable storing the output, the output shape is same as input :attr:`x`.

    Examples:
        .. code-block:: python

          import paddle.fluid as fluid

          label = fluid.layers.data(name='label', shape=[1], dtype='int64')
          limit = fluid.layers.fill_constant(shape=[1], value=1, dtype='int64')
          out = fluid.layers.not_equal(x=label, y=limit)
    r   rU  r6   ra  Nr9   r   r~   Trc  rL   rM   )r6   )r$   r%   r   r   rP   r   re  rS   )r   ra  r9   rw   rT   rU   rU   rV   r6     s    r6   c                 C   s   t  r*t| tsJ dt|tsJ d|jdgksJ d| d}| | S t|ddgd tdi t	 }t| trG| j
tjjjkrKtd	|j| jd
}|jd| g|gdd|gid |S )a
  
    This OP is used to read data at the specified position from the input array
    :ref:`api_fluid_LoDTensorArray` . ``array`` is the input array and ``i``
    is the specified read position. This OP is often used together with
    :ref:`api_fluid_layers_array_write` OP.

    Case 1:
    ::
        Input:
            The shape of first three tensors are [1], and that of the last one is [1,2]:
                array = ([0.6], [0.1], [0.3], [0.4, 0.2])
            And:
                i = [3]

        Output:
            output = [0.4, 0.2]

    Args:
        array (LoDTensorArray): The input LoDTensorArray.
        i (Variable): 1-D Tensor, whose shape is [1] and dtype is int64. It represents the
            specified read position of ``array``.

    Returns:
        Variable: The LoDTensor or Tensor that is read at the specified position of ``array``.

    Examples:
        .. code-block:: python

            # First we're going to create a LoDTensorArray, then we're going to write the Tensor into
            # the specified position, and finally we're going to read the Tensor at that position.
            import paddle.fluid as fluid
            arr = fluid.layers.create_array(dtype='float32')
            tmp = fluid.layers.fill_constant(shape=[3, 2], dtype='int64', value=5)
            i = fluid.layers.fill_constant(shape=[1], dtype='int64', value=10)
            # tmp is the Tensor with shape [3,2], and if we write it into the position with subscript 10
            # of the empty-array: arr, then the length of arr becomes 11.
            arr = fluid.layers.array_write(tmp, i, array=arr)
            # Read the data of the position with subscript 10.
            item = fluid.layers.array_read(arr, i)

            # You can print out the data via executor.
            input = fluid.layers.Print(item, message="The LoDTensor of the i-th position:")
            main_program = fluid.default_main_program()
            exe = fluid.Executor(fluid.CPUPlace())
            exe.run(main_program)

            # The printed result is:

            # 1569588169  The LoDTensor of the i-th position: The place is:CPUPlace
            # Tensor[array_read_0.tmp_0]
            #    shape: [3,2,]
            #    dtype: l
            #    data: 5,5,5,5,5,5,

            # the output is 2-D Tensor with shape [3,2].
            # dtype is the corresponding C++ data type, which may vary in different environments.
            # Eg: if the data type of tensor is int64, then the corresponding C++ data type is int64_t,
            #       so the dtype value is typeid(int64_t).Name(), which is 'x' on MacOS, 'l' on Linux,
            #       and '__int64' on Windows. They both represent 64-bit integer variables.
    z6The 'array' in array_read must be list in dygraph modez<The index 'i' in array_read must be Variable in dygraph moder   rX  r   r  r   r7   z%array should be tensor array vairabler~   read_from_arrayr\  rL   rM   N)r7   )r   rn   rQ   r   rf   r6  r^  r$   r   rP   rN   r   r	  r
  r  rv   r   re   rS   )rQ  r  rT   ri   rU   rU   rV   r7     s>   =r7   c                 C   sr   t di t }t| dtd t|dtd t|dtd |j| jd}|jd| g|g|gdd|gii d	 |S )a/  
    This function creates an operator to shrink rnn memory using the RankTable
    as mentioned in the input parameter.

    NOTE: This API is very low-level API. It is used by DynamicRNN only.

    Since the Dynamic RNN uses no-padding way to implement RNN. The sequence
    will be sorted by order, and the length of valid memory will be shrink after
    each time step.

    Args:
        x(Variable): The memory object in the previous time step.
        i(Variable): The step count variable. A int scalar as LoDTensor.
        table(Variable): The RNNRankTable object.

    Returns:
        the memory variable after shrink.

    Examples:

        Since this API is very low level API. The example is not provided.
        Please reference the implementation of class DynamicRNN for detail
        usage.
    shrink_memoryr   r  rF  r~   Zshrink_rnn_memory)rJ   r]  rI  rL   r   N)rk  )r   rP   r%   r   r   re   rS   )r   r  rF  rT   ri   rU   rU   rV   rk  &	  s   rk  c                 C   s   t  rt| tsJ dt| S t| tr| jtjjj	kr!t
dtdi t }|jdd}d|_|jdd| gid	|gid
 |S )a  
    This OP is used to get the length of the input array :ref:`api_fluid_LoDTensorArray` .
    It can be used together with :ref:`api_fluid_layers_array_read` , :ref:`api_fluid_layers_array_write` ,
    :ref:`api_fluid_layers_While` OP to traverse, read and write LoDTensorArray.

    Args:
        array (LoDTensorArray): The input array that will be used to compute the length.

    Returns:
        Variable: 1-D Tensor with shape [1], which is the length of array. Datatype: int64.

    Examples:
        .. code-block:: python

            import paddle.fluid as fluid
            tmp = fluid.layers.zeros(shape=[10], dtype='int32')
            i = fluid.layers.fill_constant(shape=[1], dtype='int64', value=10)
            # tmp is 1-D Tensor with shape [10]. We write tmp into arr on subscript 10,
            # then the length of arr becomes 11.
            arr = fluid.layers.array_write(tmp, i=i)
            # return the length of arr
            arr_len = fluid.layers.array_length(arr)

            # You can use executor to print out the length of LoDTensorArray.
            input = fluid.layers.Print(arr_len, message="The length of LoDTensorArray:")
            main_program = fluid.default_main_program()
            exe = fluid.Executor(fluid.CPUPlace())
            exe.run(main_program)

            # The printed result is:

            # 1569576542  The length of LoDTensorArray:   The place is:CPUPlace
            # Tensor[array_length_0.tmp_0]
            #    shape: [1,]
            #    dtype: l
            #    data: 11,

            # 1-D Tensor with shape [1], whose value is 11. It means that the length of LoDTensorArray
            # is 11.
            # dtype is the corresponding C++ data type, which may vary in different environments.
            # Eg: if the data type of tensor is int64, then the corresponding C++ data type is int64_t,
            #       so the dtype value is typeid(int64_t).Name(), which is 'x' on MacOS, 'l' on Linux,
            #       and '__int64' on Windows. They both represent 64-bit integer variables.
    rY  z8array should be tensor array vairable in array_length Opr8   r   r~   TZlod_array_lengthrJ   rL   rM   N)r8   )r   rn   rQ   r^   r   rN   r   r	  r
  r  rv   r   rP   r   re  rS   )rQ  rT   rS  rU   rU   rV   r8   M	  s(   .r8   c                       r   )ConditionalBlockGuarda?  
    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t| |jj || _d S )Nr   rl  )r%   ConditionalBlockr   rl  r   rT   r   r   )r   r   r   rU   rV   r   	  s   
zConditionalBlockGuard.__init__c                    s   t t|  S rm   )r   rl  r   r   r   rU   rV   r   	  r   zConditionalBlockGuard.__enter__c                    s   | j   tt| |||S rm   )r   completer   rl  r   r   r   rU   rV   r   	  s   

zConditionalBlockGuard.__exit__r   rU   rU   r   rV   rl  	  s
    rl  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 )rm  a  
    **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.fluid as fluid
             cond = layers.less_than(x=label, y=limit)
             true_image, false_image = layers.split_lod_tensor(
                 input=image, mask=cond)
             true_cond = layers.ConditionalBlock([true_image])

             with true_cond.block():
                 ...
             with false_cond.block():
                 ...
    FNc                 C   s6   |D ]	}t |dtd q|| _|| _td|d| _d S )NrE   rm  conditional_blockr   )r%   r   rO   is_scalar_conditionr   rT   )r   rO   rp  rw   Z
each_inputrU   rU   rV   r   	  s
   zConditionalBlock.__init__c                 C   r   rm   )rl  r   rU   rU   rV   r   	  r'  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rT   c                    r   rU   r*  )r   Z	each_namer   rU   rV   r   	  r   z-ConditionalBlock.complete.<locals>.<listcomp>r   ro  )r   r   )rL   ZScope)r  rp  r   )rT   r   r   r   r   r  r   r   r   r   r   r	  r
  r  rS   rO   rp  "need_append_conditional_block_gradappend_conditional_block_grad)
r   inside_blockintermediater  
param_listZout_listr,  r-  r  conditional_block_oprU   r   rV   rn  	  sD   






zConditionalBlock.completec                 C   s   |j }|j}|dko||kS rW   )backward_block_idxidx)r   rt  grad_sub_block_idxZinside_block_idxrU   rU   rV   rr  	  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 ]}||}|r]|t|j qKt|jtt |jg\}}tj }tjjj}|j }||d  ||| |d| |ddd |D  t }| D ]%}|jt |s|t! krq|j"t | |
| ||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` blongs 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@GRADrU   )r   paramrU   rU   rV   r   9
      zBConditionalBlock.append_conditional_block_grad.<locals>.<listcomp>N)'rx  rT   r   r   r  r  rn   r   r  rE   r  r  r   r   r   cptZto_textrw   r   Zget_grad_op_descdescZop_proto_and_checker_makerZkOpRoleAttrNameZOpRoleZBackwardrS   Z	copy_fromZ	_set_attrZ	set_inputZ
set_outputZoutput_arg_namesZhas_var_recursiveto_bytesZempty_var_namer   Zinfer_var_typeZinfer_shaper)   Z_sync_with_cpp)r   r   rt  rw  rz  Zgrad_sub_blockru  r  Zeach_opr  r  r  r  rv  Zinner_input_namer-  Zgrad_op_descZop_grad_to_varZop_role_attr_namebackwardZnew_op_descZnew_varsZgrad_var_nameargrU   rU   rV   rs  
  sn   









z.ConditionalBlock.append_conditional_block_gradr.  )	r   r   r   r   r   r   rn  rr  rs  rU   rU   rU   rV   rm  	  s    
,
rm  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_flowrd   )rn   r   r   r   r   r   rN   r   r	  r
  r  r   rw   r   re   rf   r   )r   layer_helperr   r   r   r  rU   rU   rV   copy_var_to_parent_blockU
  s$   





r  c                    sX  t  rKt| tsJ d| jdksJ d|  d } | r4|dur2t|s/tdt|j	| S dS |durIt|sFtdt|j	| S dS t
| dd	gd
 t|dttdfd
 tdi t  d}d} fdd}|durt|stdt|j	t| gdd}|  | }	|	durt||	}W d   n1 sw   Y  |durt|stdt|j	tt| gdd}
|
  | }|durt||}W d   n1 sw   Y  |du r|du rdS |du rtd|du rtd|du rd}dgtt| }n	 d}t|||\}}tt|tt|kr7tdtt|tt|tt|t|t|D ]$\}}}z
t||dd W qC tyg } ztd||d}~ww dd }|t|t|t| |rt||\}}t| ddfddfdd}tt|t|t|t|}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 doens'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 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 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. For example:

        .. code-block:: python

            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. 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()`` .

    Raises:
        TypeError: if ``true_fn`` or ``false_fn`` is not callable.
        ValueError: if ``true_fn`` and ``false_fn`` don't return the same nest
            structure of tensors.

    Examples:
        .. code-block:: python

            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)
            ret = paddle.static.nn.cond(pred, true_func, false_func)
            # ret is a tuple containing 2 tensors
            # ret[0] = [[1 1]]
            # ret[1] = [[ True  True  True]
            #           [ True  True  True]]

    z!The pred in cond must be Variabler   z#condition input's numel should be 1r   Nz5The true_fn in cond must be callable, but received {}z6The false_fn in cond must be callable, but received {}predr   zfluid.layers.condrw   r9   c                    s
   t |  S rm   )r  )r   rq  rU   rV   r\   
  s   
 zcond.<locals>.<lambda>Trp  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 namezJtrue fn returns {} vars, but false fn returns {} vars, which is not equalsr4  zFIncompatible return values of `{}` in true_fn and false_fn in cond: {}c                 S   s   t | ||D ]F\}}}t|}t|}tt|D ]2}|| d u r'|| d us3|| d u rK|| d urKtd|t|| || t|| ||  qqd S )NIn cond : Var '{}' or part of it is set differently in ifelse branchs, <{}, {}> in true branch and <{}, {}> in false branch. Set var to 'None' in ifelse block might lead to error.)r/  r   ranger^   r_   r`   ru   rN   )Zseq_trueZ	seq_falseZ	seq_namesZf_trueZf_falsef_namery  rU   rU   rV   check_ret_noneE  s*   

zcond.<locals>.check_ret_nonerG   r~   c                    s   t ||g | S rm   )r{   )rw   rx   ry   )rF   rU   rV   r\   i  s    
c                    s   t t || |S rm   )r   r"   )Z
false_varsZ	true_varsrw   )
merge_funcrU   rV   merge_every_var_listn  s   z"cond.<locals>.merge_every_var_list)r9   )r   rn   r   sizer6  r5  rv   ru   rN   r   r$   r%   r   r   rP   rm  r   r   r   r   r^   _to_sequence_except_dictexpand_undefined_varr/  r   change_none_to_undefinedvarr	   rQ   ra   r   r   )r  true_fnfalse_fnrw   Zreturn_namesZtrue_outputZfalse_outputZcopy_to_parent_funcZtrue_cond_blockZorigin_true_outputZfalse_cond_blockZorigin_false_outputZis_dy2staicZtrue_outZ	false_outZreturn_namerz   r  r  Zmerged_outputrU   )rT   rF   r  rV   r9   l
  s  d







r9   c                    sP   ddl m   fdd}t| tt|t| }t|tt|t|}||fS )Nr   rk   c                    s   | d u r dS | S )NpaddingrU   r(  rk   rU   rV   map_fn  s   z+change_none_to_undefinedvar.<locals>.map_fn)rq   rl   r   rQ   ra   r   )nest1nest2r  	nest1_out	nest2_outrU   rk   rV   r  }  s
   r  c                 C   s   t | tr| gS t| S );
    In this function, dict is not viewed as sequence.
    )rn   rf  r    r(  rU   rU   rV   r    s   
r  c                 C   s   t | trdS t| S )r  F)rn   rf  r   r(  rU   rU   rV   _is_sequence_except_dict  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   rk   )RETURN_VALUE_PREFIXc                    s   t |  fddt| D S )Nc                    s   g | ]} d qS )r  rU   r   rk   rU   rV   r     r|  zGexpand_undefined_var.<locals>.pack_undefined_var_as.<locals>.<listcomp>)r   r   )seqrk   rU   rV   pack_undefined_var_as  s   z3expand_undefined_var.<locals>.pack_undefined_var_asc              	      s   |  sBt| s| d u rB| d u r>|d ur>|dkr.td|t| | t|| |S td|t||t| |  |S | S )Nr   r  )
startswithrn   r_   r`   ru   rN   )Zn1Zn2rw   orderr  rl   r  rU   rV   r    s*   
z$expand_undefined_var.<locals>.map_fnc                 S      g | ]}d qS r   rU   r   rU   rU   rV   r     r]   z(expand_undefined_var.<locals>.<listcomp>c                 S   r  )r   rU   r   rU   rU   rV   r     r]   )rq   rl   Z9paddle.fluid.dygraph.dygraph_to_static.return_transformerr  rQ   ra   r  r  )r  r  namesr  r  r  rU   r  rV   r    s6   		r  c                 C   s   dj | ||||d}|S )NzW{what} of '{arg_name}' in {op_name} must be {right_value}, but received: {error_value}.)whatarg_nameop_nameright_valueerror_value)ru   )r  r  r  r  r  error_messagerU   rU   rV   _error_message  s   r  c           	      C   sV   t di t }dd }|| |\} }|}t| D ]\}}tt|||d}q|}| S )aB  
    :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 with 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)  # [[1. 1.]]
                print(res_2)  # [3 3 3]
    rA   c              
   S   s   t | dttfd | D ]J}t|tsttdddtt|t|dkr4ttddddtt|d |\}}t|t	sIttdddd	t|t
|sUtd
|jq|du rpt| d }| | d }| d| } | |fS t
|sxtd| |fS )zg
        Check arguments pred_fn_pairs and default. Return canonical pre_fn_pairs and default.
        pred_fn_pairsrA   The elements' typer   The tuple's size2-tuplezThe pred's typezboolean Variablez<The fn for {} of pred_fn_pairs in Op(case) must be callable.Nr   )The default in Op(case) must be callable.)r%   rQ   rR   rn   rv   r  rN   r^   r   r   r5  ru   rw   )r  defaultZpred_fnr  fnZdefault_indexrU   rU   rV   _case_check_args/  s^   
		

zcase.<locals>._case_check_argsr  r  r  N)rA   )r   rP   reversedr"   r9   )	r  r  rw   rT   r  r  r  r  final_fnrU   rU   rV   rA     s   A7rA   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 )r-   a  
    :api_attr: Static Graph

    This class is used to implement Switch branch control function.
    Switch branch contains several case branches and one default branch.
    Switch control flow checks whether the case branch conditions are satisfied in turn,
    and only executes the statement after the first case branch that satisfies the conditions.
    If there is no case branch that satisfies the condition,
    only the statement following the default branch is executed.

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

    Member Functions:
        case(condition): The case branch of Switch whose parameter cond is a scalar Variable of bool type. Only if the cond of the current case branch is True and the cond of the previous case branch is False, the statement after the case branch will be executed, and the statement after the case branch will not be executed.

        default(): The default branch of Switch. When cond of all case branches is False, the statement after default branch is executed.

    Case and default functions can only be used inside the scope of Switch, as shown below:

    .. code-block:: python

        '''
        with fluid.layers.Switch() as switch:
            with switch.case(cond1):
                i = fluid.layers.fill_constant(shape=[1], dtype='int64', value=1)
            with switch.case(cond2):
                i = fluid.layers.fill_constant(shape=[1], dtype='int64', value=2)
            with switch.default():
                i = fluid.layers.fill_constant(shape=[1], dtype='int64', value=0)
        '''

    Args:
        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

            import paddle.fluid as fluid

            lr = fluid.layers.create_global_var(
                shape=[1],
                value=0.0,
                dtype='float32',
                persistable=True,
                name="learning_rate")
            zero_var = fluid.layers.fill_constant(
                shape=[1], dtype='float32', value=0.0)
            one_var = fluid.layers.fill_constant(
                shape=[1], dtype='float32', value=1.0)
            two_var = fluid.layers.fill_constant(
                shape=[1], dtype='float32', value=2.0)

            global_step = fluid.layers.autoincreased_step_counter(counter_name='@LR_DECAY_COUNTER@', begin=0, step=1)

            with fluid.layers.control_flow.Switch() as switch:
                with switch.case(global_step == zero_var):
                    fluid.layers.assign(input=one_var, output=lr)
                with switch.default():
                    fluid.layers.assign(input=two_var, output=lr)

            exe = fluid.Executor(fluid.CPUPlace())
            exe.run(fluid.default_startup_program())

            res = exe.run(fluid.default_main_program(), feed={}, fetch_list=[lr])
            print(res) # [array([1.], dtype=float32)]
    Nc                 C   s   t d|d| _d| _g | _d S )Nswitchr   F)r   rT   inside_scopepre_not_conditionsr   rU   rU   rV   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|d}| j| t	|S t| j}| j|d	  }t|t|dd
}| j| tt||d
gdd}t	|S )Nz!case should be called inside with	conditionr   z/the member function case of fluid.layers.Switchr   Tr  r(  r   )r   ra  )
r  r   r$   r^   r  rm  r   r   r   rl  )r   r  
cond_blockZnot_condpre_cond_numZpre_not_condZnew_not_condrU   rU   rV   rA     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 conditionr   Tr  )r^   r  r   rm  rl  )r   r  r  rU   rU   rV   r    s   
zSwitch.defaultc                 C   s
   d| _ | S )zN
        set flag that now is inside switch.block {}
        :return:
        Tr  r   rU   rU   rV   r     s   zSwitch.__enter__c                 C   s   d| _ |d ur	dS dS r   r  r   rU   rU   rV   r     s   zSwitch.__exit__rm   )	r   r   r   r   r   rA   r  r   r   rU   rU   rU   rV   r-   q  s    
E
r-   c                   @   s$   e Zd Zdd Zdd Zdd ZdS )IfElseBlockGuardc                 C   sl   t |ts	td|jtjkrtd|| _|| _|r |j| _	n|j
| _	t | j	ts.td| j	 | _	d S )Nz*ifelse must be an instance of IfElse classz/You cannot invoke IfElse.block() inside a blockzUnexpected situation)rn   r:   rv   r   OUT_IF_ELSE_BLOCKSr   is_trueieconditional_true_blockr  conditional_false_blockrm  r   )r   r  ifelserU   rU   rV   r     s   

zIfElseBlockGuard.__init__c                 C   s$   | j rtjntj| j_| j  d S rm   )r  r:   IN_IF_ELSE_TRUE_BLOCKSIN_IF_ELSE_FALSE_BLOCKSr  r   r  r   r   rU   rU   rV   r     s
   zIfElseBlockGuard.__enter__c                 C   sH   | j |||s
dS t| jj| jrdnd dkrtdtj| j_	d S )NFr   r   zMust set output inside block)
r  r   r^   r  output_tabler  r   r:   r  r   r   rU   rU   rV   r     s
   zIfElseBlockGuard.__exit__N)r   r   r   r   r   r   rU   rU   rU   rV   r    s    r  c                   @   sV   e Zd ZdZdZdZdZdddZdd	 Zd
d Z	dd Z
dd Zdd Zdd ZdS )r:   a  
    :api_attr: Static Graph

    This class is used to implement IfElse branch control function. IfElse contains two blocks, true_block and false_block. IfElse will put data satisfying True or False conditions into different blocks to run.

    Cond is a 2-D Tensor with shape [N, 1] and data type bool, representing the execution conditions of the corresponding part of the input data.

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

    IfElse OP is different from other OPs in usage, which may cause some users confusion. Here is a simple example to illustrate this OP.

    .. code-block:: python

        # The following code completes the function: subtract 10 from the data greater than 0 in x, add 10 to the data less than 0 in x, and sum all the data.
        import numpy as np
        import paddle.fluid as fluid

        x = fluid.layers.data(name='x', shape=[4, 1], dtype='float32', append_batch_size=False)
        y = fluid.layers.data(name='y', shape=[4, 1], dtype='float32', append_batch_size=False)

        x_d = np.array([[3], [1], [-2], [-3]]).astype(np.float32)
        y_d = np.zeros((4, 1)).astype(np.float32)

        # Compare the size of x, y pairs of elements, output cond, cond is shape [4, 1], data type bool 2-D tensor.
        # Based on the input data x_d, y_d, it can be inferred that the data in cond are [[true], [true], [false], [false]].
        cond = fluid.layers.greater_than(x, y)
        # Unlike other common OPs, ie below returned by the OP is an IfElse OP object
        ie = fluid.layers.IfElse(cond)

        with ie.true_block():
            # In this block, according to cond condition, the data corresponding to true dimension in X is obtained and subtracted by 10.
            out_1 = ie.input(x)
            out_1 = out_1 - 10
            ie.output(out_1)
        with ie.false_block():
            # In this block, according to cond condition, get the data of the corresponding condition in X as false dimension, and add 10
            out_1 = ie.input(x)
            out_1 = out_1 + 10
            ie.output(out_1)

        # According to cond condition, the data processed in the two blocks are merged. The output here is output, the type is List, and the element type in List is Variable.
        output = ie() #  [array([[-7.], [-9.], [ 8.], [ 7.]], dtype=float32)]

        # Get the first Variable in the output List and add all elements.
        out = fluid.layers.reduce_sum(output[0])

        exe = fluid.Executor(fluid.CPUPlace())
        exe.run(fluid.default_startup_program())

        res = exe.run(fluid.default_main_program(), feed={"x":x_d, "y":y_d}, fetch_list=[out])
        print(res)
        # [array([-1.], dtype=float32)]

    Args:
        cond (Variable): cond is a 2-D Tensor with shape [N, 1] and data type bool, representing the corresponding execution conditions of N input data. The data type is bool.
        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:
        Unlike other common OPs, the OP call returns an IfElse OP object (e.g. ie in the example), which branches the input data by calling the internal functions of the object ``true_block ()``, ``false_block ()``, ``input ()``, ``output ()``, and integrates the data processed by different branches as the overall output by calling the internal ``call ()`` function. The output type is a list, and the type of each element in the list is Variable.

    Internal Functions:
        The block is constructed by calling the ``with ie. true_block()`` function in the object, and the computational logic under condition true is put into the block. If no corresponding block is constructed, the input data in the corresponding conditional dimension is unchanged.

        The block is constructed by calling the ``with ie. false_block()`` function in the object, and the computational logic under condition false is put into the block. If no corresponding block is constructed, the input data in the corresponding conditional dimension is unchanged.

        ``Out = ie. input (x)`` will take out the data of the corresponding conditional dimension in X and put it into out, supporting the internal processing of multiple inputs in block.

        ``ie. output (out)`` writes the result to the output of the corresponding condition.

        There is a ``call ()`` function inside the object, that is, by calling ``output = ie ()``, all the outputs inside the block of False are fused as the whole output, the output type is a list, and the type of each element in the list is Variable.

    r   r   r   Nc                 C   st   t |dtd t |dttd fd td|d| _|| _i | _tj	| _
t| jgd| _t| jgd| _g g f| _d S )Nr9   zfluid.layers.IfElserw   r  r   )rO   )r%   r   r   rN   r   rT   r9   input_tabler:   r  r   rm  r  r  r  )r   r9   rw   rU   rU   rV   r   h  s   zIfElse.__init__c                 C   s   | j tjkr
tdt|| jvrO|  }|jt	d| j
j |jd}|jt	d| j
j |jd}|jd|| jd||dddid	 ||f| jt|< n	| jt| \}}| j tjkr`|S |S )
Nzinput must in true/false blocksZifelse_inputrw   re   r}   rI   r   r|   r   r   )r   r:   r  r   idr  r   r   r   r   rT   rw   re   rS   r9   r  )r   r   r   r   r   rU   rU   rV   rE   s  s:   

	zIfElse.inputc                 C   s   | j j }| j j|jS rm   )rT   r   r   r   r   )r   r   rU   rU   rV   r     s   zIfElse._parent_blockc                 C   
   t d| S )NTr  r   rU   rU   rV   
true_block     
zIfElse.true_blockc                 C   r  r   r  r   rU   rU   rV   false_block  r  zIfElse.false_blockc                 G   s   | j | jkr
td| j| j | jkrdnd }|  }|D ]&}t|dtd |jt	
d| jjdg|jd}|| t||d	 qd S )
Nz+output can only be invoked in the sub-blockr   r   zeach outputzfluid.layers.IfElse.outputr  r   r  )rE   r   )r   r  r   r  r  r   r%   r   r   r   r   r   rT   rw   re   r   r   )r   ZoutsZ	out_tabler   Zeach_outZoutside_outrU   rU   rV   r     s&   
zIfElse.outputc              
   C   s   | j | jkr
tdttt| j\}}|dkr |dkr td||kr0|dkr0|dkr0td|dks8|dkrD| j|dkrAd S d S g }t| j D ]\}}|t	||| j
| j
dd qK|S )Nz)IfElse::__call__ must be out of sub-blockr   z2Must invoke true_block/false_block before __call__zThe output side must be samer   )r   r   rF   r   r|   )r   r  r   rQ   ra   r^   r  r/  r   r   r9   )r   Z	false_lenZtrue_lenZrlistrx   ry   rU   rU   rV   r     s.   	zIfElse.__call__rm   )r   r   r   r   r  r  r  r   rE   r   r  r  r   r   rU   rU   rU   rV   r:     s    K
$r:   c                   @   s   e Zd ZdZdZdZdZdddZd dd	Zd
d Z	e
dd Zdd Z					d!ddZdd Zdd Zdd Zdd Zdd ZdS )"r;   a  
    :api_attr: Static Graph

    **Note: the input of this class should be LoDTensor which holds the
    information of variable-length sequences. If the input is fixed-length Tensor,
    please use StaticRNN (fluid.layers.** :ref:`api_fluid_layers_StaticRNN` **) for
    better performance.**

    DynamicRNN can process a minibatch of variable-length sequences.
    The length of each sample can be different and is recorded in LoD.
    In DynamicRNN, an input sequence will be unfolded into time steps and users
    can define how to process each time step in :code:`block()` .
    The total number of time steps is determined by the longest sequence.
    DynamicRNN will not pad all sequences to the same length, instead it will
    sort the sequences internally by the sequence length in descending order.
    The input sequences will be shrank because only sequences of which the
    length is larger than the time step will participate the remaining calculation.

    If defined :code:`drnn = DynamicRNN()`, then users can call :code:`drnn()`
    to obtain the result sequences. It is a LoDTensor gained by merging all
    time steps's output. When RNN's input sequence x meets :code:`x.lod_level == 1`,
    the output LoDTensor will have the same LoD with x. The result of :code:`drnn()`
    includes RNN's outputs of all time steps, users can call
    :ref:`api_fluid_layers_sequence_last_step` to extract the data of the last time step.

    Warning:
        Currently it is not supported to set :code:`is_sparse = True` of any
        layers defined within DynamicRNN's :code:`block` function.

    Args:
        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

            import paddle.fluid as fluid

            sentence = fluid.data(name='sentence', shape=[None, 32], dtype='float32', lod_level=1)
            encoder_proj = fluid.data(name='encoder_proj', shape=[None, 32], dtype='float32', lod_level=1)
            decoder_boot = fluid.data(name='boot', shape=[None, 10], dtype='float32')

            drnn = fluid.layers.DynamicRNN()
            with drnn.block():
                # Set sentence as RNN's input, each time step processes a word from the sentence
                current_word = drnn.step_input(sentence)
                # Set encode_proj as RNN's static input
                encoder_word = drnn.static_input(encoder_proj)
                # Initialize memory with boot_memory, which need reorder according to RNN's input sequences
                memory = drnn.memory(init=decoder_boot, need_reorder=True)
                fc_1 = fluid.layers.fc(input=encoder_word, size=30)
                fc_2 = fluid.layers.fc(input=current_word, size=30)
                decoder_inputs = fc_1 + fc_2
                hidden, _, _ = fluid.layers.gru_unit(input=decoder_inputs, hidden=memory, size=30)
                # Update memory with hidden
                drnn.update_memory(ex_mem=memory, new_mem=hidden)
                out = fluid.layers.fc(input=hidden, size=10, bias_attr=True, act='softmax')
                # Set hidden and out as RNN's outputs
                drnn.output(hidden, out)

            # Get RNN's result
            hidden, out = drnn()
            # Get RNN's result of the last time step
            last = fluid.layers.sequence_last_step(out)
    r   r   r   Nc                 C   sv   t d|d| _tj| _d | _d | _d | _d | _t	 | _
g | _g | _| jjdd| _d| j_t| j| _g | _g | _d S )NZdynamic_rnnr   r   r~   F)r   rT   r;   
BEFORE_RNNr   r?  max_seq_lenstep_idxzero_idxrf  mem_dictoutput_arrayrH   r   r9   re  r,   r  input_arraymem_linkr   rU   rU   rV   r     s   
zDynamicRNN.__init__c                 C   s0  |  d t|dtd |  }| jdu rk|jtdtj	j
jd| _d| j_|jdd|id	| jid
|id |jtddd| _d| j_|jdd| jid	| jid d| j_|jd| j| jdd	| jiddid |jtdtj	j
j|jd}| j||jf |jd|| jdd	|id t|| jdS )a  
        This function is used to set sequence x as DynamicRNN's input.
        The maximum sequence length in x determines the number of time steps
        the RNN unit will be executed. DynamicRNN can take multiple inputs.
        When all inputs' :code:`lod_level` are 1, all inputs should hold the
        same LoD. When :code:`x.lod_level >= 2` , the input sequence will be
        unfold along specified level, and the slice of each time step is a
        LoDTensor whose lod_level is :code:`x.lod_level - level - 1` .
        In this case, the specified LoD level of multiple inputs should be the same.

        - Case 1:

        .. code-block:: text

            # input, where Si is slice data of shape [1, N]
            level = 0
            x.lod = [[2, 1, 3]]
            x.shape = [6, N]
            x.data = [[S0],
                      [S0],
                      [S1],
                      [S2],
                      [S2],
                      [S2]]

            # output
            # step 0, time step data of 3 sequences
            out.lod = [[]]
            out.shape = [3, N]
            out.data = [[S2],
                        [S0],
                        [S1]]

            # step 1, time step data of 2 sequences
            out.lod = [[]]
            out.shape = [2, N]
            out.data = [[S2],
                        [S0]]

            # step 2, time step data of 1 sequences
            out.lod = [[]]
            out.shape = [1, N]
            out.data = [[S2]]


        Args:
            x (Variable): The input LoDTensor which holds information of a
                minibatch of variable-length sequences and should meet :code:`x.lod_level >= 1` .
                When RNN has multiple inputs, the first dimension should match
                across all inputs, but other shape components may differ.
                Optional data types are: bool, float16, float32, float64, int8, int16, int32, int64, uint8.
            level (int, optional): The level of lod used to split steps.
                It should be in range :math:`[0, x.lod\_level)` . The default value is 0.

        Returns:
            Variable: The current time step in the input sequence. If there are :code:`num_sequences` \
                sequences in x whose length is larger than :code:`step_idx` , the returned Variable \
                will only hold the :code:`step_idx` -th time step of those `num_sequences` sequences. \
                The data type is the same as input. If :code:`x.lod_level == 1` , the return value is \
                a Tensor of shape :math:`\{num\_sequences, x.shape[1], ...\}` , or it will \
                be a variable-length LoDTensor.

        Raises:
            ValueError: When :code:`step_input()` is called outside :code:`block()` .
            TypeError: When x is not a Variable.

        Examples:
            ..  code-block:: python

                import paddle.fluid as fluid

                sentence = fluid.data(name='sentence', shape=[None, 1], dtype='int64', lod_level=1)
                embedding = fluid.layers.embedding(input=sentence, size=[65536, 32], is_sparse=True)

                drnn = fluid.layers.DynamicRNN()
                with drnn.block():
                    # Set embedding as RNN's input, each time step processes a word from the sentence
                    word = drnn.step_input(embedding)
                    # Initialize memory to a Tensor whose value is 0, shape=[batch_size, 200],
                    # where batch_size is the number of sequences in embedding.
                    memory = drnn.memory(shape=[200])
                    hidden = fluid.layers.fc(input=[word, memory], size=200, act='relu')
                    # Update memory to hidden
                    drnn.update_memory(ex_mem=memory, new_mem=hidden)
                    # Set hidden as RNN's output
                    drnn.output(hidden)

                # Get RNN's result
                rnn_output = drnn()
        r   r   z$fluid.layers.DynamicRNN.step_input()Nr?  )rw   rN   TrJ   rL   r|   r   Zdynamic_rnn_max_seq_lenr   r  FrH  rI  rM   r1   rc  rb  Zdynamic_rnn_input_arrayrN  rL  rO  rQ  r  )r   r%   r   _parent_block_r?  r   r   rD  r   r	  r
  rC  re  rS   r  r9   r  r  re   r  r   r7   )r   r   r|   r   r  rU   rU   rV   r   .  sX   
[

zDynamicRNN.step_inputc                 C   s   |  d t|dtd | jdu rtd|  }|jtdt	j
jj|jd}|jd|g| jgd	d
|gid t|| j| jS )ao  
        This function is used to set x as DynamicRNN's static input. It is optional.

        - Case 1, set static input with LoD

        .. code-block:: text

            # RNN's input is the same as the case listed in step_input
            # static input, where Si is slice data of shape [1, M]
            x.lod = [[3, 1, 2]]
            x.shape = [6, M]
            x.data = [[S0],
                      [S0],
                      [S0],
                      [S1],
                      [S2],
                      [S2]]

            # step 0, batch data corresponding to the 3 input sequences
            out.lod = [[2, 3, 1]]
            out.shape = [6, M]
            out.data = [[S2],
                        [S2],
                        [S0],
                        [S0],
                        [S0],
                        [S1]]

            # step 1, batch data corresponding to the 2 input sequences
            out.lod = [[2, 3]]
            out.shape = [5, M]
            out.data = [[S2],
                        [S2],
                        [S0],
                        [S0],
                        [S0]]

            # step 2, batch data corresponding to the 1 input sequences
            out.lod = [[2]]
            out.shape = [2, M]
            out.data = [[S2],
                        [S2]]


        - Case 2, set static input without LoD

        .. code-block:: text

            # RNN's input is the same as the case listed in step_input
            # static input, where Si is slice data of shape [1, M]
            x.lod = [[]]
            x.shape = [3, M]
            x.data = [[S0],
                      [S1],
                      [S2]]

            # step 0, batch data corresponding to the 3 input sequences
            out.lod = [[]]
            out.shape = [3, M]
            out.data = [[S2],
                        [S0],
                        [S1]]

            # step 1, batch data corresponding to the 2 input sequences
            out.lod = [[]]
            out.shape = [2, M]
            out.data = [[S2],
                        [S0]]

            # step 2, batch data corresponding to the 1 input sequences
            out.lod = [[]]
            out.shape = [1, M]
            out.data = [[S2]]


        Args:
            x (Variable): The static input LoDTensor which should hold the same number of sequences
                as RNN's input (the input LoDTensor set by :code:`step_input()` ). If the LoD is None,
                the input x will be treated as a minibatch with :code:`x.shape[0]` sequences of length 1.
                Optional data types are: bool, float16, float32, float64, int8, int16, int32, int64, uint8.

        Returns:
            Variable: The input LoDTensor after sorted and shrank. If there are :code:`num_sequences` \
                sequences in RNN's input LoDTensor whose length is larger than :code:`step_idx` , \
                the static input Tensor will be sorted to the same order as RNN's input and \
                will only retain data corresponding to those :code:`num_sequences` sequences. \
                The data type is the same as input. If :code:`x.lod == None` , the return value is \
                a Tensor of shape :math:`\{num\_sequences, x.shape[1], ...\}` , or it will \
                be a variable-length LoDTensor.

        Raises:
            ValueError: When :code:`static_input()` is called outside :code:`block()` .
            TypeError: When x is not a Variable.
            RuntimeError: When :code:`static_input()` is called before :code:`step_input()` .

        Examples:
            .. code-block:: python

                import paddle.fluid as fluid

                sentence = fluid.data(name='sentence', shape=[None, 32], dtype='float32', lod_level=1)
                encoder_proj = fluid.data(name='encoder_proj', shape=[None, 32], dtype='float32', lod_level=1)
                decoder_boot = fluid.data(name='boot', shape=[None, 10], dtype='float32')

                drnn = fluid.layers.DynamicRNN()
                with drnn.block():
                    # Set sentence as RNN's input, each time step processes a word from the sentence
                    current_word = drnn.step_input(sentence)
                    # Set encode_proj as RNN's static input
                    encoder_word = drnn.static_input(encoder_proj)
                    # Initialize memory with boot_memory, which need reorder according to RNN's input sequences
                    memory = drnn.memory(init=decoder_boot, need_reorder=True)
                    fc_1 = fluid.layers.fc(input=encoder_word, size=30)
                    fc_2 = fluid.layers.fc(input=current_word, size=30)
                    decoder_inputs = fc_1 + fc_2
                    hidden, _, _ = fluid.layers.gru_unit(input=decoder_inputs, hidden=memory, size=30)
                    # Update memory with hidden
                    drnn.update_memory(ex_mem=memory, new_mem=hidden)
                    out = fluid.layers.fc(input=hidden, size=10, bias_attr=True, act='softmax')
                    # Set out as RNN's output
                    drnn.output(out)

                # Get RNN's result
                rnn_output = drnn()
        static_inputr   z&fluid.layers.DynamicRNN.static_input()Nz1static_input() must be called after step_input().Z"dynamic_rnn_static_input_reorderedrN  r=   rO  rL   rM   )r   r%   r   r?  rs   r  r   r   rD  r   r	  r
  
LOD_TENSORre   rS   rk  r  )r   r   r   Zx_reorderedrU   rU   rV   r    s$   
~
zDynamicRNN.static_inputc                 c   s    | j tjkrtdtdgdddd| _d| j_tj| _ | j	 . dV  t
| jd	dd
 | jD ]\}}t|| j|d q1t| j| jd| jd W d   n1 sSw   Y  tj| _ | jD ]}| jt|| jd q_dS )a]  
        The function is used to list the operations executed during
        each time step in RNN. The operation list will be executed :code:`max_sequence_len`
        times (where :code:`max_sequence_len` is the maximum length of RNN's input sequences).

        Raises:
            ValueError: When :code:`block()` is called multi-times.
        z#rnn.block() can only be invoke oncer   r   r   Trf   re   r   rb  FNrT  )r   r   rW  r_  )r   ra  rb  r9   )r   rF  )r   r;   r  r   r
   r  re  IN_RNNr  r   r.   r  r/   r1   r  r9   	AFTER_RNNr  rH   r   rR  r?  )r   r  	mem_arrayZ
each_arrayrU   rU   rV   r   H  s4   


zDynamicRNN.blockc                 O   s2   | j tjkr
tdt| jdkr| jd S | jS )a(  
        This function is used to get the output  sequences of DynamicRNN.

        Args:
            None

        Returns:
            Variable or Variable list: RNN's output sequences.

        Raises:
            ValueError: When :code:`__call__()` is called before :code:`block()` .
        zDOutput of the dynamic RNN can only be visited outside the rnn block.r   r   )r   r;   r  r   r^   rH   r   rU   rU   rV   r   m  s   
zDynamicRNN.__call__r   Fr   c                 C   s  |  d |   |durt|dttfd |durt|dtd |  }|}|dkrV| jdu r4td|j	t
dtjjj|jd	}|jd
|g| jgdd|gid |}|j	t
dtjjj|jd	}	|jd|| jdd|	id t|	| jd}
t|
| j| jd}
|	| j|
j< |
S t| jdkrtd|  }|j	t
d|d}| jd \}}|j	t
d|d}|jd|g| jgdd|gid |jdd|gid|gidg| t||jdd | j|dS )a  
        Create a memory Variable for DynamicRNN to deliver data cross time steps.
        It can be initialized by an existing Tensor or a constant Tensor of given
        dtype and shape.

        Args:
            init (Variable, optional): LoDTensor used to initialize the memory.
                If init is not None, it should hold the same number of sequences
                as RNN's input (the input LoDTensor set by :code:`step_input()` )
                and the memory will be initialized to it. If init's LoD is None,
                it will be treated as a minibatch with :code:`init.shape[0]` sequences
                of length 1. The default value is None.
            shape (list|tuple, optional): When init is None, it is used to specify
                the memory's shape. Note that the shape does not include the batch_size.
                If setting shape to :math:`\{D_1, D_2, ...\}` , the shape of memory Tensor
                will be :math:`\{batch\_size, D_1, D_2, ...\}` , where batch_size is
                determined by RNN's input sequences. The default value is None.
            value (float, optional): When init is None, it is used as initialized value
                of memory. The default value is 0.0.
            need_reorder (bool, optional): When init is not None, it determines whether
                the memory needs to reorder like the RNN's input sequences. It should be
                set to True when the initialized memory depends on the order of input samples.
                The default value is False.
            dtype (str|numpy.dtype, optional): When init is None, it is used to set the
                data type of memory. The default value is "float32". Optional data types
                are: "float32", "float64", "int32", "int64".

        Returns:
            Variable: The memory LoDTensor after shrank.  If there are :code:`num_sequences` \
                sequences in RNN's input LoDTensor whose length is larger than :code:`step_idx` , \
                the memory Tensor also need to be shrank and will only retain data \
                corresponding to those :code:`num_sequences` sequences.

        Raises:
            ValueError: When :code:`memory()` is called outside :code:`block()` .
            TypeError: When init is set and is not a Variable.
            ValueError: When :code:`memory()` is called before :code:`step_input()` .

        Examples:
            .. code-block:: python

                import paddle.fluid as fluid

                sentence = fluid.data(name='sentence', shape=[None, 32], dtype='float32', lod_level=1)
                boot_memory = fluid.data(name='boot', shape=[None, 10], dtype='float32')

                drnn = fluid.layers.DynamicRNN()
                with drnn.block():
                    # Set sentence as RNN's input, each time step processes a word from the sentence
                    word = drnn.step_input(sentence)
                    # Initialize memory with boot_memory, which need reorder according to RNN's input sequences
                    memory = drnn.memory(init=boot_memory, need_reorder=True)
                    hidden = fluid.layers.fc(input=[word, memory], size=10, act='tanh')
                    # Update memory with hidden
                    drnn.update_memory(ex_mem=memory, new_mem=hidden)
                    # Set hidden as RNN's output
                    drnn.output(hidden)

                # Get RNN's result
                rnn_output = drnn()


        Examples:
            .. code-block:: python

                import paddle.fluid as fluid

                sentence = fluid.data(name='sentence', shape=[None, 32], dtype='float32', lod_level=1)

                drnn = fluid.layers.DynamicRNN()
                with drnn.block():
                    # Set sentence as RNN's input, each time step processes a word from the sentence
                    word = drnn.step_input(sentence)
                    # Initialize memory to a Tensor whose value is 0, shape=[batch_size, 10],
                    # where batch_size is the number of sequences in sentence.
                    memory = drnn.memory(shape=[10], dtype='float32', value=0)
                    hidden = fluid.layers.fc(input=[word, memory], size=10, act='tanh')
                    # Update memory with hidden
                    drnn.update_memory(ex_mem=memory, new_mem=hidden)
                    # Set hidden as RNN's output
                    drnn.output(hidden)

                # Get RNN's result
                rnn_output = drnn()
        r   Nrf   z fluid.layers.DynamicRNN.memory()r   TzpIf set need_reorder to True, make sure step_input be invoked before memory(init=init, need_reordered=True, ...).Zdynamic_rnn_mem_init_reorderedrN  r=   rO  rL   rM   Zdynamic_rnn_mem_arrayr[  r\  r  )r   r  rF  r   z@step_input should be invoked before memory(shape=..., value=...)Zmem_initr  in0rj  r   r   rX   )rf   r   re   r   r   )r   _init_zero_idx_r%   rQ   rR   r   r  r?  r   r   r   rD  r   r	  r
  r  re   rS   r  r  r7   r  rk  r  rw   r^   r  rV  r   )r   r   rf   r   Zneed_reorderre   r   Zinit_tensorZinit_reorderedr  ZretvZarrr  rU   rU   rV   r     s   
]





zDynamicRNN.memoryc                 C   sl   |  d t|dtd t|dtd | j|jd}|du r#td| jdu r,td| j	||f dS )a  
        Update the memory which need to be delivered across time steps.

        Args:
            ex_mem (Variable): The memory data of previous time step.
            new_mem (Variable): The new memory data produced in current time step.
                The shape and data type of ex_mem and new_mem should be the same.

        Returns:
            None

        Raises:
            ValueError: When :code:`update_memory()` is called outside :code:`block()` .
            TypeError: When :code:`ex_mem` or :code:`new_mem` is not a Variable.
            ValueError: When :code:`ex_mem` is defined by :code:`memory()` .
            ValueError: When :code:`update_memory()` is called before :code:`step_input()` .
        r   ex_memz'fluid.layers.DynamicRNN.update_memory()r  Nz)Please invoke memory before update_memoryz-Please invoke step_input before update_memory)
r   r%   r   r  getrw   r   r?  r  r   )r   r  r  r  rU   rU   rV   r   5  s&   

zDynamicRNN.update_memoryc              	   G   sz   |  d |  }|D ]/}t|dtd |jtd| jj	d|j	gt
jjj|jd}t|| j|d | j| qdS )	ab  
        This function is used to set :code:`outputs` as RNN's output.

        Args:
            *outputs (Variable ...): The output Tensor. DynamicRNN can mark multiple
                Variables as its output.

        Returns:
            None

        Raises:
            ValueError: When :code:`output()` is called outside :code:`block()` .
        r   rH   zfluid.layers.DynamicRNN.outputr  r  rN  r_  N)r   r  r%   r   r   r   r   r   rT   rw   r   r	  r
  r  re   r/   r  r  r   )r   rH   r   r   Zoutside_arrayrU   rU   rV   r   ]  s    
zDynamicRNN.outputc              	   C   s^   | j d u r-|  }|jtddd| _ |jdi d| j gidg| j jtddd	d
 d S d S )Nr  r   r  r
   rL   r   r   Tr  r   )r  r  r   r   rD  rS   re   rV  )r   r   rU   rU   rV   r  {  s    



zDynamicRNN._init_zero_idx_c                 C   r   r   r   r   rU   rU   rV   r    s
   

zDynamicRNN._parent_block_c                 C   r   )Nz){0} can only be invoked inside rnn block.)r   r;   r  r   ru   r   rU   rU   rV   r     s
   z DynamicRNN._assert_in_rnn_block_rm   r  )NNr   Fr   )r   r   r   r   r  r  r  r   r   r  r   r   r   r   r   r   r  r  r   rU   rU   rU   rV   r;     s2    C

 
 
$
 0(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 with 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)

            main_program = paddle.static.default_startup_program()
            startup_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])
                print(res_1)  # [[1. 1.]]
                print(res_2)  # [[2 2] [2 2]]
                print(res_3)  # [3 3 3]
    rB   c              
   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 r9tt|n|}g }|D ]a}t|tsRttdddtt|t|d	krhttd
dddtt|d |\}}t|ts}ttdddtt|||v rt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dgd|d}	t| |	}
||
|f q||fS )Nbranch_index)Zuint8rG   r   rB   r   
branch_fnsc                 s   s    | ]}t |V  qd S rm   )r5  )r   r  rU   rU   rV   	<genexpr>  s    z3switch_case.<locals>._check_args.<locals>.<genexpr>r  r   r  r  r  zThe key's typezHThe key in 'branch_fns' must be unique, but '{}' appears more than once.zThe type of function for key {}r5  rX   r   r  )rf   re   r   )r$   r#   re   r	   r%   rQ   rR   rf  rn   itemsallrB  rv   r  rN   r^   r   r   r   ru   r   r5  sortedr
   r5   )r  r  r  Zkeys_of_fnsZindex_fn_pairkeyr  r  indexZ	new_indexr  rU   rU   rV   _check_args  s   







z switch_case.<locals>._check_argsr  N)rB   )r   rP   r"   r9   )r  r  r  rw   rT   r  r  r  r  r  r  rU   rU   rV   rB     s   F_rB   c                 C   sv   t | dtd t |dtd |jtjjjkrtdtd
i t	 }|j
| jd}|jd| g|gdd|gid |S )a  
    ${comment}

    Args:
        x(${x_type}): ${x_comment}.
        rank_table(${rank_table_type}): ${rank_table_comment}.

    Returns:
        out(${out_type}): ${out_comment}.

    Examples:
        .. code-block:: python

          import paddle.fluid as fluid
          data_desc = (['input', [9], 0], ['ref', [5], 1])
          data = fluid.layers.data(name=data_desc[0][0], shape=data_desc[0][1])
          rank_data = fluid.layers.data(name=data_desc[1][0], shape=data_desc[1][1])
          table = fluid.layers.control_flow.lod_rank_table(rank_data)
          new_data = fluid.layers.reorder_lod_tensor_by_rank(
                           x=data, rank_table=table)

    r   r=   rJ  z0The type of rank_table should be LOD_RANK_TABLE.r~   rO  rL   rM   N)r=   )r%   r   rN   r   r	  r
  rC  rv   r   rP   r   re   rS   )r   rJ  rT   ri   rU   rU   rV   r=   L  s   r=   c                 C   s   t  rt| S t rt| S t| dg dd t|dttdfd t	di t
 }|jdd}d|_|jdd	| gid
|gid |S )a=  

    Test whether a Tensor is empty.

    Args:
        x (Tensor): The Tensor to be tested.
        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:
        Tensor: A bool scalar Tensor. True if 'x' is an empty Tensor.

    Examples:
        .. code-block:: python

            import paddle

            input = paddle.rand(shape=[4, 32, 32], dtype='float32')
            res = paddle.is_empty(x=input)
            print("res:", res)
            # ('res:', Tensor: eager_tmp_1
            #    - place: CPUPlace
            #    - shape: [1]
            #    - layout: NCHW
            #    - dtype: bool
            #    - data: [0])

    r   rU  r@   rw   Nr   r~   TrJ   rL   rM   )r@   )r   r*   r@   r   r+   r$   r%   r   rN   r   rP   r   re  rS   )r   rw   rT   r9   rU   rU   rV   r@   w  s   

r@   r  )	rX   Nr   TTTTTr   )Nr   Nr.  )rT  Trm   )NNN)NN)NNNN)q
__future__r   wrapped_decoratorr   Zlayer_function_generatorr   r   Ztensorr   r	   r
   r   r   Z	frameworkr   r   r   r   r   r   r   r  r   r   nnr   r   r   utilsr   r   r   r   r   r   r   r   r    r6  r_   r  	functoolsr!   r"   Zdata_feederr#   r$   r%   r&   r(   r}  r  r)   Zpaddler*   r+   __all__rD   rb   rc   r{   r}   r   r>   r?   objectr   r   r   r<   r  r   r,   r   rV  integer_typesrt   r2  rC   r7  r?  rH  rL  rR  r.   r/   r0   r1   r2   r3   r4   r5   r6   r7   rk  r8   rl  rm  r  r9   r  r  r  r  r  rA   r-   r  r:   r;   rB   r=   r@   rU   rU   rU   rV   <module>   s   $	,$
N
9C
aH   n> 
+n
&E
=
3
)
d:979
8
50Z'E -
  		A
  % ?     
K 1
*