o
    NeBw                     @   s  d dl mZmZ ddlZddlZddlZddlZddlZddlZ	ddl
mZ ddl
mZ ddlmZ ddlmZ ddlZd d	lmZ ddlZd d
lmZmZmZ ddlZddlZg dZdadd Zdd Zdd ZeeZed.ddZedd Z da!edd Z"dd Z#dd Z$d/ddZ%d d! Z&ed.d"d#Z'd/d$d%Z(G d&d' d'Z)ed/d(d)Z*ej+						d0d*d+Z,ej-d1d,d-Z.dS )2   )signature_safe_contextmanagerwrap_decorator    N)core)	framework)CleanupFuncRegistrar   )Tracer)convert_dtype)_get_paddle_place_in_legacy_dygraph_in_eager_without_dygraph_check)no_gradno_grad_gradguardenable_dygraphdisable_dygraphenabledto_variableFc                   C   s   t S )zW
    Return a bool value that indicates whether running code under `@declarative`

    _in_declarative_mode_ r   r   ID:\Projects\ConvertPro\env\Lib\site-packages\paddle/fluid/dygraph/base.pyin_declarative_mode)   s   r   c                 C   s>   t |||D ]\}}}||krt|  d| d  qdS )z
    Warning if inputs do not elementwisely equals to support_values.
    It's a utility function for dy2static when dygraph interface have
    more inputs than static interface such as paddle.grad.

    z# has unsupported parameter in jit: z, jit will discard itN)zipwarningswarn)	func_nameZinput_namesinputsZsupport_valuesnameinpsupr   r   r   &declarative_unsupport_argument_warning1   s   r#   c                    s    fdd}|S )Nc                     s<   t d   | i |W  d    S 1 sw   Y  d S N)r   _dygraph_guard)argskwargsfuncr   r   __impl__A   s   $z*_switch_to_static_graph_.<locals>.__impl__r   r)   r*   r   r(   r   _switch_to_static_graph_?   s   r,   Tc                 c   s    t }| a d V  |a d S r$   r   )Zis_declarativeoriginal_valr   r   r   _switch_declarative_mode_guard_K   s
   r.   c                 c   sB    t  }|r|j}| |_zd V  W |r||_d S d S |r ||_w r$   )r   _dygraph_tracerZ_enable_program_desc_tracing)enabletracerr-   r   r   r   program_desc_tracing_guardU   s   
r2   c                 c   sv    t  r6t s6| r6|  }|  D ]\}}t|tr#dd |D }nt|}|| |< qd V  | | d S d V  d S )Nc                 S   s   g | ]}t |qS r   )_convert_into_variable).0varr   r   r   
<listcomp>m   s    zparam_guard.<locals>.<listcomp>)	r   r   _non_static_modecopyitems
isinstancelistr3   update)
parametersZorigin_parametersr    Zvar_basenew_varr   r   r   param_guarde   s   


r?   c                 C   s   t | tjjtjfr@| j| j}|durt |tj	sJ |S t | tj
tjfr0| jdd}|S t| jdk}| jd|d}|S | S )z(
    Convert Varbase into Variable.
    NT)to_parameterr   F)r@   persistable)r:   r   eagerTensorVarBaseblockZ_find_var_recursiver    r   VariableZEagerParamBaseZ	ParamBaseZ_to_static_varlenshape)Ztensorr>   Zis_persistabler   r   r   r3   w   s    
