在numpy中计算L2内积?

时间:2015-10-20 04:36:49

标签: python arrays numpy inner-product

我正在思考L2内在产品。

我特别感兴趣的是使用numpy / scipy执行这些计算。我提出的最好的是执行基于数组的积分,例如numpy.trapz

import numpy as np
n=100000.
h=1./n
X = np.linspace(-np.pi,np.pi,n)

def L2_inner_product(f,g):
    return np.trapz(f*g,dx=2*np.pi*h)/np.pi

print L2_inner_product(np.sin(X), np.sin(X))
print L2_inner_product(np.cos(2*X), np.cos(2*X))
print L2_inner_product(np.sin(X), np.cos(X))
print L2_inner_product(np.sin(X), np.cos(3*X))
print L2_inner_product(np.ones(n),np.ones(n))

0.99999
0.99999
-3.86525742539e-18
1.6565388966e-18
1.99998

要清楚,我对使用Mathematica,Sage或Sympy不感兴趣。我特别感兴趣的是numpy / scipy,我正在探索numpy"阵列空间"作为希尔伯特空间的有限子空间。在这些参数中,让其他人实施L2内部产品,可能使用numpy.innernumpy.linalg.norm

1 个答案:

答案 0 :(得分:3)

关于速度,numpy.inner可能是固定n的最佳选择。 numpy.trapz应该更快融合。无论哪种方式,如果你担心速度,你还应该考虑到功能本身的评估还需要一些时间。

在一些简单的基准测试中,我使用不同的内部产品实现。

计时

下图显示了仅计算积分的运行时间,即不是函数评估。虽然numpy.trapz是一个较慢的常数因子,numpy.inner与直接调用BLAS一样快。正如Ophion指出的那样,numpy.inner内部调用BLAS可能会带来一些输入检查的开销。 Runtime for the computation of the sum of the products in the inner product.

查看评估函数本身所花费的时间也很有意思,当然要对计算内部产品进行评估。下方的图表显示了对标准超越函数numpy.sinnumpy.sqrtnumpy.exp的评估。对于评估而言,缩放当然是相同的,并且产品的总和和所需的总时间是相当的

Runtime for the function evaluation in the inner product.

错误

最后,还应该考虑不同方法的准确性,并且它在这里实际上变得有趣。下面是\langle exp(x),exp(x) \rangle计算的不同实现的收敛图。在这里,我们可以看到numpy.trapz实际比其他两种实现更好地扩展,在内存不足之前甚至达不到机器精度。

enter image description here

结论

考虑到numpy.inner的收敛性差,我会选择numpy.trapz。但即使这样,也需要很多集成节点才能获得令人满意的精度。由于您的集成域已修复,您甚至可能会尝试更高阶的序列。

代码

import numpy as np
import matplotlib.pyplot as plt
import seaborn as sls
from scipy.linalg.blas import ddot
import timeit

## Define inner product.
def l2_inner_blas( f, g, dx ):
    return ddot( f, g )*dx / np.pi

def l2_inner( f, g, dx ):
    return np.inner( f, g )*dx / np.pi

def l2_inner_trapz( f, g, dx ):
    return np.trapz(f*g,dx=dx) / np.pi

sin1 = lambda x: np.sin( x )
sin2 = lambda x: np.sin( 2.0 * x)

## Timing setups.
setup1 = "import numpy as np; from __main__ import l2_inner,"
setup1 += "l2_inner_trapz, l2_inner_blas, sin1, sin2;"
setup1 += "n=%d; x=np.linspace(-np.pi,np.pi,n); dx=2.0*np.pi/(n-1);"
setup1 += "f=sin1(x); g=sin2(x);"

def time( n ):
    setupstr = setup1 % n
    time1 = timeit.timeit( 'l2_inner( f, g, dx)', setupstr, number=10 )
    time2 = timeit.timeit( 'l2_inner_blas( f, g, dx)', setupstr, number=10 )
    time3 = timeit.timeit( 'l2_inner_trapz( f, g, dx)', setupstr, number=10 )
    return (time1, time2, time3)

setup2 = "import numpy as np; x = np.linspace(-np.pi,np.pi,%d);"
def time_eval( n ):
    setupstr = setup2 % n
    time_sin = timeit.timeit( 'np.sin(x)', setupstr, number=10 )
    time_sqrt = timeit.timeit( 'np.sqrt(x)', setupstr, number=10 )
    time_exp = timeit.timeit( 'np.exp(x)', setupstr, number=10 )
    return (time_sin, time_sqrt, time_exp)

## Perform timing for vector product.
times = np.zeros( (7,3) )
for i in range(7):
    times[i,:] = time( 10**(i+1) )

x = 10**np.arange(1,8,1)
f, ax = plt.subplots()
ax.set( xscale='log', yscale='log', title='Inner vs. BLAS vs. trapz', \
        ylabel='time [s]', xlabel='n')
ax.plot( x, times[:,0], label='numpy.inner' )
ax.plot( x, times[:,1], label='scipy.linalg.blas.ddot')
ax.plot( x, times[:,2], label='numpy.trapz')
plt.legend()

## Perform timing for function evaluation.
times_eval = np.zeros( (7,3) )
for i in range(7):
    times_eval[i,:] = time_eval( 10**(i+1) )

x = 10**np.arange(1,8,1)
f, ax = plt.subplots()
ax.set( xscale='log', yscale='log', title='sin vs. sqrt vs. exp', \
        ylabel='time [s]', xlabel='n')
ax.plot( x, times_eval[:,0], label='numpy.sin' )
ax.plot( x, times_eval[:,1], label='numpy.sqrt')
ax.plot( x, times_eval[:,2], label='numpy.exp' )
plt.legend()

## Test convergence.
def error( n ):
    x = np.linspace( -np.pi, np.pi, n )
    dx = 2.0 * np.pi / (n-1)
    f = np.exp( x )
    l2 = 0.5/np.pi*(np.exp(2.0*np.pi) - np.exp(-2.0*np.pi))
    err1 = np.abs( (l2 - l2_inner( f, f, dx )) / l2)
    err2 = np.abs( (l2 - l2_inner_blas( f, f, dx )) / l2)
    err3 = np.abs( (l2 - l2_inner_trapz( f, f, dx )) / l2)
    return (err1, err2, err3)

acc = np.zeros( (7,3) )
for i in range(7):
    acc[i,:] = error( 10**(i+1) )

x = 10**np.arange(1,8,1)
f, ax = plt.subplots()
ax.plot( x, acc[:,0], label='numpy.inner' )
ax.plot( x, acc[:,1], label='scipy.linalg.blas.ddot')
ax.plot( x, acc[:,2], label='numpy.trapz')
ax.set( xscale='log', yscale='log', title=r'$\langle \exp(x), \exp(x) \rangle$', \
        ylabel='Relative Error', xlabel='n')
plt.legend()