U
    ©Ê¹jþ6  ã                   @   sÆ   U d dl Z d dlZd dlmZ d dlmZmZmZmZm	Z	m
Z
mZmZmZ d dlZd dlmZ d dlmZ d dlmZ d dlmZ g Ze	e ed< e  e¡ZG dd	„ d	ejƒZe	e ed
œdd„ZdS )é    N)Údeepcopy)	ÚAnyÚCallableÚ
CollectionÚDictÚListÚMappingÚOptionalÚUnionÚoverload)Úoptim)ÚShardedTensor)ÚFullyShardedDataParallelÚ__all__c                   @   sJ  e Zd ZdZdeeeeje	f f e
jeeeeef   eej ddœdd„Zdd„ Zeeef dœd	d
„Zed dddœdd„ƒZeeg ef edœdd„ƒZd!eeg ef  ee dœdd„Zeeejef dœdd„ƒZeeef ddœdd„Zeeef ddœdd„Zddœdd„Zeeef dœdd„Zeeef dœdd„ZdS )"Ú_NamedOptimizeraë  
    ``_NamedOptimizer`` takes a dict of parameters and exposes ``state_dict`` by
    parameter key. We replace the original key (number) in an optim to the
    fully qualified name (FQN) string. User can initialize the optim as they
    initialize a PyTorch optim, the only difference is that they also need to
    pass in the FQN of each parameters.

    Args:
        named_parameters (Mapping[str, Union[torch.Tensor, ShardedTensor]]):
            Mapping from FQN to parameter.
        optimizer_class (optim.Optimizer):
            The class of optimizer to instantiate.
        param_groups (Collection[Mapping[str, Any]]):
            `param_groups` to pass to optimizer if specified.
            The key of the inner map needs to be FQNs.
            Default: None
        module (nn.Module): the module whose parameters to updated
            by the optimizer.
        args: arguments to pass to the optimizer constructor.
        kwargs: arguments to pass to the optimizer constructor.

    Example::
        >>> # xdoctest: +SKIP("distributed")
        >>> from torch import optim
        >>> from torch.distributed.optim import _NamedOptimizer
        >>>
        >>> # Define the named optimizer.
        >>> m = Model(...)
        >>> named_optim = _NamedOptimizer(m.named_parameters(), optim.SGD)
        >>> # Forward pass + backward pass.
        >>> named_optim.step()
        >>> ...
        >>> # Call state_dict for the named optimizer returns a FQN state_dict.
        >>> named_optim.state_dict()

    Warning: This API is still in development and subject to change.

    TODO: Add tutorial for _NamedOptimizer.
    TODO: Add documentation in the docstring for the public attributes
          like self.param_groups and self.named_parameters.
    N)Únamed_parametersÚoptimizer_classÚparam_groupsÚmoduleÚreturnc                 O   sà   t j d¡ || _|  ¡  t|ƒ| _|d kr6| j ¡ n|}||f|ž|Ž| _|| _	|d krlt
| j ¡ ƒ| _nft d¡ dd„ | j ¡ D ƒ}g }	|D ]8}
|
d D ]*}||krºtd|› d�ƒ‚|	 || ¡ qžq’|	| _| jj| _d S )Nz'torch.distributed.optim._NamedOptimizerzvSince we pass in param_groups, we will use param_groups to initialize the optimizer, not all parameters of the module.c                 S   s   i | ]\}}||“qS © r   ©Ú.0ÚkeyÚparamr   r   úf/var/www/html/bwave/python/venv/lib/python3.8/site-packages/torch/distributed/optim/named_optimizer.pyÚ
<dictcomp>[   s      z,_NamedOptimizer.__init__.<locals>.<dictcomp>ÚparamszExpect param name z% found in param group but is missing.)ÚtorchZ_CZ_log_api_usage_oncer   Ú_param_groups_checkÚdictr   ÚvaluesÚ
_optimizerr   ÚlistÚkeysÚordered_param_keysÚwarningsÚwarnÚitemsÚ
ValueErrorÚappend)Úselfr   r   r   r   ÚargsÚkwargsZparams_for_optimizerÚparam_to_keyr%   Úgroupr   r   r   r   Ú__init__>   s<    	
ÿÿþýÿ
ÿz_NamedOptimizer.__init__c                 C   sŽ   | j d k	rŠ| j D ]x}t|tƒs&tdƒ‚d|ks6tdƒ‚|d }t|tjƒrP|g}t|ƒ}|D ]"}t|tjƒs\tdt |¡ ƒ‚q\||d< qd S )Núparam group must be a dictr   z#param group must contain key paramsz>optimizer can only optimize Tensors, but one of the params is )	r   Ú
isinstancer    ÚAssertionErrorr   ÚTensorr#   Ú	TypeErrorÚtypename)r+   Úparam_groupr   r   r   r   r   r   h   s     