r3   c                   C   s   t  S )aL  
    This function checks whether the program runs in dynamic graph mode or not.
    You can enter dynamic graph mode with :ref:`api_fluid_dygraph_guard` api,
    or enable and disable dynamic graph mode with :ref:`api_fluid_dygraph_enable_dygraph`
    and :ref:`api_fluid_dygraph_disable_dygraph` api .

    **Note**:
        ``fluid.dygraph.enabled`` is the alias of ``fluid.in_dygraph_mode``, and
        ``fluid.in_dygraph_mode`` is recommended to use for now.

    Returns:
        bool: Whether the program is running in dynamic graph mode.

    Examples:
        .. code-block:: python

            import paddle.fluid as fluid

            fluid.enable_dygraph()  # Now we are in dygragh mode
            print(fluid.dygraph.enabled())  # True
            fluid.disable_dygraph()
            print(fluid.dygraph.enabled())  # False
    )r   r7   r   r   r   r   r      s   r   c                 C   s0   t du rtt| da t   tt dS dS )a  

    .. note::
        Dynamic graph mode is turn ON by default since paddle 2.0.0

    This API turn OFF static graph mode. You can turn ON static graph mode by `enable_static <./disable_dygraph_en.html>`_ .

    Parameters:
        place(paddle.CPUPlace|paddle.CUDAPlace|str, optional): Place to run dynamic graph. Default: None. Which means that the running place will be 
            determined according to the way of paddle compilation. If ``place`` is string, It can be ``cpu``, and ``gpu:x``, where ``x`` is the
            index of the GPUs.

    return:
        None

    Examples:
        .. code-block:: python

            import paddle
            print(paddle.in_dynamic_mode())  # True, dynamic mode is turn ON by default since paddle 2.0.0

            paddle.enable_static()
            print(paddle.in_dynamic_mode())  # False, Now we are in static mode

            paddle.disable_static()
            print(paddle.in_dynamic_mode())  # True, Now we are in dynamic mode

    Nplace)#_functional_dygraph_context_managerr   r   	__enter__r   registerr   rI   r   r   r   r      s   r   c                   C   s"   t durt jt   da dS dS )a  

    .. note::
        Dynamic graph mode is turn ON by default since paddle 2.0.0

    This API turn ON static graph mode. You can turn ON static graph mode by `disable_static <./enable_dygraph_en.html>`_ .

    return:
        None

    Examples:
        .. code-block:: python

            import paddle
            print(paddle.in_dynamic_mode())  # True, dynamic mode is turn ON by default since paddle 2.0.0

            paddle.enable_static()
            print(paddle.in_dynamic_mode())  # False, Now we are in static mode

            paddle.disable_static()
            print(paddle.in_dynamic_mode())  # True, Now we are in dynamic mode

    N)rK   __exit__sysexc_infor   r   r   r   r      s   r   c                 c   s@    t  }|r|j}| |_z	d V  W ||_d S ||_w d V  d S r$   )r   r/   	_has_grad)is_trainr1   Zhas_gradr   r   r   _switch_tracer_mode_guard_   s   
rS   c                 C   s8   t  rtd | du rtddS tjdd }|| S )a  
    :api_attr: imperative

    Create a context which disables dygraph gradient calculation.
    In this mode, the result of every computation will have `stop_gradient=True`.

    Also functions as a decorator. (Make sure to instantiate without parenthesis.)

    Examples:

     .. code-block:: python

        import numpy as np
        import paddle.fluid as fluid

        # use as generator

        data = np.array([[2, 3], [4, 5]]).astype('float32')
        with fluid.dygraph.guard():
            l0 = fluid.Linear(2, 2)  # l0.weight.gradient() is None
            l1 = fluid.Linear(2, 2)
            with fluid.dygraph.no_grad():
                # l1.weight.stop_gradient is False
                tmp = l1.weight * 2  # tmp.stop_gradient is True
            x = fluid.dygraph.to_variable(data)
            y = l0(x) + tmp
            o = l1(y)
            o.backward()
            print(tmp.gradient() is None)  # True
            print(l0.weight.gradient() is None)  # False

        # use as decorator

        @fluid.dygraph.no_grad
        def test_layer():
            with fluid.dygraph.guard():
                inp = np.ones([3, 1024], dtype='float32')
                t = fluid.dygraph.base.to_variable(inp)
                linear1 = fluid.Linear(1024, 4, bias_attr=False)
                linear2 = fluid.Linear(4, 4)
                ret = linear1(t)
                dy_ret = linear2(ret)

        test_layer()

    zfpaddle.no_grad is only supported for inference model, and not supported for training under @to_static.NFrR   c                 _   s<   t dd | |i |W  d    S 1 sw   Y  d S )NFrT   )rS   r)   r&   r'   r   r   r   r*   <  s   $zno_grad.<locals>.__impl__)r   r   r   rS   	decoratorr+   r   r   r   r     s   /

