B
    îq\.*  ã               @   s–   d Z ddlZddlZddlmZ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 d
dgZd
gZddd
„ZG dd„ dƒZdS )z(General purpose timer related functions.é    N)ÚIterableÚOrderedDict)ÚpartialÚwraps)Úunits)Úlog)Úmodelingé   )ÚAstropyUserWarningÚtimefuncÚRunTimePredictorTc                s   ‡ ‡fdd„}|S )aU  Decorator to time a function or method.

    Parameters
    ----------
    num_tries : int, optional
        Number of calls to make. Timer will take the
        average run time.

    verbose : bool, optional
        Extra log information.

    Returns
    -------
    tt : float
        Average run time in seconds.

    result
        Output(s) from the function.

    Examples
    --------
    To add timer to time `numpy.log` for 100 times with
    verbose output::

        import numpy as np
        from astropy.utils.timer import timefunc

        @timefunc(100)
        def timed_log(x):
            return np.log(x)

    To run the decorated function above:

    >>> t, y = timed_log(100)
    INFO: timed_log took 9.29832458496e-06 s on AVERAGE for 100 call(s). [...]
    >>> t
    9.298324584960938e-06
    >>> y
    4.6051701859880918

    c                s   t ˆ ƒ‡ ‡‡fdd„ƒ}|S )Nc                 sZ   t   ¡ }xtˆƒD ]}ˆ | |Ž}qW t   ¡ }|| ˆ }ˆrRt d ˆ j|ˆ¡¡ ||fS )Nz*{0} took {1} s on AVERAGE for {2} call(s).)ÚtimeÚranger   ÚinfoÚformatÚ__name__)ÚargsÚkwargsZtsÚiÚresultZteZtt)ÚfunctionÚ	num_triesÚverbose© ú2lib/python3.7/site-packages/astropy/utils/timer.pyÚwrapperB   s    z1timefunc.<locals>.real_decorator.<locals>.wrapper)r   )r   r   )r   r   )r   r   Úreal_decoratorA   s    z timefunc.<locals>.real_decoratorr   )r   r   r   r   )r   r   r   r      s    *c               @   sd   e Zd ZdZdd„ Zedd„ ƒZedddd	d
„ ƒZdd„ Z	dd„ Z
ddd„Zdd„ Zddd„ZdS )r   a©  Class to predict run time.

    .. note:: Only predict for single varying numeric input parameter.

    Parameters
    ----------
    func : function
        Function to time.

    args : tuple
        Fixed positional argument(s) for the function.

    kwargs : dict
        Fixed keyword argument(s) for the function.

    Examples
    --------
    >>> from astropy.utils.timer import RunTimePredictor

    Set up a predictor for :math:`10^{x}`:

    >>> p = RunTimePredictor(pow, 10)

    Give it baseline data to use for prediction and
    get the function output values:

    >>> p.time_func(range(10, 1000, 200))
    >>> for input, result in sorted(p.results.items()):
    ...     print("pow(10, {0})\n{1}".format(input, result))
    pow(10, 10)
    10000000000
    pow(10, 210)
    10000000000...
    pow(10, 410)
    10000000000...
    pow(10, 610)
    10000000000...
    pow(10, 810)
    10000000000...

    Fit a straight line assuming :math:`\text{arg}^{1}` relationship
    (coefficients are returned):

    >>> p.do_fit()  # doctest: +SKIP
    array([1.16777420e-05,  1.00135803e-08])

    Predict run time for :math:`10^{5000}`:

    >>> p.predict_time(5000)  # doctest: +SKIP
    6.174564361572262e-05

    Plot the prediction:

    >>> p.plot(xlabeltext='Power of 10')  # doctest: +SKIP

    .. image:: /_static/timer_prediction_pow10.png
        :width: 450px
        :alt: Example plot from `astropy.utils.timer.RunTimePredictor`

    When the changing argument is not the last, e.g.,
    :math:`x^{2}`, something like this might work:

    >>> p = RunTimePredictor(lambda x: pow(x, 2))
    >>> p.time_func([2, 3, 5])
    >>> sorted(p.results.items())
    [(2, 4), (3, 9), (5, 25)]

    c             O   sH   |j | _t|f|ž|Ž| _tƒ | _g | _tƒ | _tƒ | _d | _	d | _
d S )N)r   Ú	_funcnamer   Ú_pfuncr   Ú_cache_goodÚ
_cache_badÚ
_cache_estÚ
_cache_outÚ	_fit_funcÚ_power)ÚselfÚfuncr   r   r   r   r   Ú__init__–   s    zRunTimePredictor.__init__c             C   s   | j S )z¨Function outputs from `time_func`.

        A dictionary mapping input arguments (fixed arguments
        are not included) to their respective output values.

        )r"   )r%   r   r   r   Úresults    s    zRunTimePredictor.resultsr	   F)r   r   c             C   s
   |   |¡S )z1Run partial func once for single arg and time it.)r   )r%   Úargr   r   r   Ú_timed_pfuncª   s    zRunTimePredictor._timed_pfuncc          
   C   s‚   || j kr~|| jkr~y|  |¡}W n> tk
