U
    zhY                  
   @   s  d dl Z d dlmZ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mZ d dlmZ d dlmZ edddZeeeef ee	eef  f eeeef ee	eef  f eeee f dd	d
Zejedee	eef  f eegef e	e	edf e	edf eeee f f dddZeje	e	edf e	edf eeee f f dddZeje	e	edf e	edf eeee f f dddZd<ejee ee eddddZejeeee f ee ee dddZd=ejee ee eddddZd>ejee ee ee ee ejdd d!Zejd"d#d$Zejd"d%d&ZG d'd( d(ejZ G d)d* d*ejZ!d?ejee	e!e	edf f d+d,d-Z"d@ejee	e e	edf e	edf f d+d.d/Z#e	e	edf df e	edf d0d1d2Z$eej e	e e	edf e	edf f d3d4d5Z%dAe
ej ee	d e	e& f ej'j(d7d8d9Z)dBe
ej ee	d e	e& f ej'j(d7d:d;Z*dS )C    N)
AnyCallableDictIterableListNoReturnSequenceTupleTypeUnion)Tensor)NamedMemberAccessor)returnc                   C   s   t dd S )Na$  make_functional(module): we don't yet support models that do parameter tying (also sometimes known as weight sharing). Please try to rewrite your model by replacing all instances of the tied parameter with another and/or comment your support in https://github.com/pytorch/functorch/issues/446)RuntimeError r   r   R/var/www/html/venv/lib/python3.8/site-packages/torch/_functorch/make_functional.pyraise_parameter_tying_error    s    r   )named_paramstied_named_paramsr   c                 C   s   t | } t |}t|  }t| }||s6ti }|  D ]\}}|g f||< qB| D ]&\}}||kstt|| d | q`t | S )a[  
    named_params is a dictionary of tensors: {'A': A, 'B': B}
    tied_named_params is another dictionary of tensors {'A': A, 'B': B, 'B_tied': B}
    with potentially tied (or 'duplicated') tensors

    This function creates a mapping from the names in named_params to the
    names in tied_named_params: {'A': ['A'], 'B': ['B', 'B_tied']}.
       )dictsetkeysissubsetAssertionErroritemsappendvalues)r   r   Ztensors_dict_keysZtied_tensors_dict_keysZtensor_to_mappingkeyZtensorr   r   r   create_names_map*   s    r   .)modnamed_memberssubclassr   c                 C   s   t |dd}t |dd}t||}i }t| }|D ]:\}}	|	|kr\|tj|	dd||	< ||	 }
|||
 q6t|dkrd\}}nt| \}}|||fS )NF)Zremove_duplicateTmeta)devicer   )r   r   )tupler   r   torchZ
empty_like
set_tensorlenzip)r    r!   r"   Zall_named_membersZunique_named_members	names_mapmemoaccessornamepreplacementnamesparamsr   r   r   _extract_membersF   s    

r2   )r    r   c                 C   s   t | | jtjS )aZ  
    This function removes all the Parameters from the model and
    return them as a tuple as well as their original attribute names.
    The weights must be re-loaded with `load_weights` before the model
    can be used again.
    Note that this function modifies the model in place and after this
    call, mod.parameters() will be empty.
    )r2   Znamed_parametersnn	Parameterr    r   r   r   extract_weights_   s    r6   c                 C   s   t | | jdd S )Nc                 S   s   | S Nr   )xr   r   r   <lambda>p       z!extract_buffers.<locals>.<lambda>)r2   Znamed_buffersr5   r   r   r   extract_buffersm   s    r;   F)r    r0   r1   	as_paramsr   c                 C   s*   t | }|rdd |D }||| dS )a	  
    Reload a set of weights so that `mod` can be used again to perform a forward pass.
    Note that the `params` are regular Tensors (that can have history) and so are left
    as Tensors. This means that mod.parameters() will still be empty after this call.
    c                 S   s   g | ]}t |qS r   )r3   r4   ).0r.   r   r   r   
