Search code examples
pythonscipyintegral

nquad giving different result than tplquad for a triple integral


I'm trying to do this triple integral in scipy:

Integral

from scipy.integrate import dblquad, tplquad, nquad

def inte(z, y, x): 
    return 2*z

with tplquad I get the right result:

volume = tplquad(inte, 0, 1, lambda x: x**3, lambda x: x, lambda x, y: 0, lambda x, y: 2*x)
volume[0] 
0.33333333333333337

While this is what happens when I use nquad:

def range_3():
    return [0, 1]

def range_2(x):
    return [x**3, x]

def range_1(x, y):
    return [0, 2*x]

volume, error = nquad(inte, [range_1, range_2, range_3])
volume
0.2

What is wrong with my nquad call?


Solution

  • I stared at this problem for a while trying to figure it out, but I finally got it. When you pass functions as the integration limits for tplquad, the function arguments are ordered x, y, but for nquad it's y, x. This can be seen when you look at the documentation.

    tplquad:

    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.

    nquad:

    ranges : iterable object
    ...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).

    from scipy.integrate import tplquad, nquad
    
    def integrand(z, y, x): 
        return 2*z
    
    res_tplquad, _ = tplquad(integrand, 
                             0, 1, 
                             lambda x: x**3, lambda x: x, 
                             0, lambda x, y: 2*x)
    print(res_tplquad)      # 0.33333333333333337
    
    def range_3():
        return [0, 1]
    
    def range_2(x):
        return [x**3, x]
    
    def range_1(y, x):
        return [0, 2*x]
    
    res_nquad, _ = nquad(integrand, [range_1, range_2, range_3])
    print(res_nquad)        # 0.33333333333333337