ÿÿz#_NamedOptimizer._param_groups_check)r   c                    s¨   ˆ j  ¡ }|d }‡ fdd„|d  ¡ D ƒ}g }|D ]b}g }|d D ]}| ˆ j| ¡ qDdt|ƒi}| ¡ D ]\}	}
|	dkrnt|
ƒ||	< qn| |¡ q4ˆ  ||dœ¡S )z¬
        Return the ``state_dict`` of the optimizer. Instead of using number to index
        parameters, we will use module fully qualified name (FQN) as the key.
        r   c                    s   i | ]\}}ˆ j | |“qS r   )r%   )r   Zst_keyÚ	state_val©r+   r   r   r   �   s   ÿ z._NamedOptimizer.state_dict.<locals>.<dictcomp>Ústater   )r:   r   )r"   Ú
state_dictr(   r*   r%   Úsortedr   Ú_post_state_dict)r+   r;   r   Z	ret_stateZ
ret_groupsr/   Ú
param_keysr   Z	ret_groupÚkÚvr   r9   r   r;   y   s     


þz_NamedOptimizer.state_dict.)Úclosurer   c                 C   s   d S ©Nr   ©r+   rA   r   r   r   Ústep“   s    z_NamedOptimizer.stepc                 C   s   d S rB   r   rC   r   r   r   rD   —   s    c                 C   s   | j j|d�S )z“
        Performs a single optimization step.

        This will call :meth:`torch.optim.Optimizer.step` on the wrapped
        optimizer.
        ©rA   )r"   rD   rC   r   r   r   rD   ›   s    c                 C   s   | j jS rB   )r"   r:   r9   r   r   r   r:   ¤   s    z_NamedOptimizer.state)r;   r   c                 C   sò  | j  ¡ }|  |¡}|d }|d }t|ƒdkr8tdƒ‚t| jƒD �]l\}}|| ¡ krZqBt|| ƒt|| ƒkrœtdt|| ƒ› d|› dt|| ƒ› �ƒ‚||  ¡ D �]\}}||| krÔtd|› d|› d�ƒ‚|| | }	t	|t
ƒ�rnt	|	t
ƒsút‚t| ¡ ƒ}
t|	 ¡ ƒ}|
|k�r<td	|› d
|
› d|› d|› �ƒ‚t| ¡ |	 ¡ ƒD ]\}}|j ¡  |j¡ �qNq¨t	|tjƒ�ržt	|	tjƒ�sŽt‚| ¡  |	¡ q¨t|	ƒ|| |< q¨qB|d }|d }i }|D ]0}g }|d D ]}| |¡ �qÚ||t|ƒ< �qÊi }|D ]6}g }|d D ]}| | j| ¡ �q||t|ƒ< �q| ¡ D ]œ\}}||k�rZ�qD|| }t|ƒt|ƒk�r˜tdt|ƒ› d|› d
t|ƒ› d�ƒ‚|D ]@}||k�rÀtd|› d|› d�ƒ‚|dk�rœt|| ƒ||< �qœ�qD| j  |¡ dS )a  
        This public function defines the default behavior to load a state_dict
        for ``_NamedOptimizer``.

        Sample Code
        ```
            my_model = MyModule()
            optimizer = _NamedOptimizer(my_model.named_parameters(), Adagrad)
            ...

            optim_state_dict = optimizer.state_dict()
            ...
            ...

            optimizer.load_state_dict(optim_state_dict)
            ...
        ```
        Args:
            state_dict (Dict[str, Any]) : A ``state_dict`` to load into the optimizer.
                Note that this state dict update is performed in place.

        .. note:: PyTorch is using lazy init to initialize the optim states.
            So it is possible that there is no optim state when user call
            ``load_state_dict`` and for ``_NamedOptimizer`` we make it stricter
            that users can only call ``load_state_dict`` after the state is initialized.
            By doing this, we can validate the optim ``state_dict`` to be loaded.
        r:   r   zJExpects the optim to be initialized before load but found not initialized.zExpects equal length as z for parameter z but found: zExpects state z but not found.z"Expects equal number of shards as z but found z for ú/r   r   z"Expects equal param_group size as z for group Ú.zExpects group key z to be in group z  in `state_dict` but is missing.N)r"   r;   Ú_pre_load_state_dictÚlenr)   Ú	enumerater%   r$   r(   r2   r   r3   Zlocal_shardsÚzipZtensorÚdetachZcopy_r   r4   r   r*   Ú_gen_param_group_keyÚload_state_dict)r+   r;   Znew_state_dictr:   Ú	new_stateÚidxZ	param_keyZ	state_keyr8   Zsrc_state_valZ
