using SciPy to integrate a function that returns a matrix or array

后端 未结 5 1551
野趣味
野趣味 2020-12-16 15:07

I have a symbolic array that can be expressed as:

from sympy import lambdify, Matrix

g_sympy = Matrix([[   x,  2*x,  3*x,  4*x,  5*x,  6*x,  7*x,  8*x,   9*         


        
5条回答
  •  慢半拍i
    慢半拍i (楼主)
    2020-12-16 15:56

    Vectorizing trapezoidal and simpson's integral rules. Trapezoidal is just copy and pasted from another project that used logspace instead of linspace so that it can utilize non-uniform grids.

    def trap(func,a,b,num):
        xlinear=np.linspace(a,b,num)
        slicevol=np.diff(xlinear)
        output=integrand(xlinear)
        output=output[:,:-1]+output[:,1:]
        return np.dot(output,slicevol)/2
    
    def simpson(func,a,b,num):
        a=float(a)
        b=float(b)
        h=(b-a)/num
    
        output=4*np.sum(integrand(a+h*np.arange(1,num,2)),axis=1)
        output+=2*np.sum(integrand(a+h*np.arange(2,num-1,2)),axis=1)
        output+=np.sum(integrand(b),axis=1)
        output+=np.sum(integrand(a),axis=1)
        return output*h/3
    
    def integrand(rlin):
        first=np.arange(1,11)[:,None]
        second=np.arange(2,12)[:,None]
        return np.vstack((rlin*first,np.power(rlin,second)))
    

    Examine trapazoidal and simpsons rule cumulative relative errors:

    b=float(100)
    first=np.arange(1,11)*(b**2)/2
    second=np.power(b,np.arange(3,13))/np.arange(3,13)
    exact=np.vstack((first,second))
    
    for x in range(3):
        num=x*100+100
        tr=trap(integrand,0,b,num).reshape(2,-1)
        si=simpson(integrand,0,b,num).reshape(2,-1)
        rel_trap=np.sum(abs((tr-exact)/exact))*100
        rel_simp=np.sum(abs((si-exact)/exact))*100
        print 'Number of points:',num,'Trap Rel',round(rel_trap,6),'Simp Rel',round(rel_simp,6)
    
    Number of points: 100 Trap Rel 0.4846   Simp Rel 0.000171
    Number of points: 200 Trap Rel 0.119944 Simp Rel 1.1e-05
    Number of points: 300 Trap Rel 0.053131 Simp Rel 2e-06
    

    Timeit. Note that both trapezoidal rules use 200 points while simpsons is timed at only 100 based on the above convergence. Sorry I dont have sympy:

    s="""
    import numpy as np
    from scipy.integrate import trapz
    
    def integrand(rlin):
        first=np.arange(1,11)[:,None]
        second=np.arange(2,12)[:,None]
        return np.vstack((rlin*first,np.power(rlin,second)))
    
    def trap(func,a,b,num):
        xlinear=np.linspace(a,b,num)
        slicevol=np.diff(xlinear)
        output=integrand(xlinear)
        output=output[:,:-1]+output[:,1:]
        return np.dot(output,slicevol)/2
    
    def simpson(func,a,b,num):
        a=float(a)
        b=float(b)
        h=(b-a)/num
    
        output=4*np.sum(integrand(a+h*np.arange(1,num,2)),axis=1)
        output+=2*np.sum(integrand(a+h*np.arange(2,num-1,2)),axis=1)
        output+=np.sum(integrand(b),axis=1)
        output+=np.sum(integrand(a),axis=1)
        return output*h/3
    
    def simpson2(func,a,b,num):
        a=float(a)
        b=float(b)
        h=(b-a)/num
        p1=a+h*np.arange(1,num,2)
        p2=a+h*np.arange(2,num-1,2)
        points=np.hstack((p1,p2,a,b))
        mult=np.hstack((np.repeat(4,p1.shape[0]),np.repeat(2,p2.shape[0]),1,1))
        return np.dot(integrand(points),mult)*h/3
    
    def x2(x):
        return x**2
    def x3(x):
        return x**3
    def x4(x):
        return x**4
    def x5(x):
        return x**5
    def x5(x):
        return x**5
    def x6(x):
        return x**6
    def x7(x):
        return x**7
    def x8(x):
        return x**8
    def x9(x):
        return x**9
    def x10(x):
        return x**10
    def x11(x):
        return x**11
    def xt5(x):
        return 5*x
    """
    
    zhenya="""
    a=[xt5,xt5,xt5,xt5,xt5,xt5,xt5,xt5,xt5,xt5,x2,x3,x4,x5,x6,x7,x8,x9,x10,x11]
    vectorize(quad)(a, 0, 100)
    """
    
    usethedeathstar="""
    g=lambda x: np.array([[x,2*x,3*x,4*x,5*x,6*x,7*x,8*x,9*x,10*x],[x**2,x**3,x**4,x**5,x**6,x**7,x**8,x**9,x**10,x**11]])
    xv=np.linspace(0,100,200)
    trapz(g(xv))
    """
    
    vectrap="""
    trap(integrand,0,100,200)
    """
    
    vecsimp="""
    simpson(integrand,0,100,100)
    """
    
    vecsimp2="""
    simpson2(integrand,0,100,100)
    """
    
    print 'zhenya took',timeit.timeit(zhenya,setup=s,number=100),'seconds.'
    print 'usethedeathstar took',timeit.timeit(usethedeathstar,setup=s,number=100),'seconds.'
    print 'vectrap took',timeit.timeit(vectrap,setup=s,number=100),'seconds.'
    print 'vecsimp took',timeit.timeit(vecsimp,setup=s,number=100),'seconds.'
    print 'vecsimp2 took',timeit.timeit(vecsimp2,setup=s,number=100),'seconds.'
    

    Results:

    zhenya took 0.0500509738922 seconds.
    usethedeathstar took 0.109386920929 seconds.
    vectrap took 0.041011095047 seconds.
    vecsimp took 0.0376999378204 seconds.
    vecsimp2 took 0.0311458110809 seconds.
    

    Something to point out in the timings is zhenya's answer should be much more accurate. I believe everything is correct, please let me know if changes are required.

    If you provide the functions and range that you will be using I can probably whip up something better for your system. Also would you be interested in utilizing additional cores/nodes?

提交回复
热议问题