a
    8Sicс                     @   sH  U d dl Z d dlmZmZmZ d dlmZ d dl mZ d dlZd dl	m
Z
 d dlmZmZmZmZmZ d ai aeeeef ee f ed< e
dd	 Zd
d ZG dd deZeddddZeeddddZddeeeeedddZd$eee edddZd%eeeedddZeeddd Z d&eeee ed!d"d#Z!dS )'    N)
ModuleList
ModuleDictModule)	Parameter)Tensor)contextmanager)UnionOptionalDictTupleSequence_cachec                   c   s:   t d7 a zdV  W t d8 a t s6i ant d8 a t s4i a0 dS )a]  Context manager that enables the caching system within parametrizations
    registered with :func:`register_parametrization`.

    The value of the parametrized objects is computed and cached the first time
    they are required when this context manager is active. The cached values are
    discarded when leaving the context manager.

    This is useful when using a parametrized parameter more than once in the forward pass.
    An example of this is when parametrizing the recurrent kernel of an RNN or when
    sharing weights.

    The simplest way to activate the cache is by wrapping the forward pass of the neural network

    .. code-block:: python

        import torch.nn.utils.parametrize as P
        ...
        with P.cached():
            output = model(inputs)

    in training and evaluation. One may also wrap the parts of the modules that use
    several times the parametrized tensors. For example, the loop of an RNN with a
    parametrized recurrent kernel:

    .. code-block:: python

        with P.cached():
            for x in xs:
                out_rnn = self.rnn_cell(x, out_rnn)
       N)_cache_enabledr    r   r   V/var/www/html/django/DPS/env/lib/python3.9/site-packages/torch/nn/utils/parametrize.pycached   s    "r   c                 C   s(   t |tr| || n| || d S N)
isinstancer   register_parameterregister_buffer)modulenameXr   r   r   _register_parameter_or_buffer9   s    
r   c                       sj   e Zd ZU dZeed< eed< dee e	ee
f edd fddZedd	d
dZedddZ  ZS )ParametrizationLista\  A sequential container that holds and manages the ``original`` or ``original0``, ``original1``, ...
    parameters or buffers of a parametrized :class:`torch.nn.Module`.

    It is the type of ``module.parametrizations[tensor_name]`` when ``module[tensor_name]``
    has been parametrized with :func:`register_parametrization`.

    If the first registered parmetrization has a ``right_inverse`` that returns one tensor or
    does not have a ``right_inverse`` (in which case we assume that ``right_inverse`` is the identity),
    it will hold the tensor under the name ``original``.
    If it has a ``right_inverse`` that returns more than one tensor, these will be registered as
    ``original0``, ``original1``, ...

    .. warning::
        This class is used internally by :func:`register_parametrization`. It is documented
        here for completeness. It shall not be instantiated by the user.

    Args:
        modules (sequence): sequence of modules representing the parametrizations
        original (Parameter or Tensor): parameter or buffer that is parametrized
        unsafe (bool): a boolean flag that denotes whether the parametrization
            may change the dtype and shape of the tensor. Default: `False`
            Warning: the parametrization is not checked for consistency upon registration.
            Enable this flag at your own risk.
    originalunsafeFN)modulesr   r   returnc              
      s:  t |dkrtdt | || _|j}|j}t N |}t	| D ]0}t
