# Source code for gstools.tools.special

"""
GStools subpackage providing special functions.

.. currentmodule:: gstools.tools.special

The following functions are provided

.. autosummary::
inc_gamma
inc_gamma_low
exp_int
inc_beta
tplstable_cor
tpl_exp_spec_dens
tpl_gau_spec_dens
"""
# pylint: disable=C0103, E1101
import numpy as np
from scipy import special as sps

__all__ = [
"confidence_scaling",
"inc_gamma",
"inc_gamma_low",
"exp_int",
"inc_beta",
"tplstable_cor",
"tpl_exp_spec_dens",
"tpl_gau_spec_dens",
]

# special functions ###########################################################

[docs]def confidence_scaling(per=0.95):
"""
Scaling of standard deviation to get the desired confidence interval.

Parameters
----------
per : :class:float, optional
Confidence level. The default is 0.95.

Returns
-------
:class:float
Scale to multiply the standard deviation with.
"""
return np.sqrt(2) * sps.erfinv(per)

[docs]def inc_gamma(s, x):
r"""Calculate the (upper) incomplete gamma function.

Given by: :math:\Gamma(s,x) = \int_x^{\infty} t^{s-1}\,e^{-t}\,{\rm d}t

Parameters
----------
s : :class:float
exponent in the integral
x : :class:numpy.ndarray
input values
"""
if np.isclose(s, 0):
return sps.exp1(x)
if np.isclose(s, np.around(s)) and s < -0.5:
return x**s * sps.expn(int(1 - np.around(s)), x)
if s < 0:
return (inc_gamma(s + 1, x) - x**s * np.exp(-x)) / s
return sps.gamma(s) * sps.gammaincc(s, x)

[docs]def inc_gamma_low(s, x):
r"""Calculate the lower incomplete gamma function.

Given by: :math:\gamma(s,x) = \int_0^x t^{s-1}\,e^{-t}\,{\rm d}t

Parameters
----------
s : :class:float
exponent in the integral
x : :class:numpy.ndarray
input values
"""
if np.isclose(s, np.around(s)) and s < 0.5:
return np.full_like(x, np.inf, dtype=np.double)
if s < 0:
return (inc_gamma_low(s + 1, x) + x**s * np.exp(-x)) / s
return sps.gamma(s) * sps.gammainc(s, x)

[docs]def exp_int(s, x):
r"""Calculate the exponential integral :math:E_s(x).

Given by: :math:E_s(x) = \int_1^\infty \frac{e^{-xt}}{t^s}\,\mathrm dt

Parameters
----------
s : :class:float
exponent in the integral (should be > -100)
x : :class:numpy.ndarray
input values
"""
if np.isclose(s, 1):
return sps.exp1(x)
if np.isclose(s, np.around(s)) and s > -0.5:
return sps.expn(int(np.around(s)), x)
x = np.asarray(x, dtype=np.double)
x_neg = x < 0
x = np.abs(x)
x_compare = x ** min((10, max(((1 - s), 1))))
res = np.empty_like(x)
# use asymptotic behavior for zeros
x_zero = np.isclose(x_compare, 0, atol=1e-20)
x_inf = x > max(30, -s / 2)  # function is like exp(-x)*(1/x + s/x^2)
x_fin = np.logical_not(np.logical_or(x_zero, x_inf))
x_fin_pos = np.logical_and(x_fin, np.logical_not(x_neg))
if s > 1.0:  # limit at x=+0
res[x_zero] = 1.0 / (s - 1.0)
else:
res[x_zero] = np.inf
res[x_inf] = np.exp(-x[x_inf]) * (x[x_inf] ** -1 - s * x[x_inf] ** -2)
res[x_fin_pos] = inc_gamma(1 - s, x[x_fin_pos]) * x[x_fin_pos] ** (s - 1)
res[x_neg] = np.nan  # nan for x < 0
return res

[docs]def inc_beta(a, b, x):
r"""Calculate the incomplete Beta function.

Given by: :math:B(a,b;\,x) = \int_0^x t^{a-1}\,(1-t)^{b-1}\,dt

Parameters
----------
a : :class:float
first exponent in the integral
b : :class:float
second exponent in the integral
x : :class:numpy.ndarray
input values
"""
return sps.betainc(a, b, x) * sps.beta(a, b)

