o
    %ݫi~9                     @   s  d Z ddlZddlZddlZddlZddlmZ ddlmZ ddlZ	ddl
mZ eeZdZdZdZd	Zg d
ZdZdZdZeegZi Zdd ZG dd dZed	G dd deZedG dd deZdd ZddiZG dd dZdd Zdd Z dd Z!dS ) u   Utilities for hyperparameter optimization.
This wrapper has an optional dependency on
Oríon

https://orion.readthedocs.io/en/stable/
https://github.com/Epistimio/orion

Authors
 * Artem Ploujnikov 2021
    N)datetime)load_hyperpyyaml)
get_loggerzorion.clientz%Y%m%d%H%M%S%fhpoptgeneric)ORION_EXPERIMENT_NAMEORION_EXPERIMENT_VERSIONORION_TRIAL_ID
hpopt_modetrial_idc                    s    fdd}|S )aZ  A decorator to register a reporter implementation for
    a hyperparameter optimization mode

    Arguments
    ---------
    mode: str
        the mode to register

    Returns
    -------
    f: callable
        a callable function that registers and returns the
        reporter class

    Example
    -------
    >>> @hpopt_mode("raw")
    ... class RawHyperparameterOptimizationReporter(HyperparameterOptimizationReporter):
    ...    def __init__(self, *args, **kwargs):
    ...        super().__init__(    *args, **kwargs)
    ...    def report_objective(self, result):
    ...        objective = result[self.objective_key]
    ...        print(f"Objective: {objective}")

    >>> reporter = get_reporter("raw", objective_key="error")
    >>> result = {"error": 1.2, "train_loss": 7.2}
    >>> reporter.report_objective(result)
    Objective: 1.2
    c                    s   | t  < | S )zA "Call the function that registers and returns the reporter class)_hpopt_modes)clsmode K/home/ubuntu/.local/lib/python3.10/site-packages/speechbrain/utils/hpopt.pyfJ   s   zhpopt_mode.<locals>.fr   )r   r   r   r   r   r
   +   s   c                   @   s8   e Zd ZdZdd Zdd Zedd Zedd	 Zd
S )"HyperparameterOptimizationReporterzA base class for hyperparameter fit reporters

    Arguments
    ---------
    objective_key: str
        the key from the result dictionary to be used as the objective
    c                 C   s
   || _ d S Nobjective_keyselfr   r   r   r   __init__[   s   
z+HyperparameterOptimizationReporter.__init__c                 C      t S )a  Reports the objective for hyperparameter optimization.

        Arguments
        ---------
        result: dict
            a dictionary with the run result.

        Returns
        -------
        objective: dict
            A mapping from metric to score.
        )NotImplementedr   resultr   r   r   report_objective^   s   z3HyperparameterOptimizationReporter.report_objectivec                 C   s   dS )z-Determines whether this reporter is availableTr   r   r   r   r   is_availablem      z/HyperparameterOptimizationReporter.is_availablec                 C   r   )z4The unique ID of this trial (used for folder naming))DEFAULT_TRIAL_IDr   r   r   r   r   r   r!   z+HyperparameterOptimizationReporter.trial_idN)	__name__
__module____qualname____doc__r   r   propertyr    r   r   r   r   r   r   R   s    
r   c                       s6   e Zd ZdZd	 fdd	Zdd Zedd Z  ZS )
)GenericHyperparameterOptimizationReportera  
    A generic hyperparameter fit reporter that outputs the result as
    JSON to an arbitrary data stream, which may be read as a third-party
    tool

    Arguments
    ---------
    reference_date: datetime.datetime
        The date used to create trial id
    output: stream
        The stream to report the results to
    *args: tuple
        Arguments to be forwarded to parent class
    **kwargs: dict
        Arguments to be forwarded to parent class
    Nc                    s.   t  j|i | |ptj| _|| _d | _d S r   )superr   sysstdoutoutputreference_date	_trial_id)r   r-   r,   argskwargs	__class__r   r   r      s   
z2GenericHyperparameterOptimizationReporter.__init__c                 C   s    t t||| j d| j dS )a  Reports the objective for hyperparameter optimization.

        Arguments
        ---------
        result: dict
            a dictionary with the run result.

        Example
        -------
        >>> reporter = GenericHyperparameterOptimizationReporter(
        ...     objective_key="error"
        ... )
        >>> result = {"error": 1.2, "train_loss": 7.2}
        >>> reporter.report_objective(result)
        {"error": 1.2, "train_loss": 7.2, "objective": 1.2}
        )	objectiveN)jsondumpdictr   r,   r   r   r   r   r      s   z:GenericHyperparameterOptimizationReporter.report_objectivec                 C   s*   | j du r| jpt }|t| _ | j S )aw  The unique ID of this trial (used mainly for folder naming)

        Example
        -------
        >>> import datetime
        >>> reporter = GenericHyperparameterOptimizationReporter(
        ...     objective_key="error",
        ...     reference_date=datetime.datetime(2021, 1, 3)
        ... )
        >>> print(reporter.trial_id)
        20210103000000000000
        N)r.   r-   r   nowstrftimeFORMAT_TIMESTAMP)r   dater   r   r   r      s   
