U
    Kvfh;                     @   s   d Z ddlZddlmZmZ eejZ	dZ
dd Zddi dfd	d
Zddi dfddZddi fddZddi fddZddi fddZeddddddee
ddi dfddZeddddddee
ddi dfddZed d!d!d"d#dee
ddi fd$d%ZeZe j d&7  _ dS )'ay  numerical differentiation function, gradient, Jacobian, and Hessian

Author : josef-pkt
License : BSD

Notes
-----
These are simple forward differentiation, so that we have them available
without dependencies.

* Jacobian should be faster than numdifftools because it does not use loop over
  observations.
* numerical precision will vary and depend on the choice of stepsizes
    N)AppenderSubstitutiona  
    Calculate Hessian with finite difference derivative approximation

    Parameters
    ----------
    x : array_like
       value at which function derivative is evaluated
    f : function
       function of one array f(x, `*args`, `**kwargs`)
    epsilon : float or array_like, optional
       Stepsize used, if None, then stepsize is automatically chosen
       according to EPS**(1/%(scale)s)*x.
    args : tuple
        Arguments for function `f`.
    kwargs : dict
        Keyword arguments for function `f`.
    %(extra_params)s

    Returns
    -------
    hess : ndarray
       array of partial second derivatives, Hessian
    %(extra_returns)s

    Notes
    -----
    Equation (%(equation_number)s) in Ridout. Computes the Hessian as::

      %(equation)s

    where e[j] is a vector with element j == 1 and the rest are zero and
    d[i] is epsilon[i].

    References
    ----------:

    Ridout, M.S. (2009) Statistical applications of the complex-step method
        of numerical differentiation. The American Statistician, 63, 66-74
c                 C   sv   |d kr.t d|  ttt| d }n>t|rNt|}|| nt|}|j| jkrlt	dt|S )Ng      ?g?z6If h is not a scalar it must have the same shape as x.)
EPSnpmaximumabsasarrayZisscalaremptyfillshape
ValueError)xsepsilonnh r   =/tmp/pip-unpacked-wheel-2v6byqio/statsmodels/tools/numdiff.py_get_epsilon^   s    &


r   r   Fc                 C   sB  t | }|| f| |}t|j}t|f| tt| j}	t|ft}
|st| d||}t	|D ]D}|| |
|< || |
 f| || ||  |	|ddf< d|
|< qhntt| d||d }t	|D ]X}|| |
|< || |
 f| ||| |
 f| | d||   |	|ddf< d|
|< q|dkr4|	j
S |	 j
S dS )aR  
    Gradient of function, or Jacobian if function f returns 1d array

    Parameters
    ----------
    x : ndarray
        parameters at which the derivative is evaluated
    f : function
        `f(*((x,)+args), **kwargs)` returning either one value or 1d array
    epsilon : float, optional
        Stepsize, if None, optimal stepsize is used. This is EPS**(1/2)*x for
        `centered` == False and EPS**(1/3)*x for `centered` == True.
    args : tuple
        Tuple of additional arguments for function `f`.
    kwargs : dict
        Dictionary of additional keyword arguments for function `f`.
    centered : bool
        Whether central difference should be returned. If not, does forward
        differencing.

    Returns
    -------
    grad : ndarray
        gradient or Jacobian

    Notes
    -----
    If f returns a 1d array, it returns a Jacobian. If a 2d array is returned
    by f (e.g., with a value for each observation), it returns a 3d array
    with the Jacobian of each observation with shape xk x nobs x xk. I.e.,
    the Jacobian of the first observation would be [:, 0, :]
       Ng                  @   )lenr   Z
atleast_1dr   zerosZpromote_typesfloatZdtyper   rangeTsqueeze)r   fr   argskwargscenteredr   f0Zdimgradeikr   r   r   approx_fprimem   s.    !,


