Используя SciPy для интеграции функции, возвращающей матрицу или массив - программирование
Подтвердить что ты не робот

Используя SciPy для интеграции функции, возвращающей матрицу или массив

У меня есть символический массив, который может быть выражен как:

from sympy import lambdify, Matrix

g_sympy = Matrix([[   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]])

g = lambdify( (x), g_sympy )

Итак, для каждого x получается другая матрица:

g(1.) # matrix([[  1.,   2.,   3.,   4.,   5.,   6.,   7.,   8.,   9.,  10.],
      #         [  1.,   1.,   1.,   1.,   1.,   1.,   1.,   1.,   1.,   1.]])
g(2.) # matrix([[  2.00e+00,   4.00e+00,   6.00e+00,   8.00e+00,   1.00e+01, 1.20e+01,   1.40e+01,   1.60e+01,   1.80e+01,   2.00e+01],
      #         [  4.00e+00,   8.00e+00,   1.60e+01,   3.20e+01,   6.40e+01, 1.28e+02,   2.56e+02,   5.12e+02,   1.02e+03,   2.05e+03]])

и т.д.



Мне нужно численно интегрировать g поверх x, скажем from 0. to 100. (в реальном случае интеграл не имеет точного решения), и в моем текущем подходе я должен lambdify каждый элемент в g и интегрировать это индивидуально. Я использую quad для элементарного интегрирования, например:

ans = np.zeros( g_sympy.shape )
for (i,j), func_sympy in ndenumerate(g_sympy):
    func = lambdify( (x), func_sympy)
    ans[i,j] = quad( func, 0., 100. )

Здесь есть две проблемы: 1) lambdify используется много раз и 2) для цикла; и я считаю, что первое из них является узким местом, потому что матрица g_sympy имеет не более 10000 терминов (что не очень важно для цикла for).

Как показано выше, lambdify позволяет оценить всю матрицу, поэтому я подумал: "Есть ли способ интегрировать всю матрицу?"

scipy.integrate.quadrature имеет параметр vec_func, который дал мне надежду. Я ожидал чего-то вроде:

g_int = quadrature( g, x1, x2 )

чтобы получить полностью интегрированную матрицу, но при этом матрица ValueError: должна быть двумерной


EDIT: Я пытаюсь сделать по-видимому, в Matlab, используя quadv и уже обсуждался для SciPy

Здесь представлен реальный случай .

Чтобы запустить его, вам понадобится:

  • NumPy
  • SciPy
  • Matplotlib
  • SymPy

Просто запустите: "python curved_beam_mrs.py".

Вы увидите, что процедура выполняется медленно, главным образом из-за интеграции, указанной TODO в файле curved_beam.py.

Это будет намного медленнее, если вы удалите комментарий, указанный после TODO в файле curved_beam_mrs.py.

Матрица интегрируемых функций показана в файле print.txt.

Спасибо!

4b9b3361

Ответ 1

Первый аргумент либо quad, либо quadrature должен быть вызываемым. Аргумент vec_func quadrature относится к тому, является ли аргумент этого вызываемого является (возможно, многомерным) вектором. Технически вы можете vectorize самой quad:

>>> from math import sin, cos, pi
>>> from scipy.integrate import quad
>>> from numpy import vectorize
>>> a = [sin, cos]
>>> vectorize(quad)(a, 0, pi)
(array([  2.00000000e+00,   4.92255263e-17]), array([  2.22044605e-14,   2.21022394e-14]))

Но это просто эквивалентно явному циклу над элементами a. В частности, это не даст вам каких-либо выигрышей в производительности, если это вам нужно. Итак, в целом, вопрос в том, почему и что именно вы пытаетесь достичь здесь.

Ответ 2

В реальном случае интеграл не имеет точного решения, вы имеете в виду особенности? Не могли бы вы быть более точными на нем, а также на размер матрицы, которую вы хотите интегрировать. Я должен признать, что sympy ужасно медленна, когда дело доходит до некоторых вещей (не уверен, что интеграция является частью этого, но я предпочитаю держаться подальше от sympy и придерживаться решения numpy). Вы хотите получить более элегантное решение, выполнив его с помощью матрицы или более быстрой?

-note: видимо я не могу добавить комментарий к вашему сообщению, чтобы спросить об этом, поэтому мне пришлось опубликовать это как ответ, может быть, это потому, что у меня нет достаточной репутации или так? -

edit: что-то вроде этого?

    import numpy
    from scipy.integrate import trapz
    g=lambda x: numpy.array([[x,2*x,3*x],[x**2,x**3,x**4]])
    xv=numpy.linspace(0,100,200)
    print trapz(g(xv))

