o
    zi`+                     @   sV   d dl Z d dlZd dlZd dlZd dlmZ d dlZd dlmZ G dd dejZ	dS )    N)Path)nnc                   @   s   e Zd ZdZg dZ	 g Z	 ddg g g fdededede	d	e	d
e	fddZ
edd ZeddddededefddZg g g fddZed ddZ		d!dejeef dedefddZe		d"dejeef dedefddZdS )#	BaseModela  This is a class that adds useful save/load functionality to a
    ``torch.nn.Module`` object. ``BaseModel`` objects can be saved
    as ``torch.package`` easily, making them super easy to port between
    machines without requiring a ton of dependencies. Files can also be
    saved as just weights, in the standard way.

    >>> class Model(ml.BaseModel):
    >>>     def __init__(self, arg1: float = 1.0):
    >>>         super().__init__()
    >>>         self.arg1 = arg1
    >>>         self.linear = nn.Linear(1, 1)
    >>>
    >>>     def forward(self, x):
    >>>         return self.linear(x)
    >>>
    >>> model1 = Model()
    >>>
    >>> with tempfile.NamedTemporaryFile(suffix=".pth") as f:
    >>>     model1.save(
    >>>         f.name,
    >>>     )
    >>>     model2 = Model.load(f.name)
    >>>     out2 = seed_and_run(model2, x)
    >>>     assert torch.allclose(out1, out2)
    >>>
    >>>     model1.save(f.name, package=True)
    >>>     model2 = Model.load(f.name)
    >>>     model2.save(f.name, package=False)
    >>>     model3 = Model.load(f.name)
    >>>     out3 = seed_and_run(model3, x)
    >>>
    >>> with tempfile.TemporaryDirectory() as d:
    >>>     model1.save_to_folder(d, {"data": 1.0})
    >>>     Model.load_from_folder(d)

    )zaudiotools.**tqdm__main__znumpy.**z	julius.**ztorchaudio.**zscipy.**einopsNTpathmetadatapackageinternexternmockc                 C   s   t | j}i }|j D ]\}	}
|
j}|t jjur|||	< qt| D ]}||v r0t	| |||< q#|du r7i n|}||d< t
| dsEi | _| j| |s\|  |d}t|| |S | j||||d |S )a  Saves the model, either as a torch package, or just as
        weights, alongside some specified metadata.

        Parameters
        ----------
        path : str
            Path to save model to.
        metadata : dict, optional
            Any metadata to save alongside the model,
            by default None
        package : bool, optional
            Whether to use ``torch.package`` to save the model in
            a format that is portable, by default True
        intern : list, optional
            List of additional libraries that are internal
            to the model, used with torch.package, by default []
        extern : list, optional
            List of additional libraries that are external to
            the model, used with torch.package, by default []
        mock : list, optional
            List of libraries to mock, used with torch.package,
            by default []

        Returns
        -------
        str
            Path to saved model.
        Nkwargsr	   )
state_dictr	   )r   r   r   )inspect	signature	__class__
parametersitemsdefault	Parameteremptydirgetattrhasattrr	   updater   torchsave_save_package)selfr   r	   r
   r   r   r   sigargskeyvalarg_val	attributer    r&   M/home/ubuntu/.local/lib/python3.10/site-packages/audiotools/ml/layers/base.pyr   B   s,   %
zBaseModel.savec                 C   s   t |  d jS )zGets the device the model is on by looking at the device of
        the first parameter. May not be valid if model is split across
        multiple devices.
        r   )listr   device)r   r&   r&   r'   r)      s   zBaseModel.deviceF)package_namestrictlocationr*   r+   c                O   s   z
| j ||d}W |S    t|d}|d }|d | t| }	t|	j }
t|d  D ]}||
vr@|d 	| q3| |i |d }|j
|d |d ||_Y |S )a  Load model from a path. Tries first to load as a package, and if
        that fails, tries to load as weights. The arguments to the class are
        specified inside the model weights file.

        Parameters
        ----------
        location : str
            Path to file.
        package_name : str, optional
            Name of package, by default ``cls.__name__``.
        strict : bool, optional
            Ignore unmatched keys, by default False
        kwargs : dict
            Additional keyword arguments to the model instantiation, if
            not loading from package.

        Returns
        -------
        BaseModel
            A model that inherits from BaseModel.
        )r*   cpur	   r   r   r+   )_load_packager   loadr   r   r   r(   r   keyspopload_state_dictr	   )clsr,   r*   r+   r!   r   model
model_dictr	   r    
class_keyskr&   r&   r'   r0      s"   
zBaseModel.loadc           
   	   K   s  t | j}t | j d}t| dr| jtjjf|d< | `tjddV}tjj	|j