r   c                   @   s(   e Zd ZdZdd Zdd Zdd ZdS )	r   a  
    :api_attr: imperative

    Create a context which disables dygraph gradient calculation.
    In this mode, the result of every computation will have `stop_gradient` set
    to `True`.

    Also functions as a decorator. (Make sure to use an instance.)

    Examples:

     .. code-block:: python

        import numpy as np
        import paddle

        # use as generator

        data = np.array([[2, 3], [4, 5]]).astype('float32')
        l0 = paddle.nn.Linear(2, 2)  # l0.weight.gradient() is None
        l1 = paddle.nn.Linear(2, 2)
        with paddle.no_grad():
            # l1.weight.stop_gradient is False
            tmp = l1.weight * 2  # tmp.stop_gradient is True
        x = paddle.to_tensor(data)
        y = l0(x) + tmp
        o = l1(y)
        o.backward()
        print(tmp.gradient() is None)  # True
        print(l0.weight.gradient() is None)  # False

        # use as decorator

        @paddle.no_grad()
        def test_layer():
            inp = np.ones([3, 1024], dtype='float32')
            t = paddle.to_tensor(inp)
            linear1 = paddle.nn.Linear(1024, 4, bias_attr=False)
            linear2 = paddle.nn.Linear(4, 4)
            ret = linear1(t)
            dy_ret = linear2(ret)

        test_layer()
    c                    s>   t j  fdd}t j  fdd}t|r||S ||S )Nc                    s6     | |i |W  d    S 1 sw   Y  d S r$   r   rU   selfr   r   _decorate_functiont  s   $z-no_grad_.__call__.<locals>._decorate_functionc                 ?   sJ    | |i |}  |D ]}|V  qW d    d S 1 sw   Y  d S r$   r   )r)   r&   r'   genxrW   r   r   _decorate_generatory  s   "z.no_grad_.__call__.<locals>._decorate_generator)rV   inspectisgeneratorfunction)rX   r)   rY   r\   r   rW   r   __call__r  s   
zno_grad_.__call__c                 C   s"   t  }|r|j| _d|_d S d S )NF)r   r/   rQ   orig)rX   r1   r   r   r   rL     s
   
zno_grad_.__enter__c                 G   s   t  }|r| j|_d S d S r$   )r   r/   r`   rQ   )rX   r&   r1   r   r   r   rN     s   zno_grad_.__exit__N)__name__
__module____qualname____doc__r_   rL   rN   r   r   r   r   r   D  s
    -r   c                 c   s    t  }t  }t }tj}| durt| }nt  }t ||S t j	 5 t 
|  t | dV  W d   n1 sBw   Y  W d   n1 sQw   Y  W d   n1 s`w   Y  W d   dS W d   dS 1 sxw   Y  dS )a  
    :api_attr: imperative

    This context will create a dygraph context for dygraph to run, using python ``with`` statement.

    Parameters:
        place(fluid.CPUPlace| fluid.CUDAPlace|str, optional): Place to execute dygraph. 
            If None, the running place will be determined according to the way of paddle compilation.
            If ``place`` is string, It can be ``cpu``, ``gpu:x`` and ``xpu:x``, where ``x`` is the
            index of the GPUs or XPUs. Default: None

    return:
        None

    Examples:

     .. code-block:: python

        import numpy as np
        import paddle.fluid as fluid

        with fluid.dygraph.guard():
            inp = np.ones([3, 1024], dtype='float32')
            t = fluid.dygraph.base.to_variable(inp)
            linear1 = fluid.Linear(1024, 4, bias_attr=False)
            linear2 = fluid.Linear(4, 4)
            ret = linear1(t)
            dy_ret = linear2(ret)

    N)r   ZProgramr	   r   rD   r   _current_expected_placeZprogram_guardZunique_namer   r%   Z_dygraph_place_guard)rJ   trainZstartupr1   rD   Zexpected_placer   r   r   r     s*    
