Numerical Integration - Symbolic Calculus

Skip to content
Back to top
View this page
Edit this page

# Numerical Integration

AUTHORS:

- Josh Kantor (2007-02): first version

- William Stein (2007-02): rewrite of docs, conventions, etc.

- Robert Bradshaw (2008-08): fast float integration

- Jeroen Demeyer (2011-11-23): Issue #12047: return 0 when the integration interval is a point; reformat documentation and add to the reference manual.

class sage.calculus.integration.PyFunctionWrapper

  Bases: `object`

class sage.calculus.integration.compiled_integrand

  Bases: `object`

sage.calculus.integration.monte_carlo_integral(func, xl, xu, calls, algorithm='plain', params=None)

  Integrate `func` by Monte-Carlo method.

  Integrate `func` over the `dim`-dimensional hypercubic region defined by the lower and upper limits in the arrays `xl` and `xu`, each of size `dim`.

  The integration uses a fixed number of function calls and obtains random sampling points using the default gsl’s random number generator.

  ALGORITHM: Uses calls to the GSL (GNU Scientific Library) C library. Documentation can be found in [GSL] chapter “Monte Carlo Integration”.

  INPUT:

  - `func` – the function to integrate

  - `params` – used to pass parameters to your function

  - `xl` – list of lower limits

  - `xu` – list of upper limits

  - `calls` – number of functions calls used

  - `algorithm` – valid choices are:

    - ‘plain’ – The plain Monte Carlo algorithm samples points randomly from the integration region to estimate the integral and its error.

    - ‘miser’ – The MISER algorithm of Press and Farrar is based on recursive stratified sampling

    - ‘vegas’ – The VEGAS algorithm of Lepage is based on importance sampling.

  OUTPUT:

  A tuple whose first component is the approximated integral and whose second component is an error estimate.

  EXAMPLES:

  Sage

      sage: x, y = SR.var('x,y')
      sage: monte_carlo_integral(x*y, [0,0], [2,2], 10000)   # abs tol 0.1
      (4.0, 0.0)
      sage: integral(integral(x*y, (x,0,2)), (y,0,2))
      4

  Python

      >>> from sage.all import *
      >>> x, y = SR.var('x,y')
      >>> monte_carlo_integral(x*y, [Integer(0),Integer(0)], [Integer(2),Integer(2)], Integer(10000))   # abs tol 0.1
      (4.0, 0.0)
      >>> integral(integral(x*y, (x,Integer(0),Integer(2))), (y,Integer(0),Integer(2)))
      4

  An example with a parameter:

  Sage

      sage: x, y, z = SR.var('x,y,z')
      sage: monte_carlo_integral(x*y*z, [0,0], [2,2], 10000, params=[1.2])   # abs tol 0.1
      (4.8, 0.0)

  [Python]

      >>> from sage.all import *
      >>> x, y, z = SR.var('x,y,z')
      >>> monte_carlo_integral(x*y*z, [Integer(0),Integer(0)], [Integer(2),Integer(2)], Integer(10000), params=[RealNumber('1.2')])   # abs tol 0.1
      (4.8, 0.0)

  Integral of a constant:

  Sage

      sage: monte_carlo_integral(3, [0,0], [2,2], 10000)   # abs tol 0.1
      (12, 0.0)

  Python

      >>> from sage.all import *
      >>> monte_carlo_integral(Integer(3), [Integer(0),Integer(0)], [Integer(2),Integer(2)], Integer(10000))   # abs tol 0.1
      (12, 0.0)

  Test different algorithms:

  Sage

      sage: x, y, z = SR.var('x,y,z')
      sage: f(x,y,z) = exp(z) * cos(x + sin(y))
      sage: for algo in ['plain', 'miser', 'vegas']:  # abs tol 0.01
      ....:   monte_carlo_integral(f, [0,0,-1], [2,2,1], 10^6, algorithm=algo)
      (-1.06, 0.01)
      (-1.06, 0.01)
      (-1.06, 0.01)

  [Python]

      >>> from sage.all import *
      >>> x, y, z = SR.var('x,y,z')
      >>> __tmp__=var("x,y,z"); f = symbolic_expression(exp(z) * cos(x + sin(y))).function(x,y,z)
      >>> for algo in ['plain', 'miser', 'vegas']:  # abs tol 0.01
      ...   monte_carlo_integral(f, [Integer(0),Integer(0),-Integer(1)], [Integer(2),Integer(2),Integer(1)], Integer(10)**Integer(6), algorithm=algo)
      (-1.06, 0.01)
      (-1.06, 0.01)
      (-1.06, 0.01)

  Tests with Python functions:

  Sage

      sage: def f(u, v): return u * v
      sage: monte_carlo_integral(f, [0,0], [2,2], 10000)  # abs tol 0.1
      (4.0, 0.0)
      sage: monte_carlo_integral(lambda u,v: u*v, [0,0], [2,2], 10000)  # abs tol 0.1
      (4.0, 0.0)
      sage: def f(x1, x2, x3, x4): return x1*x2*x3*x4
      sage: monte_carlo_integral(f, [0,0], [2,2], 1000, params=[0.6,2])  # abs tol 0.2
      (4.8, 0.0)

  Python

      >>> from sage.all import *
      >>> def f(u, v): return u * v
      >>> monte_carlo_integral(f, [Integer(0),Integer(0)], [Integer(2),Integer(2)], Integer(10000))  # abs tol 0.1
      (4.0, 0.0)
      >>> monte_carlo_integral(lambda u,v: u*v, [Integer(0),Integer(0)], [Integer(2),Integer(2)], Integer(10000))  # abs tol 0.1
      (4.0, 0.0)
      >>> def f(x1, x2, x3, x4): return x1*x2*x3*x4
      >>> monte_carlo_integral(f, [Integer(0),Integer(0)], [Integer(2),Integer(2)], Integer(1000), params=[RealNumber('0.6'),Integer(2)])  # abs tol 0.2
      (4.8, 0.0)

  AUTHORS:

  - Vincent Delecroix

  - Vincent Klein

