Skip to content
This repository was archived by the owner on Jan 30, 2023. It is now read-only.

Commit 0661926

Browse files
committed
add some safety check for numerical integrals up to infinity
1 parent e2dcdee commit 0661926

File tree

1 file changed

+79
-50
lines changed

1 file changed

+79
-50
lines changed

src/sage/calculus/integration.pyx

Lines changed: 79 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ def numerical_integral(func, a, b=None,
7070
algorithm='qag',
7171
max_points=87, params=[], eps_abs=1e-6,
7272
eps_rel=1e-6, rule=6):
73-
r"""
73+
r"""
7474
Return the numerical integral of the function on the interval
7575
from a to b and an error bound.
7676
@@ -241,40 +241,52 @@ def numerical_integral(func, a, b=None,
241241
Traceback (most recent call last):
242242
...
243243
TypeError: unable to simplify to float approximation
244-
"""
245244
246-
cdef double abs_err # step size
247-
cdef double result
248-
cdef int i
249-
cdef int j
250-
cdef double _a, _b
251-
cdef PyFunctionWrapper wrapper # struct to pass information into GSL C function
245+
Check for :trac:`15496`::
252246
253-
if b is None or isinstance(a, (list, tuple)):
254-
b = a[1]
255-
a = a[0]
247+
sage: f = x^2/exp(-1/(x^2+1))/(x^2+1)
248+
sage: D = integrate(f,(x,-infinity,infinity),hold=True)
249+
sage: D.n()
250+
Traceback (most recent call last):
251+
...
252+
ValueError: integral does not converge at -infinity
253+
"""
254+
cdef double abs_err # step size
255+
cdef double result
256+
cdef int i
257+
cdef int j
258+
cdef double _a, _b
259+
cdef PyFunctionWrapper wrapper # struct to pass information into GSL C function
260+
261+
if b is None or isinstance(a, (list, tuple)):
262+
b = a[1]
263+
a = a[0]
256264

257-
# The integral over a point is always zero
258-
if a == b:
259-
return (0.0, 0.0)
265+
# The integral over a point is always zero
266+
if a == b:
267+
return (0.0, 0.0)
260268

261-
if not callable(func):
269+
if not callable(func):
262270
# handle the constant case
263271
return (((<double>b - <double>a) * <double>func), 0.0)
264272

265-
cdef gsl_function F
266-
cdef gsl_integration_workspace* W
267-
W=NULL
273+
cdef gsl_function F
274+
cdef gsl_integration_workspace* W
275+
W = NULL
268276

269-
if not isinstance(func, FastDoubleFunc):
277+
if not isinstance(func, FastDoubleFunc):
278+
from sage.rings.infinity import Infinity
270279
try:
271280
if hasattr(func, 'arguments'):
272281
vars = func.arguments()
273282
else:
274283
vars = func.variables()
275-
if len(vars) == 0:
276-
# handle the constant case
277-
return (((<double>b - <double>a) * <double>func), 0.0)
284+
except (AttributeError):
285+
pass
286+
else:
287+
if not vars:
288+
# handle the constant case
289+
return (((<double>b - <double>a) * <double>func), 0.0)
278290
if len(vars) != 1:
279291
if len(params) + 1 != len(vars):
280292
raise ValueError(("The function to be integrated depends on "
@@ -285,70 +297,85 @@ def numerical_integral(func, a, b=None,
285297

286298
to_sub = dict(zip(vars[1:], params))
287299
func = func.subs(to_sub)
288-
func = func._fast_float_(str(vars[0]))
289-
except (AttributeError):
290-
pass
291300

292-
if isinstance(func, FastDoubleFunc):
301+
# sanity checks for integration up to infinity
302+
v = str(vars[0])
303+
if a is -Infinity:
304+
try:
305+
ell = func.limit(**{v: -Infinity})
306+
except (AttributeError, ValueError):
307+
pass
308+
else:
309+
if ell.is_numeric() and not ell.is_zero():
310+
raise ValueError('integral does not converge at -infinity')
311+
if b is Infinity:
312+
try:
313+
ell = func.limit(**{v: Infinity})
314+
except (AttributeError, ValueError):
315+
pass
316+
else:
317+
if ell.is_numeric() and not ell.is_zero():
318+
raise ValueError('integral does not converge at infinity')
319+
func = func._fast_float_(v)
320+
321+
if isinstance(func, FastDoubleFunc):
293322
F.function = c_ff
294323
F.params = <void *>func
295324

296-
elif not isinstance(func, compiled_integrand):
325+
elif not isinstance(func, compiled_integrand):
297326
wrapper = PyFunctionWrapper()
298327
if not func is None:
299328
wrapper.the_function = func
300329
else:
301330
raise ValueError("No integrand defined")
302331
try:
303-
if params == [] and len(sage_getargspec(wrapper.the_function)[0]) == 1:
304-
wrapper.the_parameters=[]
305-
elif params == [] and len(sage_getargspec(wrapper.the_function)[0]) > 1:
332+
if not params and len(sage_getargspec(wrapper.the_function)[0]) == 1:
333+
wrapper.the_parameters = []
334+
elif not params and len(sage_getargspec(wrapper.the_function)[0]) > 1:
306335
raise ValueError("Integrand has parameters but no parameters specified")
307-
elif params!=[]:
336+
elif params:
308337
wrapper.the_parameters = params
309338
except TypeError:
310-
wrapper.the_function = eval("lambda x: func(x)", {'func':func})
339+
wrapper.the_function = eval("lambda x: func(x)", {'func': func})
311340
wrapper.the_parameters = []
312341

313342
F.function = c_f
314343
F.params = <void *> wrapper
315344

345+
cdef size_t n
346+
n = max_points
316347

317-
cdef size_t n
318-
n = max_points
348+
gsl_set_error_handler_off()
319349

320-
gsl_set_error_handler_off()
321-
322-
if algorithm == "qng":
350+
if algorithm == "qng":
323351
_a=a
324352
_b=b
325353
sig_on()
326354
gsl_integration_qng(&F, _a, _b, eps_abs, eps_rel, &result, &abs_err, &n)
327355
sig_off()
328356

329-
elif algorithm == "qag":
330-
from sage.rings.infinity import Infinity
331-
if a is -Infinity and b is +Infinity:
357+
elif algorithm == "qag":
358+
if a is -Infinity and b is +Infinity:
332359
W = <gsl_integration_workspace*>gsl_integration_workspace_alloc(n)
333360
sig_on()
334361
gsl_integration_qagi(&F, eps_abs, eps_rel, n, W, &result, &abs_err)
335362
sig_off()
336363

337-
elif a is -Infinity:
364+
elif a is -Infinity:
338365
_b = b
339366
W = <gsl_integration_workspace*>gsl_integration_workspace_alloc(n)
340367
sig_on()
341368
gsl_integration_qagil(&F, _b, eps_abs, eps_rel, n, W, &result, &abs_err)
342369
sig_off()
343370

344-
elif b is +Infinity:
371+
elif b is +Infinity:
345372
_a = a
346373
W = <gsl_integration_workspace*>gsl_integration_workspace_alloc(n)
347374
sig_on()
348375
gsl_integration_qagiu(&F, _a, eps_abs, eps_rel, n, W, &result, &abs_err)
349376
sig_off()
350377

351-
else:
378+
else:
352379
_a = a
353380
_b = b
354381
W = <gsl_integration_workspace*> gsl_integration_workspace_alloc(n)
@@ -357,7 +384,7 @@ def numerical_integral(func, a, b=None,
357384
sig_off()
358385

359386

360-
elif algorithm == "qags":
387+
elif algorithm == "qags":
361388

362389
W = <gsl_integration_workspace*>gsl_integration_workspace_alloc(n)
363390
sig_on()
@@ -366,13 +393,14 @@ def numerical_integral(func, a, b=None,
366393
gsl_integration_qags(&F, _a, _b, eps_abs, eps_rel, n, W, &result, &abs_err)
367394
sig_off()
368395

369-
else:
396+
else:
370397
raise TypeError("invalid integration algorithm")
371398

372-
if W != NULL:
399+
if W != NULL:
373400
gsl_integration_workspace_free(W)
374401

375-
return result, abs_err
402+
return result, abs_err
403+
376404

377405
cdef double c_monte_carlo_f(double *t, size_t dim, void *params):
378406
cdef double value
@@ -393,6 +421,7 @@ cdef double c_monte_carlo_f(double *t, size_t dim, void *params):
393421

394422
return value
395423

424+
396425
cdef double c_monte_carlo_ff(double *x, size_t dim, void *params):
397426
cdef double result
398427
(<Wrapper_rdf> params).call_c(x, &result)
@@ -598,11 +627,11 @@ def monte_carlo_integral(func, xl, xu, size_t calls, algorithm='plain',
598627
wrapper = PyFunctionWrapper()
599628
wrapper.the_function = func
600629

601-
if params == [] and len(sage_getargspec(wrapper.the_function)[0]) == dim:
630+
if not params and len(sage_getargspec(wrapper.the_function)[0]) == dim:
602631
wrapper.the_parameters = []
603-
elif params == [] and len(sage_getargspec(wrapper.the_function)[0]) > dim:
632+
elif not params and len(sage_getargspec(wrapper.the_function)[0]) > dim:
604633
raise ValueError("Integrand has parameters but no parameters specified")
605-
elif params != []:
634+
elif params:
606635
wrapper.the_parameters = params
607636
wrapper.lx = [None] * dim
608637

0 commit comments

Comments
 (0)