<listcomp>   s     z load_weights.<locals>.<listcomp>Nr   Zset_tensors)r    r0   r1   r<   r,   r   r   r   load_weightss   s    r@   )r    r*   elemsr   c           
      C   sf   g }t | }t| |D ]F\\}}}t|D ]0\}}	|dkrR|||	| q.||	| q.q|S )Nr   )r   r)   r   	enumerater   Zswap_tensorr'   )
r    r*   rA   resultr,   _
attr_nameselemi	attr_namer   r   r   _swap_state   s    rI   )r    r0   buffersr<   r   c                 C   s   t | }||| d S r7   r?   )r    r0   rJ   r<   r,   r   r   r   load_buffers   s    rK   r   )modelweightsweight_namesrJ   buffer_namesr   c                 C   sP   t |t |kstt| || t |dkrLt |t |ks@tt| || | S )zload_state(model, weights, weight_names, buffers=(), buffer_names=()) -> model

    load_state takes `weights` and `buffers` and assigns them to the model.
    This is the inverse operation of `make_functional_deprecated_v1`.
    r   )r(   r   r@   rK   )rL   rM   rN   rJ   rO   r   r   r   
load_state   s    rP   )rL   c                    sF   t  }t|dkr tdt\} } fdd}|| fS )a  make_functional_deprecated_v1(model) -> weights, func, weight_names

    Given an nn.Module, make_functional_deprecated_v1 extracts the state (weights)
    and returns a functional version of the model, `func`. This makes
    it so that it is possible use transforms over the parameters of
    `model`.

    `func` can be invoked as follows:
    ```
    x = torch.randn(4, 3)
    model = nn.Linear(3, 3)
    weights, func, _ = make_functional_deprecated_v1(model)
    func(weights, (x,))
    ```

    And here is an example of applying the grad transform:
    ```
    x = torch.randn(4, 3)
    model = nn.Linear(3, 3)
    weights, _, func = make_functional_deprecated_v1(model)
    grad_weights = grad(func)(weights, (x,))
    ```

    To put the state back into a model, use `load_state`.
    r   zmake_functional_deprecated_v1(model): `model` has buffers. Please use make_functional_with_buffers_deprecated_v1(model) instead.c                    s   t }t| |  || S r7   )copydeepcopyr@   )rM   datamutable_modelZdescriptorsrL   r   r   fun   s    