r'   c           
      C   s   t | } d}|| f| |}|sNt| d||}|| | f| || | }	nBt| d||d }|| | f| ||| | f| | d|  }	|	S )a'  
    Gradient of function vectorized for scalar parameter.

    This assumes that the function ``f`` is vectorized for a scalar parameter.
    The function value ``f(x)`` has then the same shape as the input ``x``.
    The derivative returned by this function also has the same shape as ``x``.

    Parameters
    ----------
    x : ndarray
        Parameters at which the derivative is evaluated.
    f : function
        `f(*((x,)+args), **kwargs)` returning either one value or 1d array
    epsilon : float, optional
        Stepsize, if None, optimal stepsize is used. This is EPS**(1/2)*x for
        `centered` == False and EPS**(1/3)*x for `centered` == True.
    args : tuple
        Tuple of additional arguments for function `f`.
    kwargs : dict
        Dictionary of additional keyword arguments for function `f`.
    centered : bool
        Whether central difference should be returned. If not, does forward
        differencing.

    Returns
    -------
    grad : ndarray
        Array of derivatives, gradient evaluated at parameters ``x``.
    r   r   r   r   )r   r   r   )
r   r   r   r    r!   r"   r   r#   epsr$   r   r   r   _approx_fprime_scalar   s    
r)   c                    sR   t }td|t|d  } fddt|D }t|jS )a  
    Calculate gradient or Jacobian with complex step derivative approximation

    Parameters
    ----------
    x : ndarray
        parameters at which the derivative is evaluated
    f : function
        `f(*((x,)+args), **kwargs)` returning either one value or 1d array
    epsilon : float, optional
        Stepsize, if None, optimal stepsize is used. Optimal step-size is
        EPS*x. See note.
    args : tuple
        Tuple of additional arguments for function `f`.
    kwargs : dict
        Dictionary of additional keyword arguments for function `f`.

    Returns
    -------
    partials : ndarray
       array of partial derivatives, Gradient or Jacobian

    Notes
    -----
    The complex-step derivative has truncation error O(epsilon**2), so
    truncation error can be eliminated by choosing epsilon to be very small.
    The complex-step derivative avoids the problem of round-off error with
    small epsilon because there is no subtraction.
    r                 ?c                    s.   g | ]&\}}| f j |  qS r   )imag).0iZihr    r   r   r!   r   r   r   
<listcomp>   s   z$approx_fprime_cs.<locals>.<listcomp>)r   r   r   identity	enumeratearrayr   )r   r   r   r    r!   r   Z
incrementspartialsr   r.   r   approx_fprime_cs   s    !r4   c                 C   sN   t | } | jd }t| d||}d| }|| | f||j| }t |S )a  
    Calculate gradient for scalar parameter with complex step derivatives.

    This assumes that the function ``f`` is vectorized for a scalar parameter.
    The function value ``f(x)`` has then the same shape as the input ``x``.
    The derivative returned by this function also has the same shape as ``x``.

    Parameters
    ----------
    x : ndarray
        Parameters at which the derivative is evaluated.
    f : function
        `f(*((x,)+args), **kwargs)` returning either one value or 1d array.
    epsilon : float, optional
        Stepsize, if None, optimal stepsize is used. Optimal step-size is
        EPS*x. See note.
    args : tuple
        Tuple of additional arguments for function `f`.
    kwargs : dict
        Dictionary of additional keyword arguments for function `f`.

    Returns
    -------
    partials : ndarray
       Array of derivatives, gradient evaluated for parameters ``x``.

    Notes
    -----
    The complex-step derivative has truncation error O(epsilon**2), so
    truncation error can be eliminated by choosing epsilon to be very small.
    The complex-step derivative avoids the problem of round-off error with
    small epsilon because there is no subtraction.
    r   r*   )r   r   r   r   r+   r2   )r   r   r   r    r!   r   r(   r3   r   r   r   _approx_fprime_cs_scalar  s    %

r6   c                 C   s   t | }t| d||}t|}t||}t | }t|D ]}	t|	|D ]}
t|| d||	ddf   ||
ddf  f| ||| d||	ddf   ||
ddf  f| | jd ||	|
f  ||	|
f< ||	|
f ||
|	f< qJq<|S )a  Calculate Hessian with complex-step derivative approximation

    Parameters
    ----------
    x : array_like
       value at which function derivative is evaluated
    f : function
       function of one array f(x)
    epsilon : float
       stepsize, if None, then stepsize is automatically chosen

    Returns
    -------
    hess : ndarray
       array of partial second derivatives, Hessian

    Notes
    -----
    based on equation 10 in
    M. S. RIDOUT: Statistical Applications of the Complex-step Method
    of Numerical Differentiation, University of Kent, Canterbury, Kent, U.K.

    The stepsize is the same for the complex and the finite difference part.
    r   r*   Nr   )r   r   r   diagouterr   r   r+   r   r   r   r    r!   r   r   eehessr-   jr   r   r   approx_hess_cs0  s(    
2.
r=   3zFreturn_grad : bool
        Whether or not to also return the gradient
z7grad : nparray
        Gradient if return_grad == True
7zB1/(d_j*d_k) * ((f(x + d[j]*e[j] + d[k]*e[k]) - f(x + d[j]*e[j])))
)ZscaleZextra_paramsZextra_returnsZequation_numberZequationc                 C   s$  t | }t| d||}t|}|| f| |}	t|}
t|D ](}|| ||d d f  f| ||
|< qBt||}t|D ]}t||D ]p}|| ||d d f  ||d d f  f| ||
|  |
|  |	 |||f  |||f< |||f |||f< qq|r|
|	 | }||fS |S d S )Nr   r   r   r   r7   r   r   r8   )r   r   r   r    r!   return_gradr   r   r:   r#   gr-   r;   r<   r$   r   r   r   approx_hess1]  s0    