z2GenericHyperparameterOptimizationReporter.trial_idNN)	r#   r$   r%   r&   r   r   r'   r   __classcell__r   r   r1   r   r(   x   s    r(   orionc                       sP   e Zd ZdZ fddZdd Zdd Zdd	 Zed
d Z	edd Z
  ZS )'OrionHyperparameterOptimizationReporterzA result reporter implementation based on Orion

    Arguments
    ---------
    objective_key: str
        the key from the result dictionary to be used as the objective
    c                    s&   t  j|d d | _d | _|   d S )Nr   )r)   r   orion_clientr.   _check_clientr   r1   r   r   r      s   z0OrionHyperparameterOptimizationReporter.__init__c                 C   s8   z	t t| _W d S  ty   td d | _Y d S w )NzOrion is not available)	importlibimport_moduleMODULE_ORIONr?   ImportErrorloggerwarningr   r   r   r   r@      s   
z5OrionHyperparameterOptimizationReporter._check_clientc                 C   s   d dd | D S )zFormats the log message for output

        Arguments
        ---------
        result: dict
            the result dictionary

        Returns
        -------
        message: str
            a formatted message
        z, c                 s   s"    | ]\}}| d | V  qdS )z = Nr   ).0keyvaluer   r   r   	<genexpr>   s     zJOrionHyperparameterOptimizationReporter._format_message.<locals>.<genexpr>)joinitemsr   r   r   r   _format_message   s   z7OrionHyperparameterOptimizationReporter._format_messagec                 C   sB   |  |}td|  | jdur|| j }| j| dS dS )zReports the objective for hyperparameter optimization.

        Arguments
        ---------
        result: dict
            a dictionary with the run result.
        zHyperparameter fit: N)rM   rE   infor?   r   r   )r   r   messageobjective_valuer   r   r   r      s   


z8OrionHyperparameterOptimizationReporter.report_objectivec                 C   s&   | j du rddd tD | _ | j S )z;The unique ID of this trial (used mainly for folder naming)N-c                 s   s    | ]
}t |p
d V  qdS ) NosgetenvrG   namer   r   r   rJ      s    
zCOrionHyperparameterOptimizationReporter.trial_id.<locals>.<genexpr>)r.   rK   ORION_TRIAL_ID_ENVr   r   r   r   r      s
   


z0OrionHyperparameterOptimizationReporter.trial_idc                 C   s   | j duotdd tD S )zDetermines if Orion is available. In order for it to
        be available, the library needs to be installed, and at
        least one of ORION_EXPERIMENT_NAME, ORION_EXPERIMENT_VERSION,
        ORION_TRIAL_ID needs to be set
        Nc                 s   s    | ]}t |V  qd S r   rS   rV   r   r   r   rJ     s    

zGOrionHyperparameterOptimizationReporter.is_available.<locals>.<genexpr>)r?   anyrX   r   r   r   r   r       s   z4OrionHyperparameterOptimizationReporter.is_available)r#   r$   r%   r&   r   r@   rM   r   r'   r   r    r<   r   r   r1   r   r>      s    
r>   c                 O   sd   t | }|du rtd|  d t t }||i |}|js0td t t }||i |}|S )a	  Attempts to get the reporter specified by the mode
    and reverts to a generic one if it is not available

    Arguments
    ---------
    mode: str
        a string identifier for a registered hyperparameter
        optimization mode, corresponding to a specific reporter
        instance
    *args: tuple
        Arguments to forward to the reporter class.
    **kwargs: dict
        Arguments to forward to the reporter class.

    Returns
    -------
    reporter: HyperparameterOptimizationReporter
        a reporter instance

    Example
    -------
    >>> reporter = get_reporter("generic", objective_key="error")
    >>> result = {"error": 3.4, "train_loss": 1.2}
    >>> reporter.report_objective(result)
    {"error": 3.4, "train_loss": 1.2, "objective": 3.4}
    Nzhpopt_mode z' is not supported, reverting to genericzReverting to a generic reporter)r   getrE   rF   DEFAULT_REPORTERr    )r   r/   r0   reporter_clsreporterr   r   r   get_reporter  s   


r^   currentc                   @   s6   e Zd ZdZdddZ	dddZdd	 Zd
d ZdS )!HyperparameterOptimizationContexta  
    A convenience context manager that makes it possible to conditionally
    enable hyperparameter optimization for a recipe.

    Arguments
    ---------
    reporter_args: list
        arguments to the reporter class
    reporter_kwargs: dict
        keyword arguments to the reporter class

    Example
    -------
    >>> ctx = HyperparameterOptimizationContext(
    ...     reporter_args=[],
    ...     reporter_kwargs={"objective_key": "error"}
    ... )
    Nc                 C   s.   |pg | _ |pi | _d | _d| _ddi| _d S )NFr3   g        )reporter_argsreporter_kwargsr]   enabledr   )r   ra   rb   r   r   r   r   G  s
   