sage.calculus.integration.numerical_integral(func, a, b=None, algorithm='qag', max_points=87, params=None, eps_abs=1e-06, eps_rel=1e-06, rule=6)

  Return the numerical integral of the function on the interval from a to b and an error bound.

  INPUT:

  - `a`, `b` – the interval of integration, specified as two numbers or as a tuple/list with the first element the lower bound and the second element the upper bound. Use `+Infinity` and `-Infinity` for plus or minus infinity.

  - `algorithm` – valid choices are:

    - ‘qag’ – for an adaptive integration

    - ‘qags’ – for an adaptive integration with (integrable) singularities

    - ‘qng’ – for a non-adaptive Gauss-Kronrod (samples at a maximum of 87pts)

  - `max_points` – sets the maximum number of sample points

  - `params` – used to pass parameters to your function

  - `eps_abs`, `eps_rel` – sets the absolute and relative error tolerances which satisfies the relation `|RESULT - I| <= max(eps_abs, eps_rel * |I|)`, where `I = \int_a^b f(x) d x`.

  - `rule` – this controls the Gauss-Kronrod rule used in the adaptive integration:

    - rule=1 – 15 point rule

    - rule=2 – 21 point rule

    - rule=3 – 31 point rule

    - rule=4 – 41 point rule

    - rule=5 – 51 point rule

    - rule=6 – 61 point rule

    Higher key values are more accurate for smooth functions but lower key values deal better with discontinuities.

  OUTPUT:

  A tuple whose first component is the answer and whose second component is an error estimate.

  REMARK:

  There is also a method `nintegral` on symbolic expressions that implements numerical integration using Maxima. It is potentially very useful for symbolic expressions.

  EXAMPLES:

  To integrate the function \(x^2\) from 0 to 1, we do:

  Sage

      sage: numerical_integral(x^2, 0, 1, max_points=100)
      (0.3333333333333333, 3.700743415417188e-15)

  Python

      >>> from sage.all import *
      >>> numerical_integral(x**Integer(2), Integer(0), Integer(1), max_points=Integer(100))
      (0.3333333333333333, 3.700743415417188e-15)

  To integrate the function \(\sin(x)^3 + \sin(x)\) we do:

  Sage

      sage: numerical_integral(sin(x)^3 + sin(x),  0, pi)
      (3.333333333333333, 3.700743415417188e-14)

  [Python]

      >>> from sage.all import *
      >>> numerical_integral(sin(x)**Integer(3) + sin(x),  Integer(0), pi)
      (3.333333333333333, 3.700743415417188e-14)

  The input can be any callable:

  Sage

      sage: numerical_integral(lambda x: sin(x)^3 + sin(x),  0, pi)
      (3.333333333333333, 3.700743415417188e-14)

  Python

      >>> from sage.all import *
      >>> numerical_integral(lambda x: sin(x)**Integer(3) + sin(x),  Integer(0), pi)
      (3.333333333333333, 3.700743415417188e-14)

  We check this with a symbolic integration:

  Sage

      sage: (sin(x)^3+sin(x)).integral(x,0,pi)
      10/3

  [Python]

      >>> from sage.all import *
      >>> (sin(x)**Integer(3)+sin(x)).integral(x,Integer(0),pi)
      10/3

  If we want to change the error tolerances and Gauss rule used:

  Sage

      sage: f = x^2
      sage: numerical_integral(f, 0, 1, max_points=200, eps_abs=1e-7, eps_rel=1e-7, rule=4)
      (0.3333333333333333, 3.700743415417188e-15)

  Python

      >>> from sage.all import *
      >>> f = x**Integer(2)
      >>> numerical_integral(f, Integer(0), Integer(1), max_points=Integer(200), eps_abs=RealNumber('1e-7'), eps_rel=RealNumber('1e-7'), rule=Integer(4))
      (0.3333333333333333, 3.700743415417188e-15)

  For a Python function with parameters:

  Sage

      sage: f(x,a) = 1/(a+x^2)
      sage: [numerical_integral(f, 1, 2, max_points=100, params=[n])[0]  # abs tol 1.0e-6
      ....:           for n in range(10)]
      [0.5000000000000000,
       0.3217505543966422,
       0.24030098317248832,
       0.19253082576711372,
       0.1608752771983211,
       0.138275456763492,
       0.1212997593570257,
       0.10806674191683492,
       0.09745444625553161,
       0.08875068305030848]

      sage: y = var('y')
      sage: numerical_integral(x*y, 0, 1)
      Traceback (most recent call last):
      ...
      ValueError: The function to be integrated depends on 2 variables (x, y),
      and so cannot be integrated in one dimension. Please fix additional
      variables with the 'params' argument

  [Python]

      >>> from sage.all import *
      >>> __tmp__=var("x,a"); f = symbolic_expression(Integer(1)/(a+x**Integer(2))).function(x,a)
      >>> [numerical_integral(f, Integer(1), Integer(2), max_points=Integer(100), params=[n])[Integer(0)]  # abs tol 1.0e-6
      ...           for n in range(Integer(10))]
      [0.5000000000000000,
       0.3217505543966422,
       0.24030098317248832,
       0.19253082576711372,
       0.1608752771983211,
       0.138275456763492,
       0.1212997593570257,
       0.10806674191683492,
       0.09745444625553161,
       0.08875068305030848]

      >>> y = var('y')
      >>> numerical_integral(x*y, Integer(0), Integer(1))
      Traceback (most recent call last):
      ...
      ValueError: The function to be integrated depends on 2 variables (x, y),
      and so cannot be integrated in one dimension. Please fix additional
      variables with the 'params' argument

  Note the parameters are always a tuple even if they have one component.

  It is possible to integrate on infinite intervals as well by using +Infinity or -Infinity in the interval argument. For example:

  Sage

      sage: f = exp(-x)
      sage: numerical_integral(f, 0, +Infinity)  # abs tol 1e-6
      (0.99999999999957279, 1.8429811298996553e-07)

  Python

      >>> from sage.all import *
      >>> f = exp(-x)
      >>> numerical_integral(f, Integer(0), +Infinity)  # abs tol 1e-6
      (0.99999999999957279, 1.8429811298996553e-07)

  Note the coercion to the real field RR, which prevents underflow:

  Sage

      sage: f = exp(-x**2)
      sage: numerical_integral(f, -Infinity, +Infinity)  # abs tol 1e-6
      (1.7724538509060035, 3.4295192165889879e-08)

  [Python]

      >>> from sage.all import *
      >>> f = exp(-x**Integer(2))
      >>> numerical_integral(f, -Infinity, +Infinity)  # abs tol 1e-6
      (1.7724538509060035, 3.4295192165889879e-08)

  One can integrate any real-valued callable function:

  Sage

      sage: numerical_integral(lambda x: abs(zeta(x)), [1.1,1.5])  # abs tol 1e-6
      (1.8488570602160455, 2.052643677492633e-14)

  Python

      >>> from sage.all import *
      >>> numerical_integral(lambda x: abs(zeta(x)), [RealNumber('1.1'),RealNumber('1.5')])  # abs tol 1e-6
      (1.8488570602160455, 2.052643677492633e-14)

  We can also numerically integrate symbolic expressions using either this function (which uses GSL) or the native integration (which uses Maxima):

  Sage

      sage: exp(-1/x).nintegral(x, 1, 2)  # via maxima
      (0.50479221787318..., 5.60431942934407...e-15, 21, 0)
      sage: numerical_integral(exp(-1/x), 1, 2)
      (0.50479221787318..., 5.60431942934407...e-15)

  [Python]

      >>> from sage.all import *
      >>> exp(-Integer(1)/x).nintegral(x, Integer(1), Integer(2))  # via maxima
      (0.50479221787318..., 5.60431942934407...e-15, 21, 0)
      >>> numerical_integral(exp(-Integer(1)/x), Integer(1), Integer(2))
      (0.50479221787318..., 5.60431942934407...e-15)

  We can also integrate constant expressions:

  Sage

      sage: numerical_integral(2, 1, 7)
      (12.0, 0.0)

  Python

      >>> from sage.all import *
      >>> numerical_integral(Integer(2), Integer(1), Integer(7))
      (12.0, 0.0)

  If the interval of integration is a point, then the result is always zero (this makes sense within the Lebesgue theory of integration), see Issue #12047:

  Sage

      sage: numerical_integral(log, 0, 0)
      (0.0, 0.0)
      sage: numerical_integral(lambda x: sqrt(x), (-2.0, -2.0) )
      (0.0, 0.0)

  [Python]

      >>> from sage.all import *
      >>> numerical_integral(log, Integer(0), Integer(0))
      (0.0, 0.0)
      >>> numerical_integral(lambda x: sqrt(x), (-RealNumber('2.0'), -RealNumber('2.0')) )
      (0.0, 0.0)

  In the presence of integrable singularity, the default adaptive method might fail and it is advised to use `'qags'`:

  Sage

      sage: b = 1.81759643554688
      sage: F(x) = sqrt((-x + b)/((x - 1.0)*x))
      sage: numerical_integral(F, 1, b)
      (inf, nan)
      sage: numerical_integral(F, 1, b, algorithm='qags')    # abs tol 1e-10
      (1.1817104238446596, 3.387268288079781e-07)

  Python

      >>> from sage.all import *
      >>> b = RealNumber('1.81759643554688')
      >>> __tmp__=var("x"); F = symbolic_expression(sqrt((-x + b)/((x - RealNumber('1.0'))*x))).function(x)
      >>> numerical_integral(F, Integer(1), b)
      (inf, nan)
      >>> numerical_integral(F, Integer(1), b, algorithm='qags')    # abs tol 1e-10
      (1.1817104238446596, 3.387268288079781e-07)

  AUTHORS:

  - Josh Kantor

  - William Stein

  - Robert Bradshaw

  - Jeroen Demeyer

  ALGORITHM: Uses calls to the GSL (GNU Scientific Library) C library. Documentation can be found in [GSL] chapter “Numerical Integration”.