"r   c                 C   sB  t  rddlm} tdg d||||gg d || |||S dd }	|	| d} |	|d	}|d
ur^t|ttfs;|g}|D ]}
|
d
ur\t rRt|
tj	j
sQJ dq=t|
tjs\J dq=ng }t|dkrrt|t| ksrJ d|d
u ryg }nOt|tjtj	j
fr|g}nAt|tj	j
r|g}n6t|tttfrt|}|D ]}t rt|tj	j
sJ dqt|tjsJ dqnt rtdtdt|tsJ d|d
u r|}t|tsJ dt|tsJ dt|tsJ d|sJ dt r	tj	| |||||||S t }|t  t|| |||||||	S )a   
    .. note::
        **This API is ONLY available in imperative mode.**

    This API computes the sum of gradients of `outputs` with respect to each `inputs` .

    Parameters:
        outputs (Tensor|list(Tensor)|tuple(Tensor)): the output Tensor or 
            Tensor list/tuple of the graph to compute gradients.
        inputs (Tensor|list(Tensor)|tuple(Tensor)): the input Tensor or 
            Tensor list/tuple of the graph to compute gradients. The returned
            values of this API are the gradients of `inputs` . 
        grad_outputs (Tensor|list(Tensor|None)|tuple(Tensor|None), optional): 
            initial gradient values of `outputs` . If `grad_outputs` is None, 
            the initial gradient values of `outputs` would be Tensors filled with 1; 
            if `grad_outputs` is not None, it must have the same length as `outputs` , 
            and in this case, the initial gradient value of the i-th `outputs` would
            be: (1) a Tensor filled with 1 when the i-th element of `grad_outputs` 
            is None; (2) the i-th element of `grad_outputs` when the i-th element of
            `grad_outputs` is a Tensor. Default None.
        retain_graph (bool, optional): whether to retain the forward graph which 
            is used to calculate the gradient. When it is True, the graph would 
            be retained, in which way users can calculate backward twice for the 
            same graph. When it is False, the graph would be freed. Default None,
            which means it is equal to `create_graph` . 
        create_graph (bool, optional): whether to create the gradient graphs of
            the computing process. When it is True, higher order derivatives are
            supported to compute; when it is False, the gradient graphs of the
            computing process would be discarded. Default False.
        only_inputs (bool, optional): whether to only compute the gradients of
            `inputs` . If it is False, the gradients of all remaining leaf 
            Tensors in the graph would be also computed and accumulated. 
            If it is True, only the gradients of `inputs` would be computed.
            Default True. only_inputs=False is under development, and it is
            not supported yet.    
        allow_unused (bool, optional): whether to raise error or return None if some 
            Tensors of `inputs` are unreachable in the graph. If some Tensors of 
            `inputs` are unreachable in the graph (i.e., their gradients are None),  
            error would be raised if allow_unused=False, or None would be returned as
            their gradients if allow_unused=True. Default False.
        no_grad_vars (Tensor|list(Tensor)|tuple(Tensor)|set(Tensor), optional): 
            the Tensors whose gradients are not needed to compute. Default None.

    Returns:
        list: a list of Tensors, whose length is the same as the Tensor number 
        inside `inputs`, and the i-th returned Tensor is the sum of gradients of 
        `outputs` with respect to the i-th `inputs`.

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

            import paddle

            def test_dygraph_grad(create_graph):
                x = paddle.ones(shape=[1], dtype='float32')
                x.stop_gradient = False
                y = x * x

                # Since y = x * x, dx = 2 * x
                dx = paddle.grad(
                        outputs=[y],
                        inputs=[x],
                        create_graph=create_graph,
                        retain_graph=True)[0]

                z = y + dx

                # If create_graph = False, the gradient of dx
                # would not be backpropagated. Therefore,
                # z = x * x + dx, and x.gradient() = 2 * x = 2.0

                # If create_graph = True, the gradient of dx
                # would be backpropagated. Therefore,
                # z = x * x + dx = x * x + 2 * x, and
                # x.gradient() = 2 * x + 2 = 4.0

                z.backward()
                return x.gradient()

            print(test_dygraph_grad(create_graph=False)) # [2.]
            print(test_dygraph_grad(create_graph=True)) # [4.]

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

            import paddle

            def test_dygraph_grad(grad_outputs=None):
                x = paddle.to_tensor(2.0)
                x.stop_gradient = False

                y1 = x * x
                y2 = x * 3 

                # If grad_outputs=None, dy1 = [1], dy2 = [1].
                # If grad_outputs=[g1, g2], then:
                #    - dy1 = [1] if g1 is None else g1
                #    - dy2 = [1] if g2 is None else g2

                # Since y1 = x * x, dx = 2 * x * dy1.
                # Since y2 = x * 3, dx = 3 * dy2.
                # Therefore, the final result would be:
                # dx = 2 * x * dy1 + 3 * dy2 = 4 * dy1 + 3 * dy2.

                dx = paddle.grad(
                    outputs=[y1, y2], 
                    inputs=[x],
                    grad_outputs=grad_outputs)[0]

                return dx.numpy()

            grad_value = paddle.to_tensor(4.0)
            # dy1 = [1], dy2 = [1]
            print(test_dygraph_grad(None)) # [7.]

            # dy1 = [1], dy2 = [4]
            print(test_dygraph_grad([None, grad_value])) # [16.]

            # dy1 = [4], dy2 = [1]
            print(test_dygraph_grad([grad_value, None])) # [19.]

            # dy1 = [3], dy2 = [4]
            grad_y1 = paddle.to_tensor(3.0)
            print(test_dygraph_grad([grad_y1, grad_value])) # [24.]
	r   )	gradientszpaddle.grad)retain_graphZcreate_gradonly_inputsallow_unused)NFTFc                 S   s   | d usJ d |t| ttfrEt| dksJ d || D ]!}t r5t|tjjs4J d |q!t|tj	sBJ d |q!| S t rYt| tjjsVJ d || gS t| tj	sfJ d || gS )Nz{} should not be Noner   z{} cannot be emptyzElements of {} must be TensorzElements of {} must be Variablez#{} must be Tensor or list of Tensorz'{} must be Variable or list of Variable)