z*HyperparameterOptimizationContext.__init__Tc                 C   s  |du rg }t |}t|\}}}|rt|ni }|td}|tp&t}	|rd| _t	|	g| j
R i | j| _t|trtj|rt|dd7}
t }t|
d|idd}t|fi |}tt}|sk|t |D ]}||v rz||vrz||= qmW d   n1 sw   Y  |||fS )a5  A version of speechbrain.parse_arguments enhanced for hyperparameter optimization.

        If a parameter named 'hpopt' is provided, hyperparameter
        optimization and reporting will be enabled.

        If the parameter value corresponds to a filename, it will
        be read as a hyperpyyaml file, and the contents will be added
        to "overrides". This is useful for cases where the values of
        certain hyperparameters are different during hyperparameter
        optimization vs during full training (e.g. number of epochs, saving
        files, etc)

        Arguments
        ---------
        arg_list: list
            a list of arguments
        pass_hpopt_args: enumerable
            forces arguments that are normally suppressed and only used
            for hyperparameter optimization to be passed into overrides
        pass_trial_id: bool
            whether the "trial_id" argument is passed through (enabled by default)


        Returns
        -------
        param_file : str
            The location of the parameters file.
        run_opts : dict
            Run options, such as distributed, device, etc.
        overrides : dict
            The overrides to pass to ``load_hyperpyyaml``.

        Example
        -------
        >>> ctx = HyperparameterOptimizationContext()
        >>> arg_list = ["hparams.yaml", "--x", "1", "--y", "2"]
        >>> hparams_file, run_opts, overrides = ctx.parse_arguments(arg_list)
        >>> print(f"File: {hparams_file}, Overrides: {overrides}")
        File: hparams.yaml, Overrides: {'x': 1, 'y': 2}
        NFTzutf-8)encodingr   )	overridesoverrides_must_match)setsbparse_argumentsr   rZ   	KEY_HPOPTKEY_HPOPT_MODEr[   rc   r^   ra   rb   r]   
isinstancestrrT   pathexistsopenget_trial_idr6   list
HPOPT_KEYSappendKEY_TRIAL_ID)r   arg_listpass_hpopt_argspass_trial_idhparams_filerun_optsoverrides_yamlre   r   r
   
hpopt_filer   hpopt_overrideskeysrH   r   r   r   ri   N  sF   +

z1HyperparameterOptimizationContext.parse_argumentsc                 C   s   | t d< | S Nr_   )_contextr   r   r   r   	__enter__  s   z+HyperparameterOptimizationContext.__enter__c                 C   sN   |d u r!| j d ur!| j}|sttg| jR i | j}|| j  d td< d S r   )r   r]   r^   r[   ra   rb   r   r   )r   exc_type	exc_value	tracebackr]   r   r   r   __exit__  s   z*HyperparameterOptimizationContext.__exit__r;   )NT)r#   r$   r%   r&   r   ri   r   r   r   r   r   r   r`   3  s    

Hr`   c                  O   s   t | |}|S )aa  Initializes the hyperparameter optimization context

    Arguments
    ---------
    *args : tuple
        Arguments to forward to HyperparameterOptimizationContext
    **kwargs : dict
        Arguments to forward to HyperparameterOptimizationContext

    Returns
    -------
    HyperparameterOptimizationContext

    Example
    -------
    >>> import sys
    >>> with hyperparameter_optimization(objective_key="error", output=sys.stdout) as hp_ctx:
    ...     result = {"error": 3.5, "train_loss": 2.1}
    ...     report_result(result)
    ...
    {"error": 3.5, "train_loss": 2.1, "objective": 3.5}
    )r`   )r/   r0   hpfitr   r   r   hyperparameter_optimization  s   
r   c                 C   s   t d }|r| |_dS dS )a]  Reports the result using the current reporter, if available.
    When not in hyperparameter optimization mode, this function does nothing.

    Arguments
    ---------
    result: dict
        A dictionary of stats to be reported

    Example
    -------
    >>> result = {"error": 3.5, "train_loss": 2.1}
    >>> report_result(result["error"])
    r_   N)r   r   )r   ctxr   r   r   report_result  s   
r   c                  C   s    t d } | r| jj}|S t}|S )a  
    Returns the ID of the current hyperparameter optimization trial,
    used primarily for the name of experiment folders.

    When using a context, the convention for identifying the trial ID
    will depend on the reporter being used. The default implementation
    returns a fixed value ("hpopt")

    Returns
    -------
    trial_id: str
        the trial identifier

    Example
    -------
    >>> trial_id = get_trial_id()
    >>> trial_id
    'hpopt'
    r_   )r   r]   r   r"   )r   r   r   r   r   rq     s
   rq   )"r&   rA   r4   rT   r*   r   hyperpyyamlr   speechbrainrh   speechbrain.utils.loggerr   r#   rE   rC   r9   r"   r[   rX   rj   rk   ru   rs   r   r
   r   r(   r>   r^   r   r`   r   r   rq   r   r   r   r   <module>   sH    '&
C
J)t