|drHz||}W qH tyv   Y qH0 qHW d    n1 s0    Y  t|tst|tjjstdt|j t|t| _| jrdnt || _| jrT|j|jkrtd|j d|j t  || W d    n1 s<0    Y  t| d| nnt|D ]d\}}	t|	tstd	| d
t|	j dt|trt|	}	|	|j t| d| |	 q\| js6|  }
t|
tstdt|
j d|
j|krtd| d|
j |
j|kr6td| d|
j d S )Nr   z1ParametrizationList requires one or more modules.right_inversezT'right_inverse' must return a Tensor or a Sequence of tensors (list, tuple...). Got r   zVWhen `right_inverse` outputs one tensor, it may not change the dtype.
original.dtype: z 
right_inverse(original).dtype: r   z\'right_inverse' must return a Tensor or a Sequence of tensors (list, tuple...). Got element z of the sequence with type .,A parametrization must return a tensor. Got z}Registering a parametrization may not change the dtype of the tensor, unless `unsafe` flag is enabled.
unparametrized dtype: z
parametrized dtype: z}Registering a parametrization may not change the shape of the tensor, unless `unsafe` flag is enabled.
unparametrized shape: z
parametrized shape: )len
ValueErrorsuper__init__r   shapedtypetorchno_gradreversedhasattrr    NotImplementedErrorr   r   collectionsabcr   type__name__	is_tensorntensorsset_r   	enumerater   requires_grad_requires_grad)selfr   r   r   Zoriginal_shapeZoriginal_dtypenewr   iZ	originaliZ	__class__r   r   r&   \   s    

&
*
zParametrizationList.__init__valuer   c                 C   s  t  v t| D ]0}t|dr.||}qtdt|j dq| jrt	|t
sjtdt|j |j| jjkrtd|j d| jj | j| nt	|tjjstdt|j dt|| jkrtd	| j d
t| dt|D ]z\}}t| d| }t	|t
s4td| dt|j |j|jkrftd| d|j d| d|j || qW d   n1 s0    Y  dS )a  Calls the methods ``right_inverse`` (see :func:`register_parametrization`)
        of the parametrizations in the inverse order they were registered in.
        Then, it stores the result in ``self.original`` if ``right_inverse`` outputs one tensor
        or in ``self.original0``, ``self.original1``, ... if it outputs several.

        Args:
            value (Tensor): Value to which initialize the module
        r    zparametrization z" does not implement right_inverse.z,`right_inverse` should return a tensor. Got z1The tensor returned by `right_inverse` has dtype z while `original` has dtype z7'right_inverse' must return a sequence of tensors. Got r!   z<'right_inverse' must return a sequence of tensors of length z. Got a sequence of lenght r   z?`right_inverse` must return a sequence of tensors. Got element z	 of type zTensor z' returned by `right_inverse` has dtype z while `originalz` has dtype N)r)   r*   r+   r,   r    RuntimeErrorr0   r1   r2   r   r   r$   r(   r   r4   r.   r/   r   r#   r3   r5   getattr)r8   r?   r   r:   tensorZ
original_ir   r   r   r       sd    


z!ParametrizationList.right_inverser   c                    sf    j r d  j}n$ fddt jD } d | }d}t t|rb | |}|d7 }q>|S )Nr   c                 3   s   | ]}t  d | V  qdS )r   N)rA   ).0r:   r8   r   r   	<genexpr>      z.ParametrizationList.forward.<locals>.<genexpr>r   )r2   r   ranger3   r,   str)r8   xZ	originalsZcurr_idxr   rE   r   forward   s    
zParametrizationList.forward)F)r1   
__module____qualname____doc__r   __annotations__boolr   r   r   r   r&   r    rK   __classcell__r   r   r<   r   r   @   s   
 g=r   )r   r   c                 C   s2   | j }dd }td|j |fd|i}|| _ dS )zSets up a module to be parametrized.

    This works by substituting the class of the module by a class
    that extends it to be able to inject a property

    Args:
        module (nn.Module): module into which to inject the property
    c                 S   s   t dd S )NzSerialization of parametrized modules is only supported through state_dict(). See:
https://pytorch.org/tutorials/beginner/saving_loading_models.html#saving-loading-a-general-checkpoint-for-inference-and-or-resuming-training)r@   rE   r   r   r   getstate  s    z#_inject_new_class.<locals>.getstateZParametrized__getstate__N)r=   r0   r1   )r   clsrR   Z	param_clsr   r   r   _inject_new_class  s    	
rU   )r   tensor_namer   c                    sj   t rJ tjjtdfdd td fdd}tddfdd	}tjt|| dS )
a  Injects a property into module[tensor_name].

    It assumes that the class in the module has already been modified from its
    original one using _inject_new_class and that the tensor under :attr:`tensor_name`
    has already been moved out

    Args:
        module (nn.Module): module into which to inject the property
        tensor_name (str): name of the name of the property to create
    rC   c                    s0   t  f}t|}|d u r,|  }|t|< |S r   )idr   get)parametrizationkeyrB   )r   rV   r   r   get_cached_parametrization=  s    