z*make_functional_deprecated_v1.<locals>.fun)listrJ   r(   r   r6   )rL   rJ   rM   rD   rV   r   rU   r   make_functional_deprecated_v1   s    rX   c                    s:   t \}}t\} } fdd}||| fS )a`  make_functional_with_buffers_deprecated_v1(model) -> weights, buffers, func, weight_names, buffer_names

    Given an nn.Module, make_functional_with_buffers_deprecated_v1 extracts the state (weights and buffers)
    and returns a functional version of the model, `func`.

    `func` can be invoked as follows:
    ```
    x = torch.randn(4, 3)
    model = nn.Linear(3, 3)
    weights, buffers, func, _, _ = make_functional_with_buffers_deprecated_v1(model)
    func(weights, buffers, (x,))
    ```

    And here is an example of applying the grad transform:
    ```
    x = torch.randn(4, 3)
    model = nn.Linear(3, 3)
    weights, buffers, func, _, _ = make_functional_with_buffers_deprecated_v1(model)
    func(weights, buffers, (x,))
    grad_weights = grad(func)(weights, buffers, (x,))
    ```

    To put the state back into a model, use `load_state`.
    c                    s*   t }t||  t| | || S r7   )rQ   rR   r@   rK   )rM   rJ   rS   rT   Zbuf_descriptorsrL   Zweight_descriptorsr   r   rV      s    
z7make_functional_with_buffers_deprecated_v1.<locals>.fun)r6   r;   )rL   rM   rD   rJ   rV   r   rY   r   *make_functional_with_buffers_deprecated_v1   s    rZ   c                
       s   e Zd ZdZejeedf eedf eee	e f eee	e f dd fddZ
edejeed eedf eedf f dd	d
Zee ee edddZ  ZS )FunctionalModuleWithBufferszW
    This is the callable object returned by :func:`make_functional_with_buffers`.
    .N)stateless_modelparam_namesrO   param_names_mapbuffer_names_mapr   c                    s6   t    || _|| _|| _t|| _| j| d S r7   )super__init__r\   r]   rO   r   all_names_mapupdate)selfr\   r]   rO   r^   r_   	__class__r   r   ra     s    

z$FunctionalModuleWithBuffers.__init__FrL   disable_autograd_trackingr   c           
      C   sT   t | }t|\}}}t|\}}}|r>|D ]}	|	d q.t|||||||fS NF)rQ   rR   r6   r;   requires_grad_r[   )
rL   rh   
model_copyr1   r]   r^   rJ   rO   r_   paramr   r   r   _create_from  s     
    z(FunctionalModuleWithBuffers._create_from)r1   rJ   r   c              
   O   sD   t | j| jt|t| }z| j||W S t | j| j| X d S r7   )rI   r\   rb   r%   )rd   r1   rJ   argskwargs	old_stater   r   r   forward'  s    z#FunctionalModuleWithBuffers.forward)F__name__
__module____qualname____doc__r3   Moduler	   strr   r   ra   staticmethodboolr   rm   r   r   rq   __classcell__r   r   re   r   r[      s&   

   r[   c                	       s   e Zd ZdZejeedf eee	e f dd fddZ
edejeed eedf f dd	d
Zee edddZ  ZS )FunctionalModulezJ
    This is the callable object returned by :func:`make_functional`.
    .N)r\   r]   r*   r   c                    s    t    || _|| _|| _d S r7   )r`   ra   r\   r]   r*   )rd   r\   r]   r*   re   r   r   ra   <  s    