formatr:   r;   tuplerG   r   r   rB   rC   rD   )Zin_out_listr    each_varr   r   r   check_in_outT  s@   zgrad.<locals>.check_in_outoutputsr   NzLgrad_outputs must be None, a Variable or a list containing None or Variablesz3The length of grad_outputs must be equal to outputsz%no_grad_vars can only contains Tensorz'no_grad_vars can only contains Variablez>no_grad_vars must be None, Tensor or list/tuple/set of TensorszBno_grad_vars must be None, Variable or list/tuple/set of Variablesz"create_graph must be True or Falsez(retain_graph must be None, True or Falsez"allow_unused must be True or Falsez!only_inputs must be True or Falsez&only_inputs=False is not supported yet)r   Zpaddle.staticrg   r#   r:   r;   rl   r   r   rB   rC   rD   rG   setAssertionErrorboolZrun_partial_gradZPlaceZ	set_placer   re   Zdygraph_partial_grad)ro   r   Zgrad_outputsrh   Zcreate_graphri   rj   Zno_grad_varsrg   rn   rm   r5   rJ   r   r   r   r     s    




r   c                 C   s8  t ttjtjjtjtj	tjtj
f}t| |s td|t| f t| tjjtjtj	fr.| S t| tjtj
fr<t| S tt tjjrQ|dkrPtd d}n|rWJ dt| tjsbt| } |durtt|}| j|krt| |} t rtj| t d||r|dS ddS tj| t d||r|ndd}|S )	a  
    :api_attr: imperative

    The API will create a ``Variable`` object from 
    tuple, list, numpy\.ndarray or Variable object.

    Parameters:
        value(tuple|list|ndarray|Variable|Tensor): Initial data. 
            Can be a list, tuple, NumPy ndarray, Variable, Tensor.
            The shape can be multi-dimensional. The data type is one of 
            numpy\.{float16, float32, float64, int16, int32, int64, 
            uint8, uint16, complex64, complex128}.
        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` . 
        zero_copy(bool, optional): Whether to share memory with the input numpy 
            array. This parameter only works with CPUPlace and will be set to 
            True when it is None. Default: None. (Note: zero_copy is discarded temporally for some reason.)
        dtype(str, optional): The desired data type of returned ``Variable`` .
            Can be 'bool' , 'float16' , 'float32' , 'float64' , 'int8' , 'int16' , 
            'int32' , 'int64' , 'uint8' . Default: None.

    Returns:
        Variable : If ``value`` is a tuple/list/numpy\.ndarray object, 
            return ``Tensor`` created from the corresponding numpy\.ndarray object, which has 
            same data type and shape with ``value``. 


    Examples:

     .. code-block:: python

        import numpy as np
        import paddle.fluid as fluid

        with fluid.dygraph.guard(fluid.CPUPlace()):
            x = np.ones([2, 2], np.float32)
            y = fluid.dygraph.to_variable(x, zero_copy=False)
            x[0][0] = -1
            y[0][0].numpy()  # array([1.], dtype=float32)
            y = fluid.dygraph.to_variable(x)
            x[0][0] = 0
            y[0][0].numpy()  # array([0.], dtype=float32)
            c = np.array([2+1j, 2])
            z = fluid.dygraph.to_variable(c)
            z.numpy() # array([2.+1.j, 2.+0.j])
            z.dtype # 'complex128'

            y = fluid.dygraph.to_variable([[0.1, 1.2], [2.2, 3.1], [4.9, 5.2]])
            y.shape     # [3L, 2L]

            y = fluid.dygraph.to_variable(((0.1, 1.2), (2.2, 3.1), (4.9, 5.2)), dtype='int32')
            y.shape     # [3L, 2L]

    zMThe type of 'value' in fluid.dygraph.to_variable must be %s, but received %s.Tz@Currently, zero_copy is not supported, and it will be discarded.Fz-zero_copy mode can only be used with CPUPlaceN )valuerJ   rA   	zero_copyr    )r;   rl   npZndarrayr   rB   rC   rD   r   rF   Z	LoDTensorr:   	TypeErrortypere   ZCPUPlacer   r   arrayr
   dtypeZastyper   )rt   r    ru   rz   Zsupport_typeZpy_varr   r   r   r     sX   9







r   )Tr$   )NNFTFN)NNN)/wrapped_decoratorr   r   rV   
contextlib	functoolsr]   rO   numpyrv   Zpaddle.fluidr   r   Zpaddle.fluid.multiprocess_utilsr   r1   r	   loggingZdata_feederr
   r   r   r   r   Zpaddle__all__r   r   r#   r,   Zswitch_to_static_graphr.   r2   rK   r?   r3   r   r   r   rS   r   r   r   Znon_static_onlyr   Zdygraph_onlyr   r   r   r   r   <module>   sf   		


'
?M0 {