mirror of
https://github.com/fjosw/pyerrors.git
synced 2025-05-14 19:43:41 +02:00
feat: Added numerical integration of generic functions (#201)
* feat: Added numerical integration of generic functions * refactored integration routines * tests: two trivial tests for integration added. * docs: quad docstring corrected. * Small bugfix for integration without obs --------- Co-authored-by: Fabian Joswig <fabian.joswig@ed.ac.uk>
This commit is contained in:
parent
8736d1cd3c
commit
6dcd0c3518
4 changed files with 141 additions and 2 deletions
|
@ -485,5 +485,6 @@ from . import input
|
|||
from . import linalg
|
||||
from . import mpm
|
||||
from . import roots
|
||||
from . import integrate
|
||||
|
||||
from .version import __version__
|
||||
|
|
87
pyerrors/integrate.py
Normal file
87
pyerrors/integrate.py
Normal file
|
@ -0,0 +1,87 @@
|
|||
import numpy as np
|
||||
from .obs import derived_observable, Obs
|
||||
from autograd import jacobian
|
||||
from scipy.integrate import quad as squad
|
||||
|
||||
|
||||
def quad(func, p, a, b, **kwargs):
|
||||
'''Performs a (one-dimensional) numeric integration of f(p, x) from a to b.
|
||||
|
||||
The integration is performed using scipy.integrate.quad().
|
||||
All parameters that can be passed to scipy.integrate.quad may also be passed to this function.
|
||||
The output is the same as for scipy.integrate.quad, the first element being an Obs.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
func : object
|
||||
function to integrate, has to be of the form
|
||||
|
||||
```python
|
||||
import autograd.numpy as anp
|
||||
|
||||
def func(p, x):
|
||||
return p[0] + p[1] * x + p[2] * anp.sinh(x)
|
||||
```
|
||||
where x is the integration variable.
|
||||
p : list of floats or Obs
|
||||
parameters of the function func.
|
||||
a: float or Obs
|
||||
Lower limit of integration (use -numpy.inf for -infinity).
|
||||
b: float or Obs
|
||||
Upper limit of integration (use -numpy.inf for -infinity).
|
||||
All parameters of scipy.integrate.quad
|
||||
|
||||
Returns
|
||||
-------
|
||||
y : Obs
|
||||
The integral of func from `a` to `b`.
|
||||
abserr : float
|
||||
An estimate of the absolute error in the result.
|
||||
infodict : dict
|
||||
A dictionary containing additional information.
|
||||
Run scipy.integrate.quad_explain() for more information.
|
||||
message
|
||||
A convergence message.
|
||||
explain
|
||||
Appended only with 'cos' or 'sin' weighting and infinite
|
||||
integration limits, it contains an explanation of the codes in
|
||||
infodict['ierlst']
|
||||
'''
|
||||
|
||||
Np = len(p)
|
||||
isobs = [True if isinstance(pi, Obs) else False for pi in p]
|
||||
pval = np.array([p[i].value if isobs[i] else p[i] for i in range(Np)],)
|
||||
pobs = [p[i] for i in range(Np) if isobs[i]]
|
||||
|
||||
bounds = [a, b]
|
||||
isobs_b = [True if isinstance(bi, Obs) else False for bi in bounds]
|
||||
bval = np.array([bounds[i].value if isobs_b[i] else bounds[i] for i in range(2)])
|
||||
bobs = [bounds[i] for i in range(2) if isobs_b[i]]
|
||||
bsign = [-1, 1]
|
||||
|
||||
ifunc = np.vectorize(lambda x: func(pval, x))
|
||||
|
||||
intpars = squad.__code__.co_varnames[3:3 + len(squad.__defaults__)]
|
||||
ikwargs = {k: kwargs[k] for k in intpars if k in kwargs}
|
||||
|
||||
integration_result = squad(ifunc, bval[0], bval[1], **ikwargs)
|
||||
val = integration_result[0]
|
||||
|
||||
jac = jacobian(func)
|
||||
|
||||
derivint = []
|
||||
for i in range(Np):
|
||||
if isobs[i]:
|
||||
ifunc = np.vectorize(lambda x: jac(pval, x)[i])
|
||||
derivint.append(squad(ifunc, bounds[0], bounds[1], **ikwargs)[0])
|
||||
|
||||
for i in range(2):
|
||||
if isobs_b[i]:
|
||||
derivint.append(bsign[i] * func(pval, bval[i]))
|
||||
|
||||
if len(derivint) == 0:
|
||||
return integration_result
|
||||
|
||||
res = derived_observable(lambda x, **kwargs: 0 * (x[0] + np.finfo(np.float64).eps) * (pval[0] + np.finfo(np.float64).eps) + val, pobs + bobs, man_grad=derivint)
|
||||
|
||||
return (res, *integration_result[1:])
|
Loading…
Add table
Add a link
Reference in a new issue