zFunctionalModule.__init__Frg   c                 C   s@   t | }t|\}}}|r0|D ]}|d q t||||fS ri   )rQ   rR   r6   rj   r|   )rL   rh   rk   r1   r]   r*   rl   r   r   r   rm   G  s    
zFunctionalModule._create_from)r1   r   c              
   O   s8   t | j| j|}z| j||W S t | j| j| X d S r7   )rI   r\   r*   )rd   r1   rn   ro   rp   r   r   r   rq   S  s    zFunctionalModule.forward)Frr   r   r   re   r   r|   7  s   
  r|   rg   c                 C   s.   t |  }t|dkr tdtj| |dS )a  make_functional(model, disable_autograd_tracking=False) -> func, params

    Given a ``torch.nn.Module``, :func:`make_functional` extracts the state
    (params) and returns a functional version of the model, ``func``. This
    makes it so that it is possible use transforms over the parameters of
    ``model``.

    ``func`` can be invoked as follows:

    .. code-block:: python

        import torch
        import torch.nn as nn
        from functorch import make_functional

        x = torch.randn(4, 3)
        model = nn.Linear(3, 3)
        func, params = make_functional(model)
        func(params, x)

    And here is an example of applying the grad transform over the parameters
    of a model.

    .. code-block:: python

        import torch
        import torch.nn as nn
        from functorch import make_functional, grad

        x = torch.randn(4, 3)
        t = torch.randn(4, 3)
        model = nn.Linear(3, 3)
        func, params = make_functional(model)

        def compute_loss(params, x, t):
            y = func(params, x)
            return nn.functional.mse_loss(y, t)

        grad_weights = grad(compute_loss)(params, x, t)

    If the model has any buffers, please use :func:`make_functional_with_buffers` instead.

    Args:
        model (torch.nn.Module): Input model.
        disable_autograd_tracking (bool): Flag to disable gradients tracking for output parameters.
            The returned params are unrelated to the set of params from the original model. If False (default),
            the params will have ``requires_grad=True`` on them (aka they will be trackable with regular
            PyTorch autograd), matching the requires_grad-ness of the params from the original model.
            Otherwise, the returned params will have ``requires_grad=False``. Default, False.
            If you plan on using regular PyTorch autograd (e.g., if you want to call ``.backward()`` or
            ``torch.autograd.grad()``, then set ``disable_autograd_tracking=False``.
            Otherwise, if you're only planning on using functorch's gradient transforms,
            then please set ``disable_autograd_tracking=True`` to avoid unnecessarily tracking
            history with PyTorch autograd.

    r   zdmake_functional(model): `model` has buffers. Please use make_functional_with_buffers(model) instead.rh   )rW   rJ   r(   r   r|   rm   )rL   rh   rJ   r   r   r   make_functional]  s    ; r~   c                 C   s   t j| |dS )a  make_functional_with_buffers(model, disable_autograd_tracking=False) -> func, params, buffers

    Given a ``torch.nn.Module``, make_functional_with_buffers extracts the
    state (params and buffers) and returns a functional version of the model
    ``func`` that can be invoked like a function.

    ``func`` can be invoked as follows:

    .. code-block:: python

        import torch
        import torch.nn as nn
        from functorch import make_functional_with_buffers

        x = torch.randn(4, 3)
        model = nn.Linear(3, 3)
        func, params, buffers = make_functional_with_buffers(model)
        func(params, buffers, x)

    And here is an example of applying the grad transform over the parameters
    of a model:

    .. code-block:: python

        import torch
        import torch.nn as nn
        from functorch import make_functional_with_buffers, grad

        x = torch.randn(4, 3)
        t = torch.randn(4, 3)
        model = nn.Linear(3, 3)
        func, params, buffers = make_functional_with_buffers(model)

        def compute_loss(params, buffers, x, t):
            y = func(params, buffers, x)
            return nn.functional.mse_loss(y, t)

        grad_weights = grad(compute_loss)(params, buffers, x, t)

    Args:
        model (torch.nn.Module): Input model.
        disable_autograd_tracking (bool): Flag to disable gradients tracking for output parameters.
            The returned params are unrelated to the set of params from the original model. If False (default),
            the params will have ``requires_grad=True`` on them (aka they will be trackable with regular
            PyTorch autograd), matching the requires_grad-ness of the params from the original model.
            Otherwise, the returned params will have ``requires_grad=False``. Default, False.
            If you plan on using regular PyTorch autograd (e.g., if you want to call ``.backward()`` or
            ``torch.autograd.grad()``, then set ``disable_autograd_tracking=False``.
            Otherwise, if you're only planning on using functorch's gradient transforms,
            then please set ``disable_autograd_tracking=True`` to avoid unnecessarily tracking
            history with PyTorch autograd.

    r}   )r[   rm   )rL   rh   r   r   r   make_functional_with_buffers  s    8 r   )tuple_of_tuple_of_tensorsr   c                 C   s"   t t|  } t dd | D }|S )Nc                 s   s   | ]}t | V  qd S r7   r&   stackdetachr=   Zshardsr   r   r   	<genexpr>  s    z"transpose_stack.<locals>.<genexpr>)r%   r)   )r   resultsr   r   r   transpose_stack  s
    r   )modelsr   c                    s   t | dkrtdtdd | D s@tdd | D s@tdt| d  t fdd| D sjtdtd	d
 | D  \}}}t|}t|}|d ||fS )a(  combine_state_for_ensemble(models) -> func, params, buffers

    Prepares a list of torch.nn.Modules for ensembling with :func:`vmap`.

    Given a list of ``M`` ``nn.Modules`` of the same class, stacks all of their
    parameters and buffers together to make ``params`` and ``buffers``.
    Each parameter and buffer in the result will have an additional dimension
    of size ``M``.

    :func:`combine_state_for_ensemble` also returns ``func``, a functional
    version of one of the models in :attr:`models`. One cannot directly run
    ``func(params, buffers, *args, **kwargs)`` directly, you probably want to
    use ``vmap(func, ...)(params, buffers, *args, **kwargs)``

    Here's an example of how to ensemble over a very simple model:

    .. code-block:: python

        num_models = 5
        batch_size = 64
        in_features, out_features = 3, 3
        models = [torch.nn.Linear(in_features, out_features) for i in range(num_models)]
        data = torch.randn(batch_size, 3)

        fmodel, params, buffers = combine_state_for_ensemble(models)
        output = vmap(fmodel, (0, 0, None))(params, buffers, data)

        assert output.shape == (num_models, batch_size, out_features)

    .. warning::
        All of the modules being stacked together must be the same (except for
        the values of their parameters/buffers). For example, they should be in the
        same mode (training vs eval).

        This API is subject to change -- we're investigating better ways to
        create ensembles and would love your feedback how to improve this.
    r   z?combine_state_for_ensemble: Expected at least one model, got 0.c                 s   s   | ]}|j V  qd S r7   Ztrainingr=   mr   r   r   r     s     z-combine_state_for_ensemble.<locals>.<genexpr>c                 s   s   | ]}|j  V  qd S r7   r   r   r   r   r   r     s     zTcombine_state_for_ensemble: Expected all models to have the same training/eval mode.c                 3   s   | ]}t | kV  qd S r7   )typer   Z
