o
    Zh~5                     @   s   d dl Z d dlZd dlmZ d dl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mZmZmZmZ d	gZejhZg d
ZG dd	 d	e jZdS )    N)defaultdict)AnyOptional)nn)parametrize)type_before_parametrizations   )FakeSparsityget_arg_info_from_tensor_fqnmodule_contains_parammodule_to_fqnswap_moduleBaseSparsifier)module
module_fqntensor_namec                
       s  e Zd ZdZd/deeeef  f fddZdeeef fddZ	d	eeeeef f ddfd
dZ
dd Zdeeef fddZd0deeef defddZefdejdeeej  ddfddZdd Zdd Z		d1deeedf  deeeeedf f  fd d!Zdd"efd#ejd$eeeej eej f  d%ed&eej fd'd(Zd0d)eddfd*d+Zejd#ejd,efd-d.Z  Z S )2r   a'  Base class for all sparsifiers.

    Abstract methods that need to be implemented:

    - update_mask: Function to compute a new mask for all keys in the
        `groups`.

    Args:
        - model [nn.Module]: model to configure. The model itself is not saved
            but used for the state_dict saving / loading.
        - config [list]: configuration elements should be a dict map that includes
            `tensor_fqn` of tensors to sparsify
        - defaults [dict]: default configurations will be attached to the
            configuration. Only the keys that don't exist in the `config` will
            be updated.

    Example::

        >>> # xdoctest: +SKIP("Can't instantiate abstract class BaseSparsifier with abstract method update_mask")
        >>> config = [{'tensor_fqn': 'layer1.weight', 'tensor_fqn': 'linear2.weight2', 'sparsity_level': 0.5}]
        >>> defaults = {'sparsity_level': 0.7}
        >>> # model.layer1.weight will have `sparsity_level` = 0.7 (getting default)
        >>> sparsifier = BaseSparsifier(config, defaults)
    Ndefaultsc                    s.   t    |pi | _tt| _g | _d| _d S )NT)super__init__r   r   dictstategroupsenable_mask_update)selfr   	__class__ Z/var/www/auris/lib/python3.10/site-packages/torch/ao/pruning/sparsifier/base_sparsifier.pyr   7   s
   



zBaseSparsifier.__init__returnc                 C   s   | j | j| jdS )Nr   r   r   r   )r   r   r   r   __getstate__?   s   zBaseSparsifier.__getstate__r   c                 C   s   | j | d S N)__dict__update)r   r   r   r   r   __setstate__F   s   zBaseSparsifier.__setstate__c                 C   s   | j jd }t| jD ]7\}}|d }|d7 }|d| d7 }|d| d7 }t| D ]}|dkr4q-|d| d||  d7 }q-q|d7 }|S )	Nz (r   
z	Group z	    module: z	    z: ))r   __name__	enumerater   sortedkeys)r   format_stringiZsparse_argsr   keyr   r   r   __repr__I   s   zBaseSparsifier.__repr__c                 C   s   dd | j D }| j|dS )a  Returns the state of the optimizer as a :class:`dict`.

        It contains:
        * state - current state of the sparsification.
        * groups - a list containing all sparsity configuration groups
            with the key 'tensor_fqn' specifying the path to the sparsified tensor within a model

        TODO: Need a clean way of loading the state of the "prepared" module
        c                 S   s"   g | ]}t td d | qS )c                 S   s   | d t vS )Nr   )KEYS_NOT_IN_STATE_DICT)	key_valuer   r   r   <lambda>e   s    z6BaseSparsifier.state_dict.<locals>.<listcomp>.<lambda>)r   filteritems).0mgr   r   r   
<listcomp>b   s    z-BaseSparsifier.state_dict.<locals>.<listcomp>r   r   )r   r   )r   r   r   r   r   
state_dictW   s   zBaseSparsifier.state_dictTr8   strictc                 C   s  t |d }|d }| D ]g\}}t| j|}|d }|d }	|r/|d u r/td| dd}
|j|	 D ]}t|trAd}
 nq6|
sVtt	
t||	j}t||	| |d	d d urf|d	}||_|D ]}|d
 |kru|| qhq| ||d d S )Nr   r   r   r   zError loading z into the modelFTmask
tensor_fqnr7   )copydeepcopyr3   r
   modelRuntimeErrorZparametrizations
isinstancer	   torchZonesgetattrshaper   register_parametrizationgetpopr:   r#   r$   )r   r8   r9   r   Zstatesr;   sZarg_infor   r   foundpr:   r5   r   r   r   load_state_dictq   s4   


zBaseSparsifier.load_state_dictr>   SUPPORTED_MODULESc                 C   sz   g | _ |g}|r;| }| D ]&\}}t||v r1t||}t|ts&J | j d|d i q|| q|sd S d S )Nr;   z.weight)configrF   named_childrentyper   r@   strappend)r   r>   rK   stackr   _namechildr   r   r   r   make_config_from_model   s   
z%BaseSparsifier.make_config_from_modelc                 C   s   || _ || _| jdu r| | | jD ]e}t|tsJ dt| jts&J t| j}|| |	dd}|dus?J dt
||}| D ]$}||v rl|| || ksl|dkrdd||  || kslJ d| dqH|| | j| q|   dS )zPrepares a model, by adding the parametrizations.

        Note::

            The model is modified inplace. If you need to preserve the original
            model, use copy.deepcopy.
        Nznconfig elements should be dicts not modules i.e.:[{`tensor_fqn`: `foo.bar.weight`}, {`tensor_fqn`: ... }, ...]r;   zttensor_fqn is a required argument in the sparsity config whichreplaces previous `module` and [module]`fqn` arguments.zGiven both `z?` and `tensor_fqn` in the config, it is expected them to agree!)r>   rL   rT   r@   r   r   r<   r=   r#   rE   r
   r*   r   rP   _prepare)r   r>   rL   Zmodule_configZ