z4_inject_property.<locals>.get_cached_parametrizationc                    sN   | j  }trDtj r"tdqJtj d ur:tdqJ |S n| S d S )NzTCaching is not implemented for scripting. Either disable caching or avoid scripting.z4Cannot trace a model while caching parametrizations.)parametrizationsr   r)   jitis_scriptingr@   _C_get_tracing_state)r8   rY   )r[   rV   r   r   get_parametrizedG  s    




z*_inject_property.<locals>.get_parametrizedNr>   c                    s   | j   | d S r   )r\   r    )r8   r?   )rV   r   r   set_originalW  s    z&_inject_property.<locals>.set_original)r,   r)   r]   unusedr   setattrr=   property)r   rV   ra   rb   r   )r[   r   rV   r   _inject_property.  s    	rf   Fr   )r   rV   rY   r   r   c          	   	   C   s0  | | j t| |r|s`t| |}||}t|tsPtdt|j d|j	|j	krtd| d|j	 d| d|j	 |j
|j
krtd| d|j
 d| d	|j
 t|d
r`z||}W n ty   Y n0 t|tstdt|j |j	|j	kr.td| d| d|j	 d|j	 |j
|j
kr`td| d| d|j
 d|j
 t| jtsrJ | j| | | j|  j|O  _n|| jv s|| jv rt| |}t|g||d}t| | t| st|  t | _t| | t| jts
J || j|< ntd|  d| d| S )ai  Adds a parametrization to a tensor in a module.

    Assume that ``tensor_name="weight"`` for simplicity. When accessing ``module.weight``,
    the module will return the parametrized version ``parametrization(module.weight)``.
    If the original tensor requires a gradient, the backward pass will differentiate
    through :attr:`parametrization`, and the optimizer will update the tensor accordingly.

    The first time that a module registers a parametrization, this function will add an attribute
    ``parametrizations`` to the module of type :class:`~ParametrizationList`.

    The list of parametrizations on the tensor ``weight`` will be accessible under
    ``module.parametrizations.weight``.

    The original tensor will be accessible under
    ``module.parametrizations.weight.original``.

    Parametrizations may be concatenated by registering several parametrizations
    on the same attribute.

    The training mode of a registered parametrization is updated on registration
    to match the training mode of the host module

    Parametrized parameters and buffers have an inbuilt caching system that can be activated
    using the context manager :func:`cached`.

    A :attr:`parametrization` may optionally implement a method with signature

    .. code-block:: python

        def right_inverse(self, X: Tensor) -> Union[Tensor, Sequence[Tensor]]

    This method is called on the unparametrized tensor when the first parametrization
    is registered to compute the initial value of the original tensor.
    If this method is not implemented, the original tensor will be just the unparametrized tensor.

    If all the parametrizations registered on a tensor implement `right_inverse` it is possible
    to initialize a parametrized tensor by assigning to it, as shown in the example below.

    It is possible for the first parametrization to depend on several inputs.
    This may be implemented returning a tuple of tensors from ``right_inverse``
    (see the example implementation of a ``RankOne`` parametrization below).

    In this case, the unconstrained tensors are also located under ``module.parametrizations.weight``
    with names ``original0``, ``original1``,...

    .. note::

        If unsafe=False (default) both the forward and right_inverse methods will be called
        once to perform a number of consistency checks.
        If unsafe=True, then right_inverse will be called if the tensor is not parametrized,
        and nothing will be called otherwise.

    .. note::

        In most situations, ``right_inverse`` will be a function such that
        ``forward(right_inverse(X)) == X`` (see
        `right inverse <https://en.wikipedia.org/wiki/Inverse_function#Right_inverses>`_).
        Sometimes, when the parametrization is not surjective, it may be reasonable
        to relax this.

    .. warning::

        If a parametrization depends on several inputs, :func:`~register_parametrization`
        will register a number of new parameters. If such parametrization is registered
        after the optimizer is created, these new parameters will need to be added manually
        to the optimizer. See :meth:`torch.Optimizer.add_param_group`.

    Args:
        module (nn.Module): module on which to register the parametrization
        tensor_name (str): name of the parameter or buffer on which to register
            the parametrization
        parametrization (nn.Module): the parametrization to register
    Keyword args:
        unsafe (bool): a boolean flag that denotes whether the parametrization
            may change the dtype and shape of the tensor. Default: `False`
            Warning: the parametrization is not checked for consistency upon registration.
            Enable this flag at your own risk.

    Raises:
        ValueError: if the module does not have a parameter or a buffer named :attr:`tensor_name`

    Examples:
        >>> import torch
        >>> import torch.nn as nn
        >>> import torch.nn.utils.parametrize as P
        >>>
        >>> class Symmetric(nn.Module):
        >>>     def forward(self, X):
        >>>         return X.triu() + X.triu(1).T  # Return a symmetric matrix
        >>>
        >>>     def right_inverse(self, A):
        >>>         return A.triu()
        >>>
        >>> m = nn.Linear(5, 5)
        >>> P.register_parametrization(m, "weight", Symmetric())
        >>> print(torch.allclose(m.weight, m.weight.T))  # m.weight is now symmetric
        True
        >>> A = torch.rand(5, 5)
        >>> A = A + A.T   # A is now symmetric
        >>> m.weight = A  # Initialize the weight to be the symmetric matrix A
        >>> print(torch.allclose(m.weight, A))
        True

        >>> class RankOne(nn.Module):
        >>>     def forward(self, x, y):
        >>>         # Form a rank 1 matrix multiplying two vectors
        >>>         return x.unsqueeze(-1) @ y.unsqueeze(-2)
        >>>
        >>>     def right_inverse(self, Z):
        >>>         # Project Z onto the rank 1 matrices
        >>>         U, S, Vh = torch.linalg.svd(Z, full_matrices=False)
        >>>         # Return rescaled singular vectors
        >>>         s0_sqrt = S[0].sqrt().unsqueeze(-1)
        >>>         return U[..., :, 0] * s0_sqrt, Vh[..., 0, :] * s0_sqrt
        >>>
        >>> linear_rank_one = P.register_parametrization(nn.Linear(4, 4), "weight", RankOne())
        >>> print(torch.linalg.matrix_rank(linear_rank_one.weight).item())
        1

    r"   r!   zrRegistering a parametrization may not change the dtype of the tensor, unless the `unsafe` flag is enabled.