r` } z t t|ƒt¡ | j |¡ W dd}~X Y nX |d | j |< |d | j	|< dS )z(Cache timing results without repetition.Nr   r	   )
r   r    r*   Ú	ExceptionÚwarningsÚwarnÚstrr
   Úappendr"   )r%   r)   r   Úer   r   r   Ú_cache_time¯   s    zRunTimePredictor._cache_timec             C   s,   t |tƒs|g}x|D ]}|  |¡ qW dS )aE  Time the partial function for a list of single args
        and store run time in a cache. This forms a baseline for
        the prediction.

        This also stores function outputs in `results`.

        Parameters
        ----------
        arglist : list of numbers
            List of input arguments to time.

        N)Ú
isinstancer   r1   )r%   Zarglistr)   r   r   r   Ú	time_func»   s    

zRunTimePredictor.time_funcNé   c             C   sÌ   || _ tƒ | _t t| j ¡ ƒ¡}|j|k r>t	d 
||j¡ƒ‚|dkrTtj d¡}n t|tjjƒsttj d 
|¡¡‚|dkrˆtj ¡ }n t|tjjƒs¨tj d 
|¡¡‚|||| t| j ¡ ƒƒ| _| jjS )a5  Fit a function to the lists of arguments and
        their respective run time in the cache.

        By default, this does a linear least-square fitting
        to a straight line on run time w.r.t. argument values
        raised to the given power, and returns the optimal
        intercept and slope.

        Parameters
        ----------
        model : `astropy.modeling.Model`
            Model for the expected trend of run time (Y-axis)
            w.r.t. :math:`\text{arg}^{\text{power}}` (X-axis).
            If `None`, will use `~astropy.modeling.polynomial.Polynomial1D`
            with ``degree=1``.

        fitter : `astropy.modeling.fitting.Fitter`
            Fitter for the given model to extract optimal coefficient values.
            If `None`, will use `~astropy.modeling.fitting.LinearLSQFitter`.

        power : int, optional
            Power of values to fit.

        min_datapoints : int, optional
            Minimum number of data points required for fitting.
            They can be built up with `time_func`.

        Returns
        -------
        a : array-like
            Fitted `~astropy.modeling.FittableModel` parameters.

        Raises
        ------
        ValueError
            Insufficient data points for fitting.

        ModelsError
            Invalid model or fitter.

        zrequires {0} points but has {1}Nr	   z{0} is not a model.z{0} is not a fitter.)r$   r   r!   ÚnpÚarrayÚlistr   ÚkeysÚsizeÚ
ValueErrorr   r   ZmodelsZPolynomial1Dr2   ZcoreZModelZfittingZModelsErrorZLinearLSQFitterZFitterÚvaluesr#   Z
parameters)r%   ZmodelZfitterZpowerZmin_datapointsÚx_arrr   r   r   Údo_fitÐ   s&    +
zRunTimePredictor.do_fitc             C   sF   || j kr| j | }n,| jdkr(tdƒ‚|  || j ¡}|| j |< |S )aª  Predict run time for given argument.
        If prediction is already cached, cached value is returned.

        Parameters
        ----------
        arg : number
            Input argument to predict run time for.

        Returns
        -------
        t_est : float
            Estimated run time for given argument.

        Raises
        ------
        RuntimeError
            No fitted data for prediction.

        Nzno fitted data for prediction)r!   r#   ÚRuntimeErrorr$   )r%   r)   Zt_estr   r   r   Úpredict_time  s    


zRunTimePredictor.predict_timeÚlinearr   Ú c                sº  ddl m} tˆ jƒ}t ‡ fdd„|D ƒ¡}t|ƒdkrBtdƒ‚| ¡ t	j
 }xDt	jt	j
t	jt	jt	jfD ](}	| |	¡}
d|
  krŒdkrjn qjP qjW |t	j
  |	¡}| ¡ \}}|j||dd	d
 ˆ jdk	rRtˆ j ¡ ƒ}t tˆ j ¡ ƒ¡t	j
  |	¡}|j||dddd t t|| ƒ¡}ˆ  |ˆ j ¡t	j
  |	¡}|j||ddd
 | |¡ | |¡ | |¡ | d |	 ¡ ¡¡ | ˆ j ¡ |j!ddd | "¡  |r¶| #|¡ dS )aÒ  Plot prediction.

        .. note:: Uses `matplotlib <http://matplotlib.org/>`_.

        Parameters
        ----------
        xscale, yscale : {'linear', 'log', 'symlog'}
            Scaling for `matplotlib.axes.Axes`.

        xlabeltext : str, optional
            Text for X-label.

        save_as : str, optional
            Save plot as given filename.

        Raises
        ------
        RuntimeError
            Insufficient data for plotting.

        r   Nc                s   g | ]}ˆ j | ‘qS r   )r   )Ú.0Úx)r%   r   r   ú
<listcomp>L  s    z)RunTimePredictor.plot.<locals>.<listcomp>r	   zinsufficient data for plottingiè  zkx-ZActual)ÚlabelÚoÚrZ	Predicted)ZmarkerÚcrE   zb--ZFitzRun time ({})Zbest)ZlocZ	numpoints)$Zmatplotlib.pyplotZpyplotÚsortedr   r5   r6   Úlenr>   ZmeanÚuÚsecondZminuteZmillisecondZmicrosecondZ
nanosecondZto_valueZsubplotsÚplotr#   r7   r!   r8   r;   Zscatterr$   Z
set_xscaleZ
set_yscaleZ
set_xlabelZ
set_ylabelr   Z	to_stringZ	set_titler   ZlegendZdrawZsavefig)r%   ZxscaleZyscaleZ
xlabeltextZsave_asZpltr<   Zy_arrZqmeanZcur_uÚvalZfigZaxZx_estZy_estZx_fitZy_fitr   )r%   r   rM   1  s@    




zRunTimePredictor.plot)NNr	   r4   )r@   r@   r   rA   )r   Ú
__module__Ú__qualname__Ú__doc__r'   Úpropertyr(   r   r*   r1   r3   r=   r?   rM   r   r   r   r   r   Q   s   D


D )r	   T)rQ   r   r,   Úcollectionsr   r   Ú	functoolsr   r   Znumpyr5   Zastropyr   rK   r   r   Ú
exceptionsr
   Ú__all__Z__doctest_skip__r   r   r   r   r   r   Ú<module>   s   
: