# Source code for anaflow.tools.mean

"""
Anaflow subpackage providing several mean calculating routines.

.. currentmodule:: anaflow.tools.mean

The following functions are provided

.. autosummary::
:toctree:

annular_fmean
annular_amean
annular_gmean
annular_hmean
annular_pmean
"""
# pylint: disable=E1137, C0103
import numpy as np
from scipy.integrate import quad as integ

__all__ = [
"annular_fmean",
"annular_amean",
"annular_gmean",
"annular_hmean",
"annular_pmean",
]

[docs]def annular_fmean(
func, val_arr, f_def, f_inv, ann_dim=2, arg_dict=None, **kwargs
):
r"""
Calculating the annular generalized f-mean.

Calculating the annular generalized f-mean of a radial symmetric function
for given consecutive radii defining annuli by the following formula

.. math::
\varphi_i=f^{-1}\left(\frac{d}{r_{i+1}^d-r_i^d}
\intop^{r_{i+1}}_{r_i} r^{d-1}\cdot f(\varphi(r))\, dr \right)

Parameters
----------
func : :any:callable
Function that should be used (:math:\varphi in the formula).
The first argument needs to be the radial variable:
func(r, **kwargs)
val_arr : :class:numpy.ndarray
Radii defining the annuli.
ann_dim : :class:float, optional
The dimension of the annuli.
Default: 2.0
f_def : :any:callable
Function defining the f-mean.
f_inv : :any:callable
Inverse of the function defining the f-mean.
arg_dict : :class:dict or :any:None, optional
Keyword-arguments given as a dictionary that are forwarded to the
function given in func. Will be merged with **kwargs.
This is designed for overlapping keywords in annular_fmean and
func.
Default: None
**kwargs
Keyword-arguments that are forwarded to the function given in func.
Will be merged with arg_dict

Returns
-------
:class:numpy.ndarray
Array with all calculated arithmetic means

Raises
------
ValueError
If func is not callable.
ValueError
If f_def is not callable.
ValueError
If f_inv is not callable.
ValueError
If val_arr has less than 2 values.
ValueError
If val_arr is not sorted in incresing order.

Notes
-----
If the last value in val_arr is "inf", the given function should provide
a value for "inf" as input: func(float("inf"))
"""
arg_dict = {} if arg_dict is None else arg_dict
kwargs.update(arg_dict)

val_arr = np.array(val_arr, dtype=np.double).reshape(-1)
parts = len(val_arr) - 1

if not callable(func):
raise ValueError("The given function needs to be callable")
if not callable(f_def):
raise ValueError("The f-mean function needs to be callable")
if not callable(f_inv):
raise ValueError("The inverse f-mean function needs to be callable")
if not np.all(
np.isclose(
f_inv(f_def(func(val_arr, **kwargs))), func(val_arr, **kwargs)
)
):
raise ValueError("f_def and f_inv need to be inverse to each other")
if len(val_arr) < 2:
raise ValueError("To few input values in val_arr. Need at least 2.")
if not all(val_arr[i] < val_arr[i + 1] for i in range(len(val_arr) - 1)):
raise ValueError("The input values need to be sorted")

def integrand(val):
"""Integrand for the f-mean r^(d-1)*f(phi(r))."""
return val ** (ann_dim - 1) * f_def(func(val, **kwargs))

# creating the output array
func_arr = np.zeros_like(val_arr[:-1], dtype=np.double)

# iterating over the input values
for i in range(parts):
# if one side is infinity, the function is evaluated at infinity
if val_arr[i + 1] == np.inf:
func_arr[i] = func(np.inf, **kwargs)
else:
func_arr[i] = f_inv(
ann_dim
* integ(integrand, val_arr[i], val_arr[i + 1])[0]
/ (val_arr[i + 1] ** ann_dim - val_arr[i] ** ann_dim)
)

return func_arr

[docs]def annular_amean(func, val_arr, ann_dim=2, arg_dict=None, **kwargs):
r"""
Calculating the annular arithmetic mean.

Calculating the annular arithmetic mean of a radial symmetric function
for given consecutive radii defining annuli by the following formula

.. math::
\varphi_i=\frac{d}{r_{i+1}^d-r_i^d}
\intop^{r_{i+1}}_{r_i} r^{d-1}\cdot \varphi(r)\, dr

Parameters
----------
func : :any:callable
Function that should be used (:math:\varphi in the formula).
The first argument needs to be the radial variable:
func(r, **kwargs)
val_arr : :class:numpy.ndarray
Radii defining the annuli.
ann_dim : :class:float, optional
The dimension of the annuli.
Default: 2.0
arg_dict : :class:dict or :any:None, optional
Keyword-arguments given as a dictionary that are forwarded to the
function given in func. Will be merged with **kwargs.
This is designed for overlapping keywords in annular_amean and
func.
Default: None
**kwargs
Keyword-arguments that are forwarded to the function given in func.
Will be merged with arg_dict

Returns
-------
:class:numpy.ndarray
Array with all calculated arithmetic means

Raises
------
ValueError
If func is not callable.
ValueError
If val_arr has less than 2 values.
ValueError
If val_arr is not sorted in incresing order.

Notes
-----
If the last value in val_arr is "inf", the given function should provide
a value for "inf" as input: func(float("inf"))
"""
return annular_fmean(
func=func,
val_arr=val_arr,
f_def=lambda x: x,
f_inv=lambda x: x,
ann_dim=ann_dim,
arg_dict=arg_dict,
**kwargs,
)

[docs]def annular_gmean(func, val_arr, ann_dim=2, arg_dict=None, **kwargs):
r"""
Calculating the annular geometric mean.

Calculating the annular geometric mean of a radial symmetric function
for given consecutive radii defining annuli by the following formula

.. math::
\varphi_i=\exp\left(\frac{d}{r_{i+1}^d-r_i^d}
\intop^{r_{i+1}}_{r_i} r^{d-1}\cdot \ln(\varphi(r))\, dr \right)

Parameters
----------
func : :any:callable
Function that should be used (:math:\varphi in the formula).
The first argument needs to be the radial variable:
func(r, **kwargs)
val_arr : :class:numpy.ndarray
Radii defining the annuli.
ann_dim : :class:float, optional
The dimension of the annuli.
Default: 2.0
arg_dict : :class:dict or :any:None, optional
Keyword-arguments given as a dictionary that are forwarded to the
function given in func. Will be merged with **kwargs.
This is designed for overlapping keywords in annular_gmean and
func.
Default: None
**kwargs
Keyword-arguments that are forwarded to the function given in func.
Will be merged with arg_dict

Returns
-------
:class:numpy.ndarray
Array with all calculated geometric means

Raises
------
ValueError
If func is not callable.
ValueError
If val_arr has less than 2 values.
ValueError
If val_arr is not sorted in incresing order.

Notes
-----
If the last value in val_arr is "inf", the given function should provide
a value for "inf" as input: func(float("inf"))

Examples
--------
>>> f = lambda x: x**2
>>> annular_gmean(f, [1,2,3])
array([ 2.33588885,  6.33423311])
"""
return annular_fmean(
func=func,
val_arr=val_arr,
f_def=np.log,
f_inv=np.exp,
ann_dim=ann_dim,
arg_dict=arg_dict,
**kwargs,
)

[docs]def annular_hmean(func, val_arr, ann_dim=2, arg_dict=None, **kwargs):
r"""
Calculating the annular harmonic mean.

Calculating the annular harmonic mean of a radial symmetric function
for given consecutive radii defining annuli by the following formula

.. math::
\varphi_i=\left(\frac{d}{r_{i+1}^d-r_i^d}
\intop^{r_{i+1}}_{r_i} r^{d-1}\cdot \varphi(r)^{-1}\, dr \right)^{-1}

Parameters
----------
func : :any:callable
Function that should be used (:math:\varphi in the formula).
The first argument needs to be the radial variable:
func(r, **kwargs)
val_arr : :class:numpy.ndarray
Radii defining the annuli.
ann_dim : :class:float, optional
The dimension of the annuli.
Default: 2.0
arg_dict : :class:dict or :any:None, optional
Keyword-arguments given as a dictionary that are forwarded to the
function given in func. Will be merged with **kwargs.
This is designed for overlapping keywords in annular_hmean and
func.
Default: None
**kwargs
Keyword-arguments that are forwarded to the function given in func.
Will be merged with arg_dict

Returns
-------
:class:numpy.ndarray
Array with all calculated geometric means

Raises
------
ValueError
If func is not callable.
ValueError
If val_arr has less than 2 values.
ValueError
If val_arr is not sorted in incresing order.

Notes
-----
If the last value in val_arr is "inf", the given function should provide
a value for "inf" as input: func(float("inf"))
"""
return annular_fmean(
func=func,
val_arr=val_arr,
f_def=lambda x: 1.0 / x,
f_inv=lambda x: 1.0 / x,
ann_dim=ann_dim,
arg_dict=arg_dict,
**kwargs,
)

[docs]def annular_pmean(func, val_arr, p=2.0, ann_dim=2, arg_dict=None, **kwargs):
r"""
Calculating the annular p-mean.

Calculating the annular p-mean of a radial symmetric function
for given consecutive radii defining annuli by the following formula

.. math::
\varphi_i=\left(\frac{d}{r_{i+1}^d-r_i^d}
\intop^{r_{i+1}}_{r_i} r^{d-1}\cdot\varphi(r)^p\, dr
\right)^{\frac{1}{p}}

Parameters
----------
func : :any:callable
Function that should be used (:math:\varphi in the formula).
The first argument needs to be the radial variable:
func(r, **kwargs)
val_arr : :class:numpy.ndarray
Radii defining the annuli.
p : :class:float, optional
The potency defining the p-mean.
Default: 2.0
ann_dim : :class:float, optional
The dimension of the annuli.
Default: 2.0
arg_dict : :class:dict or :any:None, optional
Keyword-arguments given as a dictionary that are forwarded to the
function given in func. Will be merged with **kwargs.
This is designed for overlapping keywords in annular_pmean and
func.
Default: None
**kwargs
Keyword-arguments that are forwarded to the function given in func.
Will be merged with arg_dict

Returns
-------
:class:numpy.ndarray
Array with all calculated p-means

Raises
------
ValueError
If func is not callable.
ValueError
If val_arr has less than 2 values.
ValueError
If val_arr is not sorted in incresing order.

Notes
-----
If the last value in val_arr is "inf", the given function should provide
a value for "inf" as input: func(float("inf"))
"""
# if p is 0, the limit-case of the geometric mean is returned
if np.isclose(p, 0):
return annular_gmean(func, val_arr, ann_dim, arg_dict, **kwargs)

return annular_fmean(
func=func,
val_arr=val_arr,
f_def=lambda x: np.power(x, p),
f_inv=lambda x: np.power(x, 1.0 / p),
ann_dim=ann_dim,
arg_dict=arg_dict,
**kwargs,
)