module.z.dtype: z
parametrization(module.z	).dtype: zrRegistering a parametrization may not change the shape of the tensor, unless the `unsafe` flag is enabled.
module.z.shape: z	).shape: r    z9parametrization.right_inverse must return a tensor. Got: zXThe tensor returned by parametrization.right_inverse must have the same dtype as module.z., unless the `unsafe` flag is enabled.
module.z
returned dtype: zXThe tensor returned by parametrization.right_inverse must have the same shape as module.z
returned shape: rg   zModule 'zL' does not have a parameter, a buffer, or a parametrized element with name '')traintrainingis_parametrizedrA   r   r   r$   r0   r1   r(   r'   r,   r    r-   r\   r   appendr   _buffers_parametersr   delattrrU   rf   )	r   rV   rY   r   Yr   r;   r   r\   r   r   r   register_parametrization\  s    {


	



rq   c                 C   sB   t | dd}|du st|ts"dS |du r6t|dkS ||v S dS )aJ  Returns ``True`` if module has an active parametrization.

    If the argument :attr:`tensor_name` is specified, returns ``True`` if
    ``module[tensor_name]`` is parametrized.

    Args:
        module (nn.Module): module to query
        name (str, optional): attribute in the module to query
            Default: ``None``
    r\   NFr   )rA   r   r   r#   )r   rV   r\   r   r   r   rk   +  s    rk   T)r   rV   leave_parametrizedr   c                 C   sr  t | |std|  d| t| jts.J | j| }|jr|j}|rt  t	| |}W d   n1 sp0    Y  t f t