fi |4}	|	| j|  |	| |	| j|  |	|||  t| dr\|	|| d| j W d    n1 sfw   Y  t|j
| W d    n1 s|w   Y  d|v r|d d | _|S )N.pthimporter)suffixr	   	.metadatar   )type__name__r   r:   r   r
   sys_importertempfileNamedTemporaryFilePackageExporternamer   INTERNr   r   EXTERNsave_pickler	   shutilcopyfile)
r   r   r   r   r   r   r*   resource_namefexpr&   r&   r'   r      s,   



zBaseModel._save_packagec                 C   sb   |d u r| j n|}| d}tj|}|||d}z||| d|_W n   Y ||_|S )Nr9   r-   r<   )r>   r   r
   PackageImporterload_pickler	   r:   )r4   r   r*   rI   impr5   r&   r&   r'   r/      s   
zBaseModel._load_packagefolder
extra_datac           
      C   s   |du ri n|}t | j }t| d| d}|jddd |r+|d }| | |d }| j|dd | D ]\}}	t|	||  q:|S )	a  Dumps a model into a folder, as both a package
        and as weights, as well as anything specified in
        ``extra_data``. ``extra_data`` is a dictionary of other
        pickleable files, with the keys being the paths
        to save them in. The model is saved under a subfolder
        specified by the name of the class (e.g. ``folder/generator/[package, weights].pth``
        if the model name was ``Generator``).

        >>> with tempfile.TemporaryDirectory() as d:
        >>>     extra_data = {
        >>>         "optimizer.pth": optimizer.state_dict()
        >>>     }
        >>>     model.save_to_folder(d, extra_data)
        >>>     Model.load_from_folder(d)

        Parameters
        ----------
        folder : typing.Union[str, Path]
            _description_
        extra_data : dict, optional
            _description_, by default None

        Returns
        -------
        str
            Path to folder
        N/T)exist_okparentspackage.pthweights.pthF)r
   )r=   r>   lowerr   mkdirr   r   r   )
r   rO   rP   r
   
model_nametarget_basepackage_pathweights_pathr   objr&   r&   r'   save_to_folder   s   !
zBaseModel.save_to_folderc           
         s   t || j  }|rdnd}|| }| j||d}i }ddg  fdd|dD }|D ]}	tj|	fi |||	j< q.||fS )a?  Loads the model from a folder generated by
        :py:func:`audiotools.ml.layers.base.BaseModel.save_to_folder`.
        Like that function, this one looks for a subfolder that has
        the name of the class (e.g. ``folder/generator/[package, weights].pth`` if the
        model name was ``Generator``).

        Parameters
        ----------
        folder : typing.Union[str, Path]
            _description_
        package : bool, optional
            Whether to use ``torch.package`` to load the model,
            loading the model from ``package.pth``.
        strict : bool, optional
            Ignore unmatched keys, by default False

        Returns
        -------
        tuple
            tuple of model and extra data as saved by
            :py:func:`audiotools.ml.layers.base.BaseModel.save_to_folder`.
        rT   rU   r.   c                    s"   g | ]}|  r|j vr|qS r&   )is_filerC   ).0xexcludedr&   r'   
<listcomp>D  s   " z.BaseModel.load_from_folder.<locals>.<listcomp>*)r   r>   rV   r0   globr   rC   )
r4   rO   r
   r+   r   	model_pthr5   rP   filesrJ   r&   ra   r'   load_from_folder  s   zBaseModel.load_from_folder)N)NT)TF)r>   
__module____qualname____doc__rE   rD   strdictboolr(   r   propertyr)   classmethodr0   r   r/   typingUnionr   r]   rh   r&   r&   r&   r'   r      st    %

A
0"
2r   )
r   rG   r@   rq   pathlibr   r   r   Moduler   r&   r&   r&   r'   <module>   s    