num_shardsZnum_new_shardsZshardZ	src_shardZsrc_param_groupsZnew_param_groupsZsrc_group_mapr/   r>   Znew_group_mapZ	new_groupZ	group_keyZ	src_groupr?   r   r   r   rN   ¨   sˆ    

ÿ$ÿÿ
ÿ ÿ
ÿ
ÿ
z_NamedOptimizer.load_state_dict)r7   r   c                 C   sš   t |tƒstdƒ‚|d }t |tjƒr2|g|d< nt|ƒ|d< dd„ | j ¡ D ƒ}|d D ]$}||krntdƒ‚| j	 
|| ¡ qZ| j |¡ | jj| _dS )zŸ
        Add a param group to the :class:`_NamedOptimizer` s `param_groups`.

        Warning: This API is still in development and subject to change.
        r1   r   c                 S   s   i | ]\}}||“qS r   r   r   r   r   r   r   !  s      z3_NamedOptimizer.add_param_group.<locals>.<dictcomp>z%some parameters are not in the moduleN)r2   r    r3   r   r4   r#   r   r(   r)   r%   r*   r"   Úadd_param_groupr   )r+   r7   r   r.   r   r   r   r   rQ     s    z_NamedOptimizer.add_param_groupc                 C   s>   | j  ¡ D ]"}|jr
t |¡}tj |¡|_q
| jdd� dS )zà
        Runs a dummy optimizer step, which allows to initialize optimizer state
        because we do lazy init for most optimizers.

        This allows doing in-place loading of optimizer state from a checkpoint.
        NrE   )	r   r!   Zrequires_gradr   Z
zeros_likeZautogradÚVariableZgradrD   )r+   r   Útr   r   r   Ú
init_state+  s
    
z_NamedOptimizer.init_statec                 C   s&   t | jtƒr"tj| j| j|dd�S |S )NT)Zis_named_optimizer)r2   r   ÚFSDPZoptim_state_dict_to_loadr"   ©r+   r;   r   r   r   rH   9  s       ÿz$_NamedOptimizer._pre_load_state_dictc                 C   s"   t | jtƒrt | j| j|¡ |S rB   )r2   r   rU   Zoptim_state_dictr"   rV   r   r   r   r=   B  s    z _NamedOptimizer._post_state_dict)NN).)N) Ú__name__Ú
__module__Ú__qualname__Ú__doc__r   Ústrr
   r   r4   r   r   Ú	Optimizerr	   r   r   ÚnnÚModuler0   r   r   r;   r   rD   r   ÚfloatÚpropertyr:   rN   rQ   rT   rH   r=   r   r   r   r   r      s0   .  ûø*"	k	r   )r>   r   c                 C   s   d  t| ƒ¡S )zQ
    Concatenate all param keys as a unique indentifier for one param group.
    rF   )Újoinr<   )r>   r   r   r   rM   J  s    rM   ) Úloggingr&   Úcopyr   Útypingr   r   r   r   r   r   r	   r
   r   r   Ztorch.nnr]   r   Z'torch.distributed._shard.sharded_tensorr   Ztorch.distributed.fsdpr   rU   r   r[   Ú__annotations__Ú	getLoggerrW   Úloggerr\   r   rM   r   r   r   r   Ú<module>   s    ,
  9