|tju r|| n<z|| W n, ty } ztdW Y d}~n
d}~0 0 W d   n1 s0    Y  n.|r t	| |}|jrt|n|}ntdt| j| | j|= t| || t | snt| d | jjd }|| _| S )a  Removes the parametrizations on a tensor in a module.

    - If ``leave_parametrized=True``, ``module[tensor_name]`` will be set to
      its current output. In this case, the parametrization shall not change the ``dtype``
      of the tensor.
    - If ``leave_parametrized=False``, ``module[tensor_name]`` will be set to
      the unparametrised tensor in ``module.parametrizations[tensor_name].original``.
      This is only possible when the parametrization depends on just one tensor.

    Args:
        module (nn.Module): module from which remove the parametrization
        tensor_name (str): name of the parametrization to be removed
        leave_parametrized (bool, optional): leave the attribute :attr:`tensor_name` parametrized.
            Default: ``True``

    Returns:
        Module: module

    Raises:
        ValueError: if ``module[tensor_name]`` is not parametrized
        ValueError: if ``leave_parametrized=False`` and the parametrization depends on several tensors
    zModule z$ does not have a parametrization on Na'  Calling remove_parametrizations() with leave_parametrized=True for a parameter that is an instance of a tensor subclass requires set_() to be implemented correctly for the tensor subclass. Either set leave_parametrized=False or provide a working implementation for set_() in the tensor subclass.zyCannot leave unparametrized (`leave_parametrized=False`) a tensor that is parametrized in terms of a sequence of tensors.r\   r   )rk   r$   r   r\   r   r2   r   r)   r*   rA   r0   r   r4   r@   r7   r   ro   r=   r   	__bases__)r   rV   rr   r\   r   teZorig_clsr   r   r   remove_parametrizations?  s8    


(
>


rv   c                 C   s    t | r| jjd S t| S dS )zReturns the module type before parametrizations were applied and if not,
    then it returns the module type.

    Args:
        module (nn.Module): module to get type of
    r   N)rk   r=   rs   r0   )r   r   r   r   type_before_parametrizations  s    rw   )from_module	to_modulerV   r   c                 C   s  t | rt| jtsJ |du r(| jn|g}t|ds<J |D ]}t||sdt||tt| | | j| D ]}t||| qnt|jtsJ t| j| dr| j| j	|j| _	q@d}dt
| }t| j| |r@t|j| |t| j| | |d }dt
| }qq@|S )a5  Transfers parametrizations and the parameters they parametrize from from_module
    to to_module. If tensor_name is specified, only transfers the specified parameter, otherwise
    transfers all parametrized parameters. If those parameters do not exist in to_module, it will create them.
    Does nothing if from_module is not parametrized.

    Args:
        from_module (nn.Module): module to transfer from
        to_module (nn.Module): module to transfer to
        tensor_name (str, optional): parameter to transfer

    Returns:
        Module: to_module
    N__iter__r   r   r   )rk   r   r\   r   r,   rd   r   rA   rq   r   rI   )rx   ry   rV   Zparameters_to_transferparameter_nameZ
param_funcnumZorig_numr   r   r   $transfer_parametrizations_and_params  s:    


r}   )N)T)N)"r)   Ztorch.nn.modules.containerr   r   r   Ztorch.nn.parameterr   r   r.   
contextlibr   typingr   r	   r
   r   r   r   r   intrI   rO   r   r   r   rU   rf   rP   rq   rk   rv   r0   rw   r}   r   r   r   r   <module>   sB   
 
* Q/ P S 
