|
|
|
|
|
import sys |
|
import warnings |
|
from functools import partial |
|
|
|
from . import _quadpack |
|
import numpy as np |
|
|
|
__all__ = ["quad", "dblquad", "tplquad", "nquad", "IntegrationWarning"] |
|
|
|
|
|
class IntegrationWarning(UserWarning): |
|
""" |
|
Warning on issues during integration. |
|
""" |
|
pass |
|
|
|
|
|
def quad(func, a, b, args=(), full_output=0, epsabs=1.49e-8, epsrel=1.49e-8, |
|
limit=50, points=None, weight=None, wvar=None, wopts=None, maxp1=50, |
|
limlst=50, complex_func=False): |
|
""" |
|
Compute a definite integral. |
|
|
|
Integrate func from `a` to `b` (possibly infinite interval) using a |
|
technique from the Fortran library QUADPACK. |
|
|
|
Parameters |
|
---------- |
|
func : {function, scipy.LowLevelCallable} |
|
A Python function or method to integrate. If `func` takes many |
|
arguments, it is integrated along the axis corresponding to the |
|
first argument. |
|
|
|
If the user desires improved integration performance, then `f` may |
|
be a `scipy.LowLevelCallable` with one of the signatures:: |
|
|
|
double func(double x) |
|
double func(double x, void *user_data) |
|
double func(int n, double *xx) |
|
double func(int n, double *xx, void *user_data) |
|
|
|
The ``user_data`` is the data contained in the `scipy.LowLevelCallable`. |
|
In the call forms with ``xx``, ``n`` is the length of the ``xx`` |
|
array which contains ``xx[0] == x`` and the rest of the items are |
|
numbers contained in the ``args`` argument of quad. |
|
|
|
In addition, certain ctypes call signatures are supported for |
|
backward compatibility, but those should not be used in new code. |
|
a : float |
|
Lower limit of integration (use -numpy.inf for -infinity). |
|
b : float |
|
Upper limit of integration (use numpy.inf for +infinity). |
|
args : tuple, optional |
|
Extra arguments to pass to `func`. |
|
full_output : int, optional |
|
Non-zero to return a dictionary of integration information. |
|
If non-zero, warning messages are also suppressed and the |
|
message is appended to the output tuple. |
|
complex_func : bool, optional |
|
Indicate if the function's (`func`) return type is real |
|
(``complex_func=False``: default) or complex (``complex_func=True``). |
|
In both cases, the function's argument is real. |
|
If full_output is also non-zero, the `infodict`, `message`, and |
|
`explain` for the real and complex components are returned in |
|
a dictionary with keys "real output" and "imag output". |
|
|
|
Returns |
|
------- |
|
y : float |
|
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. |
|
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'] |
|
|
|
Other Parameters |
|
---------------- |
|
epsabs : float or int, optional |
|
Absolute error tolerance. Default is 1.49e-8. `quad` tries to obtain |
|
an accuracy of ``abs(i-result) <= max(epsabs, epsrel*abs(i))`` |
|
where ``i`` = integral of `func` from `a` to `b`, and ``result`` is the |
|
numerical approximation. See `epsrel` below. |
|
epsrel : float or int, optional |
|
Relative error tolerance. Default is 1.49e-8. |
|
If ``epsabs <= 0``, `epsrel` must be greater than both 5e-29 |
|
and ``50 * (machine epsilon)``. See `epsabs` above. |
|
limit : float or int, optional |
|
An upper bound on the number of subintervals used in the adaptive |
|
algorithm. |
|
points : (sequence of floats,ints), optional |
|
A sequence of break points in the bounded integration interval |
|
where local difficulties of the integrand may occur (e.g., |
|
singularities, discontinuities). The sequence does not have |
|
to be sorted. Note that this option cannot be used in conjunction |
|
with ``weight``. |
|
weight : float or int, optional |
|
String indicating weighting function. Full explanation for this |
|
and the remaining arguments can be found below. |
|
wvar : optional |
|
Variables for use with weighting functions. |
|
wopts : optional |
|
Optional input for reusing Chebyshev moments. |
|
maxp1 : float or int, optional |
|
An upper bound on the number of Chebyshev moments. |
|
limlst : int, optional |
|
Upper bound on the number of cycles (>=3) for use with a sinusoidal |
|
weighting and an infinite end-point. |
|
|
|
See Also |
|
-------- |
|
dblquad : double integral |
|
tplquad : triple integral |
|
nquad : n-dimensional integrals (uses `quad` recursively) |
|
fixed_quad : fixed-order Gaussian quadrature |
|
simpson : integrator for sampled data |
|
romb : integrator for sampled data |
|
scipy.special : for coefficients and roots of orthogonal polynomials |
|
|
|
Notes |
|
----- |
|
For valid results, the integral must converge; behavior for divergent |
|
integrals is not guaranteed. |
|
|
|
**Extra information for quad() inputs and outputs** |
|
|
|
If full_output is non-zero, then the third output argument |
|
(infodict) is a dictionary with entries as tabulated below. For |
|
infinite limits, the range is transformed to (0,1) and the |
|
optional outputs are given with respect to this transformed range. |
|
Let M be the input argument limit and let K be infodict['last']. |
|
The entries are: |
|
|
|
'neval' |
|
The number of function evaluations. |
|
'last' |
|
The number, K, of subintervals produced in the subdivision process. |
|
'alist' |
|
A rank-1 array of length M, the first K elements of which are the |
|
left end points of the subintervals in the partition of the |
|
integration range. |
|
'blist' |
|
A rank-1 array of length M, the first K elements of which are the |
|
right end points of the subintervals. |
|
'rlist' |
|
A rank-1 array of length M, the first K elements of which are the |
|
integral approximations on the subintervals. |
|
'elist' |
|
A rank-1 array of length M, the first K elements of which are the |
|
moduli of the absolute error estimates on the subintervals. |
|
'iord' |
|
A rank-1 integer array of length M, the first L elements of |
|
which are pointers to the error estimates over the subintervals |
|
with ``L=K`` if ``K<=M/2+2`` or ``L=M+1-K`` otherwise. Let I be the |
|
sequence ``infodict['iord']`` and let E be the sequence |
|
``infodict['elist']``. Then ``E[I[1]], ..., E[I[L]]`` forms a |
|
decreasing sequence. |
|
|
|
If the input argument points is provided (i.e., it is not None), |
|
the following additional outputs are placed in the output |
|
dictionary. Assume the points sequence is of length P. |
|
|
|
'pts' |
|
A rank-1 array of length P+2 containing the integration limits |
|
and the break points of the intervals in ascending order. |
|
This is an array giving the subintervals over which integration |
|
will occur. |
|
'level' |
|
A rank-1 integer array of length M (=limit), containing the |
|
subdivision levels of the subintervals, i.e., if (aa,bb) is a |
|
subinterval of ``(pts[1], pts[2])`` where ``pts[0]`` and ``pts[2]`` |
|
are adjacent elements of ``infodict['pts']``, then (aa,bb) has level l |
|
if ``|bb-aa| = |pts[2]-pts[1]| * 2**(-l)``. |
|
'ndin' |
|
A rank-1 integer array of length P+2. After the first integration |
|
over the intervals (pts[1], pts[2]), the error estimates over some |
|
of the intervals may have been increased artificially in order to |
|
put their subdivision forward. This array has ones in slots |
|
corresponding to the subintervals for which this happens. |
|
|
|
**Weighting the integrand** |
|
|
|
The input variables, *weight* and *wvar*, are used to weight the |
|
integrand by a select list of functions. Different integration |
|
methods are used to compute the integral with these weighting |
|
functions, and these do not support specifying break points. The |
|
possible values of weight and the corresponding weighting functions are. |
|
|
|
========== =================================== ===================== |
|
``weight`` Weight function used ``wvar`` |
|
========== =================================== ===================== |
|
'cos' cos(w*x) wvar = w |
|
'sin' sin(w*x) wvar = w |
|
'alg' g(x) = ((x-a)**alpha)*((b-x)**beta) wvar = (alpha, beta) |
|
'alg-loga' g(x)*log(x-a) wvar = (alpha, beta) |
|
'alg-logb' g(x)*log(b-x) wvar = (alpha, beta) |
|
'alg-log' g(x)*log(x-a)*log(b-x) wvar = (alpha, beta) |
|
'cauchy' 1/(x-c) wvar = c |
|
========== =================================== ===================== |
|
|
|
wvar holds the parameter w, (alpha, beta), or c depending on the weight |
|
selected. In these expressions, a and b are the integration limits. |
|
|
|
For the 'cos' and 'sin' weighting, additional inputs and outputs are |
|
available. |
|
|
|
For finite integration limits, the integration is performed using a |
|
Clenshaw-Curtis method which uses Chebyshev moments. For repeated |
|
calculations, these moments are saved in the output dictionary: |
|
|
|
'momcom' |
|
The maximum level of Chebyshev moments that have been computed, |
|
i.e., if ``M_c`` is ``infodict['momcom']`` then the moments have been |
|
computed for intervals of length ``|b-a| * 2**(-l)``, |
|
``l=0,1,...,M_c``. |
|
'nnlog' |
|
A rank-1 integer array of length M(=limit), containing the |
|
subdivision levels of the subintervals, i.e., an element of this |
|
array is equal to l if the corresponding subinterval is |
|
``|b-a|* 2**(-l)``. |
|
'chebmo' |
|
A rank-2 array of shape (25, maxp1) containing the computed |
|
Chebyshev moments. These can be passed on to an integration |
|
over the same interval by passing this array as the second |
|
element of the sequence wopts and passing infodict['momcom'] as |
|
the first element. |
|
|
|
If one of the integration limits is infinite, then a Fourier integral is |
|
computed (assuming w neq 0). If full_output is 1 and a numerical error |
|
is encountered, besides the error message attached to the output tuple, |
|
a dictionary is also appended to the output tuple which translates the |
|
error codes in the array ``info['ierlst']`` to English messages. The |
|
output information dictionary contains the following entries instead of |
|
'last', 'alist', 'blist', 'rlist', and 'elist': |
|
|
|
'lst' |
|
The number of subintervals needed for the integration (call it ``K_f``). |
|
'rslst' |
|
A rank-1 array of length M_f=limlst, whose first ``K_f`` elements |
|
contain the integral contribution over the interval |
|
``(a+(k-1)c, a+kc)`` where ``c = (2*floor(|w|) + 1) * pi / |w|`` |
|
and ``k=1,2,...,K_f``. |
|
'erlst' |
|
A rank-1 array of length ``M_f`` containing the error estimate |
|
corresponding to the interval in the same position in |
|
``infodict['rslist']``. |
|
'ierlst' |
|
A rank-1 integer array of length ``M_f`` containing an error flag |
|
corresponding to the interval in the same position in |
|
``infodict['rslist']``. See the explanation dictionary (last entry |
|
in the output tuple) for the meaning of the codes. |
|
|
|
|
|
**Details of QUADPACK level routines** |
|
|
|
`quad` calls routines from the FORTRAN library QUADPACK. This section |
|
provides details on the conditions for each routine to be called and a |
|
short description of each routine. The routine called depends on |
|
`weight`, `points` and the integration limits `a` and `b`. |
|
|
|
================ ============== ========== ===================== |
|
QUADPACK routine `weight` `points` infinite bounds |
|
================ ============== ========== ===================== |
|
qagse None No No |
|
qagie None No Yes |
|
qagpe None Yes No |
|
qawoe 'sin', 'cos' No No |
|
qawfe 'sin', 'cos' No either `a` or `b` |
|
qawse 'alg*' No No |
|
qawce 'cauchy' No No |
|
================ ============== ========== ===================== |
|
|
|
The following provides a short description from [1]_ for each |
|
routine. |
|
|
|
qagse |
|
is an integrator based on globally adaptive interval |
|
subdivision in connection with extrapolation, which will |
|
eliminate the effects of integrand singularities of |
|
several types. |
|
qagie |
|
handles integration over infinite intervals. The infinite range is |
|
mapped onto a finite interval and subsequently the same strategy as |
|
in ``QAGS`` is applied. |
|
qagpe |
|
serves the same purposes as QAGS, but also allows the |
|
user to provide explicit information about the location |
|
and type of trouble-spots i.e. the abscissae of internal |
|
singularities, discontinuities and other difficulties of |
|
the integrand function. |
|
qawoe |
|
is an integrator for the evaluation of |
|
:math:`\\int^b_a \\cos(\\omega x)f(x)dx` or |
|
:math:`\\int^b_a \\sin(\\omega x)f(x)dx` |
|
over a finite interval [a,b], where :math:`\\omega` and :math:`f` |
|
are specified by the user. The rule evaluation component is based |
|
on the modified Clenshaw-Curtis technique |
|
|
|
An adaptive subdivision scheme is used in connection |
|
with an extrapolation procedure, which is a modification |
|
of that in ``QAGS`` and allows the algorithm to deal with |
|
singularities in :math:`f(x)`. |
|
qawfe |
|
calculates the Fourier transform |
|
:math:`\\int^\\infty_a \\cos(\\omega x)f(x)dx` or |
|
:math:`\\int^\\infty_a \\sin(\\omega x)f(x)dx` |
|
for user-provided :math:`\\omega` and :math:`f`. The procedure of |
|
``QAWO`` is applied on successive finite intervals, and convergence |
|
acceleration by means of the :math:`\\varepsilon`-algorithm is applied |
|
to the series of integral approximations. |
|
qawse |
|
approximate :math:`\\int^b_a w(x)f(x)dx`, with :math:`a < b` where |
|
:math:`w(x) = (x-a)^{\\alpha}(b-x)^{\\beta}v(x)` with |
|
:math:`\\alpha,\\beta > -1`, where :math:`v(x)` may be one of the |
|
following functions: :math:`1`, :math:`\\log(x-a)`, :math:`\\log(b-x)`, |
|
:math:`\\log(x-a)\\log(b-x)`. |
|
|
|
The user specifies :math:`\\alpha`, :math:`\\beta` and the type of the |
|
function :math:`v`. A globally adaptive subdivision strategy is |
|
applied, with modified Clenshaw-Curtis integration on those |
|
subintervals which contain `a` or `b`. |
|
qawce |
|
compute :math:`\\int^b_a f(x) / (x-c)dx` where the integral must be |
|
interpreted as a Cauchy principal value integral, for user specified |
|
:math:`c` and :math:`f`. The strategy is globally adaptive. Modified |
|
Clenshaw-Curtis integration is used on those intervals containing the |
|
point :math:`x = c`. |
|
|
|
**Integration of Complex Function of a Real Variable** |
|
|
|
A complex valued function, :math:`f`, of a real variable can be written as |
|
:math:`f = g + ih`. Similarly, the integral of :math:`f` can be |
|
written as |
|
|
|
.. math:: |
|
\\int_a^b f(x) dx = \\int_a^b g(x) dx + i\\int_a^b h(x) dx |
|
|
|
assuming that the integrals of :math:`g` and :math:`h` exist |
|
over the interval :math:`[a,b]` [2]_. Therefore, ``quad`` integrates |
|
complex-valued functions by integrating the real and imaginary components |
|
separately. |
|
|
|
|
|
References |
|
---------- |
|
|
|
.. [1] Piessens, Robert; de Doncker-Kapenga, Elise; |
|
Überhuber, Christoph W.; Kahaner, David (1983). |
|
QUADPACK: A subroutine package for automatic integration. |
|
Springer-Verlag. |
|
ISBN 978-3-540-12553-2. |
|
|
|
.. [2] McCullough, Thomas; Phillips, Keith (1973). |
|
Foundations of Analysis in the Complex Plane. |
|
Holt Rinehart Winston. |
|
ISBN 0-03-086370-8 |
|
|
|
Examples |
|
-------- |
|
Calculate :math:`\\int^4_0 x^2 dx` and compare with an analytic result |
|
|
|
>>> from scipy import integrate |
|
>>> import numpy as np |
|
>>> x2 = lambda x: x**2 |
|
>>> integrate.quad(x2, 0, 4) |
|
(21.333333333333332, 2.3684757858670003e-13) |
|
>>> print(4**3 / 3.) # analytical result |
|
21.3333333333 |
|
|
|
Calculate :math:`\\int^\\infty_0 e^{-x} dx` |
|
|
|
>>> invexp = lambda x: np.exp(-x) |
|
>>> integrate.quad(invexp, 0, np.inf) |
|
(1.0, 5.842605999138044e-11) |
|
|
|
Calculate :math:`\\int^1_0 a x \\,dx` for :math:`a = 1, 3` |
|
|
|
>>> f = lambda x, a: a*x |
|
>>> y, err = integrate.quad(f, 0, 1, args=(1,)) |
|
>>> y |
|
0.5 |
|
>>> y, err = integrate.quad(f, 0, 1, args=(3,)) |
|
>>> y |
|
1.5 |
|
|
|
Calculate :math:`\\int^1_0 x^2 + y^2 dx` with ctypes, holding |
|
y parameter as 1:: |
|
|
|
testlib.c => |
|
double func(int n, double args[n]){ |
|
return args[0]*args[0] + args[1]*args[1];} |
|
compile to library testlib.* |
|
|
|
:: |
|
|
|
from scipy import integrate |
|
import ctypes |
|
lib = ctypes.CDLL('/home/.../testlib.*') #use absolute path |
|
lib.func.restype = ctypes.c_double |
|
lib.func.argtypes = (ctypes.c_int,ctypes.c_double) |
|
integrate.quad(lib.func,0,1,(1)) |
|
#(1.3333333333333333, 1.4802973661668752e-14) |
|
print((1.0**3/3.0 + 1.0) - (0.0**3/3.0 + 0.0)) #Analytic result |
|
# 1.3333333333333333 |
|
|
|
Be aware that pulse shapes and other sharp features as compared to the |
|
size of the integration interval may not be integrated correctly using |
|
this method. A simplified example of this limitation is integrating a |
|
y-axis reflected step function with many zero values within the integrals |
|
bounds. |
|
|
|
>>> y = lambda x: 1 if x<=0 else 0 |
|
>>> integrate.quad(y, -1, 1) |
|
(1.0, 1.1102230246251565e-14) |
|
>>> integrate.quad(y, -1, 100) |
|
(1.0000000002199108, 1.0189464580163188e-08) |
|
>>> integrate.quad(y, -1, 10000) |
|
(0.0, 0.0) |
|
|
|
""" |
|
if not isinstance(args, tuple): |
|
args = (args,) |
|
|
|
|
|
flip, a, b = b < a, min(a, b), max(a, b) |
|
|
|
if complex_func: |
|
def imfunc(x, *args): |
|
return func(x, *args).imag |
|
|
|
def refunc(x, *args): |
|
return func(x, *args).real |
|
|
|
re_retval = quad(refunc, a, b, args, full_output, epsabs, |
|
epsrel, limit, points, weight, wvar, wopts, |
|
maxp1, limlst, complex_func=False) |
|
im_retval = quad(imfunc, a, b, args, full_output, epsabs, |
|
epsrel, limit, points, weight, wvar, wopts, |
|
maxp1, limlst, complex_func=False) |
|
integral = re_retval[0] + 1j*im_retval[0] |
|
error_estimate = re_retval[1] + 1j*im_retval[1] |
|
retval = integral, error_estimate |
|
if full_output: |
|
msgexp = {} |
|
msgexp["real"] = re_retval[2:] |
|
msgexp["imag"] = im_retval[2:] |
|
retval = retval + (msgexp,) |
|
|
|
return retval |
|
|
|
if weight is None: |
|
retval = _quad(func, a, b, args, full_output, epsabs, epsrel, limit, |
|
points) |
|
else: |
|
if points is not None: |
|
msg = ("Break points cannot be specified when using weighted integrand.\n" |
|
"Continuing, ignoring specified points.") |
|
warnings.warn(msg, IntegrationWarning, stacklevel=2) |
|
retval = _quad_weight(func, a, b, args, full_output, epsabs, epsrel, |
|
limlst, limit, maxp1, weight, wvar, wopts) |
|
|
|
if flip: |
|
retval = (-retval[0],) + retval[1:] |
|
|
|
ier = retval[-1] |
|
if ier == 0: |
|
return retval[:-1] |
|
|
|
msgs = {80: "A Python error occurred possibly while calling the function.", |
|
1: f"The maximum number of subdivisions ({limit}) has been achieved.\n " |
|
f"If increasing the limit yields no improvement it is advised to " |
|
f"analyze \n the integrand in order to determine the difficulties. " |
|
f"If the position of a \n local difficulty can be determined " |
|
f"(singularity, discontinuity) one will \n probably gain from " |
|
f"splitting up the interval and calling the integrator \n on the " |
|
f"subranges. Perhaps a special-purpose integrator should be used.", |
|
2: "The occurrence of roundoff error is detected, which prevents \n " |
|
"the requested tolerance from being achieved. " |
|
"The error may be \n underestimated.", |
|
3: "Extremely bad integrand behavior occurs at some points of the\n " |
|
"integration interval.", |
|
4: "The algorithm does not converge. Roundoff error is detected\n " |
|
"in the extrapolation table. It is assumed that the requested " |
|
"tolerance\n cannot be achieved, and that the returned result " |
|
"(if full_output = 1) is \n the best which can be obtained.", |
|
5: "The integral is probably divergent, or slowly convergent.", |
|
6: "The input is invalid.", |
|
7: "Abnormal termination of the routine. The estimates for result\n " |
|
"and error are less reliable. It is assumed that the requested " |
|
"accuracy\n has not been achieved.", |
|
'unknown': "Unknown error."} |
|
|
|
if weight in ['cos','sin'] and (b == np.inf or a == -np.inf): |
|
msgs[1] = ( |
|
"The maximum number of cycles allowed has been achieved., e.e.\n of " |
|
"subintervals (a+(k-1)c, a+kc) where c = (2*int(abs(omega)+1))\n " |
|
"*pi/abs(omega), for k = 1, 2, ..., lst. " |
|
"One can allow more cycles by increasing the value of limlst. " |
|
"Look at info['ierlst'] with full_output=1." |
|
) |
|
msgs[4] = ( |
|
"The extrapolation table constructed for convergence acceleration\n of " |
|
"the series formed by the integral contributions over the cycles, \n does " |
|
"not converge to within the requested accuracy. " |
|
"Look at \n info['ierlst'] with full_output=1." |
|
) |
|
msgs[7] = ( |
|
"Bad integrand behavior occurs within one or more of the cycles.\n " |
|
"Location and type of the difficulty involved can be determined from \n " |
|
"the vector info['ierlist'] obtained with full_output=1." |
|
) |
|
explain = {1: "The maximum number of subdivisions (= limit) has been \n " |
|
"achieved on this cycle.", |
|
2: "The occurrence of roundoff error is detected and prevents\n " |
|
"the tolerance imposed on this cycle from being achieved.", |
|
3: "Extremely bad integrand behavior occurs at some points of\n " |
|
"this cycle.", |
|
4: "The integral over this cycle does not converge (to within the " |
|
"required accuracy) due to roundoff in the extrapolation " |
|
"procedure invoked on this cycle. It is assumed that the result " |
|
"on this interval is the best which can be obtained.", |
|
5: "The integral over this cycle is probably divergent or " |
|
"slowly convergent."} |
|
|
|
try: |
|
msg = msgs[ier] |
|
except KeyError: |
|
msg = msgs['unknown'] |
|
|
|
if ier in [1,2,3,4,5,7]: |
|
if full_output: |
|
if weight in ['cos', 'sin'] and (b == np.inf or a == -np.inf): |
|
return retval[:-1] + (msg, explain) |
|
else: |
|
return retval[:-1] + (msg,) |
|
else: |
|
warnings.warn(msg, IntegrationWarning, stacklevel=2) |
|
return retval[:-1] |
|
|
|
elif ier == 6: |
|
if epsabs <= 0: |
|
if epsrel < max(50 * sys.float_info.epsilon, 5e-29): |
|
msg = ("If 'epsabs'<=0, 'epsrel' must be greater than both" |
|
" 5e-29 and 50*(machine epsilon).") |
|
elif weight in ['sin', 'cos'] and (abs(a) + abs(b) == np.inf): |
|
msg = ("Sine or cosine weighted integrals with infinite domain" |
|
" must have 'epsabs'>0.") |
|
|
|
elif weight is None: |
|
if points is None: |
|
msg = ("Invalid 'limit' argument. There must be" |
|
" at least one subinterval") |
|
else: |
|
if not (min(a, b) <= min(points) <= max(points) <= max(a, b)): |
|
msg = ("All break points in 'points' must lie within the" |
|
" integration limits.") |
|
elif len(points) >= limit: |
|
msg = (f"Number of break points ({len(points):d}) " |
|
f"must be less than subinterval limit ({limit:d})") |
|
|
|
else: |
|
if maxp1 < 1: |
|
msg = "Chebyshev moment limit maxp1 must be >=1." |
|
|
|
elif weight in ('cos', 'sin') and abs(a+b) == np.inf: |
|
msg = "Cycle limit limlst must be >=3." |
|
|
|
elif weight.startswith('alg'): |
|
if min(wvar) < -1: |
|
msg = "wvar parameters (alpha, beta) must both be >= -1." |
|
if b < a: |
|
msg = "Integration limits a, b must satistfy a<b." |
|
|
|
elif weight == 'cauchy' and wvar in (a, b): |
|
msg = ("Parameter 'wvar' must not equal" |
|
" integration limits 'a' or 'b'.") |
|
|
|
raise ValueError(msg) |
|
|
|
|
|
def _quad(func,a,b,args,full_output,epsabs,epsrel,limit,points): |
|
infbounds = 0 |
|
if (b != np.inf and a != -np.inf): |
|
pass |
|
elif (b == np.inf and a != -np.inf): |
|
infbounds = 1 |
|
bound = a |
|
elif (b == np.inf and a == -np.inf): |
|
infbounds = 2 |
|
bound = 0 |
|
elif (b != np.inf and a == -np.inf): |
|
infbounds = -1 |
|
bound = b |
|
else: |
|
raise RuntimeError("Infinity comparisons don't work for you.") |
|
|
|
if points is None: |
|
if infbounds == 0: |
|
return _quadpack._qagse(func,a,b,args,full_output,epsabs,epsrel,limit) |
|
else: |
|
return _quadpack._qagie(func, bound, infbounds, args, full_output, |
|
epsabs, epsrel, limit) |
|
else: |
|
if infbounds != 0: |
|
raise ValueError("Infinity inputs cannot be used with break points.") |
|
else: |
|
|
|
the_points = np.unique(points) |
|
the_points = the_points[a < the_points] |
|
the_points = the_points[the_points < b] |
|
the_points = np.concatenate((the_points, (0., 0.))) |
|
return _quadpack._qagpe(func, a, b, the_points, args, full_output, |
|
epsabs, epsrel, limit) |
|
|
|
|
|
def _quad_weight(func, a, b, args, full_output, epsabs, epsrel, |
|
limlst, limit, maxp1,weight, wvar, wopts): |
|
if weight not in ['cos','sin','alg','alg-loga','alg-logb','alg-log','cauchy']: |
|
raise ValueError(f"{weight} not a recognized weighting function.") |
|
|
|
strdict = {'cos':1,'sin':2,'alg':1,'alg-loga':2,'alg-logb':3,'alg-log':4} |
|
|
|
if weight in ['cos','sin']: |
|
integr = strdict[weight] |
|
if (b != np.inf and a != -np.inf): |
|
if wopts is None: |
|
return _quadpack._qawoe(func, a, b, wvar, integr, args, full_output, |
|
epsabs, epsrel, limit, maxp1,1) |
|
else: |
|
momcom = wopts[0] |
|
chebcom = wopts[1] |
|
return _quadpack._qawoe(func, a, b, wvar, integr, args, |
|
full_output,epsabs, epsrel, limit, maxp1, 2, |
|
momcom, chebcom) |
|
|
|
elif (b == np.inf and a != -np.inf): |
|
return _quadpack._qawfe(func, a, wvar, integr, args, full_output, |
|
epsabs, limlst, limit, maxp1) |
|
elif (b != np.inf and a == -np.inf): |
|
if weight == 'cos': |
|
def thefunc(x,*myargs): |
|
y = -x |
|
func = myargs[0] |
|
myargs = (y,) + myargs[1:] |
|
return func(*myargs) |
|
else: |
|
def thefunc(x,*myargs): |
|
y = -x |
|
func = myargs[0] |
|
myargs = (y,) + myargs[1:] |
|
return -func(*myargs) |
|
args = (func,) + args |
|
return _quadpack._qawfe(thefunc, -b, wvar, integr, args, |
|
full_output, epsabs, limlst, limit, maxp1) |
|
else: |
|
raise ValueError("Cannot integrate with this weight from -Inf to +Inf.") |
|
else: |
|
if a in [-np.inf, np.inf] or b in [-np.inf, np.inf]: |
|
message = "Cannot integrate with this weight over an infinite interval." |
|
raise ValueError(message) |
|
|
|
if weight.startswith('alg'): |
|
integr = strdict[weight] |
|
return _quadpack._qawse(func, a, b, wvar, integr, args, |
|
full_output, epsabs, epsrel, limit) |
|
else: |
|
return _quadpack._qawce(func, a, b, wvar, args, full_output, |
|
epsabs, epsrel, limit) |
|
|
|
|
|
def dblquad(func, a, b, gfun, hfun, args=(), epsabs=1.49e-8, epsrel=1.49e-8): |
|
""" |
|
Compute a double integral. |
|
|
|
Return the double (definite) integral of ``func(y, x)`` from ``x = a..b`` |
|
and ``y = gfun(x)..hfun(x)``. |
|
|
|
Parameters |
|
---------- |
|
func : callable |
|
A Python function or method of at least two variables: y must be the |
|
first argument and x the second argument. |
|
a, b : float |
|
The limits of integration in x: `a` < `b` |
|
gfun : callable or float |
|
The lower boundary curve in y which is a function taking a single |
|
floating point argument (x) and returning a floating point result |
|
or a float indicating a constant boundary curve. |
|
hfun : callable or float |
|
The upper boundary curve in y (same requirements as `gfun`). |
|
args : sequence, optional |
|
Extra arguments to pass to `func`. |
|
epsabs : float, optional |
|
Absolute tolerance passed directly to the inner 1-D quadrature |
|
integration. Default is 1.49e-8. ``dblquad`` tries to obtain |
|
an accuracy of ``abs(i-result) <= max(epsabs, epsrel*abs(i))`` |
|
where ``i`` = inner integral of ``func(y, x)`` from ``gfun(x)`` |
|
to ``hfun(x)``, and ``result`` is the numerical approximation. |
|
See `epsrel` below. |
|
epsrel : float, optional |
|
Relative tolerance of the inner 1-D integrals. Default is 1.49e-8. |
|
If ``epsabs <= 0``, `epsrel` must be greater than both 5e-29 |
|
and ``50 * (machine epsilon)``. See `epsabs` above. |
|
|
|
Returns |
|
------- |
|
y : float |
|
The resultant integral. |
|
abserr : float |
|
An estimate of the error. |
|
|
|
See Also |
|
-------- |
|
quad : single integral |
|
tplquad : triple integral |
|
nquad : N-dimensional integrals |
|
fixed_quad : fixed-order Gaussian quadrature |
|
simpson : integrator for sampled data |
|
romb : integrator for sampled data |
|
scipy.special : for coefficients and roots of orthogonal polynomials |
|
|
|
|
|
Notes |
|
----- |
|
For valid results, the integral must converge; behavior for divergent |
|
integrals is not guaranteed. |
|
|
|
**Details of QUADPACK level routines** |
|
|
|
`quad` calls routines from the FORTRAN library QUADPACK. This section |
|
provides details on the conditions for each routine to be called and a |
|
short description of each routine. For each level of integration, ``qagse`` |
|
is used for finite limits or ``qagie`` is used if either limit (or both!) |
|
are infinite. The following provides a short description from [1]_ for each |
|
routine. |
|
|
|
qagse |
|
is an integrator based on globally adaptive interval |
|
subdivision in connection with extrapolation, which will |
|
eliminate the effects of integrand singularities of |
|
several types. |
|
qagie |
|
handles integration over infinite intervals. The infinite range is |
|
mapped onto a finite interval and subsequently the same strategy as |
|
in ``QAGS`` is applied. |
|
|
|
References |
|
---------- |
|
|
|
.. [1] Piessens, Robert; de Doncker-Kapenga, Elise; |
|
Überhuber, Christoph W.; Kahaner, David (1983). |
|
QUADPACK: A subroutine package for automatic integration. |
|
Springer-Verlag. |
|
ISBN 978-3-540-12553-2. |
|
|
|
Examples |
|
-------- |
|
Compute the double integral of ``x * y**2`` over the box |
|
``x`` ranging from 0 to 2 and ``y`` ranging from 0 to 1. |
|
That is, :math:`\\int^{x=2}_{x=0} \\int^{y=1}_{y=0} x y^2 \\,dy \\,dx`. |
|
|
|
>>> import numpy as np |
|
>>> from scipy import integrate |
|
>>> f = lambda y, x: x*y**2 |
|
>>> integrate.dblquad(f, 0, 2, 0, 1) |
|
(0.6666666666666667, 7.401486830834377e-15) |
|
|
|
Calculate :math:`\\int^{x=\\pi/4}_{x=0} \\int^{y=\\cos(x)}_{y=\\sin(x)} 1 |
|
\\,dy \\,dx`. |
|
|
|
>>> f = lambda y, x: 1 |
|
>>> integrate.dblquad(f, 0, np.pi/4, np.sin, np.cos) |
|
(0.41421356237309503, 1.1083280054755938e-14) |
|
|
|
Calculate :math:`\\int^{x=1}_{x=0} \\int^{y=2-x}_{y=x} a x y \\,dy \\,dx` |
|
for :math:`a=1, 3`. |
|
|
|
>>> f = lambda y, x, a: a*x*y |
|
>>> integrate.dblquad(f, 0, 1, lambda x: x, lambda x: 2-x, args=(1,)) |
|
(0.33333333333333337, 5.551115123125783e-15) |
|
>>> integrate.dblquad(f, 0, 1, lambda x: x, lambda x: 2-x, args=(3,)) |
|
(0.9999999999999999, 1.6653345369377348e-14) |
|
|
|
Compute the two-dimensional Gaussian Integral, which is the integral of the |
|
Gaussian function :math:`f(x,y) = e^{-(x^{2} + y^{2})}`, over |
|
:math:`(-\\infty,+\\infty)`. That is, compute the integral |
|
:math:`\\iint^{+\\infty}_{-\\infty} e^{-(x^{2} + y^{2})} \\,dy\\,dx`. |
|
|
|
>>> f = lambda x, y: np.exp(-(x ** 2 + y ** 2)) |
|
>>> integrate.dblquad(f, -np.inf, np.inf, -np.inf, np.inf) |
|
(3.141592653589777, 2.5173086737433208e-08) |
|
|
|
""" |
|
|
|
def temp_ranges(*args): |
|
return [gfun(args[0]) if callable(gfun) else gfun, |
|
hfun(args[0]) if callable(hfun) else hfun] |
|
|
|
return nquad(func, [temp_ranges, [a, b]], args=args, |
|
opts={"epsabs": epsabs, "epsrel": epsrel}) |
|
|
|
|
|
def tplquad(func, a, b, gfun, hfun, qfun, rfun, args=(), epsabs=1.49e-8, |
|
epsrel=1.49e-8): |
|
""" |
|
Compute a triple (definite) integral. |
|
|
|
Return the triple integral of ``func(z, y, x)`` from ``x = a..b``, |
|
``y = gfun(x)..hfun(x)``, and ``z = qfun(x,y)..rfun(x,y)``. |
|
|
|
Parameters |
|
---------- |
|
func : function |
|
A Python function or method of at least three variables in the |
|
order (z, y, x). |
|
a, b : float |
|
The limits of integration in x: `a` < `b` |
|
gfun : function or float |
|
The lower boundary curve in y which is a function taking a single |
|
floating point argument (x) and returning a floating point result |
|
or a float indicating a constant boundary curve. |
|
hfun : function or float |
|
The upper boundary curve in y (same requirements as `gfun`). |
|
qfun : function or float |
|
The lower boundary surface in z. It must be a function that takes |
|
two floats in the order (x, y) and returns a float or a float |
|
indicating a constant boundary surface. |
|
rfun : function or float |
|
The upper boundary surface in z. (Same requirements as `qfun`.) |
|
args : tuple, optional |
|
Extra arguments to pass to `func`. |
|
epsabs : float, optional |
|
Absolute tolerance passed directly to the innermost 1-D quadrature |
|
integration. Default is 1.49e-8. |
|
epsrel : float, optional |
|
Relative tolerance of the innermost 1-D integrals. Default is 1.49e-8. |
|
|
|
Returns |
|
------- |
|
y : float |
|
The resultant integral. |
|
abserr : float |
|
An estimate of the error. |
|
|
|
See Also |
|
-------- |
|
quad : Adaptive quadrature using QUADPACK |
|
fixed_quad : Fixed-order Gaussian quadrature |
|
dblquad : Double integrals |
|
nquad : N-dimensional integrals |
|
romb : Integrators for sampled data |
|
simpson : Integrators for sampled data |
|
scipy.special : For coefficients and roots of orthogonal polynomials |
|
|
|
Notes |
|
----- |
|
For valid results, the integral must converge; behavior for divergent |
|
integrals is not guaranteed. |
|
|
|
**Details of QUADPACK level routines** |
|
|
|
`quad` calls routines from the FORTRAN library QUADPACK. This section |
|
provides details on the conditions for each routine to be called and a |
|
short description of each routine. For each level of integration, ``qagse`` |
|
is used for finite limits or ``qagie`` is used, if either limit (or both!) |
|
are infinite. The following provides a short description from [1]_ for each |
|
routine. |
|
|
|
qagse |
|
is an integrator based on globally adaptive interval |
|
subdivision in connection with extrapolation, which will |
|
eliminate the effects of integrand singularities of |
|
several types. |
|
qagie |
|
handles integration over infinite intervals. The infinite range is |
|
mapped onto a finite interval and subsequently the same strategy as |
|
in ``QAGS`` is applied. |
|
|
|
References |
|
---------- |
|
|
|
.. [1] Piessens, Robert; de Doncker-Kapenga, Elise; |
|
Überhuber, Christoph W.; Kahaner, David (1983). |
|
QUADPACK: A subroutine package for automatic integration. |
|
Springer-Verlag. |
|
ISBN 978-3-540-12553-2. |
|
|
|
Examples |
|
-------- |
|
Compute the triple integral of ``x * y * z``, over ``x`` ranging |
|
from 1 to 2, ``y`` ranging from 2 to 3, ``z`` ranging from 0 to 1. |
|
That is, :math:`\\int^{x=2}_{x=1} \\int^{y=3}_{y=2} \\int^{z=1}_{z=0} x y z |
|
\\,dz \\,dy \\,dx`. |
|
|
|
>>> import numpy as np |
|
>>> from scipy import integrate |
|
>>> f = lambda z, y, x: x*y*z |
|
>>> integrate.tplquad(f, 1, 2, 2, 3, 0, 1) |
|
(1.8749999999999998, 3.3246447942574074e-14) |
|
|
|
Calculate :math:`\\int^{x=1}_{x=0} \\int^{y=1-2x}_{y=0} |
|
\\int^{z=1-x-2y}_{z=0} x y z \\,dz \\,dy \\,dx`. |
|
Note: `qfun`/`rfun` takes arguments in the order (x, y), even though ``f`` |
|
takes arguments in the order (z, y, x). |
|
|
|
>>> f = lambda z, y, x: x*y*z |
|
>>> integrate.tplquad(f, 0, 1, 0, lambda x: 1-2*x, 0, lambda x, y: 1-x-2*y) |
|
(0.05416666666666668, 2.1774196738157757e-14) |
|
|
|
Calculate :math:`\\int^{x=1}_{x=0} \\int^{y=1}_{y=0} \\int^{z=1}_{z=0} |
|
a x y z \\,dz \\,dy \\,dx` for :math:`a=1, 3`. |
|
|
|
>>> f = lambda z, y, x, a: a*x*y*z |
|
>>> integrate.tplquad(f, 0, 1, 0, 1, 0, 1, args=(1,)) |
|
(0.125, 5.527033708952211e-15) |
|
>>> integrate.tplquad(f, 0, 1, 0, 1, 0, 1, args=(3,)) |
|
(0.375, 1.6581101126856635e-14) |
|
|
|
Compute the three-dimensional Gaussian Integral, which is the integral of |
|
the Gaussian function :math:`f(x,y,z) = e^{-(x^{2} + y^{2} + z^{2})}`, over |
|
:math:`(-\\infty,+\\infty)`. That is, compute the integral |
|
:math:`\\iiint^{+\\infty}_{-\\infty} e^{-(x^{2} + y^{2} + z^{2})} \\,dz |
|
\\,dy\\,dx`. |
|
|
|
>>> f = lambda x, y, z: np.exp(-(x ** 2 + y ** 2 + z ** 2)) |
|
>>> integrate.tplquad(f, -np.inf, np.inf, -np.inf, np.inf, -np.inf, np.inf) |
|
(5.568327996830833, 4.4619078828029765e-08) |
|
|
|
""" |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def ranges0(*args): |
|
return [qfun(args[1], args[0]) if callable(qfun) else qfun, |
|
rfun(args[1], args[0]) if callable(rfun) else rfun] |
|
|
|
def ranges1(*args): |
|
return [gfun(args[0]) if callable(gfun) else gfun, |
|
hfun(args[0]) if callable(hfun) else hfun] |
|
|
|
ranges = [ranges0, ranges1, [a, b]] |
|
return nquad(func, ranges, args=args, |
|
opts={"epsabs": epsabs, "epsrel": epsrel}) |
|
|
|
|
|
def nquad(func, ranges, args=None, opts=None, full_output=False): |
|
r""" |
|
Integration over multiple variables. |
|
|
|
Wraps `quad` to enable integration over multiple variables. |
|
Various options allow improved integration of discontinuous functions, as |
|
well as the use of weighted integration, and generally finer control of the |
|
integration process. |
|
|
|
Parameters |
|
---------- |
|
func : {callable, scipy.LowLevelCallable} |
|
The function to be integrated. Has arguments of ``x0, ... xn``, |
|
``t0, ... tm``, where integration is carried out over ``x0, ... xn``, |
|
which must be floats. Where ``t0, ... tm`` are extra arguments |
|
passed in args. |
|
Function signature should be ``func(x0, x1, ..., xn, t0, t1, ..., tm)``. |
|
Integration is carried out in order. That is, integration over ``x0`` |
|
is the innermost integral, and ``xn`` is the outermost. |
|
|
|
If the user desires improved integration performance, then `f` may |
|
be a `scipy.LowLevelCallable` with one of the signatures:: |
|
|
|
double func(int n, double *xx) |
|
double func(int n, double *xx, void *user_data) |
|
|
|
where ``n`` is the number of variables and args. The ``xx`` array |
|
contains the coordinates and extra arguments. ``user_data`` is the data |
|
contained in the `scipy.LowLevelCallable`. |
|
ranges : iterable object |
|
Each element of ranges may be either a sequence of 2 numbers, or else |
|
a callable that returns such a sequence. ``ranges[0]`` corresponds to |
|
integration over x0, and so on. If an element of ranges is a callable, |
|
then it will be called with all of the integration arguments available, |
|
as well as any parametric arguments. e.g., if |
|
``func = f(x0, x1, x2, t0, t1)``, then ``ranges[0]`` may be defined as |
|
either ``(a, b)`` or else as ``(a, b) = range0(x1, x2, t0, t1)``. |
|
args : iterable object, optional |
|
Additional arguments ``t0, ... tn``, required by ``func``, ``ranges``, |
|
and ``opts``. |
|
opts : iterable object or dict, optional |
|
Options to be passed to `quad`. May be empty, a dict, or |
|
a sequence of dicts or functions that return a dict. If empty, the |
|
default options from scipy.integrate.quad are used. If a dict, the same |
|
options are used for all levels of integraion. If a sequence, then each |
|
element of the sequence corresponds to a particular integration. e.g., |
|
``opts[0]`` corresponds to integration over ``x0``, and so on. If a |
|
callable, the signature must be the same as for ``ranges``. The |
|
available options together with their default values are: |
|
|
|
- epsabs = 1.49e-08 |
|
- epsrel = 1.49e-08 |
|
- limit = 50 |
|
- points = None |
|
- weight = None |
|
- wvar = None |
|
- wopts = None |
|
|
|
For more information on these options, see `quad`. |
|
|
|
full_output : bool, optional |
|
Partial implementation of ``full_output`` from scipy.integrate.quad. |
|
The number of integrand function evaluations ``neval`` can be obtained |
|
by setting ``full_output=True`` when calling nquad. |
|
|
|
Returns |
|
------- |
|
result : float |
|
The result of the integration. |
|
abserr : float |
|
The maximum of the estimates of the absolute error in the various |
|
integration results. |
|
out_dict : dict, optional |
|
A dict containing additional information on the integration. |
|
|
|
See Also |
|
-------- |
|
quad : 1-D numerical integration |
|
dblquad, tplquad : double and triple integrals |
|
fixed_quad : fixed-order Gaussian quadrature |
|
|
|
Notes |
|
----- |
|
For valid results, the integral must converge; behavior for divergent |
|
integrals is not guaranteed. |
|
|
|
**Details of QUADPACK level routines** |
|
|
|
`nquad` calls routines from the FORTRAN library QUADPACK. This section |
|
provides details on the conditions for each routine to be called and a |
|
short description of each routine. The routine called depends on |
|
`weight`, `points` and the integration limits `a` and `b`. |
|
|
|
================ ============== ========== ===================== |
|
QUADPACK routine `weight` `points` infinite bounds |
|
================ ============== ========== ===================== |
|
qagse None No No |
|
qagie None No Yes |
|
qagpe None Yes No |
|
qawoe 'sin', 'cos' No No |
|
qawfe 'sin', 'cos' No either `a` or `b` |
|
qawse 'alg*' No No |
|
qawce 'cauchy' No No |
|
================ ============== ========== ===================== |
|
|
|
The following provides a short description from [1]_ for each |
|
routine. |
|
|
|
qagse |
|
is an integrator based on globally adaptive interval |
|
subdivision in connection with extrapolation, which will |
|
eliminate the effects of integrand singularities of |
|
several types. |
|
qagie |
|
handles integration over infinite intervals. The infinite range is |
|
mapped onto a finite interval and subsequently the same strategy as |
|
in ``QAGS`` is applied. |
|
qagpe |
|
serves the same purposes as QAGS, but also allows the |
|
user to provide explicit information about the location |
|
and type of trouble-spots i.e. the abscissae of internal |
|
singularities, discontinuities and other difficulties of |
|
the integrand function. |
|
qawoe |
|
is an integrator for the evaluation of |
|
:math:`\int^b_a \cos(\omega x)f(x)dx` or |
|
:math:`\int^b_a \sin(\omega x)f(x)dx` |
|
over a finite interval [a,b], where :math:`\omega` and :math:`f` |
|
are specified by the user. The rule evaluation component is based |
|
on the modified Clenshaw-Curtis technique |
|
|
|
An adaptive subdivision scheme is used in connection |
|
with an extrapolation procedure, which is a modification |
|
of that in ``QAGS`` and allows the algorithm to deal with |
|
singularities in :math:`f(x)`. |
|
qawfe |
|
calculates the Fourier transform |
|
:math:`\int^\infty_a \cos(\omega x)f(x)dx` or |
|
:math:`\int^\infty_a \sin(\omega x)f(x)dx` |
|
for user-provided :math:`\omega` and :math:`f`. The procedure of |
|
``QAWO`` is applied on successive finite intervals, and convergence |
|
acceleration by means of the :math:`\varepsilon`-algorithm is applied |
|
to the series of integral approximations. |
|
qawse |
|
approximate :math:`\int^b_a w(x)f(x)dx`, with :math:`a < b` where |
|
:math:`w(x) = (x-a)^{\alpha}(b-x)^{\beta}v(x)` with |
|
:math:`\alpha,\beta > -1`, where :math:`v(x)` may be one of the |
|
following functions: :math:`1`, :math:`\log(x-a)`, :math:`\log(b-x)`, |
|
:math:`\log(x-a)\log(b-x)`. |
|
|
|
The user specifies :math:`\alpha`, :math:`\beta` and the type of the |
|
function :math:`v`. A globally adaptive subdivision strategy is |
|
applied, with modified Clenshaw-Curtis integration on those |
|
subintervals which contain `a` or `b`. |
|
qawce |
|
compute :math:`\int^b_a f(x) / (x-c)dx` where the integral must be |
|
interpreted as a Cauchy principal value integral, for user specified |
|
:math:`c` and :math:`f`. The strategy is globally adaptive. Modified |
|
Clenshaw-Curtis integration is used on those intervals containing the |
|
point :math:`x = c`. |
|
|
|
References |
|
---------- |
|
|
|
.. [1] Piessens, Robert; de Doncker-Kapenga, Elise; |
|
Überhuber, Christoph W.; Kahaner, David (1983). |
|
QUADPACK: A subroutine package for automatic integration. |
|
Springer-Verlag. |
|
ISBN 978-3-540-12553-2. |
|
|
|
Examples |
|
-------- |
|
Calculate |
|
|
|
.. math:: |
|
|
|
\int^{1}_{-0.15} \int^{0.8}_{0.13} \int^{1}_{-1} \int^{1}_{0} |
|
f(x_0, x_1, x_2, x_3) \,dx_0 \,dx_1 \,dx_2 \,dx_3 , |
|
|
|
where |
|
|
|
.. math:: |
|
|
|
f(x_0, x_1, x_2, x_3) = \begin{cases} |
|
x_0^2+x_1 x_2-x_3^3+ \sin{x_0}+1 & (x_0-0.2 x_3-0.5-0.25 x_1 > 0) \\ |
|
x_0^2+x_1 x_2-x_3^3+ \sin{x_0}+0 & (x_0-0.2 x_3-0.5-0.25 x_1 \leq 0) |
|
\end{cases} . |
|
|
|
>>> import numpy as np |
|
>>> from scipy import integrate |
|
>>> func = lambda x0,x1,x2,x3 : x0**2 + x1*x2 - x3**3 + np.sin(x0) + ( |
|
... 1 if (x0-.2*x3-.5-.25*x1>0) else 0) |
|
>>> def opts0(*args, **kwargs): |
|
... return {'points':[0.2*args[2] + 0.5 + 0.25*args[0]]} |
|
>>> integrate.nquad(func, [[0,1], [-1,1], [.13,.8], [-.15,1]], |
|
... opts=[opts0,{},{},{}], full_output=True) |
|
(1.5267454070738633, 2.9437360001402324e-14, {'neval': 388962}) |
|
|
|
Calculate |
|
|
|
.. math:: |
|
|
|
\int^{t_0+t_1+1}_{t_0+t_1-1} |
|
\int^{x_2+t_0^2 t_1^3+1}_{x_2+t_0^2 t_1^3-1} |
|
\int^{t_0 x_1+t_1 x_2+1}_{t_0 x_1+t_1 x_2-1} |
|
f(x_0,x_1, x_2,t_0,t_1) |
|
\,dx_0 \,dx_1 \,dx_2, |
|
|
|
where |
|
|
|
.. math:: |
|
|
|
f(x_0, x_1, x_2, t_0, t_1) = \begin{cases} |
|
x_0 x_2^2 + \sin{x_1}+2 & (x_0+t_1 x_1-t_0 > 0) \\ |
|
x_0 x_2^2 +\sin{x_1}+1 & (x_0+t_1 x_1-t_0 \leq 0) |
|
\end{cases} |
|
|
|
and :math:`(t_0, t_1) = (0, 1)` . |
|
|
|
>>> def func2(x0, x1, x2, t0, t1): |
|
... return x0*x2**2 + np.sin(x1) + 1 + (1 if x0+t1*x1-t0>0 else 0) |
|
>>> def lim0(x1, x2, t0, t1): |
|
... return [t0*x1 + t1*x2 - 1, t0*x1 + t1*x2 + 1] |
|
>>> def lim1(x2, t0, t1): |
|
... return [x2 + t0**2*t1**3 - 1, x2 + t0**2*t1**3 + 1] |
|
>>> def lim2(t0, t1): |
|
... return [t0 + t1 - 1, t0 + t1 + 1] |
|
>>> def opts0(x1, x2, t0, t1): |
|
... return {'points' : [t0 - t1*x1]} |
|
>>> def opts1(x2, t0, t1): |
|
... return {} |
|
>>> def opts2(t0, t1): |
|
... return {} |
|
>>> integrate.nquad(func2, [lim0, lim1, lim2], args=(0,1), |
|
... opts=[opts0, opts1, opts2]) |
|
(36.099919226771625, 1.8546948553373528e-07) |
|
|
|
""" |
|
depth = len(ranges) |
|
ranges = [rng if callable(rng) else _RangeFunc(rng) for rng in ranges] |
|
if args is None: |
|
args = () |
|
if opts is None: |
|
opts = [dict([])] * depth |
|
|
|
if isinstance(opts, dict): |
|
opts = [_OptFunc(opts)] * depth |
|
else: |
|
opts = [opt if callable(opt) else _OptFunc(opt) for opt in opts] |
|
return _NQuad(func, ranges, opts, full_output).integrate(*args) |
|
|
|
|
|
class _RangeFunc: |
|
def __init__(self, range_): |
|
self.range_ = range_ |
|
|
|
def __call__(self, *args): |
|
"""Return stored value. |
|
|
|
*args needed because range_ can be float or func, and is called with |
|
variable number of parameters. |
|
""" |
|
return self.range_ |
|
|
|
|
|
class _OptFunc: |
|
def __init__(self, opt): |
|
self.opt = opt |
|
|
|
def __call__(self, *args): |
|
"""Return stored dict.""" |
|
return self.opt |
|
|
|
|
|
class _NQuad: |
|
def __init__(self, func, ranges, opts, full_output): |
|
self.abserr = 0 |
|
self.func = func |
|
self.ranges = ranges |
|
self.opts = opts |
|
self.maxdepth = len(ranges) |
|
self.full_output = full_output |
|
if self.full_output: |
|
self.out_dict = {'neval': 0} |
|
|
|
def integrate(self, *args, **kwargs): |
|
depth = kwargs.pop('depth', 0) |
|
if kwargs: |
|
raise ValueError('unexpected kwargs') |
|
|
|
|
|
ind = -(depth + 1) |
|
fn_range = self.ranges[ind] |
|
low, high = fn_range(*args) |
|
fn_opt = self.opts[ind] |
|
opt = dict(fn_opt(*args)) |
|
|
|
if 'points' in opt: |
|
opt['points'] = [x for x in opt['points'] if low <= x <= high] |
|
if depth + 1 == self.maxdepth: |
|
f = self.func |
|
else: |
|
f = partial(self.integrate, depth=depth+1) |
|
quad_r = quad(f, low, high, args=args, full_output=self.full_output, |
|
**opt) |
|
value = quad_r[0] |
|
abserr = quad_r[1] |
|
if self.full_output: |
|
infodict = quad_r[2] |
|
|
|
|
|
|
|
if depth + 1 == self.maxdepth: |
|
self.out_dict['neval'] += infodict['neval'] |
|
self.abserr = max(self.abserr, abserr) |
|
if depth > 0: |
|
return value |
|
else: |
|
|
|
if self.full_output: |
|
return value, self.abserr, self.out_dict |
|
else: |
|
return value, self.abserr |
|
|