local_argsr;   Zinfo_from_tensor_fqnr-   r   r   r   prepare   s6   







zBaseSparsifier.preparec              	   O   sh   | j D ].}|d }|d }|dt}|dtt||}|| j|d  d< t|||| qdS )z-Adds mask parametrization to the layer weightr   r   parametrizationr:   r;   N)	r   rE   r	   rA   Z	ones_likerB   r   r   rD   )r   argskwargsrL   r   r   rX   r:   r   r   r   rV      s   

zBaseSparsifier._prepareparams_to_keep.params_to_keep_per_layerc                    s   | j D ]I  d } d }tj||dd i }|dur) fdd|D }|| |durG| d d}	|	durG fd	d|	D }
||
 |rL||_qdS )
a=	  Squashes the sparse masks into the appropriate tensors.

        If either the `params_to_keep` or `params_to_keep_per_layer` is set,
        the module will have a `sparse_params` dict attached to it.

        Args:
            params_to_keep: List of keys to save in the module or a dict
                            representing the modules and keys that will have
                            sparsity parameters saved
            params_to_keep_per_layer: Dict to specify the params that should be
                            saved for specific layers. The keys in the dict
                            should be the module fqn, while the values should
                            be a list of strings with the names of the variables
                            to save in the `sparse_params`

        Examples:
            >>> # xdoctest: +SKIP("locals are undefined")
            >>> # Don't save any sparse params
            >>> sparsifier.squash_mask()
            >>> hasattr(model.submodule1, 'sparse_params')
            False

            >>> # Keep sparse params per layer
            >>> sparsifier.squash_mask(
            ...     params_to_keep_per_layer={
            ...         'submodule1.linear1': ('foo', 'bar'),
            ...         'submodule2.linear42': ('baz',)
            ...     })
            >>> print(model.submodule1.linear1.sparse_params)
            {'foo': 42, 'bar': 24}
            >>> print(model.submodule2.linear42.sparse_params)
            {'baz': 0.1}

            >>> # Keep sparse params for all layers
            >>> sparsifier.squash_mask(params_to_keep=('foo', 'bar'))
            >>> print(model.submodule1.linear1.sparse_params)
            {'foo': 42, 'bar': 24}
            >>> print(model.submodule2.linear42.sparse_params)
            {'foo': 42, 'bar': 24}

            >>> # Keep some sparse params for all layers, and specific ones for
            >>> # some other layers
            >>> sparsifier.squash_mask(
            ...     params_to_keep=('foo', 'bar'),
            ...     params_to_keep_per_layer={
            ...         'submodule2.linear42': ('baz',)
            ...     })
            >>> print(model.submodule1.linear1.sparse_params)
            {'foo': 42, 'bar': 24}
            >>> print(model.submodule2.linear42.sparse_params)
            {'foo': 42, 'bar': 24, 'baz': 0.1}
        r   r   T)Zleave_parametrizedNc                       i | ]}| | qS r   r   r4   krL   r   r   
<dictcomp>      z.BaseSparsifier.squash_mask.<locals>.<dictcomp>r   c                    r]   r   r   r^   r`   r   r   ra   $  rb   )r   r   Zremove_parametrizationsr#   rE   sparse_params)r   r[   r\   rY   rZ   r   r   rc   Zglobal_paramsparamsZper_layer_paramsr   r`   r   squash_mask   s&   
;

zBaseSparsifier.squash_maskFr   mappinginplaceparameterizationc           
      C   s   |du rt d|st|}i }| D ]"\}}t||r,t||v r,t||||< q| j||d|d||< q| D ]	\}}	|	|j	|< q<|S )a  Converts submodules in input module to a different module according to `mapping`
        by calling `from_dense` method on the target module class
        Args:
            module: input module
            mapping: a dictionary that maps from source module type to target
                module type, can be overwritten to allow swapping user defined
                Modules
            inplace: carry out model transformations in-place, the original module
                is mutated
        NzNeed to auto generate mapping T)rf   rg   rh   )
NotImplementedErrorr<   r=   rM   r   r   r   convertr3   Z_modules)
r   r   rf   rg   rh   Zreassignnamemodr-   valuer   r   r   rj   *  s&   
zBaseSparsifier.convertuse_pathc                 C   sT   | j sd S t  | jD ]
}| jdi | qW d    d S 1 s#w   Y  d S )Nr   )r   rA   Zno_gradr   update_mask)r   rn   rL   r   r   r   stepV  s   

"zBaseSparsifier.stepr   c                 K   s   d S r!   r   )r   r   r   rZ   r   r   r   ro   ]  s   zBaseSparsifier.update_maskr!   )T)NN)!r'   
__module____qualname____doc__r   r   rO   r   r   r    r$   r.   r8   boolrJ   rK   r   ModulesetrN   LinearrT   rW   rV   tuplere   r	   rj   rp   abcabstractmethodro   __classcell__r   r   r   r   r      sP     "
3
Q
,)ry   r<   collectionsr   typingr   r   rA   r   Ztorch.nn.utilsr   Ztorch.nn.utils.parametrizer   utilsr	   r
   r   r   r   __all__rw   rK   r/   ABCr   r   r   r   r   <module>   s   	