在Python中从对角向量创建密集矩阵的有效方法?

在Python中从对角向量创建密集矩阵的有效方法?,python,numpy,Python,Numpy,我尝试使用numpy向量在Python中创建此矩阵: 其中值来自函数。我反复使用numpy.diag实现了它,但是对于大尺寸,它变得非常慢。代码如下: def makeS(N): vec = np.full(N, 2*v(x_range[1])) vec[0]*=0.5 S = np.diag(vec) vec = np.full(N-1, v(x_range[0])) S+= np.diag(vec, 1) for m in xra

我尝试使用numpy向量在Python中创建此矩阵:

其中值来自函数。我反复使用numpy.diag实现了它,但是对于大尺寸,它变得非常慢。代码如下:

def makeS(N):
    vec = np.full(N, 2*v(x_range[1]))
    vec[0]*=0.5 
    S = np.diag(vec)

    vec = np.full(N-1, v(x_range[0]))   
    S+= np.diag(vec, 1)

    for m in xrange(1, N):
        vec =  np.full(N-m, 2*v(x_range[m+1]))
        vec[0]*= 0.5
        S += np.diag(vec, -m)
    return S
其中v()是所述函数,x_range是x值的向量。有没有办法让这更有效

编辑: 以下是一个完整的示例:

import numpy as np
import math 

N = 5
x_range = np.linspace(0, 1, N+1)


def v(x):
    return math.exp(x)

def makeS(N):
    vec = np.full(N, 2*v(x_range[1]))
    vec[0]*=0.5 
    S = np.diag(vec)

    vec = np.full(N-1, v(x_range[0]))   
    S+= np.diag(vec, 1)

    for m in xrange(1, N):
        vec =  np.full(N-m, 2*v(x_range[m+1]))
        vec[0]*= 0.5
        S += np.diag(vec, -m)
    return S


print makeS(N)
哪个输出

[[ 1.22140276  1.          0.          0.          0.        ]
 [ 1.4918247   2.44280552  1.          0.          0.        ]
 [ 1.8221188   2.9836494   2.44280552  1.          0.        ]
 [ 2.22554093  3.6442376   2.9836494   2.44280552  1.        ]
 [ 2.71828183  4.45108186  3.6442376   2.9836494   2.44280552]]

这是我能找到的最快的方法:

def makeS(N):
    values = np.array([v(x) for x in x_range])
    values_doubled = 2 * values
    result = np.eye(N, k=1) * values[0]
    result[:, 0] = values[1:]
    for i in xrange(N - 1):
        result[i + 1, 1:i + 2] = values_doubled[1:i + 2][::-1]
    return result
使用
N=2000
时,我的机器上的原始版本需要26.97秒,而新版本需要0.02339秒

下面是使用一些附加方法评估计时的完整脚本

import numpy as np
import math
import timeit


def v(x):
    return math.exp(x)


def makeS1(N, x_range):
    vec = np.full(N, 2 * v(x_range[1]))
    vec[0] *= 0.5
    S = np.diag(vec)
    vec = np.full(N - 1, v(x_range[0]))
    S += np.diag(vec, 1)
    for m in xrange(1, N):
        vec = np.full(N - m, 2 * v(x_range[m + 1]))
        vec[0] *= 0.5
        S += np.diag(vec, -m)
    return S


def makeS2(N, x_range):
    values = np.array([v(x) for x in x_range])
    values_doubled = 2 * values

    def value_at_position(ai, aj):
        result = np.zeros((N, N))
        for i, j in zip(ai.flatten(), aj.flatten()):
            if j > i + 1:
                continue
            elif j == i + 1:
                result[i, j] = values[0]
            elif j == 0:
                result[i, j] = values[i + 1]
            else:
                result[i, j] = values_doubled[i - j + 1]
        return result

    return np.fromfunction(value_at_position, (N, N))


def makeS3(N, x_range):
    values = np.array([v(x) for x in x_range])
    values_doubled = 2 * values
    result = np.zeros((N, N))
    for i in xrange(N):
        for j in xrange(min(i + 2, N)):
            if j == i + 1:
                result[i, j] = values[0]
            elif j == 0:
                result[i, j] = values[i + 1]
            else:
                result[i, j] = values_doubled[i - j + 1]
    return result


def makeS4(N, x_range):
    values = np.array([v(x) for x in x_range])
    values_doubled = 2 * values
    result = np.eye(N, k=1) * values[0]
    result[:, 0] = values[1:]
    for i in xrange(N - 1):
        result[i + 1, 1:i + 2] = values_doubled[1:i + 2][::-1]
    return result


def main():
    N = 2000
    x_range = np.random.randn(N + 1)

    start = timeit.default_timer()
    s1 = makeS1(N, x_range)
    print 'makeS1', timeit.default_timer() - start
    start = timeit.default_timer()
    s2 = makeS2(N, x_range)
    print 'makeS2', timeit.default_timer() - start
    start = timeit.default_timer()
    s3 = makeS3(N, x_range)
    print 'makeS3', timeit.default_timer() - start
    start = timeit.default_timer()
    s4 = makeS4(N, x_range)
    print 'makeS4', timeit.default_timer() - start
    if N < 10:
        print s1
        print s2
        print s2
        print s4
    assert np.allclose(s1, s2)
    assert np.allclose(s2, s3)
    assert np.allclose(s3, s4)


main()

由于我试图使用此代码再现所需的输出失败,您能否提供一个带有示例输入数据的示例调用。@DanielRenshaw添加了一个示例
scipy。sparse
有一种格式可用于创建具有多条对角线的稀疏矩阵。但是你的矩阵看起来相当密集。另一种可能的工具-上/下三角函数之一,
np.tril/u
等。
makeS1 26.9707232448
makeS2 11.7728229076
makeS3 0.643742975052
makeS4 0.0233912765665