model0_typr   r   r     s     zHcombine_state_for_ensemble: Expected all models to be of the same class.c                 S   s   g | ]}t |qS r   )r   r=   rL   r   r   r   r>   !  s     z.combine_state_for_ensemble.<locals>.<listcomp>)r(   r   allr   r)   r   )r   funcsr1   rJ   r   r   r   combine_state_for_ensemble  s&    ($
r   cpu)model_classensemble_shaper$   c                    s    fdd}|S )Nc            	         s   t dkrtdt dkr8 }t|S d }|dkrXtd| dt fddt|D }t \}}}tdd |D }tt| }td	d |D }|||fS )
N   ,NYI: ensemble_shape with more than 1 elementr   num_models  should be > 0c                 3   s   | ]}  V  qd S r7   tor=   rD   rn   r$   ro   r   r   r   r   7  s    z3functional_init.<locals>.wrapped.<locals>.<genexpr>c                 s   s   | ]}t |d  V  qdS )r   N)rX   r   r   r   r   r   ;  s     c                 s   s   | ]}t | V  qd S r7   r   r   r   r   r   r   =  s     )r(   
ValueErrorr   rX   r%   ranger)   )	rn   ro   rL   
num_modelsr   rD   fnr0   rM   r$   r   r   rn   ro   r   wrapped-  s     z functional_init.<locals>.wrappedr   r   r   r$   r   r   r   r   functional_init(  s    r   c                    s    fdd}|S )Nc                     s   t dkrtdt dkr8 }t|S d }|dkrXtd| dt fddt|D }t \}}}}}ttdd |D  \}	}
tt|	 }	td	d |	D }	tt|
 }
td
d |
D }
|	|
|||fS )Nr   r   r   r   r   c                 3   s   | ]}  V  qd S r7   r   r   r   r   r   r   R  s    z@functional_init_with_buffers.<locals>.wrapped.<locals>.<genexpr>c                 s   s   | ]}t |d d V  qd S )Nr   )rZ   r   r   r   r   r   ]  s   c                 s   s   | ]}t | V  qd S r7   r   r   r   r   r   r   c  s     c                 s   s   | ]}t | V  qd S r7   r   r   r   r   r   r   e  s     )r(   r   r   rX   r%   r   rZ   r)   )rn   ro   rL   r   r   rD   r   rN   rO   rM   rJ   r   r   r   r   H  s8    	z-functional_init_with_buffers.<locals>.wrappedr   r   r   r   r   functional_init_with_buffersC  s     r   )F)F)r   r   )F)F)r   r   )r   r   )+rQ   typingr   r   r   r   r   r   r   r	   r
   r   r&   Ztorch.nnr3   r   Z%torch.nn.utils._named_member_accessorr   r   rx   r   rw   r2   r6   r;   rz   r@   rI   rK   rP   rX   rZ   r[   r|   r~   r   r   r   inttypesZDevicer   r   r   r   r   r   <module>   s   0(((
      *%8'  G  >
@    