&.
rC   z7grad : ndarray
        Gradient if return_grad == True
8z1/(2*d_j*d_k) * ((f(x + d[j]*e[j] + d[k]*e[k]) - f(x + d[j]*e[j])) -
                 (f(x + d[k]*e[k]) - f(x)) +
                 (f(x - d[j]*e[j] - d[k]*e[k]) - f(x + d[j]*e[j])) -
                 (f(x - d[k]*e[k]) - f(x)))
c              	   C   s  t | }t| d||}t|}|| f| |}	t|}
t|}t|D ]L}|| ||d d f  f| ||
|< || ||d d f  f| |||< qLt||}t|D ]}t||D ]}|| ||d d f  ||d d f  f| ||
|  |
|  |	 || ||d d f  ||d d f  f| | ||  ||  |	 d|||f   |||f< |||f |||f< qq|r|
|	 | }||fS |S d S )Nr   r   r@   )r   r   r   r    r!   rA   r   r   r:   r#   rB   Zggr-   r;   r<   r$   r   r   r   approx_hess2  sD    


$&..rE   4 9a	  1/(4*d_j*d_k) * ((f(x + d[j]*e[j] + d[k]*e[k]) - f(x + d[j]*e[j]
                                                     - d[k]*e[k])) -
                 (f(x - d[j]*e[j] + d[k]*e[k]) - f(x - d[j]*e[j]
                                                     - d[k]*e[k]))c                 C   sB  t | }t| d||}t|}t||}t|D ]}	t|	|D ]}
t|| ||	d d f  ||
d d f  f| ||| ||	d d f  ||
d d f  f| | || ||	d d f  ||
d d f  f| ||| ||	d d f  ||
d d f  f| |  d||	|
f   ||	|
f< ||	|
f ||
|	f< qDq4|S )N   g      @)r   r   r   r7   r8   r   r   r9   r   r   r   approx_hess3  s&    
....rJ   z&
    This is an alias for approx_hess3)__doc__Znumpyr   Zstatsmodels.compat.pandasr   r   Zfinfor   r(   r   Z_hessian_docsr   r'   r)   r4   r6   r=   rC   rE   rJ   Zapprox_hessr   r   r   r   <module>   sR   -):
.,/-