[docs]def tplstable_cor(r, len_scale, hurst, alpha):
r"""Calculate the correlation function of the TPLStable model.

Given by the following correlation function:

.. math::
\rho(r) =
\frac{2H}{\alpha} \cdot
E_{1+\frac{2H}{\alpha}}
\left(\left(\frac{r}{\ell}\right)^{\alpha} \right)

Parameters
----------
r : :class:numpy.ndarray
input values
len_scale : :class:float
length-scale of the model.
hurst : :class:float
Hurst coefficient of the power law.
alpha : :class:float, optional
Shape parameter of the stable model.
"""
r = np.asarray(np.abs(r / len_scale), dtype=np.double)
r[np.isclose(r, 0)] = 0  # hack to prevent numerical errors
res = np.ones_like(r)
res[r > 0] = (2 * hurst / alpha) * exp_int(
1 + 2 * hurst / alpha, (r[r > 0]) ** alpha
)
return res

[docs]def tpl_exp_spec_dens(k, dim, len_scale, hurst, len_low=0.0):
r"""
Spectral density of the TPLExponential covariance model.

Parameters
----------
k : :class:float
Radius of the phase: :math:k=\left\Vert\mathbf{k}\right\Vert
dim : :class:int
Dimension of the model.
len_scale : :class:float
Length scale of the model.
hurst : :class:float
Hurst coefficient of the power law.
len_low : :class:float, optional
The lower length scale truncation of the model.
Default: 0.0

Returns
-------
:class:float
spectral density of the TPLExponential model
"""
if np.isclose(len_low, 0.0):
k = np.asarray(k, dtype=np.double)
z = (k * len_scale) ** 2
a = hurst + dim / 2.0
b = hurst + 0.5
c = hurst + dim / 2.0 + 1.0
d = dim / 2.0 + 0.5
fac = len_scale**dim * hurst * sps.gamma(d) / (np.pi**d * a)
return fac / (1.0 + z) ** a * sps.hyp2f1(a, b, c, z / (1.0 + z))
fac_up = (len_scale + len_low) ** (2 * hurst)
spec_up = tpl_exp_spec_dens(k, dim, len_scale + len_low, hurst)
fac_low = len_low ** (2 * hurst)
spec_low = tpl_exp_spec_dens(k, dim, len_low, hurst)
return (fac_up * spec_up - fac_low * spec_low) / (fac_up - fac_low)

[docs]def tpl_gau_spec_dens(k, dim, len_scale, hurst, len_low=0.0):
r"""
Spectral density of the TPLGaussian covariance model.

Parameters
----------
k : :class:float
Radius of the phase: :math:k=\left\Vert\mathbf{k}\right\Vert
dim : :class:int
Dimension of the model.
len_scale : :class:float
Length scale of the model.
hurst : :class:float
Hurst coefficient of the power law.
len_low : :class:float, optional
The lower length scale truncation of the model.
Default: 0.0

Returns
-------
:class:float
spectral density of the TPLExponential model
"""
if np.isclose(len_low, 0.0):
k = np.asarray(k, dtype=np.double)
z = np.array((k * len_scale / 2.0) ** 2)
res = np.empty_like(z)
z_gz = z > 0.1  # greater zero
z_nz = np.logical_not(z_gz)  # near zero
a = hurst + dim / 2.0
fac = (len_scale / 2.0) ** dim * hurst / np.pi ** (dim / 2.0)
res[z_gz] = fac * inc_gamma_low(a, z[z_gz]) / z[z_gz] ** a
# first order approximation for z near zero
res[z_nz] = fac * (1.0 / a - z[z_nz] / (a + 1.0))
return res
fac_up = (len_scale + len_low) ** (2 * hurst)
spec_up = tpl_gau_spec_dens(k, dim, len_scale + len_low, hurst)
fac_low = len_low ** (2 * hurst)
spec_low = tpl_gau_spec_dens(k, dim, len_low, hurst)
return (fac_up * spec_up - fac_low * spec_low) / (fac_up - fac_low)