заметив, что вы хотите интегрировать такие вещи, как sum (a * sin (bx + c) ^ n * cos (dx + e) ​​^ m), для разных коэффициентов для a, b, c, d, e, m, n, я предлагаю сделать все это аналитически. (для этого должна быть какая-то формула, поскольку вы можете просто переписать грех на сложные экспоненты

Еще одна вещь, которую я заметил при проверке этих функций немного лучше, заключается в том, что sin (a * x + pi/2) и sin (a * x + pi) и тому подобное могут быть переписаны в cos или sin способом который удаляет pi/2 или pi. Также я вижу, что просто посмотрев на первый элемент в вашей матрице функций:

a*sin(bx+c)^2+d*cos(bx+c)^2 = a*(sin^2+cos^2)+(d-a)*cos(bx+c)^2 = a+(d-a)*cos(bx+c)^2 

что также упрощает вычисления. Если у вас были формулы таким образом, который не включал массивный txtfile или так, удостоверьтесь, что самая общая формула вам нужна для интеграции, но я думаю, что это что-то вроде * sin ^ n (bx + c) * cos ^ m (dx + e), причем m и n равны 0 1 или 2, и эти вещи могут быть упрощены во что-то, что может быть аналитически интегрировано. Поэтому, если вы обнаружите самую общую аналитическую функцию, которую вы получили, вы можете легко сделать что-то вроде

f=lambda x: [[s1(x),s2(x)],[s3(x),s4(x)]]
res=f(x2)-f(x1)

где s1 (x) и т.д. - это просто аналитически интегрированные версии ваших функций?

(на самом деле не планируете перебирать весь ваш код, чтобы увидеть, что делает все остальное, но просто ли он интегрирует эти функции в txt файл от a до b или что-то в этом роде? или есть где-то что-то подобное квадрат каждой функции или что-то еще, что может испортить возможность сделать это аналитически?)

это должно упростить ваши интегралы, я думаю?

первый интеграл и: второй

hmm, эта вторая ссылка не работает, но вы получаете идею от первого, я думаю,

так как вам не нужны аналитические решения: улучшение остается в избавлении от sympy:

from sympy import sin as SIN
from numpy import sin as SIN2
from scipy.integrate import trapz
import time
import numpy as np

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 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


A=np.linspace(0,100.,200)

B=lambda x: SIN(x)
C=lambda x: SIN2(x)

t0=time.time()
D=simpson2(B,0,100.,200)
print time.time()-t0
t1=time.time()
E=trapz(C(A))
print time.time()-t1

    t2=time.time()
    F=simpson2(C,0,100.,200)
    print time.time()-t2

приводит к:

0.000764131546021 sec for the faster method, but when using sympy

7.58171081543e-05 sec for my slower method, but which uses numpy

0.000519037246704 sec for the faster method, when using numpy, 

вывод: используйте numpy, ditch sympy (мой медленный метод numpy на самом деле быстрее в этом случае, потому что в этом примере я только пробовал его на одной функции sin, а не на ndarray из них, но точка канавки sympy по-прежнему остается при сравнении времени версии numpy более быстрого метода с одной из версий Sympy более быстрого метода)

Ответ 3

Векторизация трапециевидных и симпсонных интегральных правил. Trapezoidal просто копирует и вставляет из другого проекта, который использует пространство logspace вместо linspace, чтобы он мог использовать неравномерные сетки.

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)))

Изучение ловушкильных и симпсонов управляет кумулятивными относительными ошибками:

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. Обратите внимание, что оба трапециевидных правила используют 200 точек, в то время как симпсоны приурочены только к 100 на основе вышеуказанной конвергенции. Извините, у меня нет симпы:

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.'

Результаты:

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.

Что-то, что нужно указать в тайминги, - это ответ жены должен быть гораздо точнее. Я считаю, что все правильно, пожалуйста, сообщите мне, нужны ли изменения.

Если вы предоставляете функции и диапазон, которые вы будете использовать, я, вероятно, могу взломать что-то лучше для вашей системы. Кроме того, вы были бы заинтересованы в использовании дополнительных ядер/узлов?

Ответ 4

Я мог бы найти интересный способ сделать это за счет определения разных символов для матрицы g_symp:

import numpy as np
from scipy.integrate import quad
import sympy as sy

@np.vectorize
def vec_lambdify(var, expr, *args, **kw):
    return sy.lambdify(var, expr, *args, **kw)

@np.vectorize
def vec_quad(f, a, b, *args, **kw):
    return quad(f, a, b, *args, **kw)[0]

Y = sy.symbols("y1:11")
x = sy.symbols("x")
mul_x = [y.subs(y,x*(i+1)) for (i,y) in enumerate(Y)]
pow_x = [y.subs(y,x**(i+1)) for (i,y) in enumerate(Y)]

g_sympy = np.array(mul_x + pow_x).reshape((2,10))
X = x*np.ones_like(g_sympy)
G = vec_lambdify(X, g_sympy)
I = vec_quad(G, 0, 100)
print(I)

с результатами:

[[  5.00000000e+03   1.00000000e+04   1.50000000e+04   2.00000000e+04
    2.50000000e+04   3.00000000e+04   3.50000000e+04   4.00000000e+04
    4.50000000e+04   5.00000000e+04]
[  5.00000000e+03   3.33333333e+05   2.50000000e+07   2.00000000e+09
   1.66666667e+11   1.42857143e+13   1.25000000e+15   1.11111111e+17
   1.00000000e+19   9.09090909e+20]]

и используя магию ipython %timeit vec_quad(G,0,100) Я получил

1000 loops, best of 3: 527 µs per loop

Я думаю, что этот подход несколько более чист, несмотря на жонглирование символами.

Ответ 5

quadpy (мой проект) делает векторизованную квадратуру. Это

import numpy
import quadpy


def f(x):
    return [
        [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]
        ]


sol = quadpy.line_segment.integrate(
        f,
        numpy.array([[0.0], [100.0]]),
        quadpy.line_segment.GaussLegendre(6)
        )

print(sol)

дает

[[[  5.00000000e+03]
[  1.00000000e+04]
[  1.50000000e+04]
[  2.00000000e+04]
[  2.50000000e+04]
[  3.00000000e+04]
[  3.50000000e+04]
[  4.00000000e+04]
[  4.50000000e+04]
[  5.00000000e+04]]

[[  3.33333333e+05]
[  2.50000000e+07]
[  2.00000000e+09]
[  1.66666667e+11]
[  1.42857143e+13]
[  1.25000000e+15]
[  1.11111111e+17]
[  1.00000000e+19]
[  9.09090909e+20]
[  8.33333333e+22]]]

Тайминги:

%timeit quadpy.line_segment.integrate(f, [0.0, 100.0], gl)
10000 loops, best of 